mirror of
https://opendev.org/openstack/ansible-collections-openstack.git
synced 2026-05-06 13:23:06 +00:00
Compare commits
219 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3b12fed68 | ||
|
|
39a627d4a0 | ||
|
|
4eb7c43539 | ||
|
|
8c6d1041fa | ||
|
|
26530ac97b | ||
|
|
b3e07a1864 | ||
|
|
94933250e8 | ||
|
|
1582956dcd | ||
|
|
b1952e0c4b | ||
|
|
9cd6d2f69a | ||
|
|
86a57498e8 | ||
|
|
15f73aa72e | ||
|
|
22a7f516f3 | ||
|
|
de54be5ecd | ||
|
|
3f1a693bd6 | ||
|
|
8e87ad651f | ||
|
|
b2f3cf3210 | ||
|
|
11c7cd23f8 | ||
|
|
59d0e4c3a4 | ||
|
|
014665ddac | ||
|
|
21b70f6b9b | ||
|
|
ecaff2a798 | ||
|
|
02e9e87964 | ||
|
|
980536c32e | ||
|
|
a9a0d23441 | ||
|
|
011515de2d | ||
|
|
4292a00f75 | ||
|
|
bbefa8c156 | ||
|
|
b023aa337a | ||
|
|
c13f02fd54 | ||
|
|
ae4e7f3c06 | ||
|
|
2d554d1e22 | ||
|
|
770b283593 | ||
|
|
42921c6d9f | ||
|
|
292aabb477 | ||
|
|
3a08a9c07c | ||
|
|
0e370b2c51 | ||
|
|
0441403c42 | ||
|
|
4160888887 | ||
|
|
07374a1f0d | ||
|
|
8a395a04cf | ||
|
|
441a61fd8c | ||
|
|
c05b1fdbaf | ||
|
|
9cd92208d6 | ||
|
|
5ef192f1f2 | ||
|
|
aed60716ee | ||
|
|
938b90ea19 | ||
|
|
ce853a8f9f | ||
|
|
9911c7f93a | ||
|
|
8c890e656b | ||
|
|
b839f9e25d | ||
|
|
b7765776a7 | ||
|
|
4490e008c4 | ||
|
|
f376d4679e | ||
|
|
12ec8c63c2 | ||
|
|
72f371c157 | ||
|
|
ce421fe370 | ||
|
|
f20ec3a151 | ||
|
|
a89ec027b0 | ||
|
|
322f96c4db | ||
|
|
50b55560fc | ||
|
|
0c0013504e | ||
|
|
e651838f3e | ||
|
|
4553282545 | ||
|
|
235628dace | ||
|
|
4ee8621b37 | ||
|
|
d07039a7ec | ||
|
|
54728a83fa | ||
|
|
8431232eeb | ||
|
|
b21b8b1084 | ||
|
|
e2ffed388e | ||
|
|
ff44a1d1e5 | ||
|
|
9f893824c5 | ||
|
|
b9df964149 | ||
|
|
0c8fe9002e | ||
|
|
e010db6273 | ||
|
|
88277a59af | ||
|
|
1a4d0128c7 | ||
|
|
03a86669de | ||
|
|
fa7526a0ec | ||
|
|
7446ab23b8 | ||
|
|
4171ee52b7 | ||
|
|
60f1b8e2a5 | ||
|
|
0bff3f286f | ||
|
|
5188e281b9 | ||
|
|
966b6597f3 | ||
|
|
5636d502ce | ||
|
|
4db925b452 | ||
|
|
3ce4455bc8 | ||
|
|
309c3025ba | ||
|
|
637d321a9a | ||
|
|
74c18f34f3 | ||
|
|
00be40a462 | ||
|
|
1eb5d85b68 | ||
|
|
d0a4d6e8cc | ||
|
|
29c03592cc | ||
|
|
7888b51053 | ||
|
|
0e4dc21bab | ||
|
|
623b4afbd1 | ||
|
|
80e6597f96 | ||
|
|
8f1512166e | ||
|
|
eabe945194 | ||
|
|
06115c77b7 | ||
|
|
4a1b092efb | ||
|
|
da1fab6629 | ||
|
|
0efb855c68 | ||
|
|
329a5ef50d | ||
|
|
47708e0172 | ||
|
|
17d5c7de8e | ||
|
|
82792ab5d5 | ||
|
|
d4033b4cea | ||
|
|
426b6e782a | ||
|
|
75b2c60960 | ||
|
|
c9527889a0 | ||
|
|
42a5cb5601 | ||
|
|
aa387aab3a | ||
|
|
af79857bfb | ||
|
|
a51d922bee | ||
|
|
0cbaeb6fbb | ||
|
|
532857c0b2 | ||
|
|
acd9bc993b | ||
|
|
0904ddd2fe | ||
|
|
2120814356 | ||
|
|
ad70a20af5 | ||
|
|
a32cff23c7 | ||
|
|
f29a8407af | ||
|
|
555178ecc6 | ||
|
|
f93172677c | ||
|
|
2a461cadd9 | ||
|
|
e3c61aeefd | ||
|
|
3e4413ec31 | ||
|
|
37afe5f2e6 | ||
|
|
7e190d12b4 | ||
|
|
3d9f9a0f87 | ||
|
|
57e1087177 | ||
|
|
6cb6e70645 | ||
|
|
662ac61640 | ||
|
|
891fb6df53 | ||
|
|
0c6752215b | ||
|
|
a6f1177584 | ||
|
|
2dc2806c57 | ||
|
|
59df183660 | ||
|
|
2c287d754c | ||
|
|
28d32b53e8 | ||
|
|
2cb67620aa | ||
|
|
5217b6bdc4 | ||
|
|
96ea60a883 | ||
|
|
0eef3cb978 | ||
|
|
e695000daa | ||
|
|
0258878400 | ||
|
|
ea7ba854d5 | ||
|
|
6b3bf3bba0 | ||
|
|
daaa6bf561 | ||
|
|
2027eb1f46 | ||
|
|
944f9ca498 | ||
|
|
ca6824bffe | ||
|
|
a10be98638 | ||
|
|
6787924f13 | ||
|
|
d1d6b4d2dd | ||
|
|
06e76a1687 | ||
|
|
464d7767bb | ||
|
|
718f3a6b3a | ||
|
|
3320c662c8 | ||
|
|
77acba047b | ||
|
|
890401b605 | ||
|
|
8da4e852f0 | ||
|
|
f3e9c78c18 | ||
|
|
47382d52ed | ||
|
|
0e2058988e | ||
|
|
ab1a2a19d7 | ||
|
|
17a1d72b1c | ||
|
|
58ec1030a4 | ||
|
|
da01746e53 | ||
|
|
c329f65b41 | ||
|
|
18b03e1971 | ||
|
|
16a81fc221 | ||
|
|
50deae71f4 | ||
|
|
b1e5ca91cf | ||
|
|
185d0a5eca | ||
|
|
2b8aa2cb08 | ||
|
|
3c8fbc6b27 | ||
|
|
d212be018b | ||
|
|
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 |
357
.zuul.yaml
357
.zuul.yaml
@@ -9,6 +9,17 @@
|
|||||||
required-projects:
|
required-projects:
|
||||||
- openstack/ansible-collections-openstack
|
- openstack/ansible-collections-openstack
|
||||||
- openstack/designate
|
- openstack/designate
|
||||||
|
irrelevant-files: &ignore_files
|
||||||
|
- changelogs/.*
|
||||||
|
- galaxy.*
|
||||||
|
- COPYING
|
||||||
|
- docs/.*
|
||||||
|
- .*\.md
|
||||||
|
- .*\.rst
|
||||||
|
- tools/run-ansible-sanity.sh
|
||||||
|
- tests/sanity/.*
|
||||||
|
- contrib/.*
|
||||||
|
- .zuul.yaml
|
||||||
vars:
|
vars:
|
||||||
zuul_work_dir: src/opendev.org/openstack/ansible-collections-openstack
|
zuul_work_dir: src/opendev.org/openstack/ansible-collections-openstack
|
||||||
tox_envlist: ansible
|
tox_envlist: ansible
|
||||||
@@ -19,6 +30,43 @@
|
|||||||
devstack_services:
|
devstack_services:
|
||||||
designate: true
|
designate: true
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: ansible-collections-openstack-functional-devstack-octavia
|
||||||
|
parent: ansible-collections-openstack-functional-devstack
|
||||||
|
description: |
|
||||||
|
Run openstack collections functional tests against a master devstack
|
||||||
|
with Octavia plugin enabled, using releases of openstacksdk and latest
|
||||||
|
ansible release. Run it only on Load Balancer changes.
|
||||||
|
pre-run: ci/playbooks/get_amphora_tarball.yaml
|
||||||
|
required-projects:
|
||||||
|
- openstack/octavia
|
||||||
|
- name: github.com/ansible/ansible
|
||||||
|
override-checkout: stable-2.12
|
||||||
|
files:
|
||||||
|
- ^ci/roles/loadbalancer/.*$
|
||||||
|
- ^plugins/modules/lb_health_monitor.py
|
||||||
|
- ^plugins/modules/lb_listener.py
|
||||||
|
- ^plugins/modules/lb_member.py
|
||||||
|
- ^plugins/modules/lb_pool.py
|
||||||
|
- ^plugins/modules/loadbalancer.py
|
||||||
|
vars:
|
||||||
|
configure_swap_size: 8192
|
||||||
|
tox_envlist: ansible
|
||||||
|
devstack_plugins:
|
||||||
|
designate: https://opendev.org/openstack/designate
|
||||||
|
octavia: https://opendev.org/openstack/octavia
|
||||||
|
devstack_services:
|
||||||
|
designate: true
|
||||||
|
octavia: true
|
||||||
|
o-api: true
|
||||||
|
o-cw: true
|
||||||
|
o-hk: true
|
||||||
|
o-hm: true
|
||||||
|
devstack_localrc:
|
||||||
|
OCTAVIA_AMP_IMAGE_FILE: "/tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
|
||||||
|
OCTAVIA_AMP_IMAGE_SIZE: 3
|
||||||
|
OCTAVIA_AMP_IMAGE_NAME: "test-only-amphora-x64-haproxy-ubuntu-bionic"
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: ansible-collections-openstack-functional-devstack-releases
|
name: ansible-collections-openstack-functional-devstack-releases
|
||||||
parent: ansible-collections-openstack-functional-devstack
|
parent: ansible-collections-openstack-functional-devstack
|
||||||
@@ -43,14 +91,26 @@
|
|||||||
tox_envlist: ansible-2.9
|
tox_envlist: ansible-2.9
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: ansible-collections-openstack-functional-devstack-ansible-2.10
|
name: ansible-collections-openstack-functional-devstack-ansible-2.11
|
||||||
parent: ansible-collections-openstack-functional-devstack
|
parent: ansible-collections-openstack-functional-devstack
|
||||||
description: |
|
description: |
|
||||||
Run openstack collections functional tests against a master devstack
|
Run openstack collections functional tests against a master devstack
|
||||||
using master of openstacksdk and stable 2.10 branch of ansible
|
using master of openstacksdk and stable 2.12 branch of ansible
|
||||||
required-projects:
|
required-projects:
|
||||||
- name: github.com/ansible/ansible
|
- name: github.com/ansible/ansible
|
||||||
override-checkout: stable-2.10
|
override-checkout: stable-2.11
|
||||||
|
vars:
|
||||||
|
tox_envlist: ansible
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: ansible-collections-openstack-functional-devstack-ansible-2.12
|
||||||
|
parent: ansible-collections-openstack-functional-devstack
|
||||||
|
description: |
|
||||||
|
Run openstack collections functional tests against a master devstack
|
||||||
|
using master of openstacksdk and stable 2.12 branch of ansible
|
||||||
|
required-projects:
|
||||||
|
- name: github.com/ansible/ansible
|
||||||
|
override-checkout: stable-2.12
|
||||||
vars:
|
vars:
|
||||||
tox_envlist: ansible
|
tox_envlist: ansible
|
||||||
|
|
||||||
@@ -65,31 +125,53 @@
|
|||||||
- name: github.com/ansible/ansible
|
- name: github.com/ansible/ansible
|
||||||
override-checkout: devel
|
override-checkout: devel
|
||||||
vars:
|
vars:
|
||||||
tox_envlist: ansible-2.11
|
tox_envlist: ansible-2.12
|
||||||
|
|
||||||
# Pip installation job
|
|
||||||
- job:
|
|
||||||
name: ansible-collections-openstack-functional-devstack-ansible-pip
|
|
||||||
parent: ansible-collections-openstack-functional-devstack
|
|
||||||
description: |
|
|
||||||
Run openstack collections functional tests against a master devstack
|
|
||||||
using master of openstacksdk with latest ansible release.
|
|
||||||
Ansible collection is built using PIP.
|
|
||||||
vars:
|
|
||||||
tox_envlist: ansible-pip
|
|
||||||
|
|
||||||
# Stable branches tests
|
# Stable branches tests
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.10
|
name: ansible-collections-openstack-functional-devstack-xena-ansible-2.12
|
||||||
|
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||||
|
description: |
|
||||||
|
Run openstack collections functional tests against a xena devstack
|
||||||
|
using xena brach of openstacksdk and stable 2.12 branch of ansible
|
||||||
|
voting: true
|
||||||
|
override-checkout: stable/xena
|
||||||
|
required-projects:
|
||||||
|
- name: github.com/ansible/ansible
|
||||||
|
override-checkout: stable-2.12
|
||||||
|
- name: openstack/openstacksdk
|
||||||
|
override-checkout: stable/xena
|
||||||
|
vars:
|
||||||
|
tox_envlist: ansible
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
|
||||||
|
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||||
|
description: |
|
||||||
|
Run openstack collections functional tests against a wallaby devstack
|
||||||
|
using wallaby brach of openstacksdk and stable 2.12 branch of ansible
|
||||||
|
voting: true
|
||||||
|
override-checkout: stable/wallaby
|
||||||
|
required-projects:
|
||||||
|
- name: github.com/ansible/ansible
|
||||||
|
override-checkout: stable-2.12
|
||||||
|
- name: openstack/openstacksdk
|
||||||
|
override-checkout: stable/wallaby
|
||||||
|
vars:
|
||||||
|
tox_envlist: ansible
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
|
||||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||||
description: |
|
description: |
|
||||||
Run openstack collections functional tests against a victoria devstack
|
Run openstack collections functional tests against a victoria devstack
|
||||||
using victoria brach of openstacksdk and stable 2.10 branch of ansible
|
using victoria brach of openstacksdk and stable 2.12 branch of ansible
|
||||||
voting: true
|
voting: true
|
||||||
override-checkout: stable/victoria
|
override-checkout: stable/victoria
|
||||||
required-projects:
|
required-projects:
|
||||||
- name: github.com/ansible/ansible
|
- name: github.com/ansible/ansible
|
||||||
override-checkout: stable-2.10
|
override-checkout: stable-2.12
|
||||||
- name: openstack/openstacksdk
|
- name: openstack/openstacksdk
|
||||||
override-checkout: stable/victoria
|
override-checkout: stable/victoria
|
||||||
vars:
|
vars:
|
||||||
@@ -97,16 +179,16 @@
|
|||||||
|
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.10
|
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
|
||||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||||
description: |
|
description: |
|
||||||
Run openstack collections functional tests against a ussuri devstack
|
Run openstack collections functional tests against a ussuri devstack
|
||||||
using ussuri brach of openstacksdk and stable 2.10 branch of ansible
|
using ussuri brach of openstacksdk and stable 2.11 branch of ansible
|
||||||
voting: true
|
voting: true
|
||||||
override-checkout: stable/ussuri
|
override-checkout: stable/ussuri
|
||||||
required-projects:
|
required-projects:
|
||||||
- name: github.com/ansible/ansible
|
- name: github.com/ansible/ansible
|
||||||
override-checkout: stable-2.10
|
override-checkout: stable-2.11
|
||||||
- name: openstack/openstacksdk
|
- name: openstack/openstacksdk
|
||||||
override-checkout: stable/ussuri
|
override-checkout: stable/ussuri
|
||||||
- name: openstack/os-client-config
|
- name: openstack/os-client-config
|
||||||
@@ -115,16 +197,16 @@
|
|||||||
tox_envlist: ansible
|
tox_envlist: ansible
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: ansible-collections-openstack-functional-devstack-train-ansible-2.10
|
name: ansible-collections-openstack-functional-devstack-train-ansible-2.11
|
||||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||||
description: |
|
description: |
|
||||||
Run openstack collections functional tests against a train devstack
|
Run openstack collections functional tests against a train devstack
|
||||||
using train brach of openstacksdk and stable 2.10 branch of ansible
|
using train brach of openstacksdk and stable 2.11 branch of ansible
|
||||||
voting: false
|
voting: true
|
||||||
override-checkout: stable/train
|
override-checkout: stable/train
|
||||||
required-projects:
|
required-projects:
|
||||||
- name: github.com/ansible/ansible
|
- name: github.com/ansible/ansible
|
||||||
override-checkout: stable-2.10
|
override-checkout: stable-2.11
|
||||||
- name: openstack/openstacksdk
|
- name: openstack/openstacksdk
|
||||||
override-checkout: stable/train
|
override-checkout: stable/train
|
||||||
- name: openstack/os-client-config
|
- name: openstack/os-client-config
|
||||||
@@ -133,84 +215,27 @@
|
|||||||
tox_envlist: ansible
|
tox_envlist: ansible
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: ansible-collections-openstack-functional-devstack-stein-ansible-2.10
|
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.11
|
||||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
|
||||||
description: |
|
|
||||||
Run openstack collections functional tests against a stein devstack
|
|
||||||
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.10
|
|
||||||
- name: openstack/openstacksdk
|
|
||||||
override-checkout: stable/stein
|
|
||||||
- name: openstack/os-client-config
|
|
||||||
override-checkout: stable/stein
|
|
||||||
vars:
|
|
||||||
tox_envlist: ansible
|
|
||||||
|
|
||||||
- job:
|
|
||||||
name: ansible-collections-openstack-functional-devstack-rocky-ansible-2.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.10 branch of ansible
|
|
||||||
voting: true
|
|
||||||
override-checkout: stable/rocky
|
|
||||||
required-projects:
|
|
||||||
- name: github.com/ansible/ansible
|
|
||||||
override-checkout: stable-2.10
|
|
||||||
- name: openstack/openstacksdk
|
|
||||||
override-checkout: stable/rocky
|
|
||||||
- name: openstack/os-client-config
|
|
||||||
override-checkout: stable/rocky
|
|
||||||
- name: openstack/shade
|
|
||||||
override-checkout: stable/rocky
|
|
||||||
|
|
||||||
vars:
|
|
||||||
tox_envlist: ansible
|
|
||||||
|
|
||||||
- job:
|
|
||||||
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.10
|
|
||||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||||
description: |
|
description: |
|
||||||
Run openstack collections functional tests against a queens devstack
|
Run openstack collections functional tests against a queens devstack
|
||||||
using master branch of openstacksdk and stable 2.10 branch of ansible
|
using master branch of openstacksdk and stable 2.11 branch of ansible
|
||||||
voting: true
|
voting: true
|
||||||
override-checkout: stable/queens
|
override-checkout: stable/queens
|
||||||
required-projects:
|
required-projects:
|
||||||
- name: github.com/ansible/ansible
|
- name: github.com/ansible/ansible
|
||||||
override-checkout: stable-2.10
|
override-checkout: stable-2.11
|
||||||
- name: openstack/openstacksdk
|
- name: openstack/openstacksdk
|
||||||
# Run queens with highest possible py2 version of SDK
|
# Run queens with highest possible py2 version of SDK
|
||||||
override-checkout: stable/train
|
override-checkout: stable/train
|
||||||
vars:
|
vars:
|
||||||
tox_envlist: ansible
|
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
|
# Linters
|
||||||
- job:
|
- job:
|
||||||
name: openstack-tox-linters-ansible-devel
|
name: openstack-tox-linters-ansible-devel
|
||||||
parent: openstack-tox-linters
|
parent: openstack-tox-linters
|
||||||
nodeset: ubuntu-bionic
|
nodeset: ubuntu-focal
|
||||||
description: |
|
description: |
|
||||||
Run openstack collections linter tests using the devel branch of ansible
|
Run openstack collections linter tests using the devel branch of ansible
|
||||||
# non-voting because we can't prevent ansible devel from breaking us
|
# non-voting because we can't prevent ansible devel from breaking us
|
||||||
@@ -219,18 +244,24 @@
|
|||||||
- name: github.com/ansible/ansible
|
- name: github.com/ansible/ansible
|
||||||
override-checkout: devel
|
override-checkout: devel
|
||||||
vars:
|
vars:
|
||||||
tox_envlist: linters-2.11
|
tox_envlist: linters-2.12
|
||||||
|
python_version: 3.8
|
||||||
|
bindep_profile: test py38
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: openstack-tox-linters-ansible-2.10
|
name: openstack-tox-linters-ansible-2.12
|
||||||
parent: openstack-tox-linters
|
parent: openstack-tox-linters
|
||||||
nodeset: ubuntu-bionic
|
nodeset: ubuntu-focal
|
||||||
description: |
|
description: |
|
||||||
Run openstack collections linter tests using the 2.10 branch of ansible
|
Run openstack collections linter tests using the 2.12 branch of ansible
|
||||||
voting: true
|
voting: true
|
||||||
required-projects:
|
required-projects:
|
||||||
- name: github.com/ansible/ansible
|
- name: github.com/ansible/ansible
|
||||||
override-checkout: stable-2.10
|
override-checkout: stable-2.12
|
||||||
|
vars:
|
||||||
|
tox_envlist: linters-2.12
|
||||||
|
python_version: 3.8
|
||||||
|
bindep_profile: test py38
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: openstack-tox-linters-ansible-2.9
|
name: openstack-tox-linters-ansible-2.9
|
||||||
@@ -258,61 +289,87 @@
|
|||||||
required-projects:
|
required-projects:
|
||||||
- openstack/ansible-collections-openstack
|
- openstack/ansible-collections-openstack
|
||||||
|
|
||||||
|
# TripleO jobs
|
||||||
|
- job:
|
||||||
|
name: tripleo-ci-centos-8-standalone-osa
|
||||||
|
parent: tripleo-ci-centos-8-standalone
|
||||||
|
vars:
|
||||||
|
consumer_job: false
|
||||||
|
build_container_images: true
|
||||||
|
# Run only on files used in TripleO
|
||||||
|
files: &ooo_files
|
||||||
|
- ^plugins/modules/catalog_service.*$
|
||||||
|
- ^plugins/modules/endpoint.*$
|
||||||
|
- ^plugins/modules/identity_domain.*$
|
||||||
|
- ^plugins/modules/identity_domain_info.*$
|
||||||
|
- ^plugins/modules/identity_role.*$
|
||||||
|
- ^plugins/modules/identity_user.*$
|
||||||
|
- ^plugins/modules/keypair.*$
|
||||||
|
- ^plugins/modules/project.*$
|
||||||
|
- ^plugins/modules/role_assignment.*$
|
||||||
|
- ^plugins/modules/stack.*$
|
||||||
|
- ^plugins/module_utils/openstack.*$
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: tripleo-ci-centos-8-standalone-train-osa
|
||||||
|
parent: tripleo-ci-centos-8-standalone-osa
|
||||||
|
voting: false
|
||||||
|
override-checkout: stable/train
|
||||||
|
vars:
|
||||||
|
branch_override: stable/train
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: ansible-collections-openstack-release
|
||||||
|
parent: base
|
||||||
|
run: ci/publish/publish_collection.yml
|
||||||
|
secrets:
|
||||||
|
- ansible_galaxy_info
|
||||||
|
|
||||||
|
- secret:
|
||||||
|
name: ansible_galaxy_info
|
||||||
|
data:
|
||||||
|
url: https://galaxy.ansible.com
|
||||||
|
token: !encrypted/pkcs1-oaep
|
||||||
|
- lZFzfoCbuwqV1k6qRfl/VS7E+knUW7+zpg7BptrenK4n0g7UY0HtdVkYq0pV0Tj/LbhzG
|
||||||
|
jHD0mehcV1iS6B7ORKg4criJkdDfEx09BD8z8yv0EleiIMmhlrCoMcY593OZMBtVbGi0D
|
||||||
|
CwQtNO98QIsfZogChfLfvRNiBmUV98mEb/p6p3EtGx8J7qcAsqfWxc/CzB8GCleLAHHHT
|
||||||
|
FuikMM03ZnV0ew7E+TPkHbzzPhBZOqS5HYF0HtgttHwIXdfIWp/XdTuEEk7uRRgYZ2Iao
|
||||||
|
ifWRzoKaOQmhM++e1ydCqw9D4y9dZEFNMQLwSqcrvtb8cNwT1kl7SCFqYNE2lbutj4ne6
|
||||||
|
PTBQRsKegMB4Y3ena14fNF6tCynvJLPhF/cjPH2Jhs+B19XQhWkL3TgiOY02W24YHwRcP
|
||||||
|
+LdkM8inAvyVi3DEbEqdjBPO9OFJcBOKPlCdkGvuwdNCuEpEwctWs0gV3voflG2CDKzmJ
|
||||||
|
wu9JJOAWnq/0l1WpuDqWreKeQ/BUGZC2Gb4xRAqofulgvhs4WuYoEccjH4EJFIZ90S1EP
|
||||||
|
R/ZLadqZaEhmjwGM5sMWbBbjT23XsRgg0Tzt9m8DENYMuYDqkMdRbt2jYZa+32p4hyxVe
|
||||||
|
Y6H/pqYq5b9uOzumnShaK4WlmkQyXcNPkoSlMC1h4OGvqX/WUixpI38jyMA5Tc=
|
||||||
|
|
||||||
- project:
|
- project:
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- tox-pep8
|
- tox-pep8
|
||||||
- openstack-tox-linters-ansible-devel
|
- openstack-tox-linters-ansible-devel
|
||||||
- openstack-tox-linters-ansible-2.10
|
- openstack-tox-linters-ansible-2.12
|
||||||
- openstack-tox-linters-ansible-2.9
|
- openstack-tox-linters-ansible-2.9
|
||||||
- ansible-collections-openstack-functional-devstack:
|
- ansible-collections-openstack-functional-devstack:
|
||||||
dependencies: &deps_unit_lint
|
dependencies: &deps_unit_lint
|
||||||
- tox-pep8
|
- tox-pep8
|
||||||
- openstack-tox-linters-ansible-2.9
|
- openstack-tox-linters-ansible-2.9
|
||||||
- openstack-tox-linters-ansible-2.10
|
- openstack-tox-linters-ansible-2.12
|
||||||
irrelevant-files: &ignore_files
|
|
||||||
- changelogs/.*
|
|
||||||
- COPYING
|
|
||||||
- docs/.*
|
|
||||||
- .*\.md
|
|
||||||
- .*\.rst
|
|
||||||
- tools/run-ansible-sanity.sh
|
|
||||||
- tests/sanity/.*
|
|
||||||
- contrib/.*
|
|
||||||
|
|
||||||
- ansible-collections-openstack-functional-devstack-releases:
|
- ansible-collections-openstack-functional-devstack-releases:
|
||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-2.9:
|
- ansible-collections-openstack-functional-devstack-ansible-2.9:
|
||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
- ansible-collections-openstack-functional-devstack-ansible-2.12:
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-2.10:
|
|
||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-devel:
|
- ansible-collections-openstack-functional-devstack-ansible-devel:
|
||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
- ansible-collections-openstack-functional-devstack-xena-ansible-2.12:
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-pip:
|
|
||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12:
|
||||||
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.10:
|
|
||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
- ansible-collections-openstack-functional-devstack-train-ansible-2.11:
|
||||||
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.10:
|
|
||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
- ansible-collections-openstack-functional-devstack-octavia:
|
||||||
- ansible-collections-openstack-functional-devstack-train-ansible-2.10:
|
|
||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
|
||||||
- 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:
|
- bifrost-collections-src:
|
||||||
voting: false
|
voting: false
|
||||||
@@ -323,40 +380,54 @@
|
|||||||
dependencies: *deps_unit_lint
|
dependencies: *deps_unit_lint
|
||||||
irrelevant-files: *ignore_files
|
irrelevant-files: *ignore_files
|
||||||
|
|
||||||
|
- tripleo-ci-centos-8-standalone-osa:
|
||||||
|
dependencies: *deps_unit_lint
|
||||||
|
|
||||||
|
- tripleo-ci-centos-8-standalone-train-osa:
|
||||||
|
voting: false
|
||||||
|
dependencies: *deps_unit_lint
|
||||||
|
|
||||||
gate:
|
gate:
|
||||||
jobs:
|
jobs:
|
||||||
- tox-pep8
|
- tox-pep8
|
||||||
- openstack-tox-linters-ansible-2.10
|
- openstack-tox-linters-ansible-2.12
|
||||||
- openstack-tox-linters-ansible-2.9
|
- openstack-tox-linters-ansible-2.9
|
||||||
- ansible-collections-openstack-functional-devstack
|
- ansible-collections-openstack-functional-devstack
|
||||||
- ansible-collections-openstack-functional-devstack-releases
|
- ansible-collections-openstack-functional-devstack-releases
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-2.9
|
- ansible-collections-openstack-functional-devstack-ansible-2.9
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-ansible-2.12
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-pip
|
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
|
||||||
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-xena-ansible-2.12
|
||||||
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
|
||||||
# - ansible-collections-openstack-functional-devstack-train-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-octavia
|
||||||
- ansible-collections-openstack-functional-devstack-stein-ansible-2.10
|
- tripleo-ci-centos-8-standalone-osa
|
||||||
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.10
|
|
||||||
- ansible-collections-openstack-functional-devstack-queens-ansible-2.10
|
|
||||||
|
|
||||||
periodic:
|
periodic:
|
||||||
jobs:
|
jobs:
|
||||||
- openstack-tox-linters-ansible-devel
|
- openstack-tox-linters-ansible-devel
|
||||||
- openstack-tox-linters-ansible-2.10
|
- openstack-tox-linters-ansible-2.12
|
||||||
- openstack-tox-linters-ansible-2.9
|
- openstack-tox-linters-ansible-2.9
|
||||||
- ansible-collections-openstack-functional-devstack
|
- ansible-collections-openstack-functional-devstack
|
||||||
- ansible-collections-openstack-functional-devstack-releases
|
- ansible-collections-openstack-functional-devstack-releases
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-2.9
|
- ansible-collections-openstack-functional-devstack-ansible-2.9
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-ansible-2.12
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-devel
|
- ansible-collections-openstack-functional-devstack-ansible-devel
|
||||||
- ansible-collections-openstack-functional-devstack-ansible-pip
|
- ansible-collections-openstack-functional-devstack-xena-ansible-2.12
|
||||||
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
|
||||||
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
|
||||||
- ansible-collections-openstack-functional-devstack-train-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
|
||||||
- ansible-collections-openstack-functional-devstack-stein-ansible-2.10
|
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11
|
||||||
- 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-collections-src
|
||||||
- bifrost-keystone-collections-src
|
- bifrost-keystone-collections-src
|
||||||
|
- ansible-collections-openstack-functional-devstack-octavia
|
||||||
|
|
||||||
|
experimental:
|
||||||
|
jobs:
|
||||||
|
- ansible-collections-openstack-functional-devstack-ansible-2.11
|
||||||
|
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
|
||||||
|
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
|
||||||
|
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11
|
||||||
|
|
||||||
|
tag:
|
||||||
|
jobs:
|
||||||
|
- ansible-collections-openstack-release
|
||||||
|
|||||||
217
CHANGELOG.rst
217
CHANGELOG.rst
@@ -5,6 +5,223 @@ Openstack Cloud Ansilbe modules Release Notes
|
|||||||
.. contents:: Topics
|
.. contents:: Topics
|
||||||
|
|
||||||
|
|
||||||
|
v1.6.0
|
||||||
|
======
|
||||||
|
|
||||||
|
Release Summary
|
||||||
|
---------------
|
||||||
|
|
||||||
|
New modules for RBAC and Nova services
|
||||||
|
|
||||||
|
Minor Changes
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- quota - Adds metadata_items parameter
|
||||||
|
|
||||||
|
New Modules
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- openstack.cloud.compute_service_info - Retrieve information about one or more OpenStack compute services
|
||||||
|
- openstack.cloud.neutron_rbac_policies_info - Fetch Neutron policies.
|
||||||
|
- openstack.cloud.neutron_rbac_policy - Create or delete a Neutron policy to apply a RBAC rule against an object.
|
||||||
|
|
||||||
|
v1.5.3
|
||||||
|
======
|
||||||
|
|
||||||
|
Release Summary
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Don't require allowed_address_pairs for port
|
||||||
|
- server_volume - check specified server is found
|
||||||
|
|
||||||
|
v1.5.2
|
||||||
|
======
|
||||||
|
|
||||||
|
Release Summary
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
|
||||||
|
Minor Changes
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- Add documentation links to README.md
|
||||||
|
- Don't run functional jobs on galaxy.yml change
|
||||||
|
- Move CI to use Ansible 2.12 version as main
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Add client and member listener timeouts for persistence (Ex. SSH)
|
||||||
|
- Added missing warn() used in cloud.openstack.quota
|
||||||
|
- Fix issue with same host and group names
|
||||||
|
- Flavor properties are not deleted on changes and id will stay
|
||||||
|
|
||||||
|
v1.5.1
|
||||||
|
======
|
||||||
|
|
||||||
|
Release Summary
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Bugfixes for networking modules
|
||||||
|
|
||||||
|
Minor Changes
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- Changed minversion in tox to 3.18.0
|
||||||
|
- Update IRC server in README
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Add mandatory requires_ansible version to metadata
|
||||||
|
- Add protocol listener octavia
|
||||||
|
- Add support check mode for all info modules
|
||||||
|
- Allow to attach multiple floating ips to a server
|
||||||
|
- Only add or remove router interfaces when needed
|
||||||
|
- Wait for pool to be active and online
|
||||||
|
|
||||||
|
v1.5.0
|
||||||
|
======
|
||||||
|
|
||||||
|
Release Summary
|
||||||
|
---------------
|
||||||
|
|
||||||
|
New modules for DNS and FIPs and bugfixes.
|
||||||
|
|
||||||
|
Minor Changes
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- Add bindep.txt for ansible-builder
|
||||||
|
- Add check_mode attribute to OpenstackModule
|
||||||
|
- Migrating image module from AnsibleModule to OpenStackModule
|
||||||
|
- Switch KeystoneFederationProtocolInfo module to OpenStackModule
|
||||||
|
- Switch ProjectAccess module to OpenStackModule
|
||||||
|
- Switch Quota module to OpenStackModule
|
||||||
|
- Switch Recordset module to OpenStackModule
|
||||||
|
- Switch ServerGroup module to OpenStackModule
|
||||||
|
- Switch ServerMetadata module to OpenStackModule
|
||||||
|
- Switch Snapshot module to OpenStackModule
|
||||||
|
- Switch Stack module to OpenStackModule
|
||||||
|
- Switch auth module to OpenStackModule
|
||||||
|
- Switch catalog_service module to OpenStackModule
|
||||||
|
- Switch coe_cluster module to OpenStackModule
|
||||||
|
- Switch coe_cluster_template module to OpenStackModule
|
||||||
|
- Switch endpoint module to OpenStackModule
|
||||||
|
- Switch federation_idp module to OpenStackModule
|
||||||
|
- Switch federation_idp_info module to OpenStackModule
|
||||||
|
- Switch federation_mapping module to OpenStackModule
|
||||||
|
- Switch federation_mapping_info module to OpenStackModule
|
||||||
|
- Switch federation_protocol module to OpenStackModule
|
||||||
|
- Switch flavor module to OpenStackModule
|
||||||
|
- Switch flavor_info module to OpenStackModule
|
||||||
|
- Switch floating_ip module to OpenStackModule
|
||||||
|
- Switch group_assignment module to OpenStackModule
|
||||||
|
- Switch hostaggregate module to OpenStackModule
|
||||||
|
- Switch identity_domain module to OpenStackModule
|
||||||
|
- Switch identity_domain_info module to OpenStackModule
|
||||||
|
- Switch identity_group module to OpenStackModule
|
||||||
|
- Switch identity_group_info module to OpenStackModule
|
||||||
|
- Switch identity_role module to OpenStackModule
|
||||||
|
- Switch identity_user module to OpenStackModule
|
||||||
|
- Switch lb_listener module to OpenStackModule
|
||||||
|
- Switch lb_member module to OpenStackModule
|
||||||
|
- Switch lb_pool module to OpenStackModule
|
||||||
|
- Switch object module to OpenStackModule
|
||||||
|
- Switch port module to OpenStackModule
|
||||||
|
- Switch port_info module to OpenStackModule
|
||||||
|
- Switch project and project_info module to OpenStackModule
|
||||||
|
- Switch role_assignment module to OpenStackModule
|
||||||
|
- Switch user_info module to OpenStackModule
|
||||||
|
- image - Add support to setting image tags
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Update checks for validate_certs in openstack_cloud_from_module
|
||||||
|
- compute_flavor - Fix the idempotent of compute_flavor module
|
||||||
|
- host_aggregate - Fix host_aggregate to tolerate aggregate.hosts being None
|
||||||
|
- inventory/openstack - Fix inventory plugin on Ansible 2.11
|
||||||
|
- port - fix update on empty list of allowed address pairs
|
||||||
|
- setup.cfg Replace dashes with underscores
|
||||||
|
- subnet - Only apply necessary changes to subnets
|
||||||
|
- volume - Fail if referenced source image for a new volume does not exist
|
||||||
|
|
||||||
|
New Modules
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- openstack.cloud.address_scope - Create or delete address scopes from OpenStack
|
||||||
|
- openstack.cloud.dns_zone_info - Getting information about dns zones
|
||||||
|
- openstack.cloud.floating_ip_info - Get information about floating ips
|
||||||
|
|
||||||
|
v1.4.0
|
||||||
|
======
|
||||||
|
|
||||||
|
Release Summary
|
||||||
|
---------------
|
||||||
|
|
||||||
|
New object_container module and bugfixes.
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Add Octavia job for testing Load Balancer
|
||||||
|
- Add binding profile to port module
|
||||||
|
- Add execution environment metadata
|
||||||
|
- Fix CI for latest ansible-test with no_log
|
||||||
|
- Fix issues with newest ansible-test 2.11
|
||||||
|
- Prepare for Ansible 2.11 tests
|
||||||
|
- add option to exclude legacy groups
|
||||||
|
- security_group_rule add support ipv6-icmp
|
||||||
|
|
||||||
|
New Modules
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- openstack.cloud.object_container - Manage Swift container
|
||||||
|
|
||||||
|
v1.3.0
|
||||||
|
======
|
||||||
|
|
||||||
|
Release Summary
|
||||||
|
---------------
|
||||||
|
|
||||||
|
New modules and bugfixes.
|
||||||
|
|
||||||
|
Minor Changes
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- Fix some typos in readme
|
||||||
|
- Guidelines Fix links and formatting
|
||||||
|
- baremetal_node - Add support for new features
|
||||||
|
- baremetal_node - ironic deprecate sub-options of driver_info
|
||||||
|
- baremetal_node - ironic stop putting meaningless values to properties
|
||||||
|
- image_info - Migrating image_info module from AnsibleModule to OpenStackModule
|
||||||
|
- recordset - Update recordset docu
|
||||||
|
- server - Allow description field to be set with os_server
|
||||||
|
- server_action - Added shelve and unshelve as new server actions
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- port - Fixed check for None in os_port
|
||||||
|
- project - Fix setting custom property on os_project
|
||||||
|
- security_group_rule - Remove protocols choice in security rules
|
||||||
|
- volume_info - Fix volume_info result for SDK < 0.19
|
||||||
|
|
||||||
|
New Modules
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- openstack.cloud.identity_role_info - Retrieve information about Openstack Identity roles.
|
||||||
|
- openstack.cloud.keypair_info - Retrieve information about Openstack key pairs.
|
||||||
|
- openstack.cloud.security_group_info - Retrieve information about Openstack Security Groups.
|
||||||
|
- openstack.cloud.security_group_rule_info - Retrieve information about Openstack Security Group rules.
|
||||||
|
- openstack.cloud.stack_info - Retrieve information about Openstack Heat stacks.
|
||||||
|
|
||||||
v1.2.1
|
v1.2.1
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -11,7 +11,7 @@ The collection includes the Openstack modules and plugins supported by Openstack
|
|||||||
|
|
||||||
### Installing dependencies
|
### Installing dependencies
|
||||||
|
|
||||||
For using Openstack Cloud collection firstly need to install `ansible` and `openstacksdk` Python modules on Ansible controller.
|
For using the Openstack Cloud collection firstly you need to install `ansible` and `openstacksdk` Python modules on your Ansible controller.
|
||||||
For example with pip:
|
For example with pip:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -19,8 +19,8 @@ pip install ansible openstacksdk
|
|||||||
```
|
```
|
||||||
|
|
||||||
OpenStackSDK has to be available to Ansible and to the Python interpreter on the host, where Ansible executes the module (target host).
|
OpenStackSDK has to be available to Ansible and to the Python interpreter on the host, where Ansible executes the module (target host).
|
||||||
Please note, that under some circumstances Ansible might invoke not standard Python interpreter on the target host.
|
Please note, that under some circumstances Ansible might invoke a non-standard Python interpreter on the target host.
|
||||||
Using Python verison 3 is highly recommended for OpenstackSDK and strongly required from OpenstackSDK version 0.39.0.
|
Using Python version 3 is highly recommended for OpenstackSDK and strongly required from OpenstackSDK version 0.39.0.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ Before using the Openstack Cloud collection, you need to install the collection
|
|||||||
|
|
||||||
`ansible-galaxy collection install openstack.cloud`
|
`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
|
```yaml
|
||||||
collections:
|
collections:
|
||||||
@@ -48,7 +48,7 @@ collections:
|
|||||||
|
|
||||||
### Playbooks
|
### 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
|
```yaml
|
||||||
---
|
---
|
||||||
@@ -66,7 +66,7 @@ To use a module from Openstack Cloud collection, please reference the full names
|
|||||||
volume_size: 75
|
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
|
```yaml
|
||||||
---
|
---
|
||||||
@@ -83,6 +83,14 @@ Or you can add full namepsace and collecton name in the `collections` element:
|
|||||||
device: /dev/vdb
|
device: /dev/vdb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
See the collection docs at Ansible site:
|
||||||
|
|
||||||
|
* [openstack.cloud collection docs (version released in Ansible package)](https://docs.ansible.com/ansible/latest/collections/openstack/cloud/index.html)
|
||||||
|
|
||||||
|
* [openstack.cloud collection docs (devel version)](https://docs.ansible.com/ansible/devel/collections/openstack/cloud/index.html)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
For information on contributing, please see [CONTRIBUTING](https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/CONTRIBUTING.rst)
|
For information on contributing, please see [CONTRIBUTING](https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/CONTRIBUTING.rst)
|
||||||
@@ -114,7 +122,7 @@ TBD
|
|||||||
## Communication
|
## Communication
|
||||||
|
|
||||||
We have a dedicated Interest Group for Openstack Ansible modules.
|
We have a dedicated Interest Group for Openstack Ansible modules.
|
||||||
You can find other people interested in this in `#openstack-ansible-sig` on Freenode IRC.
|
You can find other people interested in this in `#openstack-ansible-sig` on [OFTC IRC](https://www.oftc.net/).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
7
bindep.txt
Normal file
7
bindep.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This is a cross-platform list tracking distribution packages needed by tests;
|
||||||
|
# see https://docs.openstack.org/infra/bindep/ for additional information.
|
||||||
|
|
||||||
|
gcc [compile platform:centos-8 platform:rhel-8]
|
||||||
|
python38-cryptography [platform:centos-8 platform:rhel-8]
|
||||||
|
python38-devel [compile platform:centos-8 platform:rhel-8]
|
||||||
|
python38-requests [platform:centos-8 platform:rhel-8]
|
||||||
@@ -1,111 +1,285 @@
|
|||||||
|
ancestor: null
|
||||||
releases:
|
releases:
|
||||||
1.2.1:
|
|
||||||
release_date: '2021-01-03'
|
|
||||||
changes:
|
|
||||||
release_summary: Porting modules to new OpenstackModule class and fixes.
|
|
||||||
minor_changes:
|
|
||||||
- dns_zone, recordset - Enable update for recordset and add tests for dns and recordset module
|
|
||||||
- loadbalancer - Refactor loadbalancer module
|
|
||||||
- openstack - OpenStackModule Support defining a minimum version of the SDK
|
|
||||||
- ironic - Refactor ironic authentication into a new module_utils module
|
|
||||||
- endpoint - Do not fail when endpoint state is absent
|
|
||||||
- subnet - Fix subnets update and idempotency
|
|
||||||
- volume_info - Fix volume_info arguments for SDK 0.19
|
|
||||||
- routers_info - Added deprecated_names for router_info module
|
|
||||||
- security_group_rule - Refactor TCP/UDP port check
|
|
||||||
- openstack - Fix docs-args mismatch in modules
|
|
||||||
- security_group.py - Migrating security_group from AnsibleModule to OpenStackModule
|
|
||||||
- router - Migrating routers from AnsibleModule to OpenStackModule
|
|
||||||
- server_volume - Migrating server_volume from AnsibleModule to OpenStackModule
|
|
||||||
- dns_zone - Migrating dns_zone from AnsibleModule to OpenStackModule
|
|
||||||
- network - Migrating network from AnsibleModule to OpenStackModule
|
|
||||||
- routers_info - Migrating routers_info from AnsibleModule to OpenStackModule
|
|
||||||
- networks_info - Migrating networks_info from AnsibleModule to OpenStackModule
|
|
||||||
- subnets_info - Migrating subnets_info from AnsibleModule to OpenStackModule
|
|
||||||
- server.py - Improve "server" module with OpenstackModule class
|
|
||||||
- volume.py - Migrating volume from AnsibleModule to OpenStackModule
|
|
||||||
- subnet - Migrating subnet module from AnsibleModule to OpenStackModule
|
|
||||||
- openstack - Add galaxy.yml to support install from git
|
|
||||||
1.2.0:
|
|
||||||
release_date: '2020-10-13'
|
|
||||||
changes:
|
|
||||||
release_summary: New volume backup modules.
|
|
||||||
minor_changes:
|
|
||||||
- lb_health_monitor - Make it possible to create a health monitor to a pool
|
|
||||||
modules:
|
|
||||||
- name: volume_snapshot_info module
|
|
||||||
description: Retrieve information about Openstack volume snapshots.
|
|
||||||
namespace: ''
|
|
||||||
- name: volume_backup_info module
|
|
||||||
description: Retrieve information about Openstack volume backups.
|
|
||||||
namespace: ''
|
|
||||||
- name: volume_backup module
|
|
||||||
description: Add/Delete Openstack volumes backup.
|
|
||||||
namespace: ''
|
|
||||||
1.1.0:
|
|
||||||
release_date: '2020-08-17'
|
|
||||||
changes:
|
|
||||||
release_summary: Starting redesign modules and bugfixes.
|
|
||||||
minor_changes:
|
|
||||||
- A basic module subclass was introduced and a few modules moved to inherit from it.
|
|
||||||
- Added pip installation option for collection.
|
|
||||||
- Added template for generation of artibtrary module.
|
|
||||||
- Add more useful information from exception
|
|
||||||
- inventory_openstack - Add openstack logger and Ansible display utility
|
|
||||||
- loadbalancer - Add support for setting the Flavor when creating a load balancer
|
|
||||||
- baremetal modules - Do not require ironic_url if cloud or auth.endpoint is provided
|
|
||||||
bugfixes:
|
|
||||||
- security_group_rule - Don't pass tenant_id for remote group
|
|
||||||
- Fix non existing attribuites in SDK exception
|
|
||||||
modules:
|
|
||||||
- name: volume_info
|
|
||||||
description: Retrieve information about Openstack volumes.
|
|
||||||
namespace: ''
|
|
||||||
1.0.1:
|
|
||||||
release_date: '2020-05-22'
|
|
||||||
changes:
|
|
||||||
release_summary: Bugfix for server_info
|
|
||||||
bugfixes:
|
|
||||||
- server_info - Fix broken server_info module and add tests
|
|
||||||
1.0.0:
|
1.0.0:
|
||||||
release_date: '2020-05-19'
|
|
||||||
changes:
|
changes:
|
||||||
release_summary: Initial release of collection.
|
|
||||||
minor_changes:
|
|
||||||
- Renaming all modules and removing "os" prefix from names.
|
|
||||||
- server_action - pass imageRef to rebuild
|
|
||||||
- subnet - Updated allocation pool checks
|
|
||||||
- project - Add properties for os_project
|
|
||||||
- config - Update os_client_config to use openstacksdk
|
|
||||||
- host_aggregate - Add support for not 'purging' missing hosts
|
|
||||||
- baremetal_node_action - Support json type for the ironic_node config_drive parameter
|
|
||||||
bugfixes:
|
bugfixes:
|
||||||
- federation_mapping - Fixup some minor nits found in followup reviews
|
- baremetal_node - Correct parameter name
|
||||||
- coe_cluster - Retrive id/uuid correctly
|
- coe_cluster - Retrive id/uuid correctly
|
||||||
- baremetal_node - Correct parameter name
|
- federation_mapping - Fixup some minor nits found in followup reviews
|
||||||
- network - Bump minimum openstacksdk version when using os_network/dns_domain
|
- inventory_openstack - Fix constructed compose
|
||||||
- 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 for groups in multidomain context
|
||||||
- role_assignment - Fix os_user_role issue to grant a role in a domain
|
- 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:
|
modules:
|
||||||
- name: keystone_federation_protocol_info
|
- description: Add support for Keystone Identity Providers
|
||||||
description: Add support for getting information about Keystone federation Protocols
|
name: federation_idp
|
||||||
namespace: ''
|
namespace: ''
|
||||||
- name: keystone_federation_protocol
|
- description: Add support for fetching the information about federation IDPs
|
||||||
description: Add support for Keystone federation Protocols
|
name: federation_idp_info
|
||||||
namespace: ''
|
namespace: ''
|
||||||
- name: federation_idp_info
|
- description: Add support for Keystone mappings
|
||||||
description: Add support for fetching the information about federation IDPs
|
name: federation_mapping
|
||||||
namespace: ''
|
namespace: ''
|
||||||
- name: federation_idp
|
- description: Add support for fetching the information about Keystone mappings
|
||||||
description: Add support for Keystone Identity Providers
|
name: federation_mapping_info
|
||||||
namespace: ''
|
namespace: ''
|
||||||
- name: federation_mapping_info
|
- description: Add support for Keystone federation Protocols
|
||||||
description: Add support for fetching the information about Keystone mappings
|
name: keystone_federation_protocol
|
||||||
namespace: ''
|
namespace: ''
|
||||||
- name: federation_mapping
|
- description: Add support for getting information about Keystone federation Protocols
|
||||||
description: Add support for Keystone mappings
|
name: keystone_federation_protocol_info
|
||||||
namespace: ''
|
namespace: ''
|
||||||
- name: routers_info
|
- description: Retrieve information about one or more OpenStack routers.
|
||||||
description: Retrieve information about one or more OpenStack routers.
|
name: routers_info
|
||||||
namespace: ''
|
namespace: ''
|
||||||
|
release_date: '2020-05-19'
|
||||||
|
1.0.1:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- server_info - Fix broken server_info module and add tests
|
||||||
|
release_summary: Bugfix for server_info
|
||||||
|
release_date: '2020-05-22'
|
||||||
|
1.1.0:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- Fix non existing attribuites in SDK exception
|
||||||
|
- security_group_rule - Don't pass tenant_id for remote group
|
||||||
|
minor_changes:
|
||||||
|
- A basic module subclass was introduced and a few modules moved to inherit
|
||||||
|
from it.
|
||||||
|
- Add more useful information from exception
|
||||||
|
- Added pip installation option for collection.
|
||||||
|
- Added template for generation of artibtrary module.
|
||||||
|
- baremetal modules - Do not require ironic_url if cloud or auth.endpoint is
|
||||||
|
provided
|
||||||
|
- inventory_openstack - Add openstack logger and Ansible display utility
|
||||||
|
- loadbalancer - Add support for setting the Flavor when creating a load balancer
|
||||||
|
release_summary: Starting redesign modules and bugfixes.
|
||||||
|
modules:
|
||||||
|
- description: Retrieve information about Openstack volumes.
|
||||||
|
name: volume_info
|
||||||
|
namespace: ''
|
||||||
|
release_date: '2020-08-17'
|
||||||
|
1.2.0:
|
||||||
|
changes:
|
||||||
|
minor_changes:
|
||||||
|
- lb_health_monitor - Make it possible to create a health monitor to a pool
|
||||||
|
release_summary: New volume backup modules.
|
||||||
|
modules:
|
||||||
|
- description: Add/Delete Openstack volumes backup.
|
||||||
|
name: volume_backup module
|
||||||
|
namespace: ''
|
||||||
|
- description: Retrieve information about Openstack volume backups.
|
||||||
|
name: volume_backup_info module
|
||||||
|
namespace: ''
|
||||||
|
- description: Retrieve information about Openstack volume snapshots.
|
||||||
|
name: volume_snapshot_info module
|
||||||
|
namespace: ''
|
||||||
|
release_date: '2020-10-13'
|
||||||
|
1.2.1:
|
||||||
|
changes:
|
||||||
|
minor_changes:
|
||||||
|
- dns_zone - Migrating dns_zone from AnsibleModule to OpenStackModule
|
||||||
|
- dns_zone, recordset - Enable update for recordset and add tests for dns and
|
||||||
|
recordset module
|
||||||
|
- endpoint - Do not fail when endpoint state is absent
|
||||||
|
- ironic - Refactor ironic authentication into a new module_utils module
|
||||||
|
- loadbalancer - Refactor loadbalancer module
|
||||||
|
- network - Migrating network from AnsibleModule to OpenStackModule
|
||||||
|
- networks_info - Migrating networks_info from AnsibleModule to OpenStackModule
|
||||||
|
- openstack - Add galaxy.yml to support install from git
|
||||||
|
- openstack - Fix docs-args mismatch in modules
|
||||||
|
- openstack - OpenStackModule Support defining a minimum version of the SDK
|
||||||
|
- router - Migrating routers from AnsibleModule to OpenStackModule
|
||||||
|
- routers_info - Added deprecated_names for router_info module
|
||||||
|
- routers_info - Migrating routers_info from AnsibleModule to OpenStackModule
|
||||||
|
- security_group.py - Migrating security_group from AnsibleModule to OpenStackModule
|
||||||
|
- security_group_rule - Refactor TCP/UDP port check
|
||||||
|
- server.py - Improve "server" module with OpenstackModule class
|
||||||
|
- server_volume - Migrating server_volume from AnsibleModule to OpenStackModule
|
||||||
|
- subnet - Fix subnets update and idempotency
|
||||||
|
- subnet - Migrating subnet module from AnsibleModule to OpenStackModule
|
||||||
|
- subnets_info - Migrating subnets_info from AnsibleModule to OpenStackModule
|
||||||
|
- volume.py - Migrating volume from AnsibleModule to OpenStackModule
|
||||||
|
- volume_info - Fix volume_info arguments for SDK 0.19
|
||||||
|
release_summary: Porting modules to new OpenstackModule class and fixes.
|
||||||
|
release_date: '2021-01-03'
|
||||||
|
1.3.0:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- port - Fixed check for None in os_port
|
||||||
|
- project - Fix setting custom property on os_project
|
||||||
|
- security_group_rule - Remove protocols choice in security rules
|
||||||
|
- volume_info - Fix volume_info result for SDK < 0.19
|
||||||
|
minor_changes:
|
||||||
|
- Fix some typos in readme
|
||||||
|
- Guidelines Fix links and formatting
|
||||||
|
- baremetal_node - Add support for new features
|
||||||
|
- baremetal_node - ironic deprecate sub-options of driver_info
|
||||||
|
- baremetal_node - ironic stop putting meaningless values to properties
|
||||||
|
- image_info - Migrating image_info module from AnsibleModule to OpenStackModule
|
||||||
|
- recordset - Update recordset docu
|
||||||
|
- server - Allow description field to be set with os_server
|
||||||
|
- server_action - Added shelve and unshelve as new server actions
|
||||||
|
release_summary: New modules and bugfixes.
|
||||||
|
modules:
|
||||||
|
- description: Retrieve information about Openstack Identity roles.
|
||||||
|
name: identity_role_info
|
||||||
|
namespace: ''
|
||||||
|
- description: Retrieve information about Openstack key pairs.
|
||||||
|
name: keypair_info
|
||||||
|
namespace: ''
|
||||||
|
- description: Retrieve information about Openstack Security Groups.
|
||||||
|
name: security_group_info
|
||||||
|
namespace: ''
|
||||||
|
- description: Retrieve information about Openstack Security Group rules.
|
||||||
|
name: security_group_rule_info
|
||||||
|
namespace: ''
|
||||||
|
- description: Retrieve information about Openstack Heat stacks.
|
||||||
|
name: stack_info
|
||||||
|
namespace: ''
|
||||||
|
release_date: '2021-02-16'
|
||||||
|
1.4.0:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- Add Octavia job for testing Load Balancer
|
||||||
|
- Add binding profile to port module
|
||||||
|
- Add execution environment metadata
|
||||||
|
- Fix CI for latest ansible-test with no_log
|
||||||
|
- Fix issues with newest ansible-test 2.11
|
||||||
|
- Prepare for Ansible 2.11 tests
|
||||||
|
- add option to exclude legacy groups
|
||||||
|
- security_group_rule add support ipv6-icmp
|
||||||
|
release_summary: New object_container module and bugfixes.
|
||||||
|
modules:
|
||||||
|
- description: Manage Swift container
|
||||||
|
name: object_container
|
||||||
|
namespace: ''
|
||||||
|
release_date: '2021-04-08'
|
||||||
|
1.5.0:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- Update checks for validate_certs in openstack_cloud_from_module
|
||||||
|
- compute_flavor - Fix the idempotent of compute_flavor module
|
||||||
|
- host_aggregate - Fix host_aggregate to tolerate aggregate.hosts being None
|
||||||
|
- inventory/openstack - Fix inventory plugin on Ansible 2.11
|
||||||
|
- port - fix update on empty list of allowed address pairs
|
||||||
|
- setup.cfg Replace dashes with underscores
|
||||||
|
- subnet - Only apply necessary changes to subnets
|
||||||
|
- volume - Fail if referenced source image for a new volume does not exist
|
||||||
|
minor_changes:
|
||||||
|
- Add bindep.txt for ansible-builder
|
||||||
|
- Add check_mode attribute to OpenstackModule
|
||||||
|
- Migrating image module from AnsibleModule to OpenStackModule
|
||||||
|
- Switch KeystoneFederationProtocolInfo module to OpenStackModule
|
||||||
|
- Switch ProjectAccess module to OpenStackModule
|
||||||
|
- Switch Quota module to OpenStackModule
|
||||||
|
- Switch Recordset module to OpenStackModule
|
||||||
|
- Switch ServerGroup module to OpenStackModule
|
||||||
|
- Switch ServerMetadata module to OpenStackModule
|
||||||
|
- Switch Snapshot module to OpenStackModule
|
||||||
|
- Switch Stack module to OpenStackModule
|
||||||
|
- Switch auth module to OpenStackModule
|
||||||
|
- Switch catalog_service module to OpenStackModule
|
||||||
|
- Switch coe_cluster module to OpenStackModule
|
||||||
|
- Switch coe_cluster_template module to OpenStackModule
|
||||||
|
- Switch endpoint module to OpenStackModule
|
||||||
|
- Switch federation_idp module to OpenStackModule
|
||||||
|
- Switch federation_idp_info module to OpenStackModule
|
||||||
|
- Switch federation_mapping module to OpenStackModule
|
||||||
|
- Switch federation_mapping_info module to OpenStackModule
|
||||||
|
- Switch federation_protocol module to OpenStackModule
|
||||||
|
- Switch flavor module to OpenStackModule
|
||||||
|
- Switch flavor_info module to OpenStackModule
|
||||||
|
- Switch floating_ip module to OpenStackModule
|
||||||
|
- Switch group_assignment module to OpenStackModule
|
||||||
|
- Switch hostaggregate module to OpenStackModule
|
||||||
|
- Switch identity_domain module to OpenStackModule
|
||||||
|
- Switch identity_domain_info module to OpenStackModule
|
||||||
|
- Switch identity_group module to OpenStackModule
|
||||||
|
- Switch identity_group_info module to OpenStackModule
|
||||||
|
- Switch identity_role module to OpenStackModule
|
||||||
|
- Switch identity_user module to OpenStackModule
|
||||||
|
- Switch lb_listener module to OpenStackModule
|
||||||
|
- Switch lb_member module to OpenStackModule
|
||||||
|
- Switch lb_pool module to OpenStackModule
|
||||||
|
- Switch object module to OpenStackModule
|
||||||
|
- Switch port module to OpenStackModule
|
||||||
|
- Switch port_info module to OpenStackModule
|
||||||
|
- Switch project and project_info module to OpenStackModule
|
||||||
|
- Switch role_assignment module to OpenStackModule
|
||||||
|
- Switch user_info module to OpenStackModule
|
||||||
|
- image - Add support to setting image tags
|
||||||
|
release_summary: New modules for DNS and FIPs and bugfixes.
|
||||||
|
modules:
|
||||||
|
- description: Create or delete address scopes from OpenStack
|
||||||
|
name: address_scope
|
||||||
|
namespace: ''
|
||||||
|
- description: Getting information about dns zones
|
||||||
|
name: dns_zone_info
|
||||||
|
namespace: ''
|
||||||
|
- description: Get information about floating ips
|
||||||
|
name: floating_ip_info
|
||||||
|
namespace: ''
|
||||||
|
release_date: '2021-06-23'
|
||||||
|
1.5.1:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- Add mandatory requires_ansible version to metadata
|
||||||
|
- Add protocol listener octavia
|
||||||
|
- Add support check mode for all info modules
|
||||||
|
- Allow to attach multiple floating ips to a server
|
||||||
|
- Only add or remove router interfaces when needed
|
||||||
|
- Wait for pool to be active and online
|
||||||
|
minor_changes:
|
||||||
|
- Changed minversion in tox to 3.18.0
|
||||||
|
- Update IRC server in README
|
||||||
|
release_summary: Bugfixes for networking modules
|
||||||
|
release_date: '2021-09-02'
|
||||||
|
1.5.2:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- Add client and member listener timeouts for persistence (Ex. SSH)
|
||||||
|
- Added missing warn() used in cloud.openstack.quota
|
||||||
|
- Fix issue with same host and group names
|
||||||
|
- Flavor properties are not deleted on changes and id will stay
|
||||||
|
minor_changes:
|
||||||
|
- Add documentation links to README.md
|
||||||
|
- Don't run functional jobs on galaxy.yml change
|
||||||
|
- Move CI to use Ansible 2.12 version as main
|
||||||
|
release_summary: Bugfixes
|
||||||
|
release_date: '2021-11-09'
|
||||||
|
1.5.3:
|
||||||
|
changes:
|
||||||
|
bugfixes:
|
||||||
|
- Don't require allowed_address_pairs for port
|
||||||
|
- server_volume - check specified server is found
|
||||||
|
release_summary: Bugfixes
|
||||||
|
release_date: '2021-11-11'
|
||||||
|
1.6.0:
|
||||||
|
changes:
|
||||||
|
minor_changes:
|
||||||
|
- quota - Adds metadata_items parameter
|
||||||
|
release_summary: New modules for RBAC and Nova services
|
||||||
|
modules:
|
||||||
|
- description: Retrieve information about one or more OpenStack compute services
|
||||||
|
name: compute_service_info
|
||||||
|
namespace: ''
|
||||||
|
- description: Fetch Neutron policies.
|
||||||
|
name: neutron_rbac_policies_info
|
||||||
|
namespace: ''
|
||||||
|
- description: Create or delete a Neutron policy to apply a RBAC rule against
|
||||||
|
an object.
|
||||||
|
name: neutron_rbac_policy
|
||||||
|
namespace: ''
|
||||||
|
release_date: '2022-01-13'
|
||||||
|
|||||||
6
ci/playbooks/get_amphora_tarball.yaml
Normal file
6
ci/playbooks/get_amphora_tarball.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
- hosts: all
|
||||||
|
tasks:
|
||||||
|
- name: Download amphora tarball
|
||||||
|
get_url:
|
||||||
|
url: "https://tarballs.openstack.org/octavia/test-images/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
|
||||||
|
dest: /tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2
|
||||||
75
ci/publish/publish_collection.yml
Normal file
75
ci/publish/publish_collection.yml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
- hosts: all
|
||||||
|
vars:
|
||||||
|
collection_path: "{{ ansible_user_dir }}/{{ zuul.projects['opendev.org/openstack/ansible-collections-openstack'].src_dir }}"
|
||||||
|
build_collection_path: /tmp/collection_built/
|
||||||
|
ansible_galaxy_path: "~/.local/bin/ansible-galaxy"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
- name: Include role for pip
|
||||||
|
include_role:
|
||||||
|
name: ensure-pip
|
||||||
|
|
||||||
|
- name: Install ansible
|
||||||
|
pip:
|
||||||
|
name: ansible-core<2.12
|
||||||
|
|
||||||
|
- name: Discover tag version
|
||||||
|
set_fact:
|
||||||
|
version_tag: "{{ zuul.tag|default('no_version', true) }}"
|
||||||
|
|
||||||
|
- name: Fail if no tag version found
|
||||||
|
fail:
|
||||||
|
msg: "No tag was found in Zuul vars!"
|
||||||
|
when: version_tag == 'no_version'
|
||||||
|
|
||||||
|
- name: Create a directory for collection
|
||||||
|
file:
|
||||||
|
state: "{{ item }}"
|
||||||
|
path: "{{ build_collection_path }}"
|
||||||
|
loop:
|
||||||
|
- absent
|
||||||
|
- directory
|
||||||
|
|
||||||
|
- name: Set galaxy.yml for right version from tag
|
||||||
|
lineinfile:
|
||||||
|
path: '{{ collection_path }}/galaxy.yml'
|
||||||
|
regexp: '^version:.*'
|
||||||
|
line: 'version: {{ version_tag }}'
|
||||||
|
|
||||||
|
- name: Build collection
|
||||||
|
command: "{{ ansible_galaxy_path }} collection build --output-path {{ build_collection_path }} --force"
|
||||||
|
args:
|
||||||
|
chdir: "{{ collection_path }}"
|
||||||
|
|
||||||
|
- name: Publish content to Ansible Galaxy
|
||||||
|
block:
|
||||||
|
- name: Create ansible.cfg configuration file tempfile
|
||||||
|
tempfile:
|
||||||
|
state: file
|
||||||
|
suffix: .cfg
|
||||||
|
register: _ansiblecfg_tmp
|
||||||
|
|
||||||
|
- name: Create ansible.cfg configuration file
|
||||||
|
copy:
|
||||||
|
dest: "{{ _ansiblecfg_tmp.path }}"
|
||||||
|
mode: 0600
|
||||||
|
content: |
|
||||||
|
[galaxy]
|
||||||
|
server_list = release_galaxy
|
||||||
|
|
||||||
|
[galaxy_server.release_galaxy]
|
||||||
|
url = {{ ansible_galaxy_info.url }}
|
||||||
|
token = {{ ansible_galaxy_info.token }}
|
||||||
|
|
||||||
|
- name: Publish collection to Ansible Galaxy / Automation Hub
|
||||||
|
environment:
|
||||||
|
ANSIBLE_CONFIG: "{{ _ansiblecfg_tmp.path }}"
|
||||||
|
shell: >-
|
||||||
|
{{ ansible_galaxy_path }} collection publish -vvv
|
||||||
|
{{ build_collection_path }}/openstack-cloud-{{ version_tag }}.tar.gz
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: Shred ansible-galaxy credentials
|
||||||
|
command: "shred {{ _ansiblecfg_tmp.path }}"
|
||||||
1
ci/roles/address_scope/defaults/main.yml
Normal file
1
ci/roles/address_scope/defaults/main.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
address_scope_name: "adrdess_scope"
|
||||||
40
ci/roles/address_scope/tasks/main.yml
Normal file
40
ci/roles/address_scope/tasks/main.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
- name: Create address_scope
|
||||||
|
openstack.cloud.address_scope:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ address_scope_name }}"
|
||||||
|
shared: False
|
||||||
|
ip_version: "4"
|
||||||
|
register: create_address_scope
|
||||||
|
|
||||||
|
- name: Verify address scope
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- create_address_scope is successful
|
||||||
|
- create_address_scope is changed
|
||||||
|
- create_address_scope.address_scope.name == address_scope_name
|
||||||
|
- create_address_scope.address_scope.is_shared == False
|
||||||
|
- create_address_scope.address_scope.ip_version == 4
|
||||||
|
|
||||||
|
- name: Update address scope
|
||||||
|
openstack.cloud.address_scope:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ address_scope_name }}"
|
||||||
|
shared: True
|
||||||
|
ip_version: "4"
|
||||||
|
register: update_address_scope
|
||||||
|
|
||||||
|
- name: Verify updated IPv4 address scope
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- update_address_scope is successful
|
||||||
|
- update_address_scope is changed
|
||||||
|
- update_address_scope.address_scope.name == address_scope_name
|
||||||
|
- update_address_scope.address_scope.is_shared == True
|
||||||
|
- update_address_scope.address_scope.ip_version == 4
|
||||||
|
|
||||||
|
- name: Delete created address scope
|
||||||
|
openstack.cloud.address_scope:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ address_scope_name }}"
|
||||||
|
state: absent
|
||||||
41
ci/roles/dns_zone_info/tasks/main.yml
Normal file
41
ci/roles/dns_zone_info/tasks/main.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
- name: Set random prefix
|
||||||
|
set_fact:
|
||||||
|
prefix: "{{ 99999999 | random | to_uuid | hash('md5') }}"
|
||||||
|
|
||||||
|
- name: Create dns zone
|
||||||
|
openstack.cloud.dns_zone:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ (prefix + '.test.zone.') }}"
|
||||||
|
email: test@example.net
|
||||||
|
|
||||||
|
- name: Getting info about dns zones
|
||||||
|
openstack.cloud.dns_zone_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
register: zones
|
||||||
|
|
||||||
|
- name: assert result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- zones is success
|
||||||
|
- zones is not changed
|
||||||
|
- zones | length > 0
|
||||||
|
|
||||||
|
- name: Getting info about created zone
|
||||||
|
openstack.cloud.dns_zone_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ (prefix + '.test.zone.') }}"
|
||||||
|
register: zone
|
||||||
|
|
||||||
|
- name: assert result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- zone is success
|
||||||
|
- zone is not changed
|
||||||
|
- zone.zones | length == 1
|
||||||
|
|
||||||
|
- name: Drop created dns zone
|
||||||
|
openstack.cloud.dns_zone:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ (prefix + '.test.zone.') }}"
|
||||||
|
state: absent
|
||||||
466
ci/roles/floating_ip/tasks/main.yml
Normal file
466
ci/roles/floating_ip/tasks/main.yml
Normal file
@@ -0,0 +1,466 @@
|
|||||||
|
---
|
||||||
|
# Prepare environment
|
||||||
|
- name: Gather information about public network
|
||||||
|
openstack.cloud.networks_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: public
|
||||||
|
register: public_network
|
||||||
|
|
||||||
|
- name: Assert that public network exists
|
||||||
|
assert:
|
||||||
|
that: public_network.openstack_networks|length == 1
|
||||||
|
|
||||||
|
- name: Create external network
|
||||||
|
openstack.cloud.network:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_external
|
||||||
|
external: true
|
||||||
|
|
||||||
|
- name: Create external subnet
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
network_name: ansible_external
|
||||||
|
name: ansible_external_subnet
|
||||||
|
cidr: 10.6.6.0/24
|
||||||
|
|
||||||
|
- name: Create external port 1
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_external_port1
|
||||||
|
network: ansible_external
|
||||||
|
fixed_ips:
|
||||||
|
- ip_address: 10.6.6.50
|
||||||
|
|
||||||
|
- name: Create external port 2
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_external_port2
|
||||||
|
network: ansible_external
|
||||||
|
fixed_ips:
|
||||||
|
- ip_address: 10.6.6.51
|
||||||
|
|
||||||
|
- name: Create internal network
|
||||||
|
openstack.cloud.network:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_internal
|
||||||
|
external: false
|
||||||
|
|
||||||
|
- name: Create internal subnet
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
network_name: ansible_internal
|
||||||
|
name: ansible_internal_subnet
|
||||||
|
cidr: 10.7.7.0/24
|
||||||
|
|
||||||
|
- name: Create internal port 1
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_internal_port1
|
||||||
|
network: ansible_internal
|
||||||
|
fixed_ips:
|
||||||
|
- ip_address: 10.7.7.100
|
||||||
|
|
||||||
|
- name: Create internal port 2
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_internal_port2
|
||||||
|
network: ansible_internal
|
||||||
|
fixed_ips:
|
||||||
|
- ip_address: 10.7.7.101
|
||||||
|
|
||||||
|
- name: Create internal port 3
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_internal_port3
|
||||||
|
network: ansible_internal
|
||||||
|
fixed_ips:
|
||||||
|
- ip_address: 10.7.7.102
|
||||||
|
|
||||||
|
- name: Create router 1
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_router1
|
||||||
|
network: ansible_external
|
||||||
|
external_fixed_ips:
|
||||||
|
- subnet: ansible_external_subnet
|
||||||
|
ip: 10.6.6.10
|
||||||
|
interfaces:
|
||||||
|
- net: ansible_internal
|
||||||
|
subnet: ansible_internal_subnet
|
||||||
|
portip: 10.7.7.1
|
||||||
|
|
||||||
|
# Router 2 is required for the simplest, first test that assigns a new floating IP to server
|
||||||
|
# from first available external network or nova pool which is DevStack's public network
|
||||||
|
- name: Create router 2
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_router2
|
||||||
|
network: public
|
||||||
|
interfaces:
|
||||||
|
- net: ansible_internal
|
||||||
|
subnet: ansible_internal_subnet
|
||||||
|
portip: 10.7.7.10
|
||||||
|
|
||||||
|
- name: Get all floating ips
|
||||||
|
openstack.cloud.floating_ip_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
register: fips
|
||||||
|
|
||||||
|
- name: Check if public network has any floating ips
|
||||||
|
set_fact:
|
||||||
|
public_network_had_fips: "{{ fips.floating_ips|
|
||||||
|
selectattr('floating_network_id', '==', public_network.openstack_networks.0.id)|
|
||||||
|
list|length > 0 }}"
|
||||||
|
|
||||||
|
# TODO: Replace with appropriate Ansible module once available
|
||||||
|
- name: Create a floating ip on public network (required for simplest, first floating ip test)
|
||||||
|
command: openstack --os-cloud={{ cloud }} floating ip create public
|
||||||
|
when: not public_network_had_fips
|
||||||
|
|
||||||
|
# TODO: Replace with appropriate Ansible module once available
|
||||||
|
- name: Create floating ip 1 on external network
|
||||||
|
command: >
|
||||||
|
openstack --os-cloud={{ cloud }} floating ip create
|
||||||
|
--subnet ansible_external_subnet
|
||||||
|
--floating-ip-address 10.6.6.150
|
||||||
|
ansible_external
|
||||||
|
when: fips.floating_ips|length == 0 or
|
||||||
|
"10.6.6.150" not in fips.floating_ips|map(attribute="floating_ip_address")|list
|
||||||
|
|
||||||
|
- name: Create server with one nic
|
||||||
|
openstack.cloud.server:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_server1
|
||||||
|
image: "{{ image }}"
|
||||||
|
flavor: m1.tiny
|
||||||
|
nics:
|
||||||
|
# one nic only else simple, first floating ip test does not work
|
||||||
|
- port-name: ansible_internal_port1
|
||||||
|
auto_ip: false
|
||||||
|
wait: true
|
||||||
|
|
||||||
|
- name: Get info about server
|
||||||
|
openstack.cloud.server_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
server: ansible_server1
|
||||||
|
register: info
|
||||||
|
|
||||||
|
- name: Assert one internal port and no floating ips on server 1
|
||||||
|
# If this assertion fails because server has an public ipv4 address (public_v4) then make sure
|
||||||
|
# that no floating ip on public network is associated with "10.7.7.100" before running this role
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- info.openstack_servers|length == 1
|
||||||
|
- info.openstack_servers.0.public_v4|length == 0
|
||||||
|
- info.openstack_servers.0.public_v6|length == 0
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|length == 1
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|sort|list == ["10.7.7.100"]
|
||||||
|
|
||||||
|
- name: Create server with two nics
|
||||||
|
openstack.cloud.server:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_server2
|
||||||
|
image: "{{ image }}"
|
||||||
|
flavor: m1.tiny
|
||||||
|
nics:
|
||||||
|
- port-name: ansible_internal_port2
|
||||||
|
- port-name: ansible_internal_port3
|
||||||
|
auto_ip: false
|
||||||
|
wait: true
|
||||||
|
|
||||||
|
- name: Get info about server
|
||||||
|
openstack.cloud.server_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
server: ansible_server2
|
||||||
|
register: info
|
||||||
|
|
||||||
|
- name: Assert two internal ports and no floating ips on server 2
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- info.openstack_servers|length == 1
|
||||||
|
- info.openstack_servers.0.public_v4|length == 0
|
||||||
|
- info.openstack_servers.0.public_v6|length == 0
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|length == 2
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|sort|list ==
|
||||||
|
["10.7.7.101", "10.7.7.102"]
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
- name: Assign new floating IP to server from first available external network or nova pool
|
||||||
|
openstack.cloud.floating_ip:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
server: ansible_server1
|
||||||
|
wait: true
|
||||||
|
|
||||||
|
- name: Get info about server
|
||||||
|
openstack.cloud.server_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
server: ansible_server1
|
||||||
|
register: info
|
||||||
|
|
||||||
|
- name: Assert one internal port and one floating ip on server 1
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|length == 2
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="OS-EXT-IPS:type")|sort|list ==
|
||||||
|
["fixed", "floating"]
|
||||||
|
|
||||||
|
- name: Detach floating IP from server
|
||||||
|
openstack.cloud.floating_ip:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
server: ansible_server1
|
||||||
|
network: public
|
||||||
|
floating_ip_address: "{{ (info.openstack_servers.0.addresses.ansible_internal|
|
||||||
|
selectattr('OS-EXT-IPS:type', '==', 'floating')|map(attribute='addr')|list)[0] }}"
|
||||||
|
|
||||||
|
- name: Get info about server
|
||||||
|
openstack.cloud.server_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
server: ansible_server1
|
||||||
|
register: info
|
||||||
|
# When detaching a floating ip from an instance there might be a delay until openstack.cloud.server_info
|
||||||
|
# does not list it any more in info.openstack_servers.0.addresses.ansible_internal, so retry if necessary.
|
||||||
|
retries: 10
|
||||||
|
delay: 3
|
||||||
|
until: info.openstack_servers.0.addresses.ansible_internal|length == 1
|
||||||
|
|
||||||
|
- name: Assert one internal port on server 1
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|length == 1
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|list == ["10.7.7.100"]
|
||||||
|
|
||||||
|
- name: Assign floating IP to server
|
||||||
|
openstack.cloud.floating_ip:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
reuse: yes
|
||||||
|
server: ansible_server2
|
||||||
|
network: public
|
||||||
|
fixed_address: 10.7.7.101
|
||||||
|
wait: true
|
||||||
|
|
||||||
|
- name: Get info about server
|
||||||
|
openstack.cloud.server_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
server: ansible_server2
|
||||||
|
register: info
|
||||||
|
|
||||||
|
- name: Assert two internal ports and one floating ip on server 2
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|length == 3
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="OS-EXT-IPS:type")|sort|list ==
|
||||||
|
["fixed", "fixed", "floating"]
|
||||||
|
|
||||||
|
- name: Assign a second, specific floating IP to server
|
||||||
|
openstack.cloud.floating_ip:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
reuse: yes
|
||||||
|
server: ansible_server2
|
||||||
|
network: ansible_external
|
||||||
|
fixed_address: 10.7.7.102
|
||||||
|
floating_ip_address: "10.6.6.150"
|
||||||
|
|
||||||
|
# We cannot wait for second floating ip to be attached because OpenStackSDK checks only for first floating ip
|
||||||
|
# Ref.: https://github.com/openstack/openstacksdk/blob/e0372b72af8c5f471fc17e53434d7a814ca958bd/openstack/cloud/_floating_ip.py#L733
|
||||||
|
|
||||||
|
- name: Get info about server
|
||||||
|
openstack.cloud.server_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
server: ansible_server2
|
||||||
|
register: info
|
||||||
|
# retry because we cannot wait for second floating ip
|
||||||
|
retries: 10
|
||||||
|
delay: 3
|
||||||
|
until: info.openstack_servers.0.addresses.ansible_internal|length == 4
|
||||||
|
|
||||||
|
- name: Assert two internal ports and two floating ips on server 2
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|length == 4
|
||||||
|
- ("10.6.6.150" in info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|sort|list)
|
||||||
|
|
||||||
|
- name: Detach second floating IP from server
|
||||||
|
openstack.cloud.floating_ip:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
server: ansible_server2
|
||||||
|
network: ansible_external
|
||||||
|
floating_ip_address: "10.6.6.150"
|
||||||
|
|
||||||
|
- name: Get info about server
|
||||||
|
openstack.cloud.server_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
server: ansible_server2
|
||||||
|
register: info
|
||||||
|
# When detaching a floating ip from an instance there might be a delay until openstack.cloud.server_info
|
||||||
|
# does not list it any more in info.openstack_servers.0.addresses.ansible_internal, so retry if necessary.
|
||||||
|
retries: 10
|
||||||
|
delay: 3
|
||||||
|
until: info.openstack_servers.0.addresses.ansible_internal|length == 3
|
||||||
|
|
||||||
|
- name: Assert two internal ports and one floating ip on server 2
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|length == 3
|
||||||
|
|
||||||
|
- name: Detach remaining floating IP from server
|
||||||
|
openstack.cloud.floating_ip:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
server: ansible_server2
|
||||||
|
network: public
|
||||||
|
floating_ip_address: "{{ (info.openstack_servers.0.addresses.ansible_internal|
|
||||||
|
selectattr('OS-EXT-IPS:type', '==', 'floating')|map(attribute='addr')|list)[0] }}"
|
||||||
|
|
||||||
|
- name: Get info about server
|
||||||
|
openstack.cloud.server_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
server: ansible_server2
|
||||||
|
register: info
|
||||||
|
# When detaching a floating ip from an instance there might be a delay until openstack.cloud.server_info
|
||||||
|
# does not list it any more in info.openstack_servers.0.addresses.ansible_internal, so retry if necessary.
|
||||||
|
retries: 10
|
||||||
|
delay: 3
|
||||||
|
until: info.openstack_servers.0.addresses.ansible_internal|length == 2
|
||||||
|
|
||||||
|
- name: Assert two internal ports on server 2
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|length == 2
|
||||||
|
- info.openstack_servers.0.addresses.ansible_internal|map(attribute="addr")|list == ["10.7.7.101", "10.7.7.102"]
|
||||||
|
|
||||||
|
# Clean environment
|
||||||
|
- name: Delete server with two nics
|
||||||
|
openstack.cloud.server:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_server2
|
||||||
|
wait: true
|
||||||
|
|
||||||
|
- name: Delete server with one nic
|
||||||
|
openstack.cloud.server:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_server1
|
||||||
|
wait: true
|
||||||
|
|
||||||
|
- name: Get all floating ips
|
||||||
|
openstack.cloud.floating_ip_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
register: fips
|
||||||
|
|
||||||
|
# TODO: Replace with appropriate Ansible module once available
|
||||||
|
- name: Delete floating ip on public network if we created it
|
||||||
|
when: not public_network_had_fips
|
||||||
|
command: >
|
||||||
|
openstack --os-cloud={{ cloud }} floating ip delete
|
||||||
|
{{ fips.floating_ips|selectattr('floating_network_id', '==', public_network.openstack_networks.0.id)|
|
||||||
|
map(attribute="floating_ip_address")|list|join(' ') }}
|
||||||
|
|
||||||
|
# TODO: Replace with appropriate Ansible module once available
|
||||||
|
- name: Delete floating ip 1
|
||||||
|
command: openstack --os-cloud={{ cloud }} floating ip delete 10.6.6.150
|
||||||
|
when: fips.floating_ips|length > 0 and "10.6.6.150" in fips.floating_ips|map(attribute="floating_ip_address")|list
|
||||||
|
|
||||||
|
- name: Get remaining floating ips on external network
|
||||||
|
openstack.cloud.floating_ip_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
floating_network: ansible_external
|
||||||
|
register: fips
|
||||||
|
|
||||||
|
# TODO: Replace with appropriate Ansible module once available
|
||||||
|
# The first, simple floating ip test might have allocated a floating ip on the external network.
|
||||||
|
# This floating ip must be removed before external network can be deleted.
|
||||||
|
- name: Delete remaining floating ips on external network
|
||||||
|
when: fips.floating_ips|length > 0
|
||||||
|
command: >
|
||||||
|
openstack --os-cloud={{ cloud }} floating ip delete
|
||||||
|
{{ fips.floating_ips|map(attribute="floating_ip_address")|list|join(' ') }}
|
||||||
|
|
||||||
|
# Remove routers after floating ips have been detached and disassociated else removal fails with
|
||||||
|
# Error detaching interface from router ***: Client Error for url: ***,
|
||||||
|
# Router interface for subnet *** on router *** cannot be deleted,
|
||||||
|
# as it is required by one or more floating IPs.
|
||||||
|
|
||||||
|
- name: Delete router 2
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_router2
|
||||||
|
|
||||||
|
- name: Delete router 1
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_router1
|
||||||
|
|
||||||
|
- name: Delete internal port 3
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_internal_port3
|
||||||
|
|
||||||
|
- name: Delete internal port 2
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_internal_port2
|
||||||
|
|
||||||
|
- name: Delete internal port 1
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_internal_port1
|
||||||
|
|
||||||
|
- name: Delete internal subnet
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_internal_subnet
|
||||||
|
|
||||||
|
- name: Delete internal network
|
||||||
|
openstack.cloud.network:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_internal
|
||||||
|
|
||||||
|
- name: Delete external port 2
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_external_port2
|
||||||
|
|
||||||
|
- name: Delete external port 1
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_external_port1
|
||||||
|
|
||||||
|
- name: Delete external subnet
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_external_subnet
|
||||||
|
|
||||||
|
- name: Delete external network
|
||||||
|
openstack.cloud.network:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: ansible_external
|
||||||
11
ci/roles/floating_ip_info/tasks/main.yml
Normal file
11
ci/roles/floating_ip_info/tasks/main.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Getting info about allocated ips
|
||||||
|
openstack.cloud.floating_ip_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
register: fips
|
||||||
|
|
||||||
|
- name: assert result
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- fips is success
|
||||||
|
- fips is not changed
|
||||||
@@ -1 +1,4 @@
|
|||||||
image_name: ansible_image
|
image_name: ansible_image
|
||||||
|
image_tags:
|
||||||
|
- test
|
||||||
|
- ansible
|
||||||
|
|||||||
@@ -13,9 +13,20 @@
|
|||||||
name: "{{ image_name }}"
|
name: "{{ image_name }}"
|
||||||
filename: "{{ tmp_file.stdout }}"
|
filename: "{{ tmp_file.stdout }}"
|
||||||
disk_format: raw
|
disk_format: raw
|
||||||
|
tags: "{{ image_tags }}"
|
||||||
register: image
|
register: image
|
||||||
|
|
||||||
- debug: var=image
|
- name: Get details of created image
|
||||||
|
openstack.cloud.image_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
image: "{{ image_name }}"
|
||||||
|
register: image_info_result
|
||||||
|
|
||||||
|
- name: Verify image info
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "image_info_result.openstack_image.name == image_name"
|
||||||
|
- "image_info_result.openstack_image.tags | sort == image_tags | sort"
|
||||||
|
|
||||||
- name: Delete raw image (defaults)
|
- name: Delete raw image (defaults)
|
||||||
openstack.cloud.image:
|
openstack.cloud.image:
|
||||||
@@ -40,8 +51,6 @@
|
|||||||
distro: ubuntu
|
distro: ubuntu
|
||||||
register: image
|
register: image
|
||||||
|
|
||||||
- debug: var=image
|
|
||||||
|
|
||||||
- name: Delete raw image (complex)
|
- name: Delete raw image (complex)
|
||||||
openstack.cloud.image:
|
openstack.cloud.image:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
@@ -52,3 +61,14 @@
|
|||||||
file:
|
file:
|
||||||
name: "{{ tmp_file.stdout }}"
|
name: "{{ tmp_file.stdout }}"
|
||||||
state: absent
|
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:
|
register:
|
||||||
keypair
|
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
|
# This assert verifies that Ansible is capable serializing data returned by SDK
|
||||||
- name: Ensure private key is returned
|
- name: Ensure private key is returned
|
||||||
assert:
|
assert:
|
||||||
@@ -19,6 +30,17 @@
|
|||||||
name: "{{ keypair_name }}"
|
name: "{{ keypair_name }}"
|
||||||
state: absent
|
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
|
- name: Generate test key file
|
||||||
user:
|
user:
|
||||||
name: "{{ ansible_env.USER }}"
|
name: "{{ ansible_env.USER }}"
|
||||||
@@ -32,12 +54,34 @@
|
|||||||
state: present
|
state: present
|
||||||
public_key_file: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
|
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)
|
- name: Delete keypair (file)
|
||||||
openstack.cloud.keypair:
|
openstack.cloud.keypair:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
name: "{{ keypair_name }}"
|
name: "{{ keypair_name }}"
|
||||||
state: absent
|
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)
|
- name: Create keypair (key)
|
||||||
openstack.cloud.keypair:
|
openstack.cloud.keypair:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
@@ -45,12 +89,34 @@
|
|||||||
state: present
|
state: present
|
||||||
public_key: "{{ lookup('file', '~/.ssh/shade_id_rsa.pub') }}"
|
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)
|
- name: Delete keypair (key)
|
||||||
openstack.cloud.keypair:
|
openstack.cloud.keypair:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
name: "{{ keypair_name }}"
|
name: "{{ keypair_name }}"
|
||||||
state: absent
|
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
|
- name: Delete test key pub file
|
||||||
file:
|
file:
|
||||||
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
|
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
|
||||||
|
|||||||
@@ -5,6 +5,29 @@
|
|||||||
state: present
|
state: present
|
||||||
name: "{{ role_name }}"
|
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
|
- name: Delete keystone role
|
||||||
openstack.cloud.identity_role:
|
openstack.cloud.identity_role:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
|
|||||||
3
ci/roles/loadbalancer/defaults/main.yml
Normal file
3
ci/roles/loadbalancer/defaults/main.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
network_name: network_lb
|
||||||
|
subnet_name: subnet_lb
|
||||||
|
lb_name: test_lb
|
||||||
50
ci/roles/loadbalancer/tasks/main.yml
Normal file
50
ci/roles/loadbalancer/tasks/main.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
- name: Create network {{ network_name }} for LB
|
||||||
|
openstack.cloud.network:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ network_name }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create subnet {{ subnet_name }} on network {{ network_name }} for LB
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
network_name: "{{ network_name }}"
|
||||||
|
name: "{{ subnet_name }}"
|
||||||
|
state: present
|
||||||
|
enable_dhcp: true
|
||||||
|
dns_nameservers:
|
||||||
|
- 8.8.8.7
|
||||||
|
- 8.8.8.8
|
||||||
|
cidr: 192.168.0.0/24
|
||||||
|
gateway_ip: 192.168.0.1
|
||||||
|
allocation_pool_start: 192.168.0.2
|
||||||
|
allocation_pool_end: 192.168.0.254
|
||||||
|
|
||||||
|
- name: Create loadbalancer - generic
|
||||||
|
openstack.cloud.loadbalancer:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ lb_name }}"
|
||||||
|
vip_subnet: "{{ subnet_name }}"
|
||||||
|
timeout: 450
|
||||||
|
|
||||||
|
- name: Delete loadbalancer
|
||||||
|
openstack.cloud.loadbalancer:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: "{{ lb_name }}"
|
||||||
|
timeout: 150
|
||||||
|
|
||||||
|
- name: Delete subnet {{ subnet_name }} on network {{ network_name }}
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
network_name: "{{ network_name }}"
|
||||||
|
name: "{{ subnet_name }}"
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Delete network {{ network_name }} of LB
|
||||||
|
openstack.cloud.network:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ network_name }}"
|
||||||
|
state: absent
|
||||||
85
ci/roles/neutron_rbac/tasks/main.yml
Normal file
85
ci/roles/neutron_rbac/tasks/main.yml
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
---
|
||||||
|
# General run of tests
|
||||||
|
# - Prepare projects/network objects
|
||||||
|
# - Create rbac object
|
||||||
|
# - Get rbac object info
|
||||||
|
# - Verify RBAC object match
|
||||||
|
# - Delete rbac object
|
||||||
|
# - Get rbac object info
|
||||||
|
# - Verify RBAC object deleted
|
||||||
|
|
||||||
|
- name: Create source project
|
||||||
|
openstack.cloud.project:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: source_project
|
||||||
|
description: Source project for network RBAC test
|
||||||
|
domain_id: default
|
||||||
|
enabled: True
|
||||||
|
register: source_project
|
||||||
|
|
||||||
|
- name: Create network - generic
|
||||||
|
openstack.cloud.network:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ network_name }}"
|
||||||
|
state: present
|
||||||
|
project: "{{ source_project.project.id }}"
|
||||||
|
shared: false
|
||||||
|
external: "{{ network_external }}"
|
||||||
|
register: network
|
||||||
|
|
||||||
|
- name: Create target project
|
||||||
|
openstack.cloud.project:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: ansible_project
|
||||||
|
description: Target project for network RBAC test
|
||||||
|
domain_id: default
|
||||||
|
enabled: True
|
||||||
|
register: target_project
|
||||||
|
|
||||||
|
- name: Create a new network RBAC policy
|
||||||
|
openstack.cloud.neutron_rbac_policy:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
object_id: "{{ network.network.id }}"
|
||||||
|
object_type: 'network'
|
||||||
|
action: 'access_as_shared'
|
||||||
|
target_project_id: "{{ target_project.project.id }}"
|
||||||
|
project_id: "{{ source_project.project.id }}"
|
||||||
|
register: rbac_policy
|
||||||
|
|
||||||
|
- name: Get all rbac policies for {{ source_project.project.name }} - after creation
|
||||||
|
openstack.cloud.neutron_rbac_policies_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
project_id: "{{ source_project.project.id }}"
|
||||||
|
register: rbac_policies
|
||||||
|
|
||||||
|
- name: Capture all existing policy IDs
|
||||||
|
set_fact:
|
||||||
|
rbac_policy_ids: "{{ rbac_policies.policies | map(attribute='id') | list }}"
|
||||||
|
|
||||||
|
- name: Verify policy exists - after creation
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- rbac_policy.policy.id in rbac_policy_ids
|
||||||
|
|
||||||
|
- name: Delete RBAC policy
|
||||||
|
openstack.cloud.neutron_rbac_policy:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
policy_id: "{{ rbac_policy.policy.id }}"
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Get all rbac policies for {{ source_project.project.name }} - after deletion
|
||||||
|
openstack.cloud.neutron_rbac_policies_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
project_id: "{{ source_project.project.id }}"
|
||||||
|
register: rbac_policies_remaining
|
||||||
|
|
||||||
|
- name: Capture all remaining policy IDs
|
||||||
|
set_fact:
|
||||||
|
remaining_rbac_policy_ids: "{{ rbac_policies_remaining.policies | map(attribute='id') | list }}"
|
||||||
|
|
||||||
|
- name: Verify policy does not exist - after deletion
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not rbac_policy.policy.id in remaining_rbac_policy_ids
|
||||||
15
ci/roles/nova_services/tasks/main.yml
Normal file
15
ci/roles/nova_services/tasks/main.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Get nova compute services info
|
||||||
|
openstack.cloud.compute_service_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
binary: "nova-compute"
|
||||||
|
register: result
|
||||||
|
failed_when: "result.openstack_compute_services | length <= 0"
|
||||||
|
|
||||||
|
- name: Get nova conductor services info
|
||||||
|
openstack.cloud.compute_service_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
binary: "nova-conductor"
|
||||||
|
register: result
|
||||||
|
failed_when: "result.openstack_compute_services | length <= 0"
|
||||||
1
ci/roles/object_container/defaults/main.yml
Normal file
1
ci/roles/object_container/defaults/main.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
container_name: "test-container"
|
||||||
60
ci/roles/object_container/tasks/main.yml
Normal file
60
ci/roles/object_container/tasks/main.yml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
- module_defaults:
|
||||||
|
openstack.cloud.object_container:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
block:
|
||||||
|
- name: Create an empty container
|
||||||
|
openstack.cloud.object_container:
|
||||||
|
container: "{{ container_name }}"
|
||||||
|
register: container
|
||||||
|
|
||||||
|
- name: Verify container was created
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- container is success
|
||||||
|
- container is changed
|
||||||
|
- container.container.name == container_name
|
||||||
|
|
||||||
|
- name: Set metadata for a container
|
||||||
|
openstack.cloud.object_container:
|
||||||
|
container: "{{ container_name }}"
|
||||||
|
metadata: "Cache-Control='no-cache'"
|
||||||
|
register: set_meta
|
||||||
|
|
||||||
|
- name: Verify container metadata was set
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- set_meta is success
|
||||||
|
- set_meta is changed
|
||||||
|
|
||||||
|
- name: Delete some keys from container metadata
|
||||||
|
openstack.cloud.object_container:
|
||||||
|
container: "{{ container_name }}"
|
||||||
|
keys:
|
||||||
|
- Cache-Control
|
||||||
|
register: delete_meta
|
||||||
|
|
||||||
|
- name: Verify some keys from container metadata was deleted
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- delete_meta is success
|
||||||
|
- delete_meta is changed
|
||||||
|
|
||||||
|
- name: Delete container
|
||||||
|
openstack.cloud.object_container:
|
||||||
|
container: "{{ container_name }}"
|
||||||
|
state: absent
|
||||||
|
register: deleted
|
||||||
|
|
||||||
|
- name: Verify container was deleted
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- deleted is success
|
||||||
|
- deleted is changed
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: Delete container
|
||||||
|
openstack.cloud.object_container:
|
||||||
|
container: "{{ container_name }}"
|
||||||
|
state: absent
|
||||||
|
ignore_errors: yes
|
||||||
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
|
||||||
@@ -4,3 +4,6 @@ subnet_name: ansible_port_subnet
|
|||||||
port_name: ansible_port
|
port_name: ansible_port
|
||||||
secgroup_name: ansible_port_secgroup
|
secgroup_name: ansible_port_secgroup
|
||||||
no_security_groups: True
|
no_security_groups: True
|
||||||
|
binding_profile:
|
||||||
|
"pci_slot": "0000:03:11.1"
|
||||||
|
"physical_network": "provider"
|
||||||
|
|||||||
@@ -88,6 +88,30 @@
|
|||||||
state: absent
|
state: absent
|
||||||
name: "{{ secgroup_name }}"
|
name: "{{ secgroup_name }}"
|
||||||
|
|
||||||
|
- name: Test port binding config (runs from train release sdk > 0.28)
|
||||||
|
block:
|
||||||
|
- name: Create port (with binding profile)
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ port_name }}"
|
||||||
|
network: "{{ network_name }}"
|
||||||
|
binding_profile: "{{ binding_profile }}"
|
||||||
|
register: port
|
||||||
|
|
||||||
|
- name: Assert binding:profile exists in created port
|
||||||
|
assert:
|
||||||
|
that: "port.port['binding:profile']"
|
||||||
|
|
||||||
|
- debug: var=port
|
||||||
|
|
||||||
|
- name: Delete port (with binding profile)
|
||||||
|
openstack.cloud.port:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: "{{ port_name }}"
|
||||||
|
when: sdk_version is version(0.28, '>')
|
||||||
|
|
||||||
- name: Delete subnet
|
- name: Delete subnet
|
||||||
openstack.cloud.subnet:
|
openstack.cloud.subnet:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
|
|||||||
@@ -15,6 +15,30 @@
|
|||||||
name: shade_subnet1
|
name: shade_subnet1
|
||||||
cidr: 10.7.7.0/24
|
cidr: 10.7.7.0/24
|
||||||
|
|
||||||
|
- name: Create subnet2
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
network_name: "{{ network_name }}"
|
||||||
|
name: shade_subnet2
|
||||||
|
cidr: 10.8.8.0/24
|
||||||
|
|
||||||
|
- name: Create subnet3
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
network_name: "{{ network_name }}"
|
||||||
|
name: shade_subnet3
|
||||||
|
cidr: 10.9.9.0/24
|
||||||
|
|
||||||
|
- name: Create subnet4
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
network_name: "{{ network_name }}"
|
||||||
|
name: shade_subnet4
|
||||||
|
cidr: 10.10.10.0/24
|
||||||
|
|
||||||
- name: Create router
|
- name: Create router
|
||||||
openstack.cloud.router:
|
openstack.cloud.router:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
@@ -29,6 +53,19 @@
|
|||||||
interfaces:
|
interfaces:
|
||||||
- shade_subnet1
|
- shade_subnet1
|
||||||
|
|
||||||
|
- name: Update router (add interface) again
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- shade_subnet1
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert idempotent module
|
||||||
|
assert:
|
||||||
|
that: result is not changed
|
||||||
|
|
||||||
- name: Gather routers info
|
- name: Gather routers info
|
||||||
openstack.cloud.routers_info:
|
openstack.cloud.routers_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
@@ -43,6 +80,93 @@
|
|||||||
- "result.openstack_routers.0.name == router_name"
|
- "result.openstack_routers.0.name == router_name"
|
||||||
- (result.openstack_routers.0.interfaces_info|length) == 1
|
- (result.openstack_routers.0.interfaces_info|length) == 1
|
||||||
|
|
||||||
|
- name: Update router (change interfaces)
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- net: '{{ network_name }}'
|
||||||
|
subnet: shade_subnet2
|
||||||
|
portip: 10.8.8.1
|
||||||
|
- net: '{{ network_name }}'
|
||||||
|
subnet: shade_subnet3
|
||||||
|
- shade_subnet4
|
||||||
|
|
||||||
|
- name: Update router (change interfaces) again
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- net: '{{ network_name }}'
|
||||||
|
subnet: shade_subnet2
|
||||||
|
portip: 10.8.8.1
|
||||||
|
- net: '{{ network_name }}'
|
||||||
|
subnet: shade_subnet3
|
||||||
|
- shade_subnet4
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert idempotent module
|
||||||
|
assert:
|
||||||
|
that: result is not changed
|
||||||
|
|
||||||
|
- name: Gather routers info
|
||||||
|
openstack.cloud.routers_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
filters:
|
||||||
|
admin_state_up: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Verify routers info
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.openstack_routers.0.name == router_name"
|
||||||
|
- (result.openstack_routers.0.interfaces_info|length) == 3
|
||||||
|
- result.openstack_routers.0.interfaces_info|map(attribute='ip_address')|sort|list ==
|
||||||
|
['10.10.10.1', '10.8.8.1', '10.9.9.1']
|
||||||
|
|
||||||
|
- name: Update router (remove interface)
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- net: '{{ network_name }}'
|
||||||
|
subnet: shade_subnet1
|
||||||
|
portip: 10.7.7.1
|
||||||
|
|
||||||
|
- name: Update router (remove interface) again
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- net: '{{ network_name }}'
|
||||||
|
subnet: shade_subnet1
|
||||||
|
portip: 10.7.7.1
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert idempotent module
|
||||||
|
assert:
|
||||||
|
that: result is not changed
|
||||||
|
|
||||||
|
- name: Gather routers info
|
||||||
|
openstack.cloud.routers_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
filters:
|
||||||
|
admin_state_up: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Verify routers info
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.openstack_routers.0.name == router_name"
|
||||||
|
- (result.openstack_routers.0.interfaces_info|length) == 1
|
||||||
|
- result.openstack_routers.0.interfaces_info.0.ip_address == '10.7.7.1'
|
||||||
|
|
||||||
# Admin operation
|
# Admin operation
|
||||||
- name: Create external network
|
- name: Create external network
|
||||||
openstack.cloud.network:
|
openstack.cloud.network:
|
||||||
@@ -53,12 +177,12 @@
|
|||||||
when:
|
when:
|
||||||
- network_external
|
- network_external
|
||||||
|
|
||||||
- name: Create subnet2
|
- name: Create subnet5
|
||||||
openstack.cloud.subnet:
|
openstack.cloud.subnet:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: present
|
state: present
|
||||||
network_name: "{{ external_network_name }}"
|
network_name: "{{ external_network_name }}"
|
||||||
name: shade_subnet2
|
name: shade_subnet5
|
||||||
cidr: 10.6.6.0/24
|
cidr: 10.6.6.0/24
|
||||||
when:
|
when:
|
||||||
- network_external
|
- network_external
|
||||||
@@ -88,6 +212,142 @@
|
|||||||
- "result.openstack_routers.0.name == router_name"
|
- "result.openstack_routers.0.name == router_name"
|
||||||
- (result.openstack_routers.0.interfaces_info|length) == 1
|
- (result.openstack_routers.0.interfaces_info|length) == 1
|
||||||
|
|
||||||
|
- name: Update router (change external fixed ips)
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- shade_subnet1
|
||||||
|
network: "{{ external_network_name }}"
|
||||||
|
external_fixed_ips:
|
||||||
|
- subnet: shade_subnet5
|
||||||
|
ip: 10.6.6.100
|
||||||
|
when:
|
||||||
|
- network_external
|
||||||
|
|
||||||
|
- name: Gather routers info
|
||||||
|
openstack.cloud.routers_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
filters:
|
||||||
|
admin_state_up: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Verify routers info
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.openstack_routers.0.name == router_name"
|
||||||
|
- (result.openstack_routers.0.external_gateway_info.external_fixed_ips|length) == 1
|
||||||
|
- result.openstack_routers.0.external_gateway_info.external_fixed_ips.0.ip_address == "10.6.6.100"
|
||||||
|
when:
|
||||||
|
- network_external
|
||||||
|
|
||||||
|
- name: Update router (add external fixed ips)
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- shade_subnet1
|
||||||
|
network: "{{ external_network_name }}"
|
||||||
|
external_fixed_ips:
|
||||||
|
- subnet: shade_subnet5
|
||||||
|
ip: 10.6.6.100
|
||||||
|
- subnet: shade_subnet5
|
||||||
|
ip: 10.6.6.101
|
||||||
|
when:
|
||||||
|
- network_external
|
||||||
|
|
||||||
|
- name: Update router (add external fixed ips) again
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- shade_subnet1
|
||||||
|
network: "{{ external_network_name }}"
|
||||||
|
external_fixed_ips:
|
||||||
|
- subnet: shade_subnet5
|
||||||
|
ip: 10.6.6.100
|
||||||
|
- subnet: shade_subnet5
|
||||||
|
ip: 10.6.6.101
|
||||||
|
when:
|
||||||
|
- network_external
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert idempotent module
|
||||||
|
assert:
|
||||||
|
that: result is not changed
|
||||||
|
|
||||||
|
- name: Gather routers info
|
||||||
|
openstack.cloud.routers_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
filters:
|
||||||
|
admin_state_up: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Verify routers info
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.openstack_routers.0.name == router_name"
|
||||||
|
- (result.openstack_routers.0.external_gateway_info.external_fixed_ips|length) == 2
|
||||||
|
- result.openstack_routers.0.external_gateway_info.external_fixed_ips|map(attribute='ip_address')|sort|list ==
|
||||||
|
["10.6.6.100", "10.6.6.101"]
|
||||||
|
when:
|
||||||
|
- network_external
|
||||||
|
|
||||||
|
- name: Update router (remove external fixed ips)
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- shade_subnet1
|
||||||
|
network: "{{ external_network_name }}"
|
||||||
|
external_fixed_ips:
|
||||||
|
- subnet: shade_subnet5
|
||||||
|
ip: 10.6.6.101
|
||||||
|
when:
|
||||||
|
- network_external
|
||||||
|
|
||||||
|
- name: Update router (remove external fixed ips) again
|
||||||
|
openstack.cloud.router:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: present
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
interfaces:
|
||||||
|
- shade_subnet1
|
||||||
|
network: "{{ external_network_name }}"
|
||||||
|
external_fixed_ips:
|
||||||
|
- subnet: shade_subnet5
|
||||||
|
ip: 10.6.6.101
|
||||||
|
when:
|
||||||
|
- network_external
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert idempotent module
|
||||||
|
assert:
|
||||||
|
that: result is not changed
|
||||||
|
|
||||||
|
- name: Gather routers info
|
||||||
|
openstack.cloud.routers_info:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
name: "{{ router_name }}"
|
||||||
|
filters:
|
||||||
|
admin_state_up: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Verify routers info
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.openstack_routers.0.name == router_name"
|
||||||
|
- (result.openstack_routers.0.external_gateway_info.external_fixed_ips|length) == 1
|
||||||
|
- result.openstack_routers.0.external_gateway_info.external_fixed_ips.0.ip_address == "10.6.6.101"
|
||||||
|
when:
|
||||||
|
- network_external
|
||||||
|
|
||||||
- name: Delete router
|
- name: Delete router
|
||||||
openstack.cloud.router:
|
openstack.cloud.router:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
@@ -105,6 +365,24 @@
|
|||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
state: absent
|
state: absent
|
||||||
name: shade_subnet2
|
name: shade_subnet2
|
||||||
|
|
||||||
|
- name: Delete subnet3
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: shade_subnet3
|
||||||
|
|
||||||
|
- name: Delete subnet4
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: shade_subnet4
|
||||||
|
|
||||||
|
- name: Delete subnet5
|
||||||
|
openstack.cloud.subnet:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
state: absent
|
||||||
|
name: shade_subnet5
|
||||||
when:
|
when:
|
||||||
- network_external
|
- network_external
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,71 @@
|
|||||||
remote_ip_prefix: 0.0.0.0/0
|
remote_ip_prefix: 0.0.0.0/0
|
||||||
direction: egress
|
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
|
- name: Delete empty ICMP rule
|
||||||
openstack.cloud.security_group_rule:
|
openstack.cloud.security_group_rule:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
|
|||||||
@@ -418,3 +418,103 @@
|
|||||||
that:
|
that:
|
||||||
- info19.openstack_servers.0.status in ('ACTIVE', 'REBUILD')
|
- info19.openstack_servers.0.status in ('ACTIVE', 'REBUILD')
|
||||||
- server is changed
|
- 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
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
name: "{{ subnet_name }}"
|
name: "{{ subnet_name }}"
|
||||||
state: present
|
state: present
|
||||||
cidr: 192.168.0.0/24
|
cidr: 192.168.0.0/24
|
||||||
|
gateway_ip: 192.168.0.1
|
||||||
allocation_pool_start: 192.168.0.2
|
allocation_pool_start: 192.168.0.2
|
||||||
allocation_pool_end: 192.168.0.8
|
allocation_pool_end: 192.168.0.8
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
name: ansible_volume_snapshot
|
name: ansible_volume_snapshot
|
||||||
register: snap_info
|
register: snap_info
|
||||||
ignore_errors: sdk_version is version(0.49, '<')
|
ignore_errors: sdk_version is version('0.49', '<')
|
||||||
|
|
||||||
- name: Create volume backup
|
- name: Create volume backup
|
||||||
openstack.cloud.volume_backup:
|
openstack.cloud.volume_backup:
|
||||||
@@ -30,14 +30,14 @@
|
|||||||
display_name: ansible_volume_backup
|
display_name: ansible_volume_backup
|
||||||
volume: ansible_volume
|
volume: ansible_volume
|
||||||
register: vol_backup
|
register: vol_backup
|
||||||
ignore_errors: sdk_version is version(0.49, '<')
|
ignore_errors: sdk_version is version('0.49', '<')
|
||||||
|
|
||||||
- name: Get backup info
|
- name: Get backup info
|
||||||
openstack.cloud.volume_backup_info:
|
openstack.cloud.volume_backup_info:
|
||||||
cloud: "{{ cloud }}"
|
cloud: "{{ cloud }}"
|
||||||
name: ansible_volume_backup
|
name: ansible_volume_backup
|
||||||
register: backup_info
|
register: backup_info
|
||||||
ignore_errors: sdk_version is version(0.49, '<')
|
ignore_errors: sdk_version is version('0.49', '<')
|
||||||
|
|
||||||
- debug: var=vol
|
- debug: var=vol
|
||||||
|
|
||||||
@@ -68,4 +68,3 @@
|
|||||||
display_name: ansible_volume
|
display_name: ansible_volume
|
||||||
|
|
||||||
- include_tasks: volume_info.yml
|
- include_tasks: volume_info.yml
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,27 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# In case of Octavia enabled:
|
||||||
|
_octavia_image_path="/tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
|
||||||
|
if systemctl list-units --full -all | grep -Fq "devstack@o-api.service" && \
|
||||||
|
test -f "$_octavia_image_path"
|
||||||
|
then
|
||||||
|
# Upload apmhora image for Octavia to test load balancers
|
||||||
|
OCTAVIA_AMP_IMAGE_FILE=${OCTAVIA_AMP_IMAGE_FILE:-"$_octavia_image_path"}
|
||||||
|
OCTAVIA_AMP_IMAGE_NAME=${OCTAVIA_AMP_IMAGE_NAME:-"test-only-amphora-x64-haproxy-ubuntu-bionic"}
|
||||||
|
OCTAVIA_AMP_IMAGE_SIZE=${OCTAVIA_AMP_IMAGE_SIZE:-3}
|
||||||
|
openstack --os-cloud=${CLOUD} image create \
|
||||||
|
--container-format bare \
|
||||||
|
--disk-format qcow2 \
|
||||||
|
--private \
|
||||||
|
--file $OCTAVIA_AMP_IMAGE_FILE \
|
||||||
|
--project service $OCTAVIA_AMP_IMAGE_NAME
|
||||||
|
openstack --os-cloud=${CLOUD} image set --tag amphora $OCTAVIA_AMP_IMAGE_NAME
|
||||||
|
# End of Octavia preparement
|
||||||
|
else
|
||||||
|
tag_opt="$tag_opt --skip-tags loadbalancer"
|
||||||
|
fi
|
||||||
|
|
||||||
# Discover openstackSDK version
|
# Discover openstackSDK version
|
||||||
SDK_VER=$(python -c "import openstack; print(openstack.version.__version__)")
|
SDK_VER=$(python -c "import openstack; print(openstack.version.__version__)")
|
||||||
pushd ci/
|
pushd ci/
|
||||||
|
|||||||
@@ -4,12 +4,18 @@
|
|||||||
gather_facts: true
|
gather_facts: true
|
||||||
|
|
||||||
roles:
|
roles:
|
||||||
|
- { role: address_scope, tags: address_scope }
|
||||||
- { role: auth, tags: auth }
|
- { role: auth, tags: auth }
|
||||||
- { role: client_config, tags: client_config }
|
- { role: client_config, tags: client_config }
|
||||||
|
- { role: dns_zone_info, tags: dns_zone_info }
|
||||||
|
- role: object_container
|
||||||
|
tags: object_container
|
||||||
|
when: sdk_version is version(0.18, '>=')
|
||||||
- { role: group, tags: group }
|
- { role: group, tags: group }
|
||||||
- role: dns
|
- role: dns
|
||||||
tags: dns
|
tags: dns
|
||||||
when: sdk_version is version(0.28, '>=')
|
when: sdk_version is version(0.28, '>=')
|
||||||
|
- { role: floating_ip_info, tags: floating_ip_info }
|
||||||
- { role: image, tags: image }
|
- { role: image, tags: image }
|
||||||
- { role: keypair, tags: keypair }
|
- { role: keypair, tags: keypair }
|
||||||
- { role: keystone_domain, tags: keystone_domain }
|
- { role: keystone_domain, tags: keystone_domain }
|
||||||
@@ -24,7 +30,14 @@
|
|||||||
when: sdk_version is version(0.44, '>=')
|
when: sdk_version is version(0.44, '>=')
|
||||||
- { role: keystone_role, tags: keystone_role }
|
- { role: keystone_role, tags: keystone_role }
|
||||||
- { role: network, tags: network }
|
- { role: network, tags: network }
|
||||||
|
- role: neutron_rbac
|
||||||
|
tags:
|
||||||
|
- rbac
|
||||||
|
- neutron_rbac
|
||||||
- { role: nova_flavor, tags: nova_flavor }
|
- { role: nova_flavor, tags: nova_flavor }
|
||||||
|
- role: nova_services
|
||||||
|
tags: nova_services
|
||||||
|
when: sdk_version is version(0.44, '>=')
|
||||||
- { role: object, tags: object }
|
- { role: object, tags: object }
|
||||||
- { role: port, tags: port }
|
- { role: port, tags: port }
|
||||||
- { role: project, tags: project }
|
- { role: project, tags: project }
|
||||||
@@ -39,3 +52,9 @@
|
|||||||
- { role: user_group, tags: user_group }
|
- { role: user_group, tags: user_group }
|
||||||
- { role: user_role, tags: user_role }
|
- { role: user_role, tags: user_role }
|
||||||
- { role: volume, tags: volume }
|
- { role: volume, tags: volume }
|
||||||
|
- role: orchestration
|
||||||
|
tags: orchestrate
|
||||||
|
when: sdk_version is version("0.53.0", '>=')
|
||||||
|
- role: loadbalancer
|
||||||
|
tags: loadbalancer
|
||||||
|
- { role: floating_ip, tags: floating_ip }
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ Libraries
|
|||||||
users as a primary audience, they are for intra-server communication.
|
users as a primary audience, they are for intra-server communication.
|
||||||
* All modules should be registered in ``meta/action_groups.yml`` for enabling the
|
* All modules should be registered in ``meta/action_groups.yml`` for enabling the
|
||||||
variables to be set in `group level
|
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
|
Testing
|
||||||
-------
|
-------
|
||||||
|
|
||||||
* Integration testing is currently done in `OpenStack's CI system
|
* 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>`_
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ build_ignore:
|
|||||||
- ci
|
- ci
|
||||||
- galaxy.yml.in
|
- galaxy.yml.in
|
||||||
- setup.cfg
|
- setup.cfg
|
||||||
- test-requirements.txt
|
- test-requirements*
|
||||||
- tests
|
- tests
|
||||||
- tools
|
- tools
|
||||||
- tox.ini
|
- tox.ini
|
||||||
@@ -29,8 +29,8 @@ build_ignore:
|
|||||||
- importer_result.json
|
- importer_result.json
|
||||||
- .tox
|
- .tox
|
||||||
- .env
|
- .env
|
||||||
|
- .vscode
|
||||||
- ansible_collections_openstack.egg-info
|
- ansible_collections_openstack.egg-info
|
||||||
- contrib
|
- contrib
|
||||||
- changelogs/.plugin-cache.yaml
|
- changelogs
|
||||||
- changelogs/fragments
|
version: 1.6.0
|
||||||
version: 1.2.1
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
requires_ansible: ">=2.8"
|
||||||
action_groups:
|
action_groups:
|
||||||
openstack:
|
openstack:
|
||||||
|
- address_scope
|
||||||
- auth
|
- auth
|
||||||
- baremetal_inspect
|
- baremetal_inspect
|
||||||
- baremetal_inspect
|
- baremetal_inspect
|
||||||
@@ -17,10 +19,12 @@ action_groups:
|
|||||||
- compute_flavor
|
- compute_flavor
|
||||||
- compute_flavor_info
|
- compute_flavor_info
|
||||||
- compute_flavor_info
|
- compute_flavor_info
|
||||||
|
- compute_service_info
|
||||||
|
- compute_service_info
|
||||||
- config
|
- config
|
||||||
- config
|
- config
|
||||||
- dns_zone
|
- dns_zone
|
||||||
- dns_zone
|
- dns_zone_info
|
||||||
- endpoint
|
- endpoint
|
||||||
- endpoint
|
- endpoint
|
||||||
- federation_idp
|
- federation_idp
|
||||||
@@ -32,6 +36,7 @@ action_groups:
|
|||||||
- federation_mapping_info
|
- federation_mapping_info
|
||||||
- federation_mapping_info
|
- federation_mapping_info
|
||||||
- floating_ip
|
- floating_ip
|
||||||
|
- floating_ip_info
|
||||||
- group_assignment
|
- group_assignment
|
||||||
- group_assignment
|
- group_assignment
|
||||||
- host_aggregate
|
- host_aggregate
|
||||||
@@ -53,6 +58,7 @@ action_groups:
|
|||||||
- image
|
- image
|
||||||
- image_info
|
- image_info
|
||||||
- keypair
|
- keypair
|
||||||
|
- keypair_info
|
||||||
- keystone_federation_protocol
|
- keystone_federation_protocol
|
||||||
- keystone_federation_protocol_info
|
- keystone_federation_protocol_info
|
||||||
- lb_listener
|
- lb_listener
|
||||||
@@ -65,6 +71,7 @@ action_groups:
|
|||||||
- network
|
- network
|
||||||
- networks_info
|
- networks_info
|
||||||
- object
|
- object
|
||||||
|
- object_container
|
||||||
- port
|
- port
|
||||||
- port_info
|
- port_info
|
||||||
- project
|
- project
|
||||||
@@ -77,7 +84,9 @@ action_groups:
|
|||||||
- router
|
- router
|
||||||
- routers_info
|
- routers_info
|
||||||
- security_group
|
- security_group
|
||||||
|
- security_group_info
|
||||||
- security_group_rule
|
- security_group_rule
|
||||||
|
- security_group_rule_info
|
||||||
- server
|
- server
|
||||||
- server_action
|
- server_action
|
||||||
- server_group
|
- server_group
|
||||||
@@ -147,6 +156,7 @@ action_groups:
|
|||||||
- image
|
- image
|
||||||
- image_info
|
- image_info
|
||||||
- keypair
|
- keypair
|
||||||
|
- keypair_info
|
||||||
- keystone_federation_protocol
|
- keystone_federation_protocol
|
||||||
- keystone_federation_protocol_info
|
- keystone_federation_protocol_info
|
||||||
- lb_listener
|
- lb_listener
|
||||||
@@ -159,6 +169,7 @@ action_groups:
|
|||||||
- network
|
- network
|
||||||
- networks_info
|
- networks_info
|
||||||
- object
|
- object
|
||||||
|
- object_container
|
||||||
- port
|
- port
|
||||||
- port_info
|
- port_info
|
||||||
- project
|
- project
|
||||||
@@ -171,7 +182,9 @@ action_groups:
|
|||||||
- router
|
- router
|
||||||
- routers_info
|
- routers_info
|
||||||
- security_group
|
- security_group
|
||||||
|
- security_group_info
|
||||||
- security_group_rule
|
- security_group_rule
|
||||||
|
- security_group_rule_info
|
||||||
- server
|
- server
|
||||||
- server_action
|
- server_action
|
||||||
- server_group
|
- server_group
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ options:
|
|||||||
- Whether or not SSL API requests should be verified.
|
- Whether or not SSL API requests should be verified.
|
||||||
- Before Ansible 2.3 this defaulted to C(yes).
|
- Before Ansible 2.3 this defaulted to C(yes).
|
||||||
type: bool
|
type: bool
|
||||||
default: False
|
|
||||||
aliases: [ verify ]
|
aliases: [ verify ]
|
||||||
ca_cert:
|
ca_cert:
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ options:
|
|||||||
plugin:
|
plugin:
|
||||||
description: token that ensures this is a source file for the 'openstack' plugin.
|
description: token that ensures this is a source file for the 'openstack' plugin.
|
||||||
required: True
|
required: True
|
||||||
choices: ['openstack']
|
choices: ['openstack', 'openstack.cloud.openstack']
|
||||||
show_all:
|
show_all:
|
||||||
description: toggles showing all vms vs only those with a working IP
|
description: toggles showing all vms vs only those with a working IP
|
||||||
type: bool
|
type: bool
|
||||||
@@ -98,6 +98,10 @@ options:
|
|||||||
description: Add hosts to group based on Jinja2 conditionals.
|
description: Add hosts to group based on Jinja2 conditionals.
|
||||||
type: dictionary
|
type: dictionary
|
||||||
default: {}
|
default: {}
|
||||||
|
legacy_groups:
|
||||||
|
description: Automatically create groups from host variables.
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
- inventory_cache
|
- inventory_cache
|
||||||
@@ -108,7 +112,7 @@ extends_documentation_fragment:
|
|||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
# file must be named openstack.yaml or openstack.yml
|
# file must be named openstack.yaml or openstack.yml
|
||||||
# Make the plugin behave like the default behavior of the old script
|
# Make the plugin behave like the default behavior of the old script
|
||||||
plugin: openstack
|
plugin: openstack.cloud.openstack
|
||||||
expand_hostvars: yes
|
expand_hostvars: yes
|
||||||
fail_on_errors: yes
|
fail_on_errors: yes
|
||||||
all_projects: yes
|
all_projects: yes
|
||||||
@@ -155,7 +159,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
|||||||
msg = ''
|
msg = ''
|
||||||
if not self._config_data:
|
if not self._config_data:
|
||||||
msg = 'File empty. this is not my config file'
|
msg = 'File empty. this is not my config file'
|
||||||
elif 'plugin' in self._config_data and self._config_data['plugin'] != self.NAME:
|
elif 'plugin' in self._config_data and self._config_data['plugin'] not in (self.NAME, 'openstack'):
|
||||||
msg = 'plugin config file, but not for us: %s' % self._config_data['plugin']
|
msg = 'plugin config file, but not for us: %s' % self._config_data['plugin']
|
||||||
elif 'plugin' not in self._config_data and 'clouds' not in self._config_data:
|
elif 'plugin' not in self._config_data and 'clouds' not in self._config_data:
|
||||||
msg = "it's not a plugin configuration nor a clouds.yaml file"
|
msg = "it's not a plugin configuration nor a clouds.yaml file"
|
||||||
@@ -307,7 +311,11 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
|||||||
for group_name, group_hosts in groups.items():
|
for group_name, group_hosts in groups.items():
|
||||||
gname = self.inventory.add_group(group_name)
|
gname = self.inventory.add_group(group_name)
|
||||||
for host in group_hosts:
|
for host in group_hosts:
|
||||||
self.inventory.add_child(gname, host)
|
if gname == host:
|
||||||
|
display.vvvv("Same name for host %s and group %s" % (host, gname))
|
||||||
|
self.inventory.add_host(host, gname)
|
||||||
|
else:
|
||||||
|
self.inventory.add_child(gname, host)
|
||||||
|
|
||||||
def _get_groups_from_server(self, server_vars, namegroup=True):
|
def _get_groups_from_server(self, server_vars, namegroup=True):
|
||||||
groups = []
|
groups = []
|
||||||
@@ -361,8 +369,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
|||||||
openstack=server)
|
openstack=server)
|
||||||
self.inventory.add_host(current_host)
|
self.inventory.add_host(current_host)
|
||||||
|
|
||||||
for group in self._get_groups_from_server(server, namegroup=namegroup):
|
if self.get_option('legacy_groups'):
|
||||||
groups[group].append(current_host)
|
for group in self._get_groups_from_server(server, namegroup=namegroup):
|
||||||
|
groups[group].append(current_host)
|
||||||
|
|
||||||
def verify_file(self, path):
|
def verify_file(self, path):
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ def openstack_full_argument_spec(**kwargs):
|
|||||||
auth=dict(default=None, type='dict', no_log=True),
|
auth=dict(default=None, type='dict', no_log=True),
|
||||||
region_name=dict(default=None),
|
region_name=dict(default=None),
|
||||||
availability_zone=dict(default=None),
|
availability_zone=dict(default=None),
|
||||||
validate_certs=dict(default=False, type='bool', aliases=['verify']),
|
validate_certs=dict(default=None, type='bool', aliases=['verify']),
|
||||||
ca_cert=dict(default=None, aliases=['cacert']),
|
ca_cert=dict(default=None, aliases=['cacert']),
|
||||||
client_cert=dict(default=None, aliases=['cert']),
|
client_cert=dict(default=None, aliases=['cert']),
|
||||||
client_key=dict(default=None, no_log=True, aliases=['key']),
|
client_key=dict(default=None, no_log=True, aliases=['key']),
|
||||||
@@ -256,10 +256,12 @@ class OpenStackModule:
|
|||||||
**self.module_kwargs)
|
**self.module_kwargs)
|
||||||
self.params = self.ansible.params
|
self.params = self.ansible.params
|
||||||
self.module_name = self.ansible._name
|
self.module_name = self.ansible._name
|
||||||
|
self.check_mode = self.ansible.check_mode
|
||||||
self.sdk_version = None
|
self.sdk_version = None
|
||||||
self.results = {'changed': False}
|
self.results = {'changed': False}
|
||||||
self.exit = self.exit_json = self.ansible.exit_json
|
self.exit = self.exit_json = self.ansible.exit_json
|
||||||
self.fail = self.fail_json = self.ansible.fail_json
|
self.fail = self.fail_json = self.ansible.fail_json
|
||||||
|
self.warn = self.ansible.warn
|
||||||
self.sdk, self.conn = self.openstack_cloud_from_module()
|
self.sdk, self.conn = self.openstack_cloud_from_module()
|
||||||
self.check_deprecated_names()
|
self.check_deprecated_names()
|
||||||
|
|
||||||
@@ -290,7 +292,7 @@ class OpenStackModule:
|
|||||||
"The '%s' module has been renamed to '%s' in openstack "
|
"The '%s' module has been renamed to '%s' in openstack "
|
||||||
"collection: openstack.cloud.%s" % (
|
"collection: openstack.cloud.%s" % (
|
||||||
self.module_name, new_module_name, new_module_name),
|
self.module_name, new_module_name, new_module_name),
|
||||||
version='2.10')
|
version='2.0.0', collection_name='openstack.cloud')
|
||||||
|
|
||||||
def openstack_cloud_from_module(self):
|
def openstack_cloud_from_module(self):
|
||||||
"""Sets up connection to cloud using provided options. Checks if all
|
"""Sets up connection to cloud using provided options. Checks if all
|
||||||
|
|||||||
201
plugins/modules/address_scope.py
Normal file
201
plugins/modules/address_scope.py
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 by Uemit Seren <uemit.seren@gmail.com>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
DOCUMENTATION = '''
|
||||||
|
---
|
||||||
|
module: address_scope
|
||||||
|
short_description: Create or delete address scopes from OpenStack
|
||||||
|
author: OpenStack Ansible SIG
|
||||||
|
description:
|
||||||
|
- Create or Delete address scopes from OpenStack.
|
||||||
|
options:
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- Indicate desired state of the resource
|
||||||
|
choices: ['present', 'absent']
|
||||||
|
default: present
|
||||||
|
type: str
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- Name to be give to the address scope
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
project:
|
||||||
|
description:
|
||||||
|
- Unique name or ID of the project.
|
||||||
|
type: str
|
||||||
|
ip_version:
|
||||||
|
description:
|
||||||
|
- The IP version of the subnet 4 or 6
|
||||||
|
default: 4
|
||||||
|
type: str
|
||||||
|
choices: ['4', '6']
|
||||||
|
shared:
|
||||||
|
description:
|
||||||
|
- Whether this address scope is shared or not.
|
||||||
|
type: bool
|
||||||
|
default: 'no'
|
||||||
|
extra_specs:
|
||||||
|
description:
|
||||||
|
- Dictionary with extra key/value pairs passed to the API
|
||||||
|
required: false
|
||||||
|
default: {}
|
||||||
|
type: dict
|
||||||
|
requirements:
|
||||||
|
- "python >= 3.6"
|
||||||
|
- "openstacksdk"
|
||||||
|
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- openstack.cloud.openstack
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
# Create an IPv4 address scope.
|
||||||
|
- openstack.cloud.address_scope:
|
||||||
|
cloud: mycloud
|
||||||
|
state: present
|
||||||
|
name: my_adress_scope
|
||||||
|
|
||||||
|
# Create a shared IPv6 address scope for a given project.
|
||||||
|
- openstack.cloud.address_scope:
|
||||||
|
cloud: mycloud
|
||||||
|
state: present
|
||||||
|
ip_version: 6
|
||||||
|
name: ipv6_address_scope
|
||||||
|
project: myproj
|
||||||
|
|
||||||
|
# Delete address scope.
|
||||||
|
- openstack.cloud.address_scope:
|
||||||
|
cloud: mycloud
|
||||||
|
state: absent
|
||||||
|
name: my_adress_scope
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = '''
|
||||||
|
address_scope:
|
||||||
|
description: Dictionary describing the address scope.
|
||||||
|
returned: On success when I(state) is 'present'
|
||||||
|
type: complex
|
||||||
|
contains:
|
||||||
|
id:
|
||||||
|
description: Address Scope ID.
|
||||||
|
type: str
|
||||||
|
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
|
||||||
|
name:
|
||||||
|
description: Address Scope name.
|
||||||
|
type: str
|
||||||
|
sample: "my_address_scope"
|
||||||
|
tenant_id:
|
||||||
|
description: The tenant ID.
|
||||||
|
type: str
|
||||||
|
sample: "861174b82b43463c9edc5202aadc60ef"
|
||||||
|
ip_version:
|
||||||
|
description: The IP version of the subnet 4 or 6.
|
||||||
|
type: str
|
||||||
|
sample: "4"
|
||||||
|
is_shared:
|
||||||
|
description: Indicates whether this address scope is shared across all tenants.
|
||||||
|
type: bool
|
||||||
|
sample: false
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
|
||||||
|
class AddressScopeModule(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
|
name=dict(required=True),
|
||||||
|
shared=dict(default=False, type='bool'),
|
||||||
|
ip_version=dict(type='str', default='4', choices=['4', '6']),
|
||||||
|
project=dict(default=None),
|
||||||
|
extra_specs=dict(type='dict', default=dict())
|
||||||
|
)
|
||||||
|
|
||||||
|
def _needs_update(self, address_scope, filters=None):
|
||||||
|
"""Decide if the given address_scope needs an update.
|
||||||
|
"""
|
||||||
|
ip_version = int(self.params['ip_version'])
|
||||||
|
if address_scope['is_shared'] != self.params['shared']:
|
||||||
|
return True
|
||||||
|
if ip_version and address_scope['ip_version'] != ip_version:
|
||||||
|
self.fail_json(msg='Cannot update ip_version in existing address scope')
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _system_state_change(self, address_scope, filters=None):
|
||||||
|
"""Check if the system state would be changed."""
|
||||||
|
state = self.params['state']
|
||||||
|
if state == 'absent' and address_scope:
|
||||||
|
return True
|
||||||
|
if state == 'present':
|
||||||
|
if not address_scope:
|
||||||
|
return True
|
||||||
|
return self._needs_update(address_scope, filters)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
state = self.params['state']
|
||||||
|
name = self.params['name']
|
||||||
|
shared = self.params['shared']
|
||||||
|
ip_version = self.params['ip_version']
|
||||||
|
project = self.params['project']
|
||||||
|
extra_specs = self.params['extra_specs']
|
||||||
|
|
||||||
|
if project is not None:
|
||||||
|
proj = self.conn.get_project(project)
|
||||||
|
if proj is None:
|
||||||
|
self.fail(msg='Project %s could not be found' % project)
|
||||||
|
project_id = proj['id']
|
||||||
|
else:
|
||||||
|
project_id = self.conn.current_project_id
|
||||||
|
|
||||||
|
address_scope = self.conn.network.find_address_scope(name_or_id=name)
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(
|
||||||
|
changed=self._system_state_change(address_scope)
|
||||||
|
)
|
||||||
|
|
||||||
|
if state == 'present':
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
if not address_scope:
|
||||||
|
kwargs = dict(
|
||||||
|
name=name,
|
||||||
|
ip_version=ip_version,
|
||||||
|
is_shared=shared,
|
||||||
|
tenant_id=project_id)
|
||||||
|
dup_args = set(kwargs.keys()) & set(extra_specs.keys())
|
||||||
|
if dup_args:
|
||||||
|
raise ValueError('Duplicate key(s) {0} in extra_specs'
|
||||||
|
.format(list(dup_args)))
|
||||||
|
kwargs = dict(kwargs, **extra_specs)
|
||||||
|
address_scope = self.conn.network.create_address_scope(**kwargs)
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
if self._needs_update(address_scope):
|
||||||
|
address_scope = self.conn.network.update_address_scope(address_scope['id'], is_shared=shared)
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
changed = False
|
||||||
|
self.exit_json(changed=changed, address_scope=address_scope, id=address_scope['id'])
|
||||||
|
|
||||||
|
elif state == 'absent':
|
||||||
|
if not address_scope:
|
||||||
|
self.exit(changed=False)
|
||||||
|
else:
|
||||||
|
self.conn.network.delete_address_scope(address_scope['id'])
|
||||||
|
self.exit_json(changed=True)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = AddressScopeModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -38,29 +38,24 @@ service_catalog:
|
|||||||
type: dict
|
type: dict
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import traceback
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
class AuthModule(OpenStackModule):
|
||||||
openstack_module_kwargs,
|
argument_spec = dict()
|
||||||
openstack_cloud_from_module)
|
module_kwargs = dict()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.exit_json(
|
||||||
|
changed=False,
|
||||||
|
ansible_facts=dict(
|
||||||
|
auth_token=self.conn.auth_token,
|
||||||
|
service_catalog=self.conn.service_catalog))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
module = AuthModule()
|
||||||
argument_spec = openstack_full_argument_spec()
|
module()
|
||||||
module_kwargs = openstack_module_kwargs()
|
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
|
||||||
try:
|
|
||||||
module.exit_json(
|
|
||||||
changed=False,
|
|
||||||
ansible_facts=dict(
|
|
||||||
auth_token=cloud.auth_token,
|
|
||||||
service_catalog=cloud.service_catalog))
|
|
||||||
except Exception as e:
|
|
||||||
module.fail_json(msg=str(e), exception=traceback.format_exc())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -43,29 +43,71 @@ options:
|
|||||||
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
|
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
|
||||||
settings set to None.
|
settings set to None.
|
||||||
type: str
|
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:
|
driver_info:
|
||||||
description:
|
description:
|
||||||
- Information for this server's driver. Will vary based on which
|
- Information for this server's driver. Will vary based on which
|
||||||
driver is in use. Any sub-field which is populated will be validated
|
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
|
required: true
|
||||||
type: dict
|
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:
|
nics:
|
||||||
description:
|
description:
|
||||||
- 'A list of network interface cards, eg, " - mac: aa:bb:cc:aa:bb:cc"'
|
- 'A list of network interface cards, eg, " - mac: aa:bb:cc:aa:bb:cc"'
|
||||||
@@ -149,10 +191,9 @@ EXAMPLES = '''
|
|||||||
- mac: "aa:bb:cc:aa:bb:cc"
|
- mac: "aa:bb:cc:aa:bb:cc"
|
||||||
- mac: "dd:ee:ff:dd:ee:ff"
|
- mac: "dd:ee:ff:dd:ee:ff"
|
||||||
driver_info:
|
driver_info:
|
||||||
power:
|
ipmi_address: "1.2.3.4"
|
||||||
ipmi_address: "1.2.3.4"
|
ipmi_username: "admin"
|
||||||
ipmi_username: "admin"
|
ipmi_password: "adminpass"
|
||||||
ipmi_password: "adminpass"
|
|
||||||
chassis_uuid: "00000000-0000-0000-0000-000000000001"
|
chassis_uuid: "00000000-0000-0000-0000-000000000001"
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -174,31 +215,35 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_PROPERTIES = {
|
||||||
|
'cpu_arch': 'cpu_arch',
|
||||||
|
'cpus': 'cpus',
|
||||||
|
'ram': 'memory_mb',
|
||||||
|
'disk_size': 'local_gb',
|
||||||
|
'capabilities': 'capabilities',
|
||||||
|
'root_device': 'root_device',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _parse_properties(module):
|
def _parse_properties(module):
|
||||||
|
"""Convert ansible properties into native ironic values.
|
||||||
|
|
||||||
|
Also filter out any properties that are not set.
|
||||||
|
"""
|
||||||
p = module.params['properties']
|
p = module.params['properties']
|
||||||
props = dict(
|
return {to_key: p[from_key] for (from_key, to_key) in _PROPERTIES.items()
|
||||||
cpu_arch=p.get('cpu_arch') if p.get('cpu_arch') else 'x86_64',
|
if p.get(from_key) is not None}
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_driver_info(sdk, module):
|
def _parse_driver_info(sdk, module):
|
||||||
p = module.params['driver_info']
|
info = module.params['driver_info'].copy()
|
||||||
info = p.get('power')
|
for deprecated in ('power', 'console', 'management', 'deploy'):
|
||||||
if not info:
|
if deprecated in info:
|
||||||
raise sdk.exceptions.OpenStackCloudException(
|
info.update(info.pop(deprecated))
|
||||||
"driver_info['power'] is required")
|
module.deprecate("Suboption %s of the driver_info parameter of "
|
||||||
if p.get('console'):
|
"'openstack.cloud.baremetal_node' is deprecated"
|
||||||
info.update(p.get('console'))
|
% deprecated, version='2.0.0',
|
||||||
if p.get('management'):
|
collection_name='openstack.cloud')
|
||||||
info.update(p.get('management'))
|
|
||||||
if p.get('deploy'):
|
|
||||||
info.update(p.get('deploy'))
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
@@ -233,6 +278,19 @@ def main():
|
|||||||
uuid=dict(required=False),
|
uuid=dict(required=False),
|
||||||
name=dict(required=False),
|
name=dict(required=False),
|
||||||
driver=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),
|
driver_info=dict(type='dict', required=True),
|
||||||
nics=dict(type='list', required=True, elements="dict"),
|
nics=dict(type='list', required=True, elements="dict"),
|
||||||
properties=dict(type='dict', default={}),
|
properties=dict(type='dict', default={}),
|
||||||
@@ -241,7 +299,10 @@ def main():
|
|||||||
required=False,
|
required=False,
|
||||||
type='bool',
|
type='bool',
|
||||||
aliases=['skip_update_of_driver_password'],
|
aliases=['skip_update_of_driver_password'],
|
||||||
deprecated_aliases=[dict(name='skip_update_of_driver_password', version='2.0.0')]
|
deprecated_aliases=[dict(
|
||||||
|
name='skip_update_of_driver_password',
|
||||||
|
version='2.0.0',
|
||||||
|
collection_name='openstack.cloud')]
|
||||||
),
|
),
|
||||||
state=dict(required=False, default='present', choices=['present', 'absent'])
|
state=dict(required=False, default='present', choices=['present', 'absent'])
|
||||||
)
|
)
|
||||||
@@ -269,6 +330,22 @@ def main():
|
|||||||
driver_info=driver_info,
|
driver_info=driver_info,
|
||||||
name=module.params['name'],
|
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']:
|
if module.params['chassis_uuid']:
|
||||||
kwargs['chassis_uuid'] = module.params['chassis_uuid']
|
kwargs['chassis_uuid'] = module.params['chassis_uuid']
|
||||||
|
|||||||
@@ -94,36 +94,11 @@ id:
|
|||||||
sample: "3292f020780b4d5baf27ff7e1d224c44"
|
sample: "3292f020780b4d5baf27ff7e1d224c44"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _needs_update(module, service):
|
class IdentityCatalogServiceModule(OpenStackModule):
|
||||||
if service.enabled != module.params['enabled']:
|
argument_spec = dict(
|
||||||
return True
|
|
||||||
if service.description is not None and \
|
|
||||||
service.description != module.params['description']:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(module, service):
|
|
||||||
state = module.params['state']
|
|
||||||
if state == 'absent' and service:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if state == 'present':
|
|
||||||
if service is None:
|
|
||||||
return True
|
|
||||||
return _needs_update(module, service)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
description=dict(default=None),
|
description=dict(default=None),
|
||||||
enabled=dict(default=True, type='bool'),
|
enabled=dict(default=True, type='bool'),
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
@@ -131,58 +106,79 @@ def main():
|
|||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec,
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
description = module.params['description']
|
def _needs_update(self, service):
|
||||||
enabled = module.params['enabled']
|
if service.enabled != self.params['enabled']:
|
||||||
name = module.params['name']
|
return True
|
||||||
state = module.params['state']
|
if service.description is not None and \
|
||||||
service_type = module.params['service_type']
|
service.description != self.params['description']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def _system_state_change(self, service):
|
||||||
try:
|
state = self.params['state']
|
||||||
services = cloud.search_services(name_or_id=name,
|
if state == 'absent' and service:
|
||||||
filters=dict(type=service_type))
|
return True
|
||||||
|
|
||||||
|
if state == 'present':
|
||||||
|
if service is None:
|
||||||
|
return True
|
||||||
|
return self._needs_update(service)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
description = self.params['description']
|
||||||
|
enabled = self.params['enabled']
|
||||||
|
name = self.params['name']
|
||||||
|
state = self.params['state']
|
||||||
|
service_type = self.params['service_type']
|
||||||
|
|
||||||
|
services = self.conn.search_services(
|
||||||
|
name_or_id=name, filters=dict(type=service_type))
|
||||||
|
|
||||||
if len(services) > 1:
|
if len(services) > 1:
|
||||||
module.fail_json(msg='Service name %s and type %s are not unique' %
|
self.fail_json(
|
||||||
(name, service_type))
|
msg='Service name %s and type %s are not unique'
|
||||||
|
% (name, service_type))
|
||||||
elif len(services) == 1:
|
elif len(services) == 1:
|
||||||
service = services[0]
|
service = services[0]
|
||||||
else:
|
else:
|
||||||
service = None
|
service = None
|
||||||
|
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(module, service))
|
self.exit_json(changed=self._system_state_change(service))
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if service is None:
|
if service is None:
|
||||||
service = cloud.create_service(name=name, description=description,
|
service = self.conn.create_service(
|
||||||
type=service_type, enabled=True)
|
name=name, description=description, type=service_type, enabled=True)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
if _needs_update(module, service):
|
if self._needs_update(service):
|
||||||
service = cloud.update_service(
|
service = self.conn.update_service(
|
||||||
service.id, name=name, type=service_type, enabled=enabled,
|
service.id, name=name, type=service_type, enabled=enabled,
|
||||||
description=description)
|
description=description)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
module.exit_json(changed=changed, service=service, id=service.id)
|
self.exit_json(changed=changed, service=service, id=service.id)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if service is None:
|
if service is None:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
cloud.delete_service(service.id)
|
self.conn.delete_service(service.id)
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityCatalogServiceModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -206,31 +206,16 @@ EXAMPLES = '''
|
|||||||
node_count: 5
|
node_count: 5
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_labels(labels):
|
class CoeClusterModule(OpenStackModule):
|
||||||
if isinstance(labels, str):
|
argument_spec = dict(
|
||||||
labels_dict = {}
|
|
||||||
for kv_str in labels.split(","):
|
|
||||||
k, v = kv_str.split("=")
|
|
||||||
labels_dict[k] = v
|
|
||||||
return labels_dict
|
|
||||||
if not labels:
|
|
||||||
return {}
|
|
||||||
return labels
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
cluster_template_id=dict(required=True),
|
cluster_template_id=dict(required=True),
|
||||||
discovery_url=dict(default=None),
|
discovery_url=dict(default=None),
|
||||||
docker_volume_size=dict(type='int'),
|
docker_volume_size=dict(type='int'),
|
||||||
flavor_id=dict(default=None),
|
flavor_id=dict(default=None),
|
||||||
keypair=dict(default=None),
|
keypair=dict(default=None, no_log=False),
|
||||||
labels=dict(default=None, type='raw'),
|
labels=dict(default=None, type='raw'),
|
||||||
master_count=dict(type='int', default=1),
|
master_count=dict(type='int', default=1),
|
||||||
master_flavor_id=dict(default=None),
|
master_flavor_id=dict(default=None),
|
||||||
@@ -239,35 +224,46 @@ def main():
|
|||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
timeout=dict(type='int', default=60),
|
timeout=dict(type='int', default=60),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict()
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
|
|
||||||
params = module.params.copy()
|
def _parse_labels(self, labels):
|
||||||
|
if isinstance(labels, str):
|
||||||
|
labels_dict = {}
|
||||||
|
for kv_str in labels.split(","):
|
||||||
|
k, v = kv_str.split("=")
|
||||||
|
labels_dict[k] = v
|
||||||
|
return labels_dict
|
||||||
|
if not labels:
|
||||||
|
return {}
|
||||||
|
return labels
|
||||||
|
|
||||||
state = module.params['state']
|
def run(self):
|
||||||
name = module.params['name']
|
params = self.params.copy()
|
||||||
cluster_template_id = module.params['cluster_template_id']
|
|
||||||
|
|
||||||
kwargs = dict(
|
state = self.params['state']
|
||||||
discovery_url=module.params['discovery_url'],
|
name = self.params['name']
|
||||||
docker_volume_size=module.params['docker_volume_size'],
|
cluster_template_id = self.params['cluster_template_id']
|
||||||
flavor_id=module.params['flavor_id'],
|
|
||||||
keypair=module.params['keypair'],
|
kwargs = dict(
|
||||||
labels=_parse_labels(params['labels']),
|
discovery_url=self.params['discovery_url'],
|
||||||
master_count=module.params['master_count'],
|
docker_volume_size=self.params['docker_volume_size'],
|
||||||
master_flavor_id=module.params['master_flavor_id'],
|
flavor_id=self.params['flavor_id'],
|
||||||
node_count=module.params['node_count'],
|
keypair=self.params['keypair'],
|
||||||
create_timeout=module.params['timeout'],
|
labels=self._parse_labels(params['labels']),
|
||||||
)
|
master_count=self.params['master_count'],
|
||||||
|
master_flavor_id=self.params['master_flavor_id'],
|
||||||
|
node_count=self.params['node_count'],
|
||||||
|
create_timeout=self.params['timeout'],
|
||||||
|
)
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
|
||||||
try:
|
|
||||||
changed = False
|
changed = False
|
||||||
cluster = cloud.get_coe_cluster(name_or_id=name, filters={'cluster_template_id': cluster_template_id})
|
cluster = self.conn.get_coe_cluster(
|
||||||
|
name_or_id=name, filters={'cluster_template_id': cluster_template_id})
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if not cluster:
|
if not cluster:
|
||||||
cluster = cloud.create_coe_cluster(name, cluster_template_id=cluster_template_id, **kwargs)
|
cluster = self.conn.create_coe_cluster(
|
||||||
|
name, cluster_template_id=cluster_template_id, **kwargs)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
@@ -278,15 +274,18 @@ def main():
|
|||||||
# therefore try `id` first then `uuid`.
|
# therefore try `id` first then `uuid`.
|
||||||
cluster_id = cluster.get('id', cluster.get('uuid'))
|
cluster_id = cluster.get('id', cluster.get('uuid'))
|
||||||
cluster['id'] = cluster['uuid'] = cluster_id
|
cluster['id'] = cluster['uuid'] = cluster_id
|
||||||
module.exit_json(changed=changed, cluster=cluster, id=cluster_id)
|
self.exit_json(changed=changed, cluster=cluster, id=cluster_id)
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if not cluster:
|
if not cluster:
|
||||||
module.exit_json(changed=False)
|
self.exit_json(changed=False)
|
||||||
else:
|
else:
|
||||||
cloud.delete_coe_cluster(name)
|
self.conn.delete_coe_cluster(name)
|
||||||
module.exit_json(changed=True)
|
self.exit_json(changed=True)
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
|
||||||
|
def main():
|
||||||
|
module = CoeClusterModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -281,26 +281,11 @@ EXAMPLES = '''
|
|||||||
public: no
|
public: no
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_labels(labels):
|
class CoeClusterTemplateModule(OpenStackModule):
|
||||||
if isinstance(labels, str):
|
argument_spec = dict(
|
||||||
labels_dict = {}
|
|
||||||
for kv_str in labels.split(","):
|
|
||||||
k, v = kv_str.split("=")
|
|
||||||
labels_dict[k] = v
|
|
||||||
return labels_dict
|
|
||||||
if not labels:
|
|
||||||
return {}
|
|
||||||
return labels
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
coe=dict(required=True, choices=['kubernetes', 'swarm', 'mesos']),
|
coe=dict(required=True, choices=['kubernetes', 'swarm', 'mesos']),
|
||||||
dns_nameserver=dict(default='8.8.8.8'),
|
dns_nameserver=dict(default='8.8.8.8'),
|
||||||
docker_storage_driver=dict(choices=['devicemapper', 'overlay', 'overlay2']),
|
docker_storage_driver=dict(choices=['devicemapper', 'overlay', 'overlay2']),
|
||||||
@@ -327,61 +312,76 @@ def main():
|
|||||||
tls_disabled=dict(type='bool', default=False),
|
tls_disabled=dict(type='bool', default=False),
|
||||||
volume_driver=dict(choices=['cinder', 'rexray']),
|
volume_driver=dict(choices=['cinder', 'rexray']),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict()
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
|
|
||||||
params = module.params.copy()
|
def _parse_labels(self, labels):
|
||||||
|
if isinstance(labels, str):
|
||||||
|
labels_dict = {}
|
||||||
|
for kv_str in labels.split(","):
|
||||||
|
k, v = kv_str.split("=")
|
||||||
|
labels_dict[k] = v
|
||||||
|
return labels_dict
|
||||||
|
if not labels:
|
||||||
|
return {}
|
||||||
|
return labels
|
||||||
|
|
||||||
state = module.params['state']
|
def run(self):
|
||||||
name = module.params['name']
|
params = self.params.copy()
|
||||||
coe = module.params['coe']
|
|
||||||
image_id = module.params['image_id']
|
|
||||||
|
|
||||||
kwargs = dict(
|
state = self.params['state']
|
||||||
dns_nameserver=module.params['dns_nameserver'],
|
name = self.params['name']
|
||||||
docker_storage_driver=module.params['docker_storage_driver'],
|
coe = self.params['coe']
|
||||||
docker_volume_size=module.params['docker_volume_size'],
|
image_id = self.params['image_id']
|
||||||
external_network_id=module.params['external_network_id'],
|
|
||||||
fixed_network=module.params['fixed_network'],
|
kwargs = dict(
|
||||||
fixed_subnet=module.params['fixed_subnet'],
|
dns_nameserver=self.params['dns_nameserver'],
|
||||||
flavor_id=module.params['flavor_id'],
|
docker_storage_driver=self.params['docker_storage_driver'],
|
||||||
floating_ip_enabled=module.params['floating_ip_enabled'],
|
docker_volume_size=self.params['docker_volume_size'],
|
||||||
keypair_id=module.params['keypair_id'],
|
external_network_id=self.params['external_network_id'],
|
||||||
labels=_parse_labels(params['labels']),
|
fixed_network=self.params['fixed_network'],
|
||||||
http_proxy=module.params['http_proxy'],
|
fixed_subnet=self.params['fixed_subnet'],
|
||||||
https_proxy=module.params['https_proxy'],
|
flavor_id=self.params['flavor_id'],
|
||||||
master_lb_enabled=module.params['master_lb_enabled'],
|
floating_ip_enabled=self.params['floating_ip_enabled'],
|
||||||
master_flavor_id=module.params['master_flavor_id'],
|
keypair_id=self.params['keypair_id'],
|
||||||
network_driver=module.params['network_driver'],
|
labels=self._parse_labels(params['labels']),
|
||||||
no_proxy=module.params['no_proxy'],
|
http_proxy=self.params['http_proxy'],
|
||||||
public=module.params['public'],
|
https_proxy=self.params['https_proxy'],
|
||||||
registry_enabled=module.params['registry_enabled'],
|
master_lb_enabled=self.params['master_lb_enabled'],
|
||||||
server_type=module.params['server_type'],
|
master_flavor_id=self.params['master_flavor_id'],
|
||||||
tls_disabled=module.params['tls_disabled'],
|
network_driver=self.params['network_driver'],
|
||||||
volume_driver=module.params['volume_driver'],
|
no_proxy=self.params['no_proxy'],
|
||||||
)
|
public=self.params['public'],
|
||||||
|
registry_enabled=self.params['registry_enabled'],
|
||||||
|
server_type=self.params['server_type'],
|
||||||
|
tls_disabled=self.params['tls_disabled'],
|
||||||
|
volume_driver=self.params['volume_driver'],
|
||||||
|
)
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
|
||||||
try:
|
|
||||||
changed = False
|
changed = False
|
||||||
template = cloud.get_coe_cluster_template(name_or_id=name, filters={'coe': coe, 'image_id': image_id})
|
template = self.conn.get_coe_cluster_template(
|
||||||
|
name_or_id=name, filters={'coe': coe, 'image_id': image_id})
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if not template:
|
if not template:
|
||||||
template = cloud.create_coe_cluster_template(name, coe=coe, image_id=image_id, **kwargs)
|
template = self.conn.create_coe_cluster_template(
|
||||||
|
name, coe=coe, image_id=image_id, **kwargs)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
module.exit_json(changed=changed, cluster_template=template, id=template['uuid'])
|
self.exit_json(
|
||||||
|
changed=changed, cluster_template=template, id=template['uuid'])
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if not template:
|
if not template:
|
||||||
module.exit_json(changed=False)
|
self.exit_json(changed=False)
|
||||||
else:
|
else:
|
||||||
cloud.delete_coe_cluster_template(name)
|
self.conn.delete_coe_cluster_template(name)
|
||||||
module.exit_json(changed=True)
|
self.exit_json(changed=True)
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
|
||||||
|
def main():
|
||||||
|
module = CoeClusterTemplateModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -160,23 +160,11 @@ flavor:
|
|||||||
"aggregate_instance_extra_specs:pinned": false
|
"aggregate_instance_extra_specs:pinned": false
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(module, flavor):
|
class ComputeFlavorModule(OpenStackModule):
|
||||||
state = module.params['state']
|
argument_spec = dict(
|
||||||
if state == 'present' and not flavor:
|
|
||||||
return True
|
|
||||||
if state == 'absent' and flavor:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
state=dict(required=False, default='present',
|
state=dict(required=False, default='present',
|
||||||
choices=['absent', 'present']),
|
choices=['absent', 'present']),
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
@@ -194,25 +182,30 @@ def main():
|
|||||||
extra_specs=dict(required=False, default=None, type='dict'),
|
extra_specs=dict(required=False, default=None, type='dict'),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
required_if=[
|
required_if=[
|
||||||
('state', 'present', ['ram', 'vcpus', 'disk'])
|
('state', 'present', ['ram', 'vcpus', 'disk'])
|
||||||
],
|
],
|
||||||
**module_kwargs)
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
state = module.params['state']
|
def _system_state_change(self, flavor):
|
||||||
name = module.params['name']
|
state = self.params['state']
|
||||||
extra_specs = module.params['extra_specs'] or {}
|
if state == 'present' and not flavor:
|
||||||
|
return True
|
||||||
|
if state == 'absent' and flavor:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def run(self):
|
||||||
try:
|
state = self.params['state']
|
||||||
flavor = cloud.get_flavor(name)
|
name = self.params['name']
|
||||||
|
extra_specs = self.params['extra_specs'] or {}
|
||||||
|
|
||||||
if module.check_mode:
|
flavor = self.conn.get_flavor(name)
|
||||||
module.exit_json(changed=_system_state_change(module, flavor))
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(changed=self._system_state_change(flavor))
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
old_extra_specs = {}
|
old_extra_specs = {}
|
||||||
@@ -220,26 +213,32 @@ def main():
|
|||||||
|
|
||||||
if flavor:
|
if flavor:
|
||||||
old_extra_specs = flavor['extra_specs']
|
old_extra_specs = flavor['extra_specs']
|
||||||
for param_key in ['ram', 'vcpus', 'disk', 'ephemeral', 'swap', 'rxtx_factor', 'is_public']:
|
if flavor['swap'] == "":
|
||||||
if module.params[param_key] != flavor[param_key]:
|
flavor['swap'] = 0
|
||||||
|
for param_key in ['ram', 'vcpus', 'disk', 'ephemeral',
|
||||||
|
'swap', 'rxtx_factor', 'is_public']:
|
||||||
|
if self.params[param_key] != flavor[param_key]:
|
||||||
require_update = True
|
require_update = True
|
||||||
break
|
break
|
||||||
|
flavorid = self.params['flavorid']
|
||||||
if flavor and require_update:
|
if flavor and require_update:
|
||||||
cloud.delete_flavor(name)
|
self.conn.delete_flavor(name)
|
||||||
|
old_extra_specs = {}
|
||||||
|
if flavorid == 'auto':
|
||||||
|
flavorid = flavor['id']
|
||||||
flavor = None
|
flavor = None
|
||||||
|
|
||||||
if not flavor:
|
if not flavor:
|
||||||
flavor = cloud.create_flavor(
|
flavor = self.conn.create_flavor(
|
||||||
name=name,
|
name=name,
|
||||||
ram=module.params['ram'],
|
ram=self.params['ram'],
|
||||||
vcpus=module.params['vcpus'],
|
vcpus=self.params['vcpus'],
|
||||||
disk=module.params['disk'],
|
disk=self.params['disk'],
|
||||||
flavorid=module.params['flavorid'],
|
flavorid=flavorid,
|
||||||
ephemeral=module.params['ephemeral'],
|
ephemeral=self.params['ephemeral'],
|
||||||
swap=module.params['swap'],
|
swap=self.params['swap'],
|
||||||
rxtx_factor=module.params['rxtx_factor'],
|
rxtx_factor=self.params['rxtx_factor'],
|
||||||
is_public=module.params['is_public']
|
is_public=self.params['is_public']
|
||||||
)
|
)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
@@ -249,25 +248,26 @@ def main():
|
|||||||
unset_keys = set(old_extra_specs.keys()) - set(extra_specs.keys())
|
unset_keys = set(old_extra_specs.keys()) - set(extra_specs.keys())
|
||||||
|
|
||||||
if unset_keys and not require_update:
|
if unset_keys and not require_update:
|
||||||
cloud.unset_flavor_specs(flavor['id'], unset_keys)
|
self.conn.unset_flavor_specs(flavor['id'], unset_keys)
|
||||||
|
|
||||||
if old_extra_specs != new_extra_specs:
|
if old_extra_specs != new_extra_specs:
|
||||||
cloud.set_flavor_specs(flavor['id'], extra_specs)
|
self.conn.set_flavor_specs(flavor['id'], extra_specs)
|
||||||
|
|
||||||
changed = (changed or old_extra_specs != new_extra_specs)
|
changed = (changed or old_extra_specs != new_extra_specs)
|
||||||
|
|
||||||
module.exit_json(changed=changed,
|
self.exit_json(
|
||||||
flavor=flavor,
|
changed=changed, flavor=flavor, id=flavor['id'])
|
||||||
id=flavor['id'])
|
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if flavor:
|
if flavor:
|
||||||
cloud.delete_flavor(name)
|
self.conn.delete_flavor(name)
|
||||||
module.exit_json(changed=True)
|
self.exit_json(changed=True)
|
||||||
module.exit_json(changed=False)
|
self.exit_json(changed=False)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = ComputeFlavorModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -158,70 +158,60 @@ openstack_flavors:
|
|||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class ComputeFlavorInfoModule(OpenStackModule):
|
||||||
argument_spec = openstack_full_argument_spec(
|
argument_spec = dict(
|
||||||
name=dict(required=False, default=None),
|
name=dict(required=False, default=None),
|
||||||
ram=dict(required=False, default=None),
|
ram=dict(required=False, default=None),
|
||||||
vcpus=dict(required=False, default=None),
|
vcpus=dict(required=False, default=None),
|
||||||
limit=dict(required=False, default=None, type='int'),
|
limit=dict(required=False, default=None, type='int'),
|
||||||
ephemeral=dict(required=False, default=None),
|
ephemeral=dict(required=False, default=None),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
['name', 'ram'],
|
['name', 'ram'],
|
||||||
['name', 'vcpus'],
|
['name', 'vcpus'],
|
||||||
['name', 'ephemeral']
|
['name', 'ephemeral']
|
||||||
]
|
],
|
||||||
|
supports_check_mode=True
|
||||||
)
|
)
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
is_old_facts = module._name == 'openstack.cloud.compute_flavor_facts'
|
|
||||||
if is_old_facts:
|
|
||||||
module.deprecate("The 'openstack.cloud.compute_flavor_facts' module has been renamed to 'openstack.cloud.compute_flavor_info', "
|
|
||||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
|
||||||
|
|
||||||
name = module.params['name']
|
deprecated_names = ('openstack.cloud.compute_flavor_facts')
|
||||||
vcpus = module.params['vcpus']
|
|
||||||
ram = module.params['ram']
|
|
||||||
ephemeral = module.params['ephemeral']
|
|
||||||
limit = module.params['limit']
|
|
||||||
|
|
||||||
filters = {}
|
def run(self):
|
||||||
if vcpus:
|
name = self.params['name']
|
||||||
filters['vcpus'] = vcpus
|
vcpus = self.params['vcpus']
|
||||||
if ram:
|
ram = self.params['ram']
|
||||||
filters['ram'] = ram
|
ephemeral = self.params['ephemeral']
|
||||||
if ephemeral:
|
limit = self.params['limit']
|
||||||
filters['ephemeral'] = ephemeral
|
|
||||||
|
filters = {}
|
||||||
|
if vcpus:
|
||||||
|
filters['vcpus'] = vcpus
|
||||||
|
if ram:
|
||||||
|
filters['ram'] = ram
|
||||||
|
if ephemeral:
|
||||||
|
filters['ephemeral'] = ephemeral
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
|
||||||
try:
|
|
||||||
if name:
|
if name:
|
||||||
flavors = cloud.search_flavors(filters={'name': name})
|
flavors = self.conn.search_flavors(filters={'name': name})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
flavors = cloud.list_flavors()
|
flavors = self.conn.list_flavors()
|
||||||
if filters:
|
if filters:
|
||||||
flavors = cloud.range_search(flavors, filters)
|
flavors = self.conn.range_search(flavors, filters)
|
||||||
|
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
flavors = flavors[:limit]
|
flavors = flavors[:limit]
|
||||||
|
|
||||||
if is_old_facts:
|
self.exit_json(changed=False, openstack_flavors=flavors)
|
||||||
module.exit_json(changed=False,
|
|
||||||
ansible_facts=dict(openstack_flavors=flavors))
|
|
||||||
else:
|
|
||||||
module.exit_json(changed=False,
|
|
||||||
openstack_flavors=flavors)
|
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = ComputeFlavorInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
111
plugins/modules/compute_service_info.py
Normal file
111
plugins/modules/compute_service_info.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2016 Hewlett-Packard Enterprise Corporation
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
DOCUMENTATION = '''
|
||||||
|
---
|
||||||
|
module: compute_service_info
|
||||||
|
short_description: Retrieve information about one or more OpenStack compute services
|
||||||
|
author: OpenStack Ansible SIG
|
||||||
|
description:
|
||||||
|
- Retrieve information about nova compute services
|
||||||
|
options:
|
||||||
|
binary:
|
||||||
|
description:
|
||||||
|
- Filter by service binary type
|
||||||
|
type: str
|
||||||
|
host:
|
||||||
|
description:
|
||||||
|
- Filter by service host
|
||||||
|
type: str
|
||||||
|
requirements:
|
||||||
|
- "python >= 3.6"
|
||||||
|
- "openstacksdk"
|
||||||
|
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- openstack.cloud.openstack
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
# Gather information about compute services
|
||||||
|
- openstack.cloud.compute_service_info:
|
||||||
|
cloud: awesomecloud
|
||||||
|
binary: "nova-compute"
|
||||||
|
host: "localhost"
|
||||||
|
register: result
|
||||||
|
- openstack.cloud.compute_service_info:
|
||||||
|
cloud: awesomecloud
|
||||||
|
register: result
|
||||||
|
- debug:
|
||||||
|
msg: "{{ result.openstack_compute_services }}"
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
RETURN = '''
|
||||||
|
openstack_compute_services:
|
||||||
|
description: has all the OpenStack information about compute services
|
||||||
|
returned: always, but can be null
|
||||||
|
type: complex
|
||||||
|
contains:
|
||||||
|
id:
|
||||||
|
description: Unique UUID.
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
binary:
|
||||||
|
description: The binary name of the service.
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
host:
|
||||||
|
description: The name of the host.
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
zone:
|
||||||
|
description: The availability zone name.
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
status:
|
||||||
|
description: The status of the service. One of enabled or disabled.
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
state:
|
||||||
|
description: The state of the service. One of up or down.
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
update:
|
||||||
|
description: The date and time when the resource was updated
|
||||||
|
returned: success
|
||||||
|
type: str
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
|
||||||
|
class ComputeServiceInfoModule(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
|
binary=dict(required=False, default=None),
|
||||||
|
host=dict(required=False, default=None),
|
||||||
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
binary = self.params['binary']
|
||||||
|
host = self.params['host']
|
||||||
|
filters = {}
|
||||||
|
if binary:
|
||||||
|
filters['binary'] = binary
|
||||||
|
if host:
|
||||||
|
filters['host'] = host
|
||||||
|
services = self.conn.compute.services(**filters)
|
||||||
|
services = list(services)
|
||||||
|
self.exit_json(changed=False, openstack_compute_services=services)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = ComputeServiceInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
1
plugins/modules/container.py
Symbolic link
1
plugins/modules/container.py
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
object_container.py
|
||||||
180
plugins/modules/dns_zone_info.py
Normal file
180
plugins/modules/dns_zone_info.py
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 by Open Telekom Cloud, operated by T-Systems International GmbH
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTATION = '''
|
||||||
|
---
|
||||||
|
module: dns_zone_info
|
||||||
|
short_description: Getting information about dns zones
|
||||||
|
author: OpenStack Ansible SIG
|
||||||
|
description:
|
||||||
|
- Getting information about dns zones. Output can be filtered.
|
||||||
|
options:
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- Zone name.
|
||||||
|
type: str
|
||||||
|
type:
|
||||||
|
description:
|
||||||
|
- Zone type.
|
||||||
|
choices: [primary, secondary]
|
||||||
|
type: str
|
||||||
|
email:
|
||||||
|
description:
|
||||||
|
- Email of the zone owner (only applies if zone_type is primary).
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
description:
|
||||||
|
- Zone description.
|
||||||
|
type: str
|
||||||
|
ttl:
|
||||||
|
description:
|
||||||
|
- TTL (Time To Live) value in seconds.
|
||||||
|
type: int
|
||||||
|
|
||||||
|
requirements:
|
||||||
|
- "python >= 3.6"
|
||||||
|
- "openstacksdk"
|
||||||
|
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- openstack.cloud.openstack
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
# Create a zone named "example.net"
|
||||||
|
- openstack.cloud.dns_zones:
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = '''
|
||||||
|
zone:
|
||||||
|
description: Dictionary describing the zone.
|
||||||
|
returned: On success when I(state) is 'present'.
|
||||||
|
type: complex
|
||||||
|
contains:
|
||||||
|
action:
|
||||||
|
description: Current action in progress on the resource.
|
||||||
|
type: str
|
||||||
|
sample: "CREATE"
|
||||||
|
attributes:
|
||||||
|
description: Key:Value pairs of information about this zone, and the pool the user would like to place \
|
||||||
|
the zone in. This information can be used by the scheduler to place zones on the correct pool.
|
||||||
|
type: dict
|
||||||
|
sample: {"tier": "gold", "ha": "true"}
|
||||||
|
created_at:
|
||||||
|
description: Date / Time when resource was created.
|
||||||
|
type: str
|
||||||
|
sample: "2014-07-07T18:25:31.275934"
|
||||||
|
description:
|
||||||
|
description: Description for this zone.
|
||||||
|
type: str
|
||||||
|
sample: "This is an example zone."
|
||||||
|
email:
|
||||||
|
description: E-mail for the zone. Used in SOA records for the zone.
|
||||||
|
type: str
|
||||||
|
sample: "test@example.org"
|
||||||
|
id:
|
||||||
|
description: ID for the resource.
|
||||||
|
type: int
|
||||||
|
sample: "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"
|
||||||
|
links:
|
||||||
|
description: Links to the resource, and other related resources. When a response has been broken into\
|
||||||
|
pages, we will include a next link that should be followed to retrieve all results.
|
||||||
|
type: dict
|
||||||
|
sample: {"self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"}
|
||||||
|
masters:
|
||||||
|
description: Mandatory for secondary zones. The servers to slave from to get DNS information.
|
||||||
|
type: list
|
||||||
|
sample: "[]"
|
||||||
|
name:
|
||||||
|
description: DNS Name for the zone.
|
||||||
|
type: str
|
||||||
|
sample: "test.test."
|
||||||
|
pool_id:
|
||||||
|
description: ID for the pool hosting this zone.
|
||||||
|
type: str
|
||||||
|
sample: "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"
|
||||||
|
project_id:
|
||||||
|
description: ID for the project that owns the resource.
|
||||||
|
type: str
|
||||||
|
sample: "4335d1f0-f793-11e2-b778-0800200c9a66"
|
||||||
|
serial:
|
||||||
|
description: Current serial number for the zone.
|
||||||
|
type: int
|
||||||
|
sample: 1404757531
|
||||||
|
status:
|
||||||
|
description: Status of the resource.
|
||||||
|
type: str
|
||||||
|
sample: "ACTIVE"
|
||||||
|
ttl:
|
||||||
|
description: TTL (Time to Live) for the zone.
|
||||||
|
type: int
|
||||||
|
sample: 7200
|
||||||
|
type:
|
||||||
|
description: Type of zone. PRIMARY is controlled by Designate, SECONDARY zones are slaved from another\
|
||||||
|
DNS Server. Defaults to PRIMARY
|
||||||
|
type: str
|
||||||
|
sample: "PRIMARY"
|
||||||
|
updated_at:
|
||||||
|
description: Date / Time when resource last updated.
|
||||||
|
type: str
|
||||||
|
sample: "2014-07-07T18:25:31.275934"
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
|
||||||
|
class DnsZoneInfoModule(OpenStackModule):
|
||||||
|
|
||||||
|
argument_spec = dict(
|
||||||
|
name=dict(required=False, type='str'),
|
||||||
|
type=dict(required=False, choices=['primary', 'secondary'], type='str'),
|
||||||
|
email=dict(required=False, type='str'),
|
||||||
|
description=dict(required=False, type='str'),
|
||||||
|
ttl=dict(required=False, type='int')
|
||||||
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
name = self.params['name']
|
||||||
|
type = self.params['type']
|
||||||
|
email = self.params['email']
|
||||||
|
description = self.params['description']
|
||||||
|
ttl = self.params['ttl']
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
if name:
|
||||||
|
kwargs['name'] = name
|
||||||
|
if type:
|
||||||
|
kwargs['type'] = type
|
||||||
|
if email:
|
||||||
|
kwargs['email'] = email
|
||||||
|
if description:
|
||||||
|
kwargs['description'] = description
|
||||||
|
if ttl:
|
||||||
|
kwargs['ttl'] = ttl
|
||||||
|
|
||||||
|
data = []
|
||||||
|
|
||||||
|
for raw in self.conn.dns.zones(**kwargs):
|
||||||
|
dt = raw.to_dict()
|
||||||
|
dt.pop('location')
|
||||||
|
data.append(dt)
|
||||||
|
|
||||||
|
self.exit_json(zones=data, changed=False)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = DnsZoneInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -103,35 +103,11 @@ endpoint:
|
|||||||
sample: True
|
sample: True
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _needs_update(module, endpoint):
|
class IdentityEndpointModule(OpenStackModule):
|
||||||
if endpoint.enabled != module.params['enabled']:
|
argument_spec = dict(
|
||||||
return True
|
|
||||||
if endpoint.url != module.params['url']:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(module, endpoint):
|
|
||||||
state = module.params['state']
|
|
||||||
if state == 'absent' and endpoint:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if state == 'present':
|
|
||||||
if endpoint is None:
|
|
||||||
return True
|
|
||||||
return _needs_update(module, endpoint)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
service=dict(type='str', required=True),
|
service=dict(type='str', required=True),
|
||||||
endpoint_interface=dict(type='str', required=True, choices=['admin', 'public', 'internal']),
|
endpoint_interface=dict(type='str', required=True, choices=['admin', 'public', 'internal']),
|
||||||
url=dict(type='str', required=True),
|
url=dict(type='str', required=True),
|
||||||
@@ -140,71 +116,89 @@ def main():
|
|||||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec,
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
service_name_or_id = module.params['service']
|
def _needs_update(self, endpoint):
|
||||||
interface = module.params['endpoint_interface']
|
if endpoint.enabled != self.params['enabled']:
|
||||||
url = module.params['url']
|
return True
|
||||||
region = module.params['region']
|
if endpoint.url != self.params['url']:
|
||||||
enabled = module.params['enabled']
|
return True
|
||||||
state = module.params['state']
|
return False
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def _system_state_change(self, endpoint):
|
||||||
try:
|
state = self.params['state']
|
||||||
|
if state == 'absent' and endpoint:
|
||||||
|
return True
|
||||||
|
|
||||||
service = cloud.get_service(service_name_or_id)
|
if state == 'present':
|
||||||
|
if endpoint is None:
|
||||||
|
return True
|
||||||
|
return self._needs_update(endpoint)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
service_name_or_id = self.params['service']
|
||||||
|
interface = self.params['endpoint_interface']
|
||||||
|
url = self.params['url']
|
||||||
|
region = self.params['region']
|
||||||
|
enabled = self.params['enabled']
|
||||||
|
state = self.params['state']
|
||||||
|
|
||||||
|
service = self.conn.get_service(service_name_or_id)
|
||||||
if service is None and state == 'absent':
|
if service is None and state == 'absent':
|
||||||
module.exit_json(changed=False)
|
self.exit_json(changed=False)
|
||||||
|
|
||||||
elif service is None and state == 'present':
|
elif service is None and state == 'present':
|
||||||
module.fail_json(msg='Service %s does not exist' % service_name_or_id)
|
self.fail_json(msg='Service %s does not exist' % service_name_or_id)
|
||||||
|
|
||||||
filters = dict(service_id=service.id, interface=interface)
|
filters = dict(service_id=service.id, interface=interface)
|
||||||
if region is not None:
|
if region is not None:
|
||||||
filters['region'] = region
|
filters['region'] = region
|
||||||
endpoints = cloud.search_endpoints(filters=filters)
|
endpoints = self.conn.search_endpoints(filters=filters)
|
||||||
|
|
||||||
if len(endpoints) > 1:
|
if len(endpoints) > 1:
|
||||||
module.fail_json(msg='Service %s, interface %s and region %s are '
|
self.fail_json(msg='Service %s, interface %s and region %s are '
|
||||||
'not unique' %
|
'not unique' %
|
||||||
(service_name_or_id, interface, region))
|
(service_name_or_id, interface, region))
|
||||||
elif len(endpoints) == 1:
|
elif len(endpoints) == 1:
|
||||||
endpoint = endpoints[0]
|
endpoint = endpoints[0]
|
||||||
else:
|
else:
|
||||||
endpoint = None
|
endpoint = None
|
||||||
|
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(module, endpoint))
|
self.exit_json(changed=self._system_state_change(endpoint))
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if endpoint is None:
|
if endpoint is None:
|
||||||
result = cloud.create_endpoint(service_name_or_id=service,
|
result = self.conn.create_endpoint(
|
||||||
url=url, interface=interface,
|
service_name_or_id=service, url=url, interface=interface,
|
||||||
region=region, enabled=enabled)
|
region=region, enabled=enabled)
|
||||||
endpoint = result[0]
|
endpoint = result[0]
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
if _needs_update(module, endpoint):
|
if self._needs_update(endpoint):
|
||||||
endpoint = cloud.update_endpoint(
|
endpoint = self.conn.update_endpoint(
|
||||||
endpoint.id, url=url, enabled=enabled)
|
endpoint.id, url=url, enabled=enabled)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
module.exit_json(changed=changed, endpoint=endpoint)
|
self.exit_json(changed=changed, endpoint=endpoint)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if endpoint is None:
|
if endpoint is None:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
cloud.delete_endpoint(endpoint.id)
|
self.conn.delete_endpoint(endpoint.id)
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityEndpointModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -72,124 +72,11 @@ EXAMPLES = '''
|
|||||||
RETURN = '''
|
RETURN = '''
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_idp(idp):
|
class IdentityFederationIdpModule(OpenStackModule):
|
||||||
"""
|
argument_spec = dict(
|
||||||
Normalizes the IDP definitions so that the outputs are consistent with the
|
|
||||||
parameters
|
|
||||||
|
|
||||||
- "enabled" (parameter) == "is_enabled" (SDK)
|
|
||||||
- "name" (parameter) == "id" (SDK)
|
|
||||||
"""
|
|
||||||
if idp is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
_idp = idp.to_dict()
|
|
||||||
_idp['enabled'] = idp['is_enabled']
|
|
||||||
_idp['name'] = idp['id']
|
|
||||||
return _idp
|
|
||||||
|
|
||||||
|
|
||||||
def delete_identity_provider(module, sdk, cloud, idp):
|
|
||||||
"""
|
|
||||||
Delete an existing Identity Provider
|
|
||||||
|
|
||||||
returns: the "Changed" state
|
|
||||||
"""
|
|
||||||
|
|
||||||
if idp is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
cloud.identity.delete_identity_provider(idp)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to delete identity provider: {0}'.format(str(ex)))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def create_identity_provider(module, sdk, cloud, name):
|
|
||||||
"""
|
|
||||||
Create a new Identity Provider
|
|
||||||
|
|
||||||
returns: the "Changed" state and the new identity provider
|
|
||||||
"""
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
description = module.params.get('description')
|
|
||||||
enabled = module.params.get('enabled')
|
|
||||||
domain_id = module.params.get('domain_id')
|
|
||||||
remote_ids = module.params.get('remote_ids')
|
|
||||||
|
|
||||||
if enabled is None:
|
|
||||||
enabled = True
|
|
||||||
if remote_ids is None:
|
|
||||||
remote_ids = []
|
|
||||||
|
|
||||||
attributes = {
|
|
||||||
'domain_id': domain_id,
|
|
||||||
'enabled': enabled,
|
|
||||||
'remote_ids': remote_ids,
|
|
||||||
}
|
|
||||||
if description is not None:
|
|
||||||
attributes['description'] = description
|
|
||||||
|
|
||||||
try:
|
|
||||||
idp = cloud.identity.create_identity_provider(id=name, **attributes)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to create identity provider: {0}'.format(str(ex)))
|
|
||||||
return (True, idp)
|
|
||||||
|
|
||||||
|
|
||||||
def update_identity_provider(module, sdk, cloud, idp):
|
|
||||||
"""
|
|
||||||
Update an existing Identity Provider
|
|
||||||
|
|
||||||
returns: the "Changed" state and the new identity provider
|
|
||||||
"""
|
|
||||||
|
|
||||||
description = module.params.get('description')
|
|
||||||
enabled = module.params.get('enabled')
|
|
||||||
domain_id = module.params.get('domain_id')
|
|
||||||
remote_ids = module.params.get('remote_ids')
|
|
||||||
|
|
||||||
attributes = {}
|
|
||||||
|
|
||||||
if (description is not None) and (description != idp.description):
|
|
||||||
attributes['description'] = description
|
|
||||||
if (enabled is not None) and (enabled != idp.is_enabled):
|
|
||||||
attributes['enabled'] = enabled
|
|
||||||
if (domain_id is not None) and (domain_id != idp.domain_id):
|
|
||||||
attributes['domain_id'] = domain_id
|
|
||||||
if (remote_ids is not None) and (remote_ids != idp.remote_ids):
|
|
||||||
attributes['remote_ids'] = remote_ids
|
|
||||||
|
|
||||||
if not attributes:
|
|
||||||
return False, idp
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_idp = cloud.identity.update_identity_provider(idp, **attributes)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to update identity provider: {0}'.format(str(ex)))
|
|
||||||
return (True, new_idp)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" Module entry point """
|
|
||||||
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True, aliases=['id']),
|
name=dict(required=True, aliases=['id']),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
description=dict(),
|
description=dict(),
|
||||||
@@ -197,45 +84,136 @@ def main():
|
|||||||
enabled=dict(type='bool', aliases=['is_enabled']),
|
enabled=dict(type='bool', aliases=['is_enabled']),
|
||||||
remote_ids=dict(type='list', elements='str'),
|
remote_ids=dict(type='list', elements='str'),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
)
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
**module_kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
name = module.params.get('name')
|
def normalize_idp(self, idp):
|
||||||
state = module.params.get('state')
|
"""
|
||||||
changed = False
|
Normalizes the IDP definitions so that the outputs are consistent with the
|
||||||
|
parameters
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
|
- "enabled" (parameter) == "is_enabled" (SDK)
|
||||||
|
- "name" (parameter) == "id" (SDK)
|
||||||
try:
|
"""
|
||||||
idp = cloud.identity.get_identity_provider(name)
|
|
||||||
except sdk.exceptions.ResourceNotFound:
|
|
||||||
idp = None
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to get identity provider: {0}'.format(str(ex)))
|
|
||||||
|
|
||||||
if state == 'absent':
|
|
||||||
if idp is not None:
|
|
||||||
changed = delete_identity_provider(module, sdk, cloud, idp)
|
|
||||||
module.exit_json(changed=changed)
|
|
||||||
|
|
||||||
# state == 'present'
|
|
||||||
else:
|
|
||||||
if idp is None:
|
if idp is None:
|
||||||
if module.params.get('domain_id') is None:
|
return None
|
||||||
module.fail_json(msg='A domain_id must be passed when creating'
|
|
||||||
' an identity provider')
|
|
||||||
(changed, idp) = create_identity_provider(module, sdk, cloud, name)
|
|
||||||
idp = normalize_idp(idp)
|
|
||||||
module.exit_json(changed=changed, identity_provider=idp)
|
|
||||||
|
|
||||||
(changed, new_idp) = update_identity_provider(module, sdk, cloud, idp)
|
_idp = idp.to_dict()
|
||||||
new_idp = normalize_idp(new_idp)
|
_idp['enabled'] = idp['is_enabled']
|
||||||
module.exit_json(changed=changed, identity_provider=new_idp)
|
_idp['name'] = idp['id']
|
||||||
|
return _idp
|
||||||
|
|
||||||
|
def delete_identity_provider(self, idp):
|
||||||
|
"""
|
||||||
|
Delete an existing Identity Provider
|
||||||
|
|
||||||
|
returns: the "Changed" state
|
||||||
|
"""
|
||||||
|
if idp is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.conn.identity.delete_identity_provider(idp)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def create_identity_provider(self, name):
|
||||||
|
"""
|
||||||
|
Create a new Identity Provider
|
||||||
|
|
||||||
|
returns: the "Changed" state and the new identity provider
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
description = self.params.get('description')
|
||||||
|
enabled = self.params.get('enabled')
|
||||||
|
domain_id = self.params.get('domain_id')
|
||||||
|
remote_ids = self.params.get('remote_ids')
|
||||||
|
|
||||||
|
if enabled is None:
|
||||||
|
enabled = True
|
||||||
|
if remote_ids is None:
|
||||||
|
remote_ids = []
|
||||||
|
|
||||||
|
attributes = {
|
||||||
|
'domain_id': domain_id,
|
||||||
|
'enabled': enabled,
|
||||||
|
'remote_ids': remote_ids,
|
||||||
|
}
|
||||||
|
if description is not None:
|
||||||
|
attributes['description'] = description
|
||||||
|
|
||||||
|
idp = self.conn.identity.create_identity_provider(id=name, **attributes)
|
||||||
|
return (True, idp)
|
||||||
|
|
||||||
|
def update_identity_provider(self, idp):
|
||||||
|
"""
|
||||||
|
Update an existing Identity Provider
|
||||||
|
|
||||||
|
returns: the "Changed" state and the new identity provider
|
||||||
|
"""
|
||||||
|
|
||||||
|
description = self.params.get('description')
|
||||||
|
enabled = self.params.get('enabled')
|
||||||
|
domain_id = self.params.get('domain_id')
|
||||||
|
remote_ids = self.params.get('remote_ids')
|
||||||
|
|
||||||
|
attributes = {}
|
||||||
|
|
||||||
|
if (description is not None) and (description != idp.description):
|
||||||
|
attributes['description'] = description
|
||||||
|
if (enabled is not None) and (enabled != idp.is_enabled):
|
||||||
|
attributes['enabled'] = enabled
|
||||||
|
if (domain_id is not None) and (domain_id != idp.domain_id):
|
||||||
|
attributes['domain_id'] = domain_id
|
||||||
|
if (remote_ids is not None) and (remote_ids != idp.remote_ids):
|
||||||
|
attributes['remote_ids'] = remote_ids
|
||||||
|
|
||||||
|
if not attributes:
|
||||||
|
return False, idp
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
new_idp = self.conn.identity.update_identity_provider(idp, **attributes)
|
||||||
|
return (True, new_idp)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Module entry point """
|
||||||
|
|
||||||
|
name = self.params.get('name')
|
||||||
|
state = self.params.get('state')
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
idp = self.conn.identity.find_identity_provider(name)
|
||||||
|
|
||||||
|
if state == 'absent':
|
||||||
|
if idp is not None:
|
||||||
|
changed = self.delete_identity_provider(idp)
|
||||||
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
|
# state == 'present'
|
||||||
|
else:
|
||||||
|
if idp is None:
|
||||||
|
if self.params.get('domain_id') is None:
|
||||||
|
self.fail_json(msg='A domain_id must be passed when creating'
|
||||||
|
' an identity provider')
|
||||||
|
(changed, idp) = self.create_identity_provider(name)
|
||||||
|
idp = self.normalize_idp(idp)
|
||||||
|
self.exit_json(changed=changed, identity_provider=idp)
|
||||||
|
|
||||||
|
(changed, new_idp) = self.update_identity_provider(idp)
|
||||||
|
new_idp = self.normalize_idp(new_idp)
|
||||||
|
self.exit_json(changed=changed, identity_provider=new_idp)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = IdentityFederationIdpModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -39,62 +39,50 @@ EXAMPLES = '''
|
|||||||
RETURN = '''
|
RETURN = '''
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_idp(idp):
|
class IdentityFederationIdpInfoModule(OpenStackModule):
|
||||||
"""
|
argument_spec = dict(
|
||||||
Normalizes the IDP definitions so that the outputs are consistent with the
|
name=dict(aliases=['id']),
|
||||||
parameters
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
- "enabled" (parameter) == "is_enabled" (SDK)
|
def normalize_idp(self, idp):
|
||||||
- "name" (parameter) == "id" (SDK)
|
"""
|
||||||
"""
|
Normalizes the IDP definitions so that the outputs are consistent with the
|
||||||
if idp is None:
|
parameters
|
||||||
return
|
|
||||||
|
|
||||||
_idp = idp.to_dict()
|
- "enabled" (parameter) == "is_enabled" (SDK)
|
||||||
_idp['enabled'] = idp['is_enabled']
|
- "name" (parameter) == "id" (SDK)
|
||||||
_idp['name'] = idp['id']
|
"""
|
||||||
return _idp
|
if idp is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
_idp = idp.to_dict()
|
||||||
|
_idp['enabled'] = idp['is_enabled']
|
||||||
|
_idp['name'] = idp['id']
|
||||||
|
return _idp
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Module entry point """
|
||||||
|
|
||||||
|
name = self.params.get('name')
|
||||||
|
|
||||||
|
if name:
|
||||||
|
idp = self.normalize_idp(self.conn.identity.get_identity_provider(name))
|
||||||
|
self.exit_json(changed=False, identity_providers=[idp])
|
||||||
|
|
||||||
|
else:
|
||||||
|
providers = list(map(self.normalize_idp, self.conn.identity.identity_providers()))
|
||||||
|
self.exit_json(changed=False, identity_providers=providers)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" Module entry point """
|
module = IdentityFederationIdpInfoModule()
|
||||||
|
module()
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(aliases=['id']),
|
|
||||||
)
|
|
||||||
module_kwargs = openstack_module_kwargs(
|
|
||||||
)
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
**module_kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
name = module.params.get('name')
|
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
|
|
||||||
|
|
||||||
if name:
|
|
||||||
try:
|
|
||||||
idp = normalize_idp(cloud.identity.get_identity_provider(name))
|
|
||||||
except sdk.exceptions.ResourceNotFound:
|
|
||||||
module.fail_json(msg='Failed to find identity provider')
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to get identity provider: {0}'.format(str(ex)))
|
|
||||||
module.exit_json(changed=False, identity_providers=[idp])
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
providers = list(map(normalize_idp, cloud.identity.identity_providers()))
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to list identity providers: {0}'.format(str(ex)))
|
|
||||||
module.exit_json(changed=False, identity_providers=providers)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -78,93 +78,11 @@ EXAMPLES = '''
|
|||||||
RETURN = '''
|
RETURN = '''
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_mapping(mapping):
|
class IdentityFederationMappingModule(OpenStackModule):
|
||||||
"""
|
argument_spec = dict(
|
||||||
Normalizes the mapping definitions so that the outputs are consistent with
|
|
||||||
the parameters
|
|
||||||
|
|
||||||
- "name" (parameter) == "id" (SDK)
|
|
||||||
"""
|
|
||||||
if mapping is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
_mapping = mapping.to_dict()
|
|
||||||
_mapping['name'] = mapping['id']
|
|
||||||
return _mapping
|
|
||||||
|
|
||||||
|
|
||||||
def create_mapping(module, sdk, cloud, name):
|
|
||||||
"""
|
|
||||||
Attempt to create a Mapping
|
|
||||||
|
|
||||||
returns: A tuple containing the "Changed" state and the created mapping
|
|
||||||
"""
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return (True, None)
|
|
||||||
|
|
||||||
rules = module.params.get('rules')
|
|
||||||
|
|
||||||
try:
|
|
||||||
mapping = cloud.identity.create_mapping(id=name, rules=rules)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to create mapping: {0}'.format(str(ex)))
|
|
||||||
return (True, mapping)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_mapping(module, sdk, cloud, mapping):
|
|
||||||
"""
|
|
||||||
Attempt to delete a Mapping
|
|
||||||
|
|
||||||
returns: the "Changed" state
|
|
||||||
"""
|
|
||||||
if mapping is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
cloud.identity.delete_mapping(mapping)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to delete mapping: {0}'.format(str(ex)))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def update_mapping(module, sdk, cloud, mapping):
|
|
||||||
"""
|
|
||||||
Attempt to delete a Mapping
|
|
||||||
|
|
||||||
returns: The "Changed" state and the the new mapping
|
|
||||||
"""
|
|
||||||
|
|
||||||
current_rules = mapping.rules
|
|
||||||
new_rules = module.params.get('rules')
|
|
||||||
|
|
||||||
# Nothing to do
|
|
||||||
if current_rules == new_rules:
|
|
||||||
return (False, mapping)
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return (True, None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_mapping = cloud.identity.update_mapping(mapping, rules=new_rules)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to update mapping: {0}'.format(str(ex)))
|
|
||||||
return (True, new_mapping)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" Module entry point """
|
|
||||||
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True, aliases=['id']),
|
name=dict(required=True, aliases=['id']),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
rules=dict(type='list', elements='dict', options=dict(
|
rules=dict(type='list', elements='dict', options=dict(
|
||||||
@@ -172,46 +90,107 @@ def main():
|
|||||||
remote=dict(required=True, type='list', elements='dict')
|
remote=dict(required=True, type='list', elements='dict')
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
required_if=[('state', 'present', ['rules'])]
|
required_if=[('state', 'present', ['rules'])],
|
||||||
)
|
supports_check_mode=True
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
**module_kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
name = module.params.get('name')
|
def normalize_mapping(self, mapping):
|
||||||
state = module.params.get('state')
|
"""
|
||||||
changed = False
|
Normalizes the mapping definitions so that the outputs are consistent with
|
||||||
|
the parameters
|
||||||
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
|
|
||||||
|
|
||||||
try:
|
|
||||||
mapping = cloud.identity.get_mapping(name)
|
|
||||||
except sdk.exceptions.ResourceNotFound:
|
|
||||||
mapping = None
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to fetch mapping: {0}'.format(str(ex)))
|
|
||||||
|
|
||||||
if state == 'absent':
|
|
||||||
if mapping is not None:
|
|
||||||
changed = delete_mapping(module, sdk, cloud, mapping)
|
|
||||||
module.exit_json(changed=changed)
|
|
||||||
|
|
||||||
# state == 'present'
|
|
||||||
else:
|
|
||||||
if len(module.params.get('rules')) < 1:
|
|
||||||
module.fail_json(msg='At least one rule must be passed')
|
|
||||||
|
|
||||||
|
- "name" (parameter) == "id" (SDK)
|
||||||
|
"""
|
||||||
if mapping is None:
|
if mapping is None:
|
||||||
(changed, mapping) = create_mapping(module, sdk, cloud, name)
|
return None
|
||||||
mapping = normalize_mapping(mapping)
|
|
||||||
module.exit_json(changed=changed, mapping=mapping)
|
_mapping = mapping.to_dict()
|
||||||
|
_mapping['name'] = mapping['id']
|
||||||
|
return _mapping
|
||||||
|
|
||||||
|
def create_mapping(self, name):
|
||||||
|
"""
|
||||||
|
Attempt to create a Mapping
|
||||||
|
|
||||||
|
returns: A tuple containing the "Changed" state and the created mapping
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return (True, None)
|
||||||
|
|
||||||
|
rules = self.params.get('rules')
|
||||||
|
|
||||||
|
mapping = self.conn.identity.create_mapping(id=name, rules=rules)
|
||||||
|
return (True, mapping)
|
||||||
|
|
||||||
|
def delete_mapping(self, mapping):
|
||||||
|
"""
|
||||||
|
Attempt to delete a Mapping
|
||||||
|
|
||||||
|
returns: the "Changed" state
|
||||||
|
"""
|
||||||
|
if mapping is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.conn.identity.delete_mapping(mapping)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update_mapping(self, mapping):
|
||||||
|
"""
|
||||||
|
Attempt to delete a Mapping
|
||||||
|
|
||||||
|
returns: The "Changed" state and the the new mapping
|
||||||
|
"""
|
||||||
|
|
||||||
|
current_rules = mapping.rules
|
||||||
|
new_rules = self.params.get('rules')
|
||||||
|
|
||||||
|
# Nothing to do
|
||||||
|
if current_rules == new_rules:
|
||||||
|
return (False, mapping)
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return (True, None)
|
||||||
|
|
||||||
|
new_mapping = self.conn.identity.update_mapping(mapping, rules=new_rules)
|
||||||
|
return (True, new_mapping)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Module entry point """
|
||||||
|
|
||||||
|
name = self.params.get('name')
|
||||||
|
state = self.params.get('state')
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
mapping = self.conn.identity.find_mapping(name)
|
||||||
|
|
||||||
|
if state == 'absent':
|
||||||
|
if mapping is not None:
|
||||||
|
changed = self.delete_mapping(mapping)
|
||||||
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
|
# state == 'present'
|
||||||
else:
|
else:
|
||||||
(changed, new_mapping) = update_mapping(module, sdk, cloud, mapping)
|
if len(self.params.get('rules')) < 1:
|
||||||
new_mapping = normalize_mapping(new_mapping)
|
self.fail_json(msg='At least one rule must be passed')
|
||||||
module.exit_json(mapping=new_mapping, changed=changed)
|
|
||||||
|
if mapping is None:
|
||||||
|
(changed, mapping) = self.create_mapping(name)
|
||||||
|
mapping = self.normalize_mapping(mapping)
|
||||||
|
self.exit_json(changed=changed, mapping=mapping)
|
||||||
|
else:
|
||||||
|
(changed, new_mapping) = self.update_mapping(mapping)
|
||||||
|
new_mapping = self.normalize_mapping(new_mapping)
|
||||||
|
self.exit_json(mapping=new_mapping, changed=changed)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = IdentityFederationMappingModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -38,60 +38,50 @@ EXAMPLES = '''
|
|||||||
RETURN = '''
|
RETURN = '''
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_mapping(mapping):
|
class IdentityFederationMappingInfoModule(OpenStackModule):
|
||||||
"""
|
argument_spec = dict(
|
||||||
Normalizes the mapping definitions so that the outputs are consistent with the
|
name=dict(aliases=['id']),
|
||||||
parameters
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
- "name" (parameter) == "id" (SDK)
|
module_min_sdk_version = "0.44"
|
||||||
"""
|
|
||||||
if mapping is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
_mapping = mapping.to_dict()
|
def normalize_mapping(self, mapping):
|
||||||
_mapping['name'] = mapping['id']
|
"""
|
||||||
return _mapping
|
Normalizes the mapping definitions so that the outputs are consistent with the
|
||||||
|
parameters
|
||||||
|
|
||||||
|
- "name" (parameter) == "id" (SDK)
|
||||||
|
"""
|
||||||
|
if mapping is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
_mapping = mapping.to_dict()
|
||||||
|
_mapping['name'] = mapping['id']
|
||||||
|
return _mapping
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Module entry point """
|
||||||
|
name = self.params.get('name')
|
||||||
|
|
||||||
|
if name:
|
||||||
|
mapping = self.normalize_mapping(
|
||||||
|
self.conn.identity.get_mapping(name))
|
||||||
|
self.exit_json(changed=False, mappings=[mapping])
|
||||||
|
else:
|
||||||
|
mappings = list(map(
|
||||||
|
self.normalize_mapping, self.conn.identity.mappings()))
|
||||||
|
self.exit_json(changed=False, mappings=mappings)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" Module entry point """
|
module = IdentityFederationMappingInfoModule()
|
||||||
|
module()
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(aliases=['id']),
|
|
||||||
)
|
|
||||||
module_kwargs = openstack_module_kwargs(
|
|
||||||
)
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
**module_kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
name = module.params.get('name')
|
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
|
|
||||||
|
|
||||||
if name:
|
|
||||||
try:
|
|
||||||
mapping = normalize_mapping(cloud.identity.get_mapping(name))
|
|
||||||
except sdk.exceptions.ResourceNotFound:
|
|
||||||
module.fail_json(msg='Failed to find mapping')
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to get mapping: {0}'.format(str(ex)))
|
|
||||||
module.exit_json(changed=False, mappings=[mapping])
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
mappings = list(map(normalize_mapping, cloud.identity.mappings()))
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to list mappings: {0}'.format(str(ex)))
|
|
||||||
module.exit_json(changed=False, mappings=mappings)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ short_description: Add/Remove floating IP from an instance
|
|||||||
description:
|
description:
|
||||||
- Add or Remove a floating IP to an instance.
|
- Add or Remove a floating IP to an instance.
|
||||||
- Returns the floating IP when attaching only if I(wait=true).
|
- Returns the floating IP when attaching only if I(wait=true).
|
||||||
|
- When detaching a floating IP there might be a delay until an instance does not list the floating IP any more.
|
||||||
options:
|
options:
|
||||||
server:
|
server:
|
||||||
description:
|
description:
|
||||||
@@ -24,9 +25,9 @@ options:
|
|||||||
type: str
|
type: str
|
||||||
floating_ip_address:
|
floating_ip_address:
|
||||||
description:
|
description:
|
||||||
- A floating IP address to attach or to detach. Required only if I(state)
|
- A floating IP address to attach or to detach. When I(state) is present
|
||||||
is absent. When I(state) is present can be used to specify a IP address
|
can be used to specify a IP address to attach. I(floating_ip_address)
|
||||||
to attach.
|
requires I(network) to be set.
|
||||||
type: str
|
type: str
|
||||||
reuse:
|
reuse:
|
||||||
description:
|
description:
|
||||||
@@ -49,7 +50,7 @@ options:
|
|||||||
wait:
|
wait:
|
||||||
description:
|
description:
|
||||||
- When attaching a floating IP address, specify whether to wait for it to appear as attached.
|
- When attaching a floating IP address, specify whether to wait for it to appear as attached.
|
||||||
- Must be set to C(yes) for the module to return the value of the floating IP.
|
- Must be set to C(yes) for the module to return the value of the floating IP when attaching.
|
||||||
type: bool
|
type: bool
|
||||||
default: 'no'
|
default: 'no'
|
||||||
timeout:
|
timeout:
|
||||||
@@ -118,23 +119,12 @@ EXAMPLES = '''
|
|||||||
server: cattle001
|
server: cattle001
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, remove_values
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
import itertools
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_floating_ip(cloud, floating_ip_address):
|
class NetworkingFloatingIPModule(OpenStackModule):
|
||||||
f_ips = cloud.search_floating_ips(
|
argument_spec = dict(
|
||||||
filters={'floating_ip_address': floating_ip_address})
|
|
||||||
if not f_ips:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return f_ips[0]
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
server=dict(required=True),
|
server=dict(required=True),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
network=dict(required=False, default=None),
|
network=dict(required=False, default=None),
|
||||||
@@ -148,107 +138,169 @@ def main():
|
|||||||
purge=dict(required=False, type='bool', default=False),
|
purge=dict(required=False, type='bool', default=False),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
required_if=[
|
||||||
|
['state', 'absent', ['floating_ip_address']]
|
||||||
|
],
|
||||||
|
required_by=dict(
|
||||||
|
floating_ip_address=('network',)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
server_name_or_id = module.params['server']
|
def _get_floating_ip(self, floating_ip_address):
|
||||||
state = module.params['state']
|
f_ips = self.conn.search_floating_ips(
|
||||||
network = module.params['network']
|
filters={'floating_ip_address': floating_ip_address})
|
||||||
floating_ip_address = module.params['floating_ip_address']
|
|
||||||
reuse = module.params['reuse']
|
|
||||||
fixed_address = module.params['fixed_address']
|
|
||||||
nat_destination = module.params['nat_destination']
|
|
||||||
wait = module.params['wait']
|
|
||||||
timeout = module.params['timeout']
|
|
||||||
purge = module.params['purge']
|
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
if not f_ips:
|
||||||
try:
|
return None
|
||||||
|
|
||||||
server = cloud.get_server(server_name_or_id)
|
return f_ips[0]
|
||||||
if server is None:
|
|
||||||
module.fail_json(
|
def _list_floating_ips(self, server):
|
||||||
|
return itertools.chain.from_iterable([
|
||||||
|
(addr['addr'] for addr in server.addresses[net] if addr['OS-EXT-IPS:type'] == 'floating')
|
||||||
|
for net in server.addresses
|
||||||
|
])
|
||||||
|
|
||||||
|
def _match_floating_ip(self, server,
|
||||||
|
floating_ip_address,
|
||||||
|
network_id,
|
||||||
|
fixed_address,
|
||||||
|
nat_destination):
|
||||||
|
|
||||||
|
if floating_ip_address:
|
||||||
|
return self._get_floating_ip(floating_ip_address)
|
||||||
|
elif not fixed_address and nat_destination:
|
||||||
|
nat_destination_name = self.conn.get_network(nat_destination)['name']
|
||||||
|
return next(
|
||||||
|
(self._get_floating_ip(addr['addr'])
|
||||||
|
for addr in server.addresses.get(nat_destination_name, [])
|
||||||
|
if addr['OS-EXT-IPS:type'] == 'floating'),
|
||||||
|
None)
|
||||||
|
else:
|
||||||
|
# not floating_ip_address and (fixed_address or not nat_destination)
|
||||||
|
|
||||||
|
# get any of the floating ips that matches fixed_address and/or network
|
||||||
|
f_ip_addrs = self._list_floating_ips(server)
|
||||||
|
f_ips = [f_ip for f_ip in self.conn.list_floating_ips() if f_ip['floating_ip_address'] in f_ip_addrs]
|
||||||
|
return next(
|
||||||
|
(f_ip for f_ip in f_ips
|
||||||
|
if ((fixed_address and f_ip.fixed_ip_address == fixed_address) or not fixed_address)
|
||||||
|
and ((network_id and f_ip.network == network_id) or not network_id)),
|
||||||
|
None)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
server_name_or_id = self.params['server']
|
||||||
|
state = self.params['state']
|
||||||
|
network = self.params['network']
|
||||||
|
floating_ip_address = self.params['floating_ip_address']
|
||||||
|
reuse = self.params['reuse']
|
||||||
|
fixed_address = self.params['fixed_address']
|
||||||
|
nat_destination = self.params['nat_destination']
|
||||||
|
wait = self.params['wait']
|
||||||
|
timeout = self.params['timeout']
|
||||||
|
purge = self.params['purge']
|
||||||
|
|
||||||
|
server = self.conn.get_server(server_name_or_id)
|
||||||
|
if not server:
|
||||||
|
self.fail_json(
|
||||||
msg="server {0} not found".format(server_name_or_id))
|
msg="server {0} not found".format(server_name_or_id))
|
||||||
|
|
||||||
|
# Extract floating ips from server
|
||||||
|
f_ip_addrs = self._list_floating_ips(server)
|
||||||
|
|
||||||
|
# Get details about requested floating ip
|
||||||
|
f_ip = self._get_floating_ip(floating_ip_address) if floating_ip_address else None
|
||||||
|
|
||||||
|
if network:
|
||||||
|
network_id = self.conn.get_network(name_or_id=network)["id"]
|
||||||
|
else:
|
||||||
|
network_id = None
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
# If f_ip already assigned to server, check that it matches
|
if floating_ip_address and f_ip and floating_ip_address in f_ip_addrs:
|
||||||
# requirements.
|
# Floating ip address has been assigned to server
|
||||||
public_ip = cloud.get_server_public_ip(server)
|
self.exit_json(changed=False, floating_ip=f_ip)
|
||||||
f_ip = _get_floating_ip(cloud, public_ip) if public_ip else public_ip
|
|
||||||
if f_ip:
|
|
||||||
if network:
|
|
||||||
network_id = cloud.get_network(name_or_id=network)["id"]
|
|
||||||
else:
|
|
||||||
network_id = None
|
|
||||||
# check if we have floating ip on given nat_destination network
|
|
||||||
if nat_destination:
|
|
||||||
nat_floating_addrs = [
|
|
||||||
addr for addr in server.addresses.get(
|
|
||||||
cloud.get_network(nat_destination)['name'], [])
|
|
||||||
if addr['addr'] == public_ip
|
|
||||||
and addr['OS-EXT-IPS:type'] == 'floating'
|
|
||||||
]
|
|
||||||
|
|
||||||
if len(nat_floating_addrs) == 0:
|
if f_ip and f_ip['attached'] and floating_ip_address not in f_ip_addrs:
|
||||||
module.fail_json(msg="server {server} already has a "
|
# Requested floating ip has been attached to different server
|
||||||
"floating-ip on a different "
|
self.fail_json(msg="floating-ip {floating_ip_address} already has been attached to different server"
|
||||||
"nat-destination than '{nat_destination}'"
|
.format(floating_ip_address=floating_ip_address))
|
||||||
.format(server=server_name_or_id,
|
|
||||||
nat_destination=nat_destination))
|
|
||||||
|
|
||||||
if all([fixed_address, f_ip.fixed_ip_address == fixed_address,
|
if not floating_ip_address:
|
||||||
network, f_ip.network != network_id]):
|
# No specific floating ip requested, i.e. if any floating ip is already assigned to server,
|
||||||
# Current state definitely conflicts with requirements
|
# check that it matches requirements.
|
||||||
module.fail_json(msg="server {server} already has a "
|
|
||||||
"floating-ip on requested "
|
|
||||||
"interface but it doesn't match "
|
|
||||||
"requested network {network}: {fip}"
|
|
||||||
.format(server=server_name_or_id,
|
|
||||||
network=network,
|
|
||||||
fip=remove_values(f_ip,
|
|
||||||
module.no_log_values)))
|
|
||||||
if not network or f_ip.network == network_id:
|
|
||||||
# Requirements are met
|
|
||||||
module.exit_json(changed=False, floating_ip=f_ip)
|
|
||||||
|
|
||||||
# Requirements are vague enough to ignore existing f_ip and try
|
if not fixed_address and nat_destination:
|
||||||
# to create a new f_ip to the server.
|
# Check if we have any floating ip on the given nat_destination network
|
||||||
|
nat_destination_name = self.conn.get_network(nat_destination)['name']
|
||||||
|
for addr in server.addresses.get(nat_destination_name, []):
|
||||||
|
if addr['OS-EXT-IPS:type'] == 'floating':
|
||||||
|
# A floating ip address has been assigned to the requested nat_destination
|
||||||
|
f_ip = self._get_floating_ip(addr['addr'])
|
||||||
|
self.exit_json(changed=False, floating_ip=f_ip)
|
||||||
|
# else fixed_address or not nat_destination, hence an
|
||||||
|
# analysis of all floating ips of server is required
|
||||||
|
f_ips = [f_ip for f_ip in self.conn.list_floating_ips() if f_ip['floating_ip_address'] in f_ip_addrs]
|
||||||
|
for f_ip in f_ips:
|
||||||
|
if network_id and f_ip.network != network_id:
|
||||||
|
# requested network does not match network of floating ip
|
||||||
|
continue
|
||||||
|
|
||||||
server = cloud.add_ips_to_server(
|
if not fixed_address and not nat_destination:
|
||||||
server=server, ips=floating_ip_address, ip_pool=network,
|
# any floating ip will fullfil these requirements
|
||||||
reuse=reuse, fixed_address=fixed_address, wait=wait,
|
self.exit_json(changed=False, floating_ip=f_ip)
|
||||||
|
|
||||||
|
if fixed_address and f_ip.fixed_ip_address == fixed_address:
|
||||||
|
# a floating ip address has been assigned that points to the requested fixed_address
|
||||||
|
self.exit_json(changed=False, floating_ip=f_ip)
|
||||||
|
|
||||||
|
if floating_ip_address and not f_ip:
|
||||||
|
# openstacksdk's create_ip requires floating_ip_address and floating_network_id to be set
|
||||||
|
self.conn.network.create_ip(floating_ip_address=floating_ip_address, floating_network_id=network_id)
|
||||||
|
# Else floating ip either does not exist or has not been attached yet
|
||||||
|
|
||||||
|
# Both floating_ip_address and network are mutually exclusive in add_ips_to_server, i.e.
|
||||||
|
# add_ips_to_server will ignore floating_ip_address if network is set
|
||||||
|
# Ref.: https://github.com/openstack/openstacksdk/blob/a6b0ece2821ea79330c4067100295f6bdcbe456e/openstack/cloud/_floating_ip.py#L987
|
||||||
|
server = self.conn.add_ips_to_server(
|
||||||
|
server=server,
|
||||||
|
ips=floating_ip_address,
|
||||||
|
ip_pool=network if not floating_ip_address else None,
|
||||||
|
reuse=reuse,
|
||||||
|
fixed_address=fixed_address,
|
||||||
|
wait=wait,
|
||||||
timeout=timeout, nat_destination=nat_destination)
|
timeout=timeout, nat_destination=nat_destination)
|
||||||
fip_address = cloud.get_server_public_ip(server)
|
|
||||||
# Update the floating IP status
|
# Update the floating ip status
|
||||||
f_ip = _get_floating_ip(cloud, fip_address)
|
f_ip = self._match_floating_ip(server, floating_ip_address, network_id, fixed_address, nat_destination)
|
||||||
module.exit_json(changed=True, floating_ip=f_ip)
|
self.exit_json(changed=True, floating_ip=f_ip)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if floating_ip_address is None:
|
f_ip = self._match_floating_ip(server, floating_ip_address, network_id, fixed_address, nat_destination)
|
||||||
if not server_name_or_id:
|
|
||||||
module.fail_json(msg="either server or floating_ip_address are required")
|
|
||||||
server = cloud.get_server(server_name_or_id)
|
|
||||||
floating_ip_address = cloud.get_server_public_ip(server)
|
|
||||||
|
|
||||||
f_ip = _get_floating_ip(cloud, floating_ip_address)
|
|
||||||
|
|
||||||
if not f_ip:
|
if not f_ip:
|
||||||
# Nothing to detach
|
# Nothing to detach
|
||||||
module.exit_json(changed=False)
|
self.exit_json(changed=False)
|
||||||
changed = False
|
changed = False
|
||||||
if f_ip["fixed_ip_address"]:
|
|
||||||
cloud.detach_ip_from_server(
|
|
||||||
server_id=server['id'], floating_ip_id=f_ip['id'])
|
|
||||||
# Update the floating IP status
|
|
||||||
f_ip = cloud.get_floating_ip(id=f_ip['id'])
|
|
||||||
changed = True
|
|
||||||
if purge:
|
|
||||||
cloud.delete_floating_ip(f_ip['id'])
|
|
||||||
module.exit_json(changed=True)
|
|
||||||
module.exit_json(changed=changed, floating_ip=f_ip)
|
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
if f_ip["fixed_ip_address"]:
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
self.conn.detach_ip_from_server(server_id=server['id'], floating_ip_id=f_ip['id'])
|
||||||
|
# OpenStackSDK sets {"port_id": None} to detach a floating ip from an instance,
|
||||||
|
# but there might be a delay until a server does not list it in addresses any more.
|
||||||
|
|
||||||
|
# Update the floating IP status
|
||||||
|
f_ip = self.conn.get_floating_ip(id=f_ip['id'])
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if purge:
|
||||||
|
self.conn.delete_floating_ip(f_ip['id'])
|
||||||
|
self.exit_json(changed=True)
|
||||||
|
self.exit_json(changed=changed, floating_ip=f_ip)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = NetworkingFloatingIPModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
214
plugins/modules/floating_ip_info.py
Normal file
214
plugins/modules/floating_ip_info.py
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 by Open Telekom Cloud, operated by T-Systems International GmbH
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
DOCUMENTATION = '''
|
||||||
|
---
|
||||||
|
module: floating_ip_info
|
||||||
|
short_description: Get information about floating ips
|
||||||
|
author: OpenStack Ansible SIG
|
||||||
|
description:
|
||||||
|
- Get a generator of floating ips.
|
||||||
|
options:
|
||||||
|
description:
|
||||||
|
description:
|
||||||
|
- The description of a floating IP.
|
||||||
|
type: str
|
||||||
|
fixed_ip_address:
|
||||||
|
description:
|
||||||
|
- The fixed IP address associated with a floating IP address.
|
||||||
|
type: str
|
||||||
|
floating_ip_address:
|
||||||
|
description:
|
||||||
|
- The IP address of a floating IP.
|
||||||
|
type: str
|
||||||
|
floating_network:
|
||||||
|
description:
|
||||||
|
- The name or id of the network associated with a floating IP.
|
||||||
|
type: str
|
||||||
|
port:
|
||||||
|
description:
|
||||||
|
- The name or id of the port to which a floating IP is associated.
|
||||||
|
type: str
|
||||||
|
project_id:
|
||||||
|
description:
|
||||||
|
- The ID of the project a floating IP is associated with.
|
||||||
|
type: str
|
||||||
|
router:
|
||||||
|
description:
|
||||||
|
- The name or id of an associated router.
|
||||||
|
type: str
|
||||||
|
status:
|
||||||
|
description:
|
||||||
|
- The status of a floating IP, which can be ``ACTIVE``or ``DOWN``.
|
||||||
|
choices: ['active', 'down']
|
||||||
|
type: str
|
||||||
|
requirements:
|
||||||
|
- "python >= 3.6"
|
||||||
|
- "openstacksdk"
|
||||||
|
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- openstack.cloud.openstack
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = '''
|
||||||
|
floating_ips:
|
||||||
|
description: The floating ip objects list.
|
||||||
|
type: complex
|
||||||
|
returned: On Success.
|
||||||
|
contains:
|
||||||
|
created_at:
|
||||||
|
description: Timestamp at which the floating IP was assigned.
|
||||||
|
type: str
|
||||||
|
description:
|
||||||
|
description: The description of a floating IP.
|
||||||
|
type: str
|
||||||
|
dns_domain:
|
||||||
|
description: The DNS domain.
|
||||||
|
type: str
|
||||||
|
dns_name:
|
||||||
|
description: The DNS name.
|
||||||
|
type: str
|
||||||
|
fixed_ip_address:
|
||||||
|
description: The fixed IP address associated with a floating IP address.
|
||||||
|
type: str
|
||||||
|
floating_ip_address:
|
||||||
|
description: The IP address of a floating IP.
|
||||||
|
type: str
|
||||||
|
floating_network_id:
|
||||||
|
description: The id of the network associated with a floating IP.
|
||||||
|
type: str
|
||||||
|
id:
|
||||||
|
description: Id of the floating ip.
|
||||||
|
type: str
|
||||||
|
name:
|
||||||
|
description: Name of the floating ip.
|
||||||
|
type: str
|
||||||
|
port_details:
|
||||||
|
description: The details of the port that this floating IP associates \
|
||||||
|
with. Present if ``fip-port-details`` extension is loaded.
|
||||||
|
type: str
|
||||||
|
port_id:
|
||||||
|
description: The port ID floating ip associated with.
|
||||||
|
type: str
|
||||||
|
project_id:
|
||||||
|
description: The ID of the project this floating IP is associated with.
|
||||||
|
type: str
|
||||||
|
qos_policy_id:
|
||||||
|
description: The ID of the QoS policy attached to the floating IP.
|
||||||
|
type: str
|
||||||
|
revision_number:
|
||||||
|
description: Revision number.
|
||||||
|
type: str
|
||||||
|
router_id:
|
||||||
|
description: The id of the router floating ip associated with.
|
||||||
|
type: str
|
||||||
|
status:
|
||||||
|
description: The status of a floating IP, which can be ``ACTIVE``or ``DOWN``.\
|
||||||
|
Can be 'ACTIVE' and 'DOWN'.
|
||||||
|
type: str
|
||||||
|
subnet_id:
|
||||||
|
description: The id of the subnet the floating ip associated with.
|
||||||
|
type: str
|
||||||
|
tags:
|
||||||
|
description: List of tags.
|
||||||
|
type: str
|
||||||
|
updated_at:
|
||||||
|
description: Timestamp at which the floating IP was last updated.
|
||||||
|
type: str
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
# Getting all floating ips
|
||||||
|
- openstack.cloud.floating_ip_info:
|
||||||
|
register: fips
|
||||||
|
|
||||||
|
# Getting fip by associated fixed IP address.
|
||||||
|
- openstack.cloud.floating_ip_info:
|
||||||
|
fixed_ip_address: 192.168.10.8
|
||||||
|
register: fip
|
||||||
|
|
||||||
|
# Getting fip by associated router.
|
||||||
|
- openstack.cloud.floating_ip_info:
|
||||||
|
router: my-router
|
||||||
|
register: fip
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
|
||||||
|
class FloatingIPInfoModule(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
|
description=dict(required=False),
|
||||||
|
fixed_ip_address=dict(required=False),
|
||||||
|
floating_ip_address=dict(required=False),
|
||||||
|
floating_network=dict(required=False),
|
||||||
|
port=dict(required=False),
|
||||||
|
project_id=dict(required=False),
|
||||||
|
router=dict(required=False),
|
||||||
|
status=dict(required=False, choices=['active', 'down']),
|
||||||
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
description = self.params['description']
|
||||||
|
fixed_ip_address = self.params['fixed_ip_address']
|
||||||
|
floating_ip_address = self.params['floating_ip_address']
|
||||||
|
floating_network = self.params['floating_network']
|
||||||
|
port = self.params['port']
|
||||||
|
project_id = self.params['project_id']
|
||||||
|
router = self.params['router']
|
||||||
|
status = self.params['status']
|
||||||
|
|
||||||
|
data = []
|
||||||
|
query = {}
|
||||||
|
if description:
|
||||||
|
query['description'] = description
|
||||||
|
if fixed_ip_address:
|
||||||
|
query['fixed_ip_address'] = fixed_ip_address
|
||||||
|
if floating_ip_address:
|
||||||
|
query['floating_ip_address'] = floating_ip_address
|
||||||
|
if floating_network:
|
||||||
|
try:
|
||||||
|
query['floating_network_id'] = self.conn.network.find_network(name_or_id=floating_network,
|
||||||
|
ignore_missing=False).id
|
||||||
|
except self.sdk.exceptions.ResourceNotFound:
|
||||||
|
self.fail_json(msg="floating_network not found")
|
||||||
|
if port:
|
||||||
|
try:
|
||||||
|
query['port_id'] = self.conn.network.find_port(name_or_id=port, ignore_missing=False).id
|
||||||
|
except self.sdk.exceptions.ResourceNotFound:
|
||||||
|
self.fail_json(msg="port not found")
|
||||||
|
if project_id:
|
||||||
|
query['project_id'] = project_id
|
||||||
|
if router:
|
||||||
|
try:
|
||||||
|
query['router_id'] = self.conn.network.find_router(name_or_id=router, ignore_missing=False).id
|
||||||
|
except self.sdk.exceptions.ResourceNotFound:
|
||||||
|
self.fail_json(msg="router not found")
|
||||||
|
if status:
|
||||||
|
query['status'] = status.upper()
|
||||||
|
|
||||||
|
for raw in self.conn.network.ips(**query):
|
||||||
|
dt = raw.to_dict()
|
||||||
|
dt.pop('location')
|
||||||
|
data.append(dt)
|
||||||
|
|
||||||
|
self.exit_json(
|
||||||
|
changed=False,
|
||||||
|
floating_ips=data
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = FloatingIPInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -42,58 +42,54 @@ EXAMPLES = '''
|
|||||||
group: demo
|
group: demo
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(state, in_group):
|
class IdentityGroupAssignment(OpenStackModule):
|
||||||
if state == 'present' and not in_group:
|
argument_spec = dict(
|
||||||
return True
|
|
||||||
if state == 'absent' and in_group:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
user=dict(required=True),
|
user=dict(required=True),
|
||||||
group=dict(required=True),
|
group=dict(required=True),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec,
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
user = module.params['user']
|
def _system_state_change(self, state, in_group):
|
||||||
group = module.params['group']
|
if state == 'present' and not in_group:
|
||||||
state = module.params['state']
|
return True
|
||||||
|
if state == 'absent' and in_group:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def run(self):
|
||||||
try:
|
user = self.params['user']
|
||||||
in_group = cloud.is_user_in_group(user, group)
|
group = self.params['group']
|
||||||
|
state = self.params['state']
|
||||||
|
|
||||||
if module.check_mode:
|
in_group = self.conn.is_user_in_group(user, group)
|
||||||
module.exit_json(changed=_system_state_change(state, in_group))
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(changed=self._system_state_change(state, in_group))
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if not in_group:
|
if not in_group:
|
||||||
cloud.add_user_to_group(user, group)
|
self.conn.add_user_to_group(user, group)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if in_group:
|
if in_group:
|
||||||
cloud.remove_user_from_group(user, group)
|
self.conn.remove_user_from_group(user, group)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
def main():
|
||||||
|
module = IdentityGroupAssignment()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -77,69 +77,11 @@ RETURN = '''
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _needs_update(module, aggregate):
|
class ComputeHostAggregateModule(OpenStackModule):
|
||||||
new_metadata = (module.params['metadata'] or {})
|
argument_spec = dict(
|
||||||
|
|
||||||
if module.params['availability_zone'] is not None:
|
|
||||||
new_metadata['availability_zone'] = module.params['availability_zone']
|
|
||||||
|
|
||||||
if module.params['name'] != aggregate.name:
|
|
||||||
return True
|
|
||||||
if module.params['hosts'] is not None:
|
|
||||||
if module.params['purge_hosts']:
|
|
||||||
if set(module.params['hosts']) != set(aggregate.hosts):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
intersection = set(module.params['hosts']).intersection(set(aggregate.hosts))
|
|
||||||
if set(module.params['hosts']) != intersection:
|
|
||||||
return True
|
|
||||||
if module.params['availability_zone'] is not None:
|
|
||||||
if module.params['availability_zone'] != aggregate.availability_zone:
|
|
||||||
return True
|
|
||||||
if module.params['metadata'] is not None:
|
|
||||||
if new_metadata != aggregate.metadata:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(module, aggregate):
|
|
||||||
state = module.params['state']
|
|
||||||
if state == 'absent' and aggregate:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if state == 'present':
|
|
||||||
if aggregate is None:
|
|
||||||
return True
|
|
||||||
return _needs_update(module, aggregate)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _update_hosts(cloud, aggregate, hosts, purge_hosts):
|
|
||||||
if hosts is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
hosts_to_add = set(hosts) - set(aggregate.hosts)
|
|
||||||
for i in hosts_to_add:
|
|
||||||
cloud.add_host_to_aggregate(aggregate.id, i)
|
|
||||||
|
|
||||||
if not purge_hosts:
|
|
||||||
return
|
|
||||||
|
|
||||||
hosts_to_remove = set(aggregate.hosts) - set(hosts)
|
|
||||||
for i in hosts_to_remove:
|
|
||||||
cloud.remove_host_from_aggregate(aggregate.id, i)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
metadata=dict(required=False, default=None, type='dict'),
|
metadata=dict(required=False, default=None, type='dict'),
|
||||||
availability_zone=dict(required=False, default=None),
|
availability_zone=dict(required=False, default=None),
|
||||||
@@ -148,24 +90,74 @@ def main():
|
|||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec,
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
name = module.params['name']
|
def _needs_update(self, aggregate):
|
||||||
metadata = module.params['metadata']
|
new_metadata = (self.params['metadata'] or {})
|
||||||
availability_zone = module.params['availability_zone']
|
|
||||||
hosts = module.params['hosts']
|
|
||||||
purge_hosts = module.params['purge_hosts']
|
|
||||||
state = module.params['state']
|
|
||||||
|
|
||||||
if metadata is not None:
|
if self.params['availability_zone'] is not None:
|
||||||
metadata.pop('availability_zone', None)
|
new_metadata['availability_zone'] = self.params['availability_zone']
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
if self.params['name'] != aggregate.name:
|
||||||
try:
|
return True
|
||||||
aggregates = cloud.search_aggregates(name_or_id=name)
|
if self.params['hosts'] is not None:
|
||||||
|
if self.params['purge_hosts']:
|
||||||
|
if set(self.params['hosts']) != set(aggregate.hosts):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
intersection = set(self.params['hosts']).intersection(set(aggregate.hosts))
|
||||||
|
if set(self.params['hosts']) != intersection:
|
||||||
|
return True
|
||||||
|
if self.params['availability_zone'] is not None:
|
||||||
|
if self.params['availability_zone'] != aggregate.availability_zone:
|
||||||
|
return True
|
||||||
|
if self.params['metadata'] is not None:
|
||||||
|
if new_metadata != aggregate.metadata:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _system_state_change(self, aggregate):
|
||||||
|
state = self.params['state']
|
||||||
|
if state == 'absent' and aggregate:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if state == 'present':
|
||||||
|
if aggregate is None:
|
||||||
|
return True
|
||||||
|
return self._needs_update(aggregate)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _update_hosts(self, aggregate, hosts, purge_hosts):
|
||||||
|
if hosts is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
hosts_to_add = set(hosts) - set(aggregate.get("hosts", []))
|
||||||
|
for i in hosts_to_add:
|
||||||
|
self.conn.add_host_to_aggregate(aggregate.id, i)
|
||||||
|
|
||||||
|
if not purge_hosts:
|
||||||
|
return
|
||||||
|
|
||||||
|
hosts_to_remove = set(aggregate.get("hosts", [])) - set(hosts)
|
||||||
|
for i in hosts_to_remove:
|
||||||
|
self.conn.remove_host_from_aggregate(aggregate.id, i)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
name = self.params['name']
|
||||||
|
metadata = self.params['metadata']
|
||||||
|
availability_zone = self.params['availability_zone']
|
||||||
|
hosts = self.params['hosts']
|
||||||
|
purge_hosts = self.params['purge_hosts']
|
||||||
|
state = self.params['state']
|
||||||
|
|
||||||
|
if metadata is not None:
|
||||||
|
metadata.pop('availability_zone', None)
|
||||||
|
|
||||||
|
aggregates = self.conn.search_aggregates(name_or_id=name)
|
||||||
|
|
||||||
if len(aggregates) == 1:
|
if len(aggregates) == 1:
|
||||||
aggregate = aggregates[0]
|
aggregate = aggregates[0]
|
||||||
@@ -174,45 +166,48 @@ def main():
|
|||||||
else:
|
else:
|
||||||
raise Exception("Should not happen")
|
raise Exception("Should not happen")
|
||||||
|
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(module, aggregate))
|
self.exit_json(changed=self._system_state_change(aggregate))
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if aggregate is None:
|
if aggregate is None:
|
||||||
aggregate = cloud.create_aggregate(name=name,
|
aggregate = self.conn.create_aggregate(
|
||||||
availability_zone=availability_zone)
|
name=name, availability_zone=availability_zone)
|
||||||
_update_hosts(cloud, aggregate, hosts, False)
|
self._update_hosts(aggregate, hosts, False)
|
||||||
if metadata:
|
if metadata:
|
||||||
cloud.set_aggregate_metadata(aggregate.id, metadata)
|
self.conn.set_aggregate_metadata(aggregate.id, metadata)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
if _needs_update(module, aggregate):
|
if self._needs_update(aggregate):
|
||||||
if availability_zone is not None:
|
if availability_zone is not None:
|
||||||
aggregate = cloud.update_aggregate(aggregate.id, name=name,
|
aggregate = self.conn.update_aggregate(
|
||||||
availability_zone=availability_zone)
|
aggregate.id, name=name,
|
||||||
|
availability_zone=availability_zone)
|
||||||
if metadata is not None:
|
if metadata is not None:
|
||||||
metas = metadata
|
metas = metadata
|
||||||
for i in (set(aggregate.metadata.keys()) - set(metadata.keys())):
|
for i in (set(aggregate.metadata.keys()) - set(metadata.keys())):
|
||||||
if i != 'availability_zone':
|
if i != 'availability_zone':
|
||||||
metas[i] = None
|
metas[i] = None
|
||||||
cloud.set_aggregate_metadata(aggregate.id, metas)
|
self.conn.set_aggregate_metadata(aggregate.id, metas)
|
||||||
_update_hosts(cloud, aggregate, hosts, purge_hosts)
|
self._update_hosts(aggregate, hosts, purge_hosts)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if aggregate is None:
|
if aggregate is None:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
_update_hosts(cloud, aggregate, [], True)
|
self._update_hosts(aggregate, [], True)
|
||||||
cloud.delete_aggregate(aggregate.id)
|
self.conn.delete_aggregate(aggregate.id)
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = ComputeHostAggregateModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -85,92 +85,86 @@ id:
|
|||||||
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
|
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _needs_update(module, domain):
|
class IdentityDomainModule(OpenStackModule):
|
||||||
if module.params['description'] is not None and \
|
argument_spec = dict(
|
||||||
domain.description != module.params['description']:
|
|
||||||
return True
|
|
||||||
if domain.enabled != module.params['enabled']:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(module, domain):
|
|
||||||
state = module.params['state']
|
|
||||||
if state == 'absent' and domain:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if state == 'present':
|
|
||||||
if domain is None:
|
|
||||||
return True
|
|
||||||
return _needs_update(module, domain)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
description=dict(default=None),
|
description=dict(default=None),
|
||||||
enabled=dict(default=True, type='bool'),
|
enabled=dict(default=True, type='bool'),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec,
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
name = module.params['name']
|
def _needs_update(self, domain):
|
||||||
description = module.params['description']
|
if self.params['description'] is not None and \
|
||||||
enabled = module.params['enabled']
|
domain.description != self.params['description']:
|
||||||
state = module.params['state']
|
return True
|
||||||
|
if domain.enabled != self.params['enabled']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def _system_state_change(self, domain):
|
||||||
try:
|
state = self.params['state']
|
||||||
|
if state == 'absent' and domain:
|
||||||
|
return True
|
||||||
|
|
||||||
domains = cloud.search_domains(filters=dict(name=name))
|
if state == 'present':
|
||||||
|
if domain is None:
|
||||||
|
return True
|
||||||
|
return self._needs_update(domain)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
name = self.params['name']
|
||||||
|
description = self.params['description']
|
||||||
|
enabled = self.params['enabled']
|
||||||
|
state = self.params['state']
|
||||||
|
|
||||||
|
domains = self.conn.search_domains(filters=dict(name=name))
|
||||||
|
|
||||||
if len(domains) > 1:
|
if len(domains) > 1:
|
||||||
module.fail_json(msg='Domain name %s is not unique' % name)
|
self.fail_json(msg='Domain name %s is not unique' % name)
|
||||||
elif len(domains) == 1:
|
elif len(domains) == 1:
|
||||||
domain = domains[0]
|
domain = domains[0]
|
||||||
else:
|
else:
|
||||||
domain = None
|
domain = None
|
||||||
|
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(module, domain))
|
self.exit_json(changed=self._system_state_change(domain))
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if domain is None:
|
if domain is None:
|
||||||
domain = cloud.create_domain(
|
domain = self.conn.create_domain(
|
||||||
name=name, description=description, enabled=enabled)
|
name=name, description=description, enabled=enabled)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
if _needs_update(module, domain):
|
if self._needs_update(domain):
|
||||||
domain = cloud.update_domain(
|
domain = self.conn.update_domain(
|
||||||
domain.id, name=name, description=description,
|
domain.id, name=name, description=description,
|
||||||
enabled=enabled)
|
enabled=enabled)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
module.exit_json(changed=changed, domain=domain, id=domain.id)
|
self.exit_json(changed=changed, domain=domain, id=domain.id)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if domain is None:
|
if domain is None:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
cloud.delete_domain(domain.id)
|
self.conn.delete_domain(domain.id)
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityDomainModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -81,52 +81,43 @@ openstack_domains:
|
|||||||
type: bool
|
type: bool
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class IdentityDomainInfoModule(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=False, default=None),
|
name=dict(required=False, default=None),
|
||||||
filters=dict(required=False, type='dict', default=None),
|
filters=dict(required=False, type='dict', default=None),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
['name', 'filters'],
|
['name', 'filters'],
|
||||||
]
|
],
|
||||||
|
supports_check_mode=True
|
||||||
)
|
)
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
is_old_facts = module._name == 'openstack.cloud.identity_domain_facts'
|
|
||||||
if is_old_facts:
|
|
||||||
module.deprecate("The 'openstack.cloud.identity_domain_facts' module has been renamed to 'openstack.cloud.identity_domain_info', "
|
|
||||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
|
||||||
|
|
||||||
sdk, opcloud = openstack_cloud_from_module(module)
|
deprecated_names = ('openstack.cloud.identity_domain_facts')
|
||||||
try:
|
|
||||||
name = module.params['name']
|
def run(self):
|
||||||
filters = module.params['filters']
|
name = self.params['name']
|
||||||
|
filters = self.params['filters']
|
||||||
|
|
||||||
if name:
|
if name:
|
||||||
# Let's suppose user is passing domain ID
|
# Let's suppose user is passing domain ID
|
||||||
try:
|
try:
|
||||||
domains = opcloud.get_domain(name)
|
domains = self.conn.get_domain(name)
|
||||||
except Exception:
|
except Exception:
|
||||||
domains = opcloud.search_domains(filters={'name': name})
|
domains = self.conn.search_domains(filters={'name': name})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
domains = opcloud.search_domains(filters)
|
domains = self.conn.search_domains(filters)
|
||||||
|
|
||||||
if is_old_facts:
|
self.exit_json(changed=False, openstack_domains=domains)
|
||||||
module.exit_json(changed=False, ansible_facts=dict(
|
|
||||||
openstack_domains=domains))
|
|
||||||
else:
|
|
||||||
module.exit_json(changed=False, openstack_domains=domains)
|
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityDomainInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -86,75 +86,71 @@ group:
|
|||||||
sample: "default"
|
sample: "default"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(state, description, group):
|
class IdentityGroupModule(OpenStackModule):
|
||||||
if state == 'present' and not group:
|
argument_spec = dict(
|
||||||
return True
|
|
||||||
if state == 'present' and description is not None and group.description != description:
|
|
||||||
return True
|
|
||||||
if state == 'absent' and group:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
description=dict(required=False, default=None),
|
description=dict(required=False, default=None),
|
||||||
domain_id=dict(required=False, default=None),
|
domain_id=dict(required=False, default=None),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec,
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
name = module.params.get('name')
|
def _system_state_change(self, state, description, group):
|
||||||
description = module.params.get('description')
|
if state == 'present' and not group:
|
||||||
state = module.params.get('state')
|
return True
|
||||||
|
if state == 'present' and description is not None and group.description != description:
|
||||||
|
return True
|
||||||
|
if state == 'absent' and group:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
domain_id = module.params.pop('domain_id')
|
def run(self):
|
||||||
|
name = self.params.get('name')
|
||||||
|
description = self.params.get('description')
|
||||||
|
state = self.params.get('state')
|
||||||
|
|
||||||
|
domain_id = self.params.pop('domain_id')
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
|
||||||
try:
|
|
||||||
if domain_id:
|
if domain_id:
|
||||||
group = cloud.get_group(name, filters={'domain_id': domain_id})
|
group = self.conn.get_group(name, filters={'domain_id': domain_id})
|
||||||
else:
|
else:
|
||||||
group = cloud.get_group(name)
|
group = self.conn.get_group(name)
|
||||||
|
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(state, description, group))
|
self.exit_json(changed=self._system_state_change(state, description, group))
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if group is None:
|
if group is None:
|
||||||
group = cloud.create_group(
|
group = self.conn.create_group(
|
||||||
name=name, description=description, domain=domain_id)
|
name=name, description=description, domain=domain_id)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
if description is not None and group.description != description:
|
if description is not None and group.description != description:
|
||||||
group = cloud.update_group(
|
group = self.conn.update_group(
|
||||||
group.id, description=description)
|
group.id, description=description)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
module.exit_json(changed=changed, group=group)
|
self.exit_json(changed=changed, group=group)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if group is None:
|
if group is None:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
cloud.delete_group(group.id)
|
self.conn.delete_group(group.id)
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityGroupModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -110,48 +110,48 @@ openstack_groups:
|
|||||||
type: bool
|
type: bool
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class IdentityGroupInfoModule(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=False, default=None),
|
name=dict(required=False, default=None),
|
||||||
domain=dict(required=False, default=None),
|
domain=dict(required=False, default=None),
|
||||||
filters=dict(required=False, type='dict', default=None),
|
filters=dict(required=False, type='dict', default=None),
|
||||||
)
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec)
|
def run(self):
|
||||||
|
name = self.params['name']
|
||||||
sdk, opcloud = openstack_cloud_from_module(module)
|
domain = self.params['domain']
|
||||||
try:
|
filters = self.params['filters']
|
||||||
name = module.params['name']
|
|
||||||
domain = module.params['domain']
|
|
||||||
filters = module.params['filters']
|
|
||||||
|
|
||||||
if domain:
|
if domain:
|
||||||
try:
|
try:
|
||||||
# We assume admin is passing domain id
|
# We assume admin is passing domain id
|
||||||
dom = opcloud.get_domain(domain)['id']
|
dom = self.conn.get_domain(domain)['id']
|
||||||
domain = dom
|
domain = dom
|
||||||
except Exception:
|
except Exception:
|
||||||
# If we fail, maybe admin is passing a domain name.
|
# If we fail, maybe admin is passing a domain name.
|
||||||
# Note that domains have unique names, just like id.
|
# Note that domains have unique names, just like id.
|
||||||
dom = opcloud.search_domains(filters={'name': domain})
|
dom = self.conn.search_domains(filters={'name': domain})
|
||||||
if dom:
|
if dom:
|
||||||
domain = dom[0]['id']
|
domain = dom[0]['id']
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg='Domain name or ID does not exist')
|
self.fail_json(msg='Domain name or ID does not exist')
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = {}
|
filters = {}
|
||||||
|
|
||||||
groups = opcloud.search_groups(name, filters, domain_id=domain)
|
groups = self.conn.search_groups(name, filters, domain_id=domain)
|
||||||
module.exit_json(changed=False, groups=groups)
|
self.exit_json(changed=False, groups=groups)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityGroupInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -59,58 +59,54 @@ role:
|
|||||||
sample: "demo"
|
sample: "demo"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(state, role):
|
class IdentityRoleModule(OpenStackModule):
|
||||||
if state == 'present' and not role:
|
argument_spec = dict(
|
||||||
return True
|
|
||||||
if state == 'absent' and role:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec,
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
name = module.params.get('name')
|
def _system_state_change(self, state, role):
|
||||||
state = module.params.get('state')
|
if state == 'present' and not role:
|
||||||
|
return True
|
||||||
|
if state == 'absent' and role:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def run(self):
|
||||||
try:
|
name = self.params.get('name')
|
||||||
role = cloud.get_role(name)
|
state = self.params.get('state')
|
||||||
|
|
||||||
if module.check_mode:
|
role = self.conn.get_role(name)
|
||||||
module.exit_json(changed=_system_state_change(state, role))
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(changed=self._system_state_change(state, role))
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if role is None:
|
if role is None:
|
||||||
role = cloud.create_role(name)
|
role = self.conn.create_role(name)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
module.exit_json(changed=changed, role=role)
|
self.exit_json(changed=changed, role=role)
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if role is None:
|
if role is None:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
cloud.delete_role(name)
|
self.conn.delete_role(name)
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityRoleModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
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()
|
||||||
@@ -131,54 +131,12 @@ user:
|
|||||||
type: str
|
type: str
|
||||||
sample: "demouser"
|
sample: "demouser"
|
||||||
'''
|
'''
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _needs_update(params_dict, user):
|
class IdentityUserModule(OpenStackModule):
|
||||||
for k in params_dict:
|
argument_spec = dict(
|
||||||
if k not in ('password', 'update_password') and user[k] != params_dict[k]:
|
|
||||||
return True
|
|
||||||
|
|
||||||
# We don't get password back in the user object, so assume any supplied
|
|
||||||
# password is a change.
|
|
||||||
if (
|
|
||||||
params_dict['password'] is not None
|
|
||||||
and params_dict['update_password'] == 'always'
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _get_domain_id(cloud, domain):
|
|
||||||
try:
|
|
||||||
# We assume admin is passing domain id
|
|
||||||
domain_id = cloud.get_domain(domain)['id']
|
|
||||||
except Exception:
|
|
||||||
# If we fail, maybe admin is passing a domain name.
|
|
||||||
# Note that domains have unique names, just like id.
|
|
||||||
try:
|
|
||||||
domain_id = cloud.search_domains(filters={'name': domain})[0]['id']
|
|
||||||
except Exception:
|
|
||||||
# Ok, let's hope the user is non-admin and passing a sane id
|
|
||||||
domain_id = domain
|
|
||||||
|
|
||||||
return domain_id
|
|
||||||
|
|
||||||
|
|
||||||
def _get_default_project_id(cloud, default_project, domain_id, module):
|
|
||||||
project = cloud.get_project(default_project, domain_id=domain_id)
|
|
||||||
if not project:
|
|
||||||
module.fail_json(msg='Default project %s is not valid' % default_project)
|
|
||||||
|
|
||||||
return project['id']
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
password=dict(required=False, default=None, no_log=True),
|
password=dict(required=False, default=None, no_log=True),
|
||||||
email=dict(required=False, default=None),
|
email=dict(required=False, default=None),
|
||||||
@@ -190,47 +148,81 @@ def main():
|
|||||||
update_password=dict(default=None, choices=['always', 'on_create']),
|
update_password=dict(default=None, choices=['always', 'on_create']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict()
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
name = module.params['name']
|
def _needs_update(self, params_dict, user):
|
||||||
password = module.params.get('password')
|
for k in params_dict:
|
||||||
email = module.params['email']
|
if k not in ('password', 'update_password') and user[k] != params_dict[k]:
|
||||||
default_project = module.params['default_project']
|
return True
|
||||||
domain = module.params['domain']
|
|
||||||
enabled = module.params['enabled']
|
# We don't get password back in the user object, so assume any supplied
|
||||||
state = module.params['state']
|
# password is a change.
|
||||||
update_password = module.params['update_password']
|
if (
|
||||||
description = module.params['description']
|
params_dict['password'] is not None
|
||||||
|
and params_dict['update_password'] == 'always'
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _get_domain_id(self, domain):
|
||||||
|
try:
|
||||||
|
# We assume admin is passing domain id
|
||||||
|
domain_id = self.conn.get_domain(domain)['id']
|
||||||
|
except Exception:
|
||||||
|
# If we fail, maybe admin is passing a domain name.
|
||||||
|
# Note that domains have unique names, just like id.
|
||||||
|
try:
|
||||||
|
domain_id = self.conn.search_domains(filters={'name': domain})[0]['id']
|
||||||
|
except Exception:
|
||||||
|
# Ok, let's hope the user is non-admin and passing a sane id
|
||||||
|
domain_id = domain
|
||||||
|
|
||||||
|
return domain_id
|
||||||
|
|
||||||
|
def _get_default_project_id(self, default_project, domain_id):
|
||||||
|
project = self.conn.get_project(default_project, domain_id=domain_id)
|
||||||
|
if not project:
|
||||||
|
self.fail_json(msg='Default project %s is not valid' % default_project)
|
||||||
|
|
||||||
|
return project['id']
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
name = self.params['name']
|
||||||
|
password = self.params.get('password')
|
||||||
|
email = self.params['email']
|
||||||
|
default_project = self.params['default_project']
|
||||||
|
domain = self.params['domain']
|
||||||
|
enabled = self.params['enabled']
|
||||||
|
state = self.params['state']
|
||||||
|
update_password = self.params['update_password']
|
||||||
|
description = self.params['description']
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
|
||||||
try:
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if domain:
|
if domain:
|
||||||
domain_id = _get_domain_id(cloud, domain)
|
domain_id = self._get_domain_id(domain)
|
||||||
user = cloud.get_user(name, domain_id=domain_id)
|
user = self.conn.get_user(name, domain_id=domain_id)
|
||||||
else:
|
else:
|
||||||
user = cloud.get_user(name)
|
user = self.conn.get_user(name)
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if update_password in ('always', 'on_create'):
|
if update_password in ('always', 'on_create'):
|
||||||
if not password:
|
if not password:
|
||||||
msg = "update_password is %s but a password value is missing" % update_password
|
msg = "update_password is %s but a password value is missing" % update_password
|
||||||
module.fail_json(msg=msg)
|
self.fail_json(msg=msg)
|
||||||
default_project_id = None
|
default_project_id = None
|
||||||
if default_project:
|
if default_project:
|
||||||
default_project_id = _get_default_project_id(cloud, default_project, domain_id, module)
|
default_project_id = self._get_default_project_id(
|
||||||
|
default_project, domain_id)
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
if description is not None:
|
if description is not None:
|
||||||
user = cloud.create_user(
|
user = self.conn.create_user(
|
||||||
name=name, password=password, email=email,
|
name=name, password=password, email=email,
|
||||||
default_project=default_project_id, domain_id=domain_id,
|
default_project=default_project_id, domain_id=domain_id,
|
||||||
enabled=enabled, description=description)
|
enabled=enabled, description=description)
|
||||||
else:
|
else:
|
||||||
user = cloud.create_user(
|
user = self.conn.create_user(
|
||||||
name=name, password=password, email=email,
|
name=name, password=password, email=email,
|
||||||
default_project=default_project_id, domain_id=domain_id,
|
default_project=default_project_id, domain_id=domain_id,
|
||||||
enabled=enabled)
|
enabled=enabled)
|
||||||
@@ -246,47 +238,49 @@ def main():
|
|||||||
if default_project_id is not None:
|
if default_project_id is not None:
|
||||||
params_dict['default_project_id'] = default_project_id
|
params_dict['default_project_id'] = default_project_id
|
||||||
|
|
||||||
if _needs_update(params_dict, user):
|
if self._needs_update(params_dict, user):
|
||||||
if update_password == 'always':
|
if update_password == 'always':
|
||||||
if description is not None:
|
if description is not None:
|
||||||
user = cloud.update_user(
|
user = self.conn.update_user(
|
||||||
user['id'], password=password, email=email,
|
user['id'], password=password, email=email,
|
||||||
default_project=default_project_id,
|
default_project=default_project_id,
|
||||||
domain_id=domain_id, enabled=enabled, description=description)
|
domain_id=domain_id, enabled=enabled, description=description)
|
||||||
else:
|
else:
|
||||||
user = cloud.update_user(
|
user = self.conn.update_user(
|
||||||
user['id'], password=password, email=email,
|
user['id'], password=password, email=email,
|
||||||
default_project=default_project_id,
|
default_project=default_project_id,
|
||||||
domain_id=domain_id, enabled=enabled)
|
domain_id=domain_id, enabled=enabled)
|
||||||
else:
|
else:
|
||||||
if description is not None:
|
if description is not None:
|
||||||
user = cloud.update_user(
|
user = self.conn.update_user(
|
||||||
user['id'], email=email,
|
user['id'], email=email,
|
||||||
default_project=default_project_id,
|
default_project=default_project_id,
|
||||||
domain_id=domain_id, enabled=enabled, description=description)
|
domain_id=domain_id, enabled=enabled, description=description)
|
||||||
else:
|
else:
|
||||||
user = cloud.update_user(
|
user = self.conn.update_user(
|
||||||
user['id'], email=email,
|
user['id'], email=email,
|
||||||
default_project=default_project_id,
|
default_project=default_project_id,
|
||||||
domain_id=domain_id, enabled=enabled)
|
domain_id=domain_id, enabled=enabled)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
module.exit_json(changed=changed, user=user)
|
self.exit_json(changed=changed, user=user)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if user is None:
|
if user is None:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
if domain:
|
if domain:
|
||||||
cloud.delete_user(user['id'], domain_id=domain_id)
|
self.conn.delete_user(user['id'], domain_id=domain_id)
|
||||||
else:
|
else:
|
||||||
cloud.delete_user(user['id'])
|
self.conn.delete_user(user['id'])
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
def main():
|
||||||
|
module = IdentityUserModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -107,61 +107,52 @@ openstack_users:
|
|||||||
type: str
|
type: str
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
|
||||||
openstack_full_argument_spec,
|
|
||||||
openstack_cloud_from_module,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class IdentityUserInfoModule(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=False, default=None),
|
name=dict(required=False, default=None),
|
||||||
domain=dict(required=False, default=None),
|
domain=dict(required=False, default=None),
|
||||||
filters=dict(required=False, type='dict', default=None),
|
filters=dict(required=False, type='dict', default=None),
|
||||||
)
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec)
|
deprecated_names = ('openstack.cloud.identity_user_facts')
|
||||||
is_old_facts = module._name == 'openstack.cloud.identity_user_facts'
|
|
||||||
if is_old_facts:
|
|
||||||
module.deprecate("The 'openstack.cloud.identity_user_facts' module has been renamed to 'openstack.cloud.identity_user_info', "
|
|
||||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
|
||||||
|
|
||||||
sdk, opcloud = openstack_cloud_from_module(module)
|
def run(self):
|
||||||
try:
|
name = self.params['name']
|
||||||
name = module.params['name']
|
domain = self.params['domain']
|
||||||
domain = module.params['domain']
|
filters = self.params['filters']
|
||||||
filters = module.params['filters']
|
|
||||||
|
|
||||||
if domain:
|
if domain:
|
||||||
try:
|
try:
|
||||||
# We assume admin is passing domain id
|
# We assume admin is passing domain id
|
||||||
dom = opcloud.get_domain(domain)['id']
|
dom = self.conn.get_domain(domain)['id']
|
||||||
domain = dom
|
domain = dom
|
||||||
except Exception:
|
except Exception:
|
||||||
# If we fail, maybe admin is passing a domain name.
|
# If we fail, maybe admin is passing a domain name.
|
||||||
# Note that domains have unique names, just like id.
|
# Note that domains have unique names, just like id.
|
||||||
dom = opcloud.search_domains(filters={'name': domain})
|
dom = self.conn.search_domains(filters={'name': domain})
|
||||||
if dom:
|
if dom:
|
||||||
domain = dom[0]['id']
|
domain = dom[0]['id']
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg='Domain name or ID does not exist')
|
self.fail_json(msg='Domain name or ID does not exist')
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = {}
|
filters = {}
|
||||||
|
|
||||||
filters['domain_id'] = domain
|
filters['domain_id'] = domain
|
||||||
|
|
||||||
users = opcloud.search_users(name, filters)
|
users = self.conn.search_users(name, filters)
|
||||||
if is_old_facts:
|
self.exit_json(changed=False, openstack_users=users)
|
||||||
module.exit_json(changed=False, ansible_facts=dict(
|
|
||||||
openstack_users=users))
|
|
||||||
else:
|
|
||||||
module.exit_json(changed=False, openstack_users=users)
|
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityUserInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -85,6 +85,12 @@ options:
|
|||||||
choices: [present, absent]
|
choices: [present, absent]
|
||||||
default: present
|
default: present
|
||||||
type: str
|
type: str
|
||||||
|
tags:
|
||||||
|
description:
|
||||||
|
- List of tags to be applied to the image
|
||||||
|
default: []
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
volume:
|
volume:
|
||||||
description:
|
description:
|
||||||
- ID of a volume to create an image from.
|
- ID of a volume to create an image from.
|
||||||
@@ -115,6 +121,8 @@ EXAMPLES = '''
|
|||||||
filename: cirros-0.3.0-x86_64-disk.img
|
filename: cirros-0.3.0-x86_64-disk.img
|
||||||
kernel: cirros-vmlinuz
|
kernel: cirros-vmlinuz
|
||||||
ramdisk: cirros-initrd
|
ramdisk: cirros-initrd
|
||||||
|
tags:
|
||||||
|
- custom
|
||||||
properties:
|
properties:
|
||||||
cpu_arch: x86_64
|
cpu_arch: x86_64
|
||||||
distro: ubuntu
|
distro: ubuntu
|
||||||
@@ -147,95 +155,99 @@ EXAMPLES = '''
|
|||||||
name: myvol_image
|
name: myvol_image
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class ImageModule(OpenStackModule):
|
||||||
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
deprecated_names = ('os_image', 'openstack.cloud.os_image')
|
||||||
name=dict(required=True),
|
|
||||||
id=dict(default=None),
|
argument_spec = dict(
|
||||||
checksum=dict(default=None),
|
name=dict(required=True, type='str'),
|
||||||
disk_format=dict(default='qcow2', choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']),
|
id=dict(type='str'),
|
||||||
|
checksum=dict(type='str'),
|
||||||
|
disk_format=dict(default='qcow2',
|
||||||
|
choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']),
|
||||||
container_format=dict(default='bare', choices=['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']),
|
container_format=dict(default='bare', choices=['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']),
|
||||||
owner=dict(default=None),
|
owner=dict(type='str'),
|
||||||
min_disk=dict(type='int', default=0),
|
min_disk=dict(type='int', default=0),
|
||||||
min_ram=dict(type='int', default=0),
|
min_ram=dict(type='int', default=0),
|
||||||
is_public=dict(type='bool', default=False),
|
is_public=dict(type='bool', default=False),
|
||||||
protected=dict(type='bool', default=False),
|
protected=dict(type='bool', default=False),
|
||||||
filename=dict(default=None),
|
filename=dict(type='str'),
|
||||||
ramdisk=dict(default=None),
|
ramdisk=dict(type='str'),
|
||||||
kernel=dict(default=None),
|
kernel=dict(type='str'),
|
||||||
properties=dict(type='dict', default={}),
|
properties=dict(type='dict', default={}),
|
||||||
volume=dict(default=None),
|
volume=dict(type='str'),
|
||||||
|
tags=dict(type='list', default=[], elements='str'),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
mutually_exclusive=[['filename', 'volume']],
|
mutually_exclusive=[['filename', 'volume']],
|
||||||
)
|
)
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def run(self):
|
||||||
try:
|
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
if module.params['id']:
|
if self.params['id']:
|
||||||
image = cloud.get_image(name_or_id=module.params['id'])
|
image = self.conn.get_image(name_or_id=self.params['id'])
|
||||||
elif module.params['checksum']:
|
elif self.params['checksum']:
|
||||||
image = cloud.get_image(name_or_id=module.params['name'], filters={'checksum': module.params['checksum']})
|
image = self.conn.get_image(name_or_id=self.params['name'], filters={'checksum': self.params['checksum']})
|
||||||
else:
|
else:
|
||||||
image = cloud.get_image(name_or_id=module.params['name'])
|
image = self.conn.get_image(name_or_id=self.params['name'])
|
||||||
|
|
||||||
if module.params['state'] == 'present':
|
if self.params['state'] == 'present':
|
||||||
if not image:
|
if not image:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if module.params['id'] is not None:
|
if self.params['id'] is not None:
|
||||||
kwargs['id'] = module.params['id']
|
kwargs['id'] = self.params['id']
|
||||||
image = cloud.create_image(
|
image = self.conn.create_image(
|
||||||
name=module.params['name'],
|
name=self.params['name'],
|
||||||
filename=module.params['filename'],
|
filename=self.params['filename'],
|
||||||
disk_format=module.params['disk_format'],
|
disk_format=self.params['disk_format'],
|
||||||
container_format=module.params['container_format'],
|
container_format=self.params['container_format'],
|
||||||
wait=module.params['wait'],
|
wait=self.params['wait'],
|
||||||
timeout=module.params['timeout'],
|
timeout=self.params['timeout'],
|
||||||
is_public=module.params['is_public'],
|
is_public=self.params['is_public'],
|
||||||
protected=module.params['protected'],
|
protected=self.params['protected'],
|
||||||
min_disk=module.params['min_disk'],
|
min_disk=self.params['min_disk'],
|
||||||
min_ram=module.params['min_ram'],
|
min_ram=self.params['min_ram'],
|
||||||
volume=module.params['volume'],
|
volume=self.params['volume'],
|
||||||
|
tags=self.params['tags'],
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
changed = True
|
changed = True
|
||||||
if not module.params['wait']:
|
if not self.params['wait']:
|
||||||
module.exit_json(changed=changed, image=image, id=image.id)
|
self.exit(changed=changed, image=image, id=image.id)
|
||||||
|
|
||||||
cloud.update_image_properties(
|
self.conn.update_image_properties(
|
||||||
image=image,
|
image=image,
|
||||||
kernel=module.params['kernel'],
|
kernel=self.params['kernel'],
|
||||||
ramdisk=module.params['ramdisk'],
|
ramdisk=self.params['ramdisk'],
|
||||||
protected=module.params['protected'],
|
protected=self.params['protected'],
|
||||||
**module.params['properties'])
|
**self.params['properties'])
|
||||||
image = cloud.get_image(name_or_id=image.id)
|
if self.params['tags']:
|
||||||
module.exit_json(changed=changed, image=image, id=image.id)
|
self.conn.image.update_image(image.id, tags=self.params['tags'])
|
||||||
|
image = self.conn.get_image(name_or_id=image.id)
|
||||||
|
self.exit(changed=changed, image=image, id=image.id)
|
||||||
|
|
||||||
elif module.params['state'] == 'absent':
|
elif self.params['state'] == 'absent':
|
||||||
if not image:
|
if not image:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
cloud.delete_image(
|
self.conn.delete_image(
|
||||||
name_or_id=module.params['name'],
|
name_or_id=self.params['name'],
|
||||||
wait=module.params['wait'],
|
wait=self.params['wait'],
|
||||||
timeout=module.params['timeout'])
|
timeout=self.params['timeout'])
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def main():
|
||||||
|
module = ImageModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -140,46 +140,40 @@ openstack_image:
|
|||||||
description: Size of the image.
|
description: Size of the image.
|
||||||
returned: success
|
returned: success
|
||||||
type: int
|
type: int
|
||||||
|
tags:
|
||||||
|
description: List of tags assigned to the image
|
||||||
|
returned: success
|
||||||
|
type: list
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
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),
|
||||||
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
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():
|
def main():
|
||||||
|
module = ImageInfoModule()
|
||||||
argument_spec = openstack_full_argument_spec(
|
module()
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
156
plugins/modules/keypair_info.py
Normal file
156
plugins/modules/keypair_info.py
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#!/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)
|
||||||
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
@@ -60,151 +60,127 @@ EXAMPLES = '''
|
|||||||
RETURN = '''
|
RETURN = '''
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_protocol(protocol):
|
class IdentityFederationProtocolModule(OpenStackModule):
|
||||||
"""
|
argument_spec = dict(
|
||||||
Normalizes the protocol definitions so that the outputs are consistent with the
|
|
||||||
parameters
|
|
||||||
|
|
||||||
- "name" (parameter) == "id" (SDK)
|
|
||||||
"""
|
|
||||||
if protocol is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
_protocol = protocol.to_dict()
|
|
||||||
_protocol['name'] = protocol['id']
|
|
||||||
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
|
|
||||||
_protocol['idp_id'] = protocol['idp_id']
|
|
||||||
return _protocol
|
|
||||||
|
|
||||||
|
|
||||||
def delete_protocol(module, sdk, cloud, protocol):
|
|
||||||
"""
|
|
||||||
Delete an existing Protocol
|
|
||||||
|
|
||||||
returns: the "Changed" state
|
|
||||||
"""
|
|
||||||
|
|
||||||
if protocol is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
cloud.identity.delete_federation_protocol(None, protocol)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to delete protocol: {0}'.format(str(ex)))
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def create_protocol(module, sdk, cloud, name):
|
|
||||||
"""
|
|
||||||
Create a new Protocol
|
|
||||||
|
|
||||||
returns: the "Changed" state and the new protocol
|
|
||||||
"""
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
idp_name = module.params.get('idp_id')
|
|
||||||
mapping_id = module.params.get('mapping_id')
|
|
||||||
|
|
||||||
attributes = {
|
|
||||||
'idp_id': idp_name,
|
|
||||||
'mapping_id': mapping_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
protocol = cloud.identity.create_federation_protocol(id=name, **attributes)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to create protocol: {0}'.format(str(ex)))
|
|
||||||
return (True, protocol)
|
|
||||||
|
|
||||||
|
|
||||||
def update_protocol(module, sdk, cloud, protocol):
|
|
||||||
"""
|
|
||||||
Update an existing Protocol
|
|
||||||
|
|
||||||
returns: the "Changed" state and the new protocol
|
|
||||||
"""
|
|
||||||
|
|
||||||
mapping_id = module.params.get('mapping_id')
|
|
||||||
|
|
||||||
attributes = {}
|
|
||||||
|
|
||||||
if (mapping_id is not None) and (mapping_id != protocol.mapping_id):
|
|
||||||
attributes['mapping_id'] = mapping_id
|
|
||||||
|
|
||||||
if not attributes:
|
|
||||||
return False, protocol
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_protocol = cloud.identity.update_federation_protocol(None, protocol, **attributes)
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to update protocol: {0}'.format(str(ex)))
|
|
||||||
return (True, new_protocol)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" Module entry point """
|
|
||||||
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True, aliases=['id']),
|
name=dict(required=True, aliases=['id']),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
idp_id=dict(required=True, aliases=['idp_name']),
|
idp_id=dict(required=True, aliases=['idp_name']),
|
||||||
mapping_id=dict(aliases=['mapping_name']),
|
mapping_id=dict(aliases=['mapping_name']),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
)
|
supports_check_mode=True
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
**module_kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
name = module.params.get('name')
|
def normalize_protocol(self, protocol):
|
||||||
state = module.params.get('state')
|
"""
|
||||||
idp = module.params.get('idp_id')
|
Normalizes the protocol definitions so that the outputs are consistent with the
|
||||||
changed = False
|
parameters
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
|
- "name" (parameter) == "id" (SDK)
|
||||||
|
"""
|
||||||
try:
|
|
||||||
protocol = cloud.identity.get_federation_protocol(idp, name)
|
|
||||||
except sdk.exceptions.ResourceNotFound:
|
|
||||||
protocol = None
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to get protocol: {0}'.format(str(ex)))
|
|
||||||
|
|
||||||
if state == 'absent':
|
|
||||||
if protocol is not None:
|
|
||||||
changed = delete_protocol(module, sdk, cloud, protocol)
|
|
||||||
module.exit_json(changed=changed)
|
|
||||||
|
|
||||||
# state == 'present'
|
|
||||||
else:
|
|
||||||
if protocol is None:
|
if protocol is None:
|
||||||
if module.params.get('mapping_id') is None:
|
return None
|
||||||
module.fail_json(msg='A mapping_id must be passed when creating'
|
|
||||||
' a protocol')
|
|
||||||
(changed, protocol) = create_protocol(module, sdk, cloud, name)
|
|
||||||
protocol = normalize_protocol(protocol)
|
|
||||||
module.exit_json(changed=changed, protocol=protocol)
|
|
||||||
|
|
||||||
|
_protocol = protocol.to_dict()
|
||||||
|
_protocol['name'] = protocol['id']
|
||||||
|
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
|
||||||
|
_protocol['idp_id'] = protocol['idp_id']
|
||||||
|
return _protocol
|
||||||
|
|
||||||
|
def delete_protocol(self, protocol):
|
||||||
|
"""
|
||||||
|
Delete an existing Protocol
|
||||||
|
|
||||||
|
returns: the "Changed" state
|
||||||
|
"""
|
||||||
|
if protocol is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.conn.identity.delete_federation_protocol(None, protocol)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def create_protocol(self, name):
|
||||||
|
"""
|
||||||
|
Create a new Protocol
|
||||||
|
|
||||||
|
returns: the "Changed" state and the new protocol
|
||||||
|
"""
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
idp_name = self.params.get('idp_id')
|
||||||
|
mapping_id = self.params.get('mapping_id')
|
||||||
|
|
||||||
|
attributes = {
|
||||||
|
'idp_id': idp_name,
|
||||||
|
'mapping_id': mapping_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol = self.conn.identity.create_federation_protocol(id=name, **attributes)
|
||||||
|
return (True, protocol)
|
||||||
|
|
||||||
|
def update_protocol(self, protocol):
|
||||||
|
"""
|
||||||
|
Update an existing Protocol
|
||||||
|
|
||||||
|
returns: the "Changed" state and the new protocol
|
||||||
|
"""
|
||||||
|
mapping_id = self.params.get('mapping_id')
|
||||||
|
|
||||||
|
attributes = {}
|
||||||
|
|
||||||
|
if (mapping_id is not None) and (mapping_id != protocol.mapping_id):
|
||||||
|
attributes['mapping_id'] = mapping_id
|
||||||
|
|
||||||
|
if not attributes:
|
||||||
|
return False, protocol
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
new_protocol = self.conn.identity.update_federation_protocol(None, protocol, **attributes)
|
||||||
|
return (True, new_protocol)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Module entry point """
|
||||||
|
name = self.params.get('name')
|
||||||
|
state = self.params.get('state')
|
||||||
|
idp = self.params.get('idp_id')
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
protocol = self.conn.identity.find_federation_protocol(idp, name)
|
||||||
|
|
||||||
|
if state == 'absent':
|
||||||
|
if protocol is not None:
|
||||||
|
changed = self.delete_protocol(protocol)
|
||||||
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
|
# state == 'present'
|
||||||
else:
|
else:
|
||||||
(changed, new_protocol) = update_protocol(module, sdk, cloud, protocol)
|
if protocol is None:
|
||||||
new_protocol = normalize_protocol(new_protocol)
|
if self.params.get('mapping_id') is None:
|
||||||
module.exit_json(changed=changed, protocol=new_protocol)
|
self.fail_json(
|
||||||
|
msg='A mapping_id must be passed when creating'
|
||||||
|
' a protocol')
|
||||||
|
(changed, protocol) = self.create_protocol(name)
|
||||||
|
protocol = self.normalize_protocol(protocol)
|
||||||
|
self.exit_json(changed=changed, protocol=protocol)
|
||||||
|
|
||||||
|
else:
|
||||||
|
(changed, new_protocol) = self.update_protocol(protocol)
|
||||||
|
new_protocol = self.normalize_protocol(new_protocol)
|
||||||
|
self.exit_json(changed=changed, protocol=new_protocol)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = IdentityFederationProtocolModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -45,65 +45,53 @@ EXAMPLES = '''
|
|||||||
RETURN = '''
|
RETURN = '''
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_protocol(protocol):
|
class IdentityFederationProtocolInfoModule(OpenStackModule):
|
||||||
"""
|
argument_spec = dict(
|
||||||
Normalizes the protocol definitions so that the outputs are consistent with the
|
|
||||||
parameters
|
|
||||||
|
|
||||||
- "name" (parameter) == "id" (SDK)
|
|
||||||
"""
|
|
||||||
if protocol is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
_protocol = protocol.to_dict()
|
|
||||||
_protocol['name'] = protocol['id']
|
|
||||||
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
|
|
||||||
_protocol['idp_id'] = protocol['idp_id']
|
|
||||||
return _protocol
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" Module entry point """
|
|
||||||
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(aliases=['id']),
|
name=dict(aliases=['id']),
|
||||||
idp_id=dict(required=True, aliases=['idp_name']),
|
idp_id=dict(required=True, aliases=['idp_name']),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
)
|
supports_check_mode=True
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
**module_kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
name = module.params.get('name')
|
def normalize_protocol(self, protocol):
|
||||||
idp = module.params.get('idp_id')
|
"""
|
||||||
|
Normalizes the protocol definitions so that the outputs are consistent with the
|
||||||
|
parameters
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
|
- "name" (parameter) == "id" (SDK)
|
||||||
|
"""
|
||||||
|
if protocol is None:
|
||||||
|
return None
|
||||||
|
|
||||||
if name:
|
_protocol = protocol.to_dict()
|
||||||
try:
|
_protocol['name'] = protocol['id']
|
||||||
protocol = cloud.identity.get_federation_protocol(idp, name)
|
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
|
||||||
protocol = normalize_protocol(protocol)
|
_protocol['idp_id'] = protocol['idp_id']
|
||||||
except sdk.exceptions.ResourceNotFound:
|
return _protocol
|
||||||
module.fail_json(msg='Failed to find protocol')
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
|
||||||
module.fail_json(msg='Failed to get protocol: {0}'.format(str(ex)))
|
|
||||||
module.exit_json(changed=False, protocols=[protocol])
|
|
||||||
|
|
||||||
else:
|
def run(self):
|
||||||
try:
|
""" Module entry point """
|
||||||
protocols = list(map(normalize_protocol, cloud.identity.federation_protocols(idp)))
|
|
||||||
except sdk.exceptions.OpenStackCloudException as ex:
|
name = self.params.get('name')
|
||||||
module.fail_json(msg='Failed to list protocols: {0}'.format(str(ex)))
|
idp = self.params.get('idp_id')
|
||||||
module.exit_json(changed=False, protocols=protocols)
|
|
||||||
|
if name:
|
||||||
|
protocol = self.conn.identity.get_federation_protocol(idp, name)
|
||||||
|
protocol = self.normalize_protocol(protocol)
|
||||||
|
self.exit_json(changed=False, protocols=[protocol])
|
||||||
|
|
||||||
|
else:
|
||||||
|
protocols = list(map(self.normalize_protocol, self.conn.identity.federation_protocols(idp)))
|
||||||
|
self.exit_json(changed=False, protocols=protocols)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = IdentityFederationProtocolInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -223,10 +223,10 @@ class HealthMonitorModule(OpenStackModule):
|
|||||||
expected_codes=dict(required=False, default="200"),
|
expected_codes=dict(required=False, default="200"),
|
||||||
admin_state_up=dict(required=False, default=True, type='bool'),
|
admin_state_up=dict(required=False, default=True, type='bool'),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
http_method=dict(default="GET", requried=False, choices=["GET", "CONNECT", "DELETE",
|
http_method=dict(default="GET", required=False, choices=["GET", "CONNECT", "DELETE",
|
||||||
"HEAD", "OPTIONS", "PATCH",
|
"HEAD", "OPTIONS", "PATCH",
|
||||||
"POST", "PUT", "TRACE"]),
|
"POST", "PUT", "TRACE"]),
|
||||||
url_path=dict(default="/", requires=False),
|
url_path=dict(default="/", required=False),
|
||||||
type=dict(default='HTTP',
|
type=dict(default='HTTP',
|
||||||
choices=['HTTP', 'HTTPS', 'PING', 'SCTP', 'TCP', 'TLS-HELLO', 'UDP-CONNECT']))
|
choices=['HTTP', 'HTTPS', 'PING', 'SCTP', 'TCP', 'TLS-HELLO', 'UDP-CONNECT']))
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ options:
|
|||||||
protocol:
|
protocol:
|
||||||
description:
|
description:
|
||||||
- The protocol for the listener.
|
- The protocol for the listener.
|
||||||
choices: [HTTP, HTTPS, TCP, TERMINATED_HTTPS]
|
choices: [HTTP, HTTPS, TCP, TERMINATED_HTTPS, UDP, SCTP]
|
||||||
default: HTTP
|
default: HTTP
|
||||||
type: str
|
type: str
|
||||||
protocol_port:
|
protocol_port:
|
||||||
@@ -38,6 +38,16 @@ options:
|
|||||||
- The protocol port number for the listener.
|
- The protocol port number for the listener.
|
||||||
default: 80
|
default: 80
|
||||||
type: int
|
type: int
|
||||||
|
timeout_client_data:
|
||||||
|
description:
|
||||||
|
- Client inactivity timeout in milliseconds.
|
||||||
|
default: 50000
|
||||||
|
type: int
|
||||||
|
timeout_member_data:
|
||||||
|
description:
|
||||||
|
- Member inactivity timeout in milliseconds.
|
||||||
|
default: 50000
|
||||||
|
type: int
|
||||||
wait:
|
wait:
|
||||||
description:
|
description:
|
||||||
- If the module should wait for the load balancer to be ACTIVE.
|
- If the module should wait for the load balancer to be ACTIVE.
|
||||||
@@ -108,6 +118,14 @@ listener:
|
|||||||
description: The protocol port number for the listener.
|
description: The protocol port number for the listener.
|
||||||
type: int
|
type: int
|
||||||
sample: 80
|
sample: 80
|
||||||
|
timeout_client_data:
|
||||||
|
description: Client inactivity timeout in milliseconds.
|
||||||
|
type: int
|
||||||
|
sample: 50000
|
||||||
|
timeout_member_data:
|
||||||
|
description: Member inactivity timeout in milliseconds.
|
||||||
|
type: int
|
||||||
|
sample: 50000
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
@@ -139,116 +157,130 @@ EXAMPLES = '''
|
|||||||
state: absent
|
state: absent
|
||||||
name: test-listener
|
name: test-listener
|
||||||
loadbalancer: test-loadbalancer
|
loadbalancer: test-loadbalancer
|
||||||
|
|
||||||
|
# Create a listener, increase timeouts for connection persistence (for SSH for example).
|
||||||
|
- openstack.cloud.lb_listener:
|
||||||
|
cloud: mycloud
|
||||||
|
endpoint_type: admin
|
||||||
|
state: present
|
||||||
|
name: test-listener
|
||||||
|
loadbalancer: test-loadbalancer
|
||||||
|
protocol: TCP
|
||||||
|
protocol_port: 22
|
||||||
|
timeout_client_data: 1800000
|
||||||
|
timeout_member_data: 1800000
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _lb_wait_for_status(module, cloud, lb, status, failures, interval=5):
|
class LoadbalancerListenerModule(OpenStackModule):
|
||||||
"""Wait for load balancer to be in a particular provisioning status."""
|
argument_spec = dict(
|
||||||
timeout = module.params['timeout']
|
|
||||||
|
|
||||||
total_sleep = 0
|
|
||||||
if failures is None:
|
|
||||||
failures = []
|
|
||||||
|
|
||||||
while total_sleep < timeout:
|
|
||||||
lb = cloud.load_balancer.get_load_balancer(lb.id)
|
|
||||||
if lb.provisioning_status == status:
|
|
||||||
return None
|
|
||||||
if lb.provisioning_status in failures:
|
|
||||||
module.fail_json(
|
|
||||||
msg="Load Balancer %s transitioned to failure state %s" %
|
|
||||||
(lb.id, lb.provisioning_status)
|
|
||||||
)
|
|
||||||
|
|
||||||
time.sleep(interval)
|
|
||||||
total_sleep += interval
|
|
||||||
|
|
||||||
module.fail_json(
|
|
||||||
msg="Timeout waiting for Load Balancer %s to transition to %s" %
|
|
||||||
(lb.id, status)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
loadbalancer=dict(required=True),
|
loadbalancer=dict(required=True),
|
||||||
protocol=dict(default='HTTP',
|
protocol=dict(default='HTTP',
|
||||||
choices=['HTTP', 'HTTPS', 'TCP', 'TERMINATED_HTTPS']),
|
choices=['HTTP', 'HTTPS', 'TCP', 'TERMINATED_HTTPS', 'UDP', 'SCTP']),
|
||||||
protocol_port=dict(default=80, type='int', required=False),
|
protocol_port=dict(default=80, type='int', required=False),
|
||||||
|
timeout_client_data=dict(default=50000, type='int', required=False),
|
||||||
|
timeout_member_data=dict(default=50000, type='int', required=False),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict()
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def _lb_wait_for_status(self, lb, status, failures, interval=5):
|
||||||
loadbalancer = module.params['loadbalancer']
|
"""Wait for load balancer to be in a particular provisioning status."""
|
||||||
loadbalancer_id = None
|
timeout = self.params['timeout']
|
||||||
|
|
||||||
|
total_sleep = 0
|
||||||
|
if failures is None:
|
||||||
|
failures = []
|
||||||
|
|
||||||
|
while total_sleep < timeout:
|
||||||
|
lb = self.conn.load_balancer.get_load_balancer(lb.id)
|
||||||
|
if lb.provisioning_status == status:
|
||||||
|
return None
|
||||||
|
if lb.provisioning_status in failures:
|
||||||
|
self.fail_json(
|
||||||
|
msg="Load Balancer %s transitioned to failure state %s" %
|
||||||
|
(lb.id, lb.provisioning_status)
|
||||||
|
)
|
||||||
|
|
||||||
|
time.sleep(interval)
|
||||||
|
total_sleep += interval
|
||||||
|
|
||||||
|
self.fail_json(
|
||||||
|
msg="Timeout waiting for Load Balancer %s to transition to %s" %
|
||||||
|
(lb.id, status)
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
loadbalancer = self.params['loadbalancer']
|
||||||
|
loadbalancer_id = None
|
||||||
|
|
||||||
try:
|
|
||||||
changed = False
|
changed = False
|
||||||
listener = cloud.load_balancer.find_listener(
|
listener = self.conn.load_balancer.find_listener(
|
||||||
name_or_id=module.params['name'])
|
name_or_id=self.params['name'])
|
||||||
|
|
||||||
if module.params['state'] == 'present':
|
if self.params['state'] == 'present':
|
||||||
if not listener:
|
if not listener:
|
||||||
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
|
lb = self.conn.load_balancer.find_load_balancer(loadbalancer)
|
||||||
if not lb:
|
if not lb:
|
||||||
module.fail_json(
|
self.fail_json(
|
||||||
msg='load balancer %s is not found' % loadbalancer
|
msg='load balancer %s is not found' % loadbalancer
|
||||||
)
|
)
|
||||||
loadbalancer_id = lb.id
|
loadbalancer_id = lb.id
|
||||||
|
|
||||||
listener = cloud.load_balancer.create_listener(
|
listener = self.conn.load_balancer.create_listener(
|
||||||
name=module.params['name'],
|
name=self.params['name'],
|
||||||
loadbalancer_id=loadbalancer_id,
|
loadbalancer_id=loadbalancer_id,
|
||||||
protocol=module.params['protocol'],
|
protocol=self.params['protocol'],
|
||||||
protocol_port=module.params['protocol_port'],
|
protocol_port=self.params['protocol_port'],
|
||||||
|
timeout_client_data=self.params['timeout_client_data'],
|
||||||
|
timeout_member_data=self.params['timeout_member_data'],
|
||||||
)
|
)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
if not module.params['wait']:
|
if not self.params['wait']:
|
||||||
module.exit_json(changed=changed,
|
self.exit_json(
|
||||||
listener=listener.to_dict(),
|
changed=changed, listener=listener.to_dict(),
|
||||||
id=listener.id)
|
id=listener.id)
|
||||||
|
|
||||||
if module.params['wait']:
|
if self.params['wait']:
|
||||||
# Check in case the listener already exists.
|
# Check in case the listener already exists.
|
||||||
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
|
lb = self.conn.load_balancer.find_load_balancer(loadbalancer)
|
||||||
if not lb:
|
if not lb:
|
||||||
module.fail_json(
|
self.fail_json(
|
||||||
msg='load balancer %s is not found' % loadbalancer
|
msg='load balancer %s is not found' % loadbalancer
|
||||||
)
|
)
|
||||||
_lb_wait_for_status(module, cloud, lb, "ACTIVE", ["ERROR"])
|
self._lb_wait_for_status(lb, "ACTIVE", ["ERROR"])
|
||||||
|
|
||||||
module.exit_json(changed=changed, listener=listener.to_dict(),
|
self.exit_json(
|
||||||
id=listener.id)
|
changed=changed, listener=listener.to_dict(), id=listener.id)
|
||||||
elif module.params['state'] == 'absent':
|
elif self.params['state'] == 'absent':
|
||||||
if not listener:
|
if not listener:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
cloud.load_balancer.delete_listener(listener)
|
self.conn.load_balancer.delete_listener(listener)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
if module.params['wait']:
|
if self.params['wait']:
|
||||||
# Wait for the load balancer to be active after deleting
|
# Wait for the load balancer to be active after deleting
|
||||||
# the listener.
|
# the listener.
|
||||||
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
|
lb = self.conn.load_balancer.find_load_balancer(loadbalancer)
|
||||||
if not lb:
|
if not lb:
|
||||||
module.fail_json(
|
self.fail_json(
|
||||||
msg='load balancer %s is not found' % loadbalancer
|
msg='load balancer %s is not found' % loadbalancer
|
||||||
)
|
)
|
||||||
_lb_wait_for_status(module, cloud, lb, "ACTIVE", ["ERROR"])
|
self._lb_wait_for_status(lb, "ACTIVE", ["ERROR"])
|
||||||
|
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
|
||||||
|
def main():
|
||||||
|
module = LoadbalancerListenerModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -130,42 +130,11 @@ EXAMPLES = '''
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _wait_for_member_status(module, cloud, pool_id, member_id, status,
|
class LoadbalancerMemberModule(OpenStackModule):
|
||||||
failures, interval=5):
|
argument_spec = dict(
|
||||||
timeout = module.params['timeout']
|
|
||||||
|
|
||||||
total_sleep = 0
|
|
||||||
if failures is None:
|
|
||||||
failures = []
|
|
||||||
|
|
||||||
while total_sleep < timeout:
|
|
||||||
member = cloud.load_balancer.get_member(member_id, pool_id)
|
|
||||||
provisioning_status = member.provisioning_status
|
|
||||||
if provisioning_status == status:
|
|
||||||
return member
|
|
||||||
if provisioning_status in failures:
|
|
||||||
module.fail_json(
|
|
||||||
msg="Member %s transitioned to failure state %s" %
|
|
||||||
(member_id, provisioning_status)
|
|
||||||
)
|
|
||||||
|
|
||||||
time.sleep(interval)
|
|
||||||
total_sleep += interval
|
|
||||||
|
|
||||||
module.fail_json(
|
|
||||||
msg="Timeout waiting for member %s to transition to %s" %
|
|
||||||
(member_id, status)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
pool=dict(required=True),
|
pool=dict(required=True),
|
||||||
@@ -173,54 +142,81 @@ def main():
|
|||||||
protocol_port=dict(default=80, type='int'),
|
protocol_port=dict(default=80, type='int'),
|
||||||
subnet_id=dict(default=None),
|
subnet_id=dict(default=None),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict()
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def _wait_for_member_status(self, pool_id, member_id, status,
|
||||||
name = module.params['name']
|
failures, interval=5):
|
||||||
pool = module.params['pool']
|
timeout = self.params['timeout']
|
||||||
|
|
||||||
|
total_sleep = 0
|
||||||
|
if failures is None:
|
||||||
|
failures = []
|
||||||
|
|
||||||
|
while total_sleep < timeout:
|
||||||
|
member = self.conn.load_balancer.get_member(member_id, pool_id)
|
||||||
|
provisioning_status = member.provisioning_status
|
||||||
|
if provisioning_status == status:
|
||||||
|
return member
|
||||||
|
if provisioning_status in failures:
|
||||||
|
self.fail_json(
|
||||||
|
msg="Member %s transitioned to failure state %s" %
|
||||||
|
(member_id, provisioning_status)
|
||||||
|
)
|
||||||
|
|
||||||
|
time.sleep(interval)
|
||||||
|
total_sleep += interval
|
||||||
|
|
||||||
|
self.fail_json(
|
||||||
|
msg="Timeout waiting for member %s to transition to %s" %
|
||||||
|
(member_id, status)
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
name = self.params['name']
|
||||||
|
pool = self.params['pool']
|
||||||
|
|
||||||
try:
|
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
pool_ret = cloud.load_balancer.find_pool(name_or_id=pool)
|
pool_ret = self.conn.load_balancer.find_pool(name_or_id=pool)
|
||||||
if not pool_ret:
|
if not pool_ret:
|
||||||
module.fail_json(msg='pool %s is not found' % pool)
|
self.fail_json(msg='pool %s is not found' % pool)
|
||||||
|
|
||||||
pool_id = pool_ret.id
|
pool_id = pool_ret.id
|
||||||
member = cloud.load_balancer.find_member(name, pool_id)
|
member = self.conn.load_balancer.find_member(name, pool_id)
|
||||||
|
|
||||||
if module.params['state'] == 'present':
|
if self.params['state'] == 'present':
|
||||||
if not member:
|
if not member:
|
||||||
member = cloud.load_balancer.create_member(
|
member = self.conn.load_balancer.create_member(
|
||||||
pool_ret,
|
pool_ret,
|
||||||
address=module.params['address'],
|
address=self.params['address'],
|
||||||
name=name,
|
name=name,
|
||||||
protocol_port=module.params['protocol_port'],
|
protocol_port=self.params['protocol_port'],
|
||||||
subnet_id=module.params['subnet_id']
|
subnet_id=self.params['subnet_id']
|
||||||
)
|
)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
if not module.params['wait']:
|
if not self.params['wait']:
|
||||||
module.exit_json(changed=changed,
|
self.exit_json(
|
||||||
member=member.to_dict(),
|
changed=changed, member=member.to_dict(), id=member.id)
|
||||||
id=member.id)
|
|
||||||
|
|
||||||
if module.params['wait']:
|
if self.params['wait']:
|
||||||
member = _wait_for_member_status(module, cloud, pool_id,
|
member = self._wait_for_member_status(
|
||||||
member.id, "ACTIVE",
|
pool_id, member.id, "ACTIVE", ["ERROR"])
|
||||||
["ERROR"])
|
|
||||||
|
|
||||||
module.exit_json(changed=changed, member=member.to_dict(),
|
self.exit_json(
|
||||||
id=member.id)
|
changed=changed, member=member.to_dict(), id=member.id)
|
||||||
|
|
||||||
elif module.params['state'] == 'absent':
|
elif self.params['state'] == 'absent':
|
||||||
if member:
|
if member:
|
||||||
cloud.load_balancer.delete_member(member, pool_ret)
|
self.conn.load_balancer.delete_member(member, pool_ret)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
|
||||||
|
def main():
|
||||||
|
module = LoadbalancerMemberModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -149,41 +149,11 @@ EXAMPLES = '''
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, \
|
|
||||||
openstack_module_kwargs, openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def _wait_for_pool_status(module, cloud, pool_id, status, failures,
|
class LoadbalancerPoolModule(OpenStackModule):
|
||||||
interval=5):
|
argument_spec = dict(
|
||||||
timeout = module.params['timeout']
|
|
||||||
|
|
||||||
total_sleep = 0
|
|
||||||
if failures is None:
|
|
||||||
failures = []
|
|
||||||
|
|
||||||
while total_sleep < timeout:
|
|
||||||
pool = cloud.load_balancer.get_pool(pool_id)
|
|
||||||
provisioning_status = pool.provisioning_status
|
|
||||||
if provisioning_status == status:
|
|
||||||
return pool
|
|
||||||
if provisioning_status in failures:
|
|
||||||
module.fail_json(
|
|
||||||
msg="pool %s transitioned to failure state %s" %
|
|
||||||
(pool_id, provisioning_status)
|
|
||||||
)
|
|
||||||
|
|
||||||
time.sleep(interval)
|
|
||||||
total_sleep += interval
|
|
||||||
|
|
||||||
module.fail_json(
|
|
||||||
msg="timeout waiting for pool %s to transition to %s" %
|
|
||||||
(pool_id, status)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
loadbalancer=dict(default=None),
|
loadbalancer=dict(default=None),
|
||||||
@@ -195,71 +165,98 @@ def main():
|
|||||||
choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP']
|
choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP']
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
mutually_exclusive=[['loadbalancer', 'listener']]
|
mutually_exclusive=[['loadbalancer', 'listener']]
|
||||||
)
|
)
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
|
||||||
|
|
||||||
loadbalancer = module.params['loadbalancer']
|
def _wait_for_pool_status(self, pool_id, status, failures,
|
||||||
listener = module.params['listener']
|
interval=5):
|
||||||
|
timeout = self.params['timeout']
|
||||||
|
|
||||||
|
total_sleep = 0
|
||||||
|
if failures is None:
|
||||||
|
failures = []
|
||||||
|
|
||||||
|
while total_sleep < timeout:
|
||||||
|
pool = self.conn.load_balancer.get_pool(pool_id)
|
||||||
|
provisioning_status = pool.provisioning_status
|
||||||
|
if provisioning_status == status:
|
||||||
|
return pool
|
||||||
|
if provisioning_status in failures:
|
||||||
|
self.fail_json(
|
||||||
|
msg="pool %s transitioned to failure state %s" %
|
||||||
|
(pool_id, provisioning_status)
|
||||||
|
)
|
||||||
|
|
||||||
|
time.sleep(interval)
|
||||||
|
total_sleep += interval
|
||||||
|
|
||||||
|
self.fail_json(
|
||||||
|
msg="timeout waiting for pool %s to transition to %s" %
|
||||||
|
(pool_id, status)
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
loadbalancer = self.params['loadbalancer']
|
||||||
|
listener = self.params['listener']
|
||||||
|
|
||||||
try:
|
|
||||||
changed = False
|
changed = False
|
||||||
pool = cloud.load_balancer.find_pool(name_or_id=module.params['name'])
|
pool = self.conn.load_balancer.find_pool(name_or_id=self.params['name'])
|
||||||
|
|
||||||
if module.params['state'] == 'present':
|
if self.params['state'] == 'present':
|
||||||
if not pool:
|
if not pool:
|
||||||
loadbalancer_id = None
|
loadbalancer_id = None
|
||||||
if not (loadbalancer or listener):
|
if not (loadbalancer or listener):
|
||||||
module.fail_json(
|
self.fail_json(
|
||||||
msg="either loadbalancer or listener must be provided"
|
msg="either loadbalancer or listener must be provided"
|
||||||
)
|
)
|
||||||
|
|
||||||
if loadbalancer:
|
if loadbalancer:
|
||||||
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
|
lb = self.conn.load_balancer.find_load_balancer(loadbalancer)
|
||||||
if not lb:
|
if not lb:
|
||||||
module.fail_json(msg='load balancer %s is not '
|
self.fail_json(
|
||||||
'found' % loadbalancer)
|
msg='load balancer %s is not found' % loadbalancer)
|
||||||
loadbalancer_id = lb.id
|
loadbalancer_id = lb.id
|
||||||
|
|
||||||
listener_id = None
|
listener_id = None
|
||||||
if listener:
|
if listener:
|
||||||
listener_ret = cloud.load_balancer.find_listener(listener)
|
listener_ret = self.conn.load_balancer.find_listener(listener)
|
||||||
if not listener_ret:
|
if not listener_ret:
|
||||||
module.fail_json(msg='listener %s is not found'
|
self.fail_json(
|
||||||
% listener)
|
msg='listener %s is not found' % listener)
|
||||||
listener_id = listener_ret.id
|
listener_id = listener_ret.id
|
||||||
|
|
||||||
pool = cloud.load_balancer.create_pool(
|
pool = self.conn.load_balancer.create_pool(
|
||||||
name=module.params['name'],
|
name=self.params['name'],
|
||||||
loadbalancer_id=loadbalancer_id,
|
loadbalancer_id=loadbalancer_id,
|
||||||
listener_id=listener_id,
|
listener_id=listener_id,
|
||||||
protocol=module.params['protocol'],
|
protocol=self.params['protocol'],
|
||||||
lb_algorithm=module.params['lb_algorithm']
|
lb_algorithm=self.params['lb_algorithm']
|
||||||
)
|
)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
if not module.params['wait']:
|
if not self.params['wait']:
|
||||||
module.exit_json(changed=changed,
|
self.exit_json(
|
||||||
pool=pool.to_dict(),
|
changed=changed, pool=pool.to_dict(), id=pool.id)
|
||||||
id=pool.id)
|
|
||||||
|
|
||||||
if module.params['wait']:
|
if self.params['wait']:
|
||||||
pool = _wait_for_pool_status(module, cloud, pool.id, "ACTIVE",
|
pool = self._wait_for_pool_status(
|
||||||
["ERROR"])
|
pool.id, "ACTIVE", ["ERROR"])
|
||||||
|
|
||||||
module.exit_json(changed=changed, pool=pool.to_dict(),
|
self.exit_json(
|
||||||
id=pool.id)
|
changed=changed, pool=pool.to_dict(), id=pool.id)
|
||||||
|
|
||||||
elif module.params['state'] == 'absent':
|
elif self.params['state'] == 'absent':
|
||||||
if pool:
|
if pool:
|
||||||
cloud.load_balancer.delete_pool(pool)
|
self.conn.load_balancer.delete_pool(pool)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
|
||||||
|
def main():
|
||||||
|
module = LoadbalancerPoolModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -296,6 +296,35 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
|
|||||||
|
|
||||||
class LoadBalancerModule(OpenStackModule):
|
class LoadBalancerModule(OpenStackModule):
|
||||||
|
|
||||||
|
def _wait_for_pool(self, pool, provisioning_status, operating_status, failures, interval=5):
|
||||||
|
"""Wait for pool to be in a particular provisioning and operating status."""
|
||||||
|
timeout = self.params['timeout'] # reuse loadbalancer timeout
|
||||||
|
|
||||||
|
total_sleep = 0
|
||||||
|
if failures is None:
|
||||||
|
failures = []
|
||||||
|
|
||||||
|
while total_sleep < timeout:
|
||||||
|
pool = self.conn.load_balancer.find_pool(name_or_id=pool.id)
|
||||||
|
if pool:
|
||||||
|
if pool.provisioning_status == provisioning_status and pool.operating_status == operating_status:
|
||||||
|
return None
|
||||||
|
if pool.provisioning_status in failures:
|
||||||
|
self.fail_json(
|
||||||
|
msg="Pool %s transitioned to failure state %s" %
|
||||||
|
(pool.id, pool.provisioning_status)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if provisioning_status == "DELETED":
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
self.fail_json(
|
||||||
|
msg="Pool %s transitioned to DELETED" % pool.id
|
||||||
|
)
|
||||||
|
|
||||||
|
time.sleep(interval)
|
||||||
|
total_sleep += interval
|
||||||
|
|
||||||
def _wait_for_lb(self, lb, status, failures, interval=5):
|
def _wait_for_lb(self, lb, status, failures, interval=5):
|
||||||
"""Wait for load balancer to be in a particular provisioning status."""
|
"""Wait for load balancer to be in a particular provisioning status."""
|
||||||
timeout = self.params['timeout']
|
timeout = self.params['timeout']
|
||||||
@@ -497,6 +526,7 @@ class LoadBalancerModule(OpenStackModule):
|
|||||||
protocol=protocol,
|
protocol=protocol,
|
||||||
lb_algorithm=lb_algorithm
|
lb_algorithm=lb_algorithm
|
||||||
)
|
)
|
||||||
|
self._wait_for_pool(pool, "ACTIVE", "ONLINE", ["ERROR"])
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
# Ensure members in the pool
|
# Ensure members in the pool
|
||||||
@@ -538,6 +568,7 @@ class LoadBalancerModule(OpenStackModule):
|
|||||||
protocol_port=protocol_port,
|
protocol_port=protocol_port,
|
||||||
subnet_id=subnet_id
|
subnet_id=subnet_id
|
||||||
)
|
)
|
||||||
|
self._wait_for_pool(pool, "ACTIVE", "ONLINE", ["ERROR"])
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
# Associate public ip to the load balancer VIP. If
|
# Associate public ip to the load balancer VIP. If
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ class NetworkInfoModule(OpenStackModule):
|
|||||||
name=dict(required=False, default=None),
|
name=dict(required=False, default=None),
|
||||||
filters=dict(required=False, type='dict', default=None)
|
filters=dict(required=False, type='dict', default=None)
|
||||||
)
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
|
|||||||
237
plugins/modules/neutron_rbac_policies_info.py
Normal file
237
plugins/modules/neutron_rbac_policies_info.py
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright: Ansible Project
|
||||||
|
# (c) 2021, Ashraf Hasson <ahasson@redhat.com>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: neutron_rbac_policies_info
|
||||||
|
short_description: Fetch Neutron policies.
|
||||||
|
author: OpenStack Ansible SIG
|
||||||
|
description:
|
||||||
|
- Get RBAC policies against a network, security group or a QoS Policy for one or more projects.
|
||||||
|
- If a C(policy_id) was not provided, this module will attempt to fetch all available policies.
|
||||||
|
- Accepts same arguments as OpenStackSDK network proxy C(find_rbac_policy) and C(rbac_policies) functions which are ultimately passed over to C(RBACPolicy)
|
||||||
|
- All parameters passed in to this module act as a filter for when no C(policy_id) was provided, otherwise they're ignored.
|
||||||
|
- Returns None if no matching policy was found as opposed to failing.
|
||||||
|
|
||||||
|
options:
|
||||||
|
policy_id:
|
||||||
|
description:
|
||||||
|
- The RBAC policy ID
|
||||||
|
- If provided, all other filters are ignored
|
||||||
|
type: str
|
||||||
|
object_id:
|
||||||
|
description:
|
||||||
|
- The object ID (the subject of the policy) to which the RBAC rules applies
|
||||||
|
- This would be the ID of a network, security group or a qos policy
|
||||||
|
- Mutually exclusive with the C(object_type)
|
||||||
|
type: str
|
||||||
|
object_type:
|
||||||
|
description:
|
||||||
|
- Can be one of the following object types C(network), C(security_group) or C(qos_policy)
|
||||||
|
- Mutually exclusive with the C(object_id)
|
||||||
|
choices: ['network', 'security_group', 'qos_policy']
|
||||||
|
type: str
|
||||||
|
target_project_id:
|
||||||
|
description:
|
||||||
|
- Filters the RBAC rules based on the target project id
|
||||||
|
- Logically AND'ed with other filters
|
||||||
|
- Mutually exclusive with C(project_id)
|
||||||
|
type: str
|
||||||
|
project_id:
|
||||||
|
description:
|
||||||
|
- Filters the RBAC rules based on the project id to which the object belongs to
|
||||||
|
- Logically AND'ed with other filters
|
||||||
|
- Mutually exclusive with C(target_project_id)
|
||||||
|
type: str
|
||||||
|
project:
|
||||||
|
description:
|
||||||
|
- Filters the RBAC rules based on the project name
|
||||||
|
- Logically AND'ed with other filters
|
||||||
|
type: str
|
||||||
|
action:
|
||||||
|
description:
|
||||||
|
- Can be either of the following options C(access_as_shared) | C(access_as_external)
|
||||||
|
- Logically AND'ed with other filters
|
||||||
|
choices: ['access_as_shared', 'access_as_external']
|
||||||
|
type: str
|
||||||
|
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- openstack.cloud.openstack
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = r'''
|
||||||
|
# Gather all rbac policies for a project
|
||||||
|
- name: Get all rbac policies for {{ project }}
|
||||||
|
openstack.cloud.neutron_rbac_policies_info:
|
||||||
|
project_id: "{{ project.id }}"
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = r'''
|
||||||
|
# return value can either be plural or signular depending on what was passed in as parameters
|
||||||
|
policies:
|
||||||
|
description:
|
||||||
|
- List of rbac policies, this could also be returned as a singular element, i.e., 'policy'
|
||||||
|
type: complex
|
||||||
|
returned: always
|
||||||
|
contains:
|
||||||
|
object_id:
|
||||||
|
description:
|
||||||
|
- The UUID of the object to which the RBAC rules apply
|
||||||
|
type: str
|
||||||
|
sample: "7422172b-2961-475c-ac68-bd0f2a9960ad"
|
||||||
|
target_project_id:
|
||||||
|
description:
|
||||||
|
- The UUID of the target project
|
||||||
|
type: str
|
||||||
|
sample: "c201a689c016435c8037977166f77368"
|
||||||
|
project_id:
|
||||||
|
description:
|
||||||
|
- The UUID of the project to which access is granted
|
||||||
|
type: str
|
||||||
|
sample: "84b8774d595b41e89f3dfaa1fd76932c"
|
||||||
|
object_type:
|
||||||
|
description:
|
||||||
|
- The object type to which the RBACs apply
|
||||||
|
type: str
|
||||||
|
sample: "network"
|
||||||
|
action:
|
||||||
|
description:
|
||||||
|
- The access model specified by the RBAC rules
|
||||||
|
type: str
|
||||||
|
sample: "access_as_shared"
|
||||||
|
id:
|
||||||
|
description:
|
||||||
|
- The ID of the RBAC rule/policy
|
||||||
|
type: str
|
||||||
|
sample: "4154ce0c-71a7-4d87-a905-09762098ddb9"
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- The name of the RBAC rule; usually null
|
||||||
|
type: str
|
||||||
|
sample: null
|
||||||
|
location:
|
||||||
|
description:
|
||||||
|
- A dictionary of the project details to which access is granted
|
||||||
|
type: dict
|
||||||
|
sample: >-
|
||||||
|
{
|
||||||
|
"cloud": "devstack",
|
||||||
|
"region_name": "",
|
||||||
|
"zone": null,
|
||||||
|
"project": {
|
||||||
|
"id": "84b8774d595b41e89f3dfaa1fd76932c",
|
||||||
|
"name": null,
|
||||||
|
"domain_id": null,
|
||||||
|
"domain_name": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
import re
|
||||||
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronRbacPoliciesInfo(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
|
policy_id=dict(),
|
||||||
|
object_id=dict(), # ID of the object that this RBAC policy affects.
|
||||||
|
object_type=dict(choices=['security_group', 'qos_policy', 'network']), # Type of the object that this RBAC policy affects.
|
||||||
|
target_project_id=dict(), # The ID of the project this RBAC will be enforced.
|
||||||
|
project_id=dict(), # The owner project ID.
|
||||||
|
project=dict(),
|
||||||
|
action=dict(choices=['access_as_external', 'access_as_shared']), # Action for the RBAC policy.
|
||||||
|
)
|
||||||
|
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _filter_policies_by(self, policies, key, value):
|
||||||
|
filtered = []
|
||||||
|
regexp = re.compile(r"location\.project\.([A-Za-z]+)")
|
||||||
|
if regexp.match(key):
|
||||||
|
attribute = key.split('.')[-1]
|
||||||
|
for p in policies:
|
||||||
|
if p['location']['project'][attribute] == value:
|
||||||
|
filtered.append(p)
|
||||||
|
else:
|
||||||
|
for p in policies:
|
||||||
|
if getattr(p, key) == value:
|
||||||
|
filtered.append(p)
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
|
||||||
|
def _get_rbac_policies(self):
|
||||||
|
object_type = self.params.get('object_type')
|
||||||
|
project_id = self.params.get('project_id')
|
||||||
|
action = self.params.get('action')
|
||||||
|
|
||||||
|
search_attributes = {}
|
||||||
|
if object_type is not None:
|
||||||
|
search_attributes['object_type'] = object_type
|
||||||
|
if project_id is not None:
|
||||||
|
search_attributes['project_id'] = project_id
|
||||||
|
if action is not None:
|
||||||
|
search_attributes['action'] = action
|
||||||
|
|
||||||
|
try:
|
||||||
|
policies = []
|
||||||
|
generator = self.conn.network.rbac_policies(**search_attributes)
|
||||||
|
for p in generator:
|
||||||
|
policies.append(p)
|
||||||
|
except self.sdk.exceptions.OpenStackCloudException as ex:
|
||||||
|
self.fail_json(msg='Failed to get RBAC policies: {0}'.format(str(ex)))
|
||||||
|
|
||||||
|
return policies
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
policy_id = self.params.get('policy_id')
|
||||||
|
object_id = self.params.get('object_id')
|
||||||
|
object_type = self.params.get('object_type')
|
||||||
|
project_id = self.params.get('project_id')
|
||||||
|
project = self.params.get('project')
|
||||||
|
target_project_id = self.params.get('target_project_id')
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(changed=False)
|
||||||
|
|
||||||
|
if policy_id is not None:
|
||||||
|
try:
|
||||||
|
policy = self.conn.network.get_rbac_policy(policy_id)
|
||||||
|
self.exit_json(changed=False, policy=policy)
|
||||||
|
except self.sdk.exceptions.ResourceNotFound:
|
||||||
|
self.exit_json(changed=False, policy=None)
|
||||||
|
except self.sdk.exceptions.OpenStackCloudException as ex:
|
||||||
|
self.fail_json(msg='Failed to get RBAC policy: {0}'.format(str(ex)))
|
||||||
|
else:
|
||||||
|
if object_id is not None and object_type is not None:
|
||||||
|
self.fail_json(msg='object_id and object_type are mutually exclusive, please specify one of the two.')
|
||||||
|
if project_id is not None and target_project_id is not None:
|
||||||
|
self.fail_json(msg='project_id and target_project_id are mutually exclusive, please specify one of the two.')
|
||||||
|
|
||||||
|
filtered_policies = self._get_rbac_policies()
|
||||||
|
|
||||||
|
if project is not None:
|
||||||
|
filtered_policies = self._filter_policies_by(filtered_policies, 'location.project.name', project)
|
||||||
|
if object_id is not None:
|
||||||
|
filtered_policies = self._filter_policies_by(filtered_policies, 'object_id', object_id)
|
||||||
|
if target_project_id is not None:
|
||||||
|
filtered_policies = self._filter_policies_by(filtered_policies, 'target_project_id', target_project_id)
|
||||||
|
|
||||||
|
self.exit_json(policies=filtered_policies, changed=False)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = NeutronRbacPoliciesInfo()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
308
plugins/modules/neutron_rbac_policy.py
Normal file
308
plugins/modules/neutron_rbac_policy.py
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# Copyright: Ansible Project
|
||||||
|
# (c) 2021, Ashraf Hasson <ahasson@redhat.com>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: neutron_rbac_policy
|
||||||
|
short_description: Create or delete a Neutron policy to apply a RBAC rule against an object.
|
||||||
|
author: OpenStack Ansible SIG
|
||||||
|
description:
|
||||||
|
- Create a policy to apply a RBAC rule against a network, security group or a QoS Policy or update/delete an existing policy.
|
||||||
|
- If a C(policy_id) was provided but not found, this module will attempt to create a new policy rather than error out when updating an existing rule.
|
||||||
|
- Accepts same arguments as OpenStackSDK network proxy C(find_rbac_policy) and C(rbac_policies) functions which are ultimately passed over to C(RBACPolicy)
|
||||||
|
|
||||||
|
options:
|
||||||
|
policy_id:
|
||||||
|
description:
|
||||||
|
- The RBAC policy ID
|
||||||
|
- Required when deleting or updating an existing RBAC policy rule, ignored otherwise
|
||||||
|
type: str
|
||||||
|
object_id:
|
||||||
|
description:
|
||||||
|
- The object ID (the subject of the policy) to which the RBAC rule applies
|
||||||
|
- Cannot be changed when updating an existing policy
|
||||||
|
- Required when creating a RBAC policy rule, ignored when deleting a policy
|
||||||
|
type: str
|
||||||
|
object_type:
|
||||||
|
description:
|
||||||
|
- Can be one of the following object types C(network), C(security_group) or C(qos_policy)
|
||||||
|
- Cannot be changed when updating an existing policy
|
||||||
|
- Required when creating a RBAC policy rule, ignored when deleting a policy
|
||||||
|
choices: ['network', 'security_group', 'qos_policy']
|
||||||
|
type: str
|
||||||
|
target_project_id:
|
||||||
|
description:
|
||||||
|
- The project to which access to be allowed or revoked/disallowed
|
||||||
|
- Can be specified/changed when updating an existing policy
|
||||||
|
- Required when creating or updating a RBAC policy rule, ignored when deleting a policy
|
||||||
|
type: str
|
||||||
|
project_id:
|
||||||
|
description:
|
||||||
|
- The project to which the object_id belongs
|
||||||
|
- Cannot be changed when updating an existing policy
|
||||||
|
- Required when creating a RBAC policy rule, ignored when deleting a policy
|
||||||
|
type: str
|
||||||
|
action:
|
||||||
|
description:
|
||||||
|
- Can be either of the following options C(access_as_shared) | C(access_as_external)
|
||||||
|
- Cannot be changed when updating an existing policy
|
||||||
|
- Required when creating a RBAC policy rule, ignored when deleting a policy
|
||||||
|
choices: ['access_as_shared', 'access_as_external']
|
||||||
|
type: str
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- Whether the RBAC rule should be C(present) or C(absent).
|
||||||
|
choices: ['present', 'absent']
|
||||||
|
default: present
|
||||||
|
type: str
|
||||||
|
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- openstack.cloud.openstack
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = r'''
|
||||||
|
# Ensure network RBAC policy exists
|
||||||
|
- name: Create a new network RBAC policy
|
||||||
|
neutron_rbac_policy:
|
||||||
|
object_id: '7422172b-2961-475c-ac68-bd0f2a9960ad'
|
||||||
|
object_type: 'network'
|
||||||
|
target_project_id: 'a12f9ce1de0645e0a0b01c2e679f69ec'
|
||||||
|
project_id: '84b8774d595b41e89f3dfaa1fd76932d'
|
||||||
|
|
||||||
|
# Update network RBAC policy
|
||||||
|
- name: Update an existing network RBAC policy
|
||||||
|
neutron_rbac_policy:
|
||||||
|
policy_id: 'f625242a-6a73-47ac-8d1f-91440b2c617f'
|
||||||
|
target_project_id: '163c89e065a94e069064e551e15daf0e'
|
||||||
|
|
||||||
|
# Delete an existing RBAC policy
|
||||||
|
- name: Delete RBAC policy
|
||||||
|
openstack.cloud.openstack.neutron_rbac_policy:
|
||||||
|
policy_id: 'f625242a-6a73-47ac-8d1f-91440b2c617f'
|
||||||
|
state: absent
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = r'''
|
||||||
|
policy:
|
||||||
|
description:
|
||||||
|
- A hash representing the policy
|
||||||
|
type: complex
|
||||||
|
returned: always
|
||||||
|
contains:
|
||||||
|
object_id:
|
||||||
|
description:
|
||||||
|
- The UUID of the object to which the RBAC rules apply
|
||||||
|
type: str
|
||||||
|
sample: "7422172b-2961-475c-ac68-bd0f2a9960ad"
|
||||||
|
target_project_id:
|
||||||
|
description:
|
||||||
|
- The UUID of the target project
|
||||||
|
type: str
|
||||||
|
sample: "c201a689c016435c8037977166f77368"
|
||||||
|
project_id:
|
||||||
|
description:
|
||||||
|
- The UUID of the project to which access is granted
|
||||||
|
type: str
|
||||||
|
sample: "84b8774d595b41e89f3dfaa1fd76932c"
|
||||||
|
object_type:
|
||||||
|
description:
|
||||||
|
- The object type to which the RBACs apply
|
||||||
|
type: str
|
||||||
|
sample: "network"
|
||||||
|
action:
|
||||||
|
description:
|
||||||
|
- The access model specified by the RBAC rules
|
||||||
|
type: str
|
||||||
|
sample: "access_as_shared"
|
||||||
|
id:
|
||||||
|
description:
|
||||||
|
- The ID of the RBAC rule/policy
|
||||||
|
type: str
|
||||||
|
sample: "4154ce0c-71a7-4d87-a905-09762098ddb9"
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- The name of the RBAC rule; usually null
|
||||||
|
type: str
|
||||||
|
sample: null
|
||||||
|
location:
|
||||||
|
description:
|
||||||
|
- A dictionary of the project details to which access is granted
|
||||||
|
type: dict
|
||||||
|
sample: >-
|
||||||
|
{
|
||||||
|
"cloud": "devstack",
|
||||||
|
"region_name": "",
|
||||||
|
"zone": null,
|
||||||
|
"project": {
|
||||||
|
"id": "84b8774d595b41e89f3dfaa1fd76932c",
|
||||||
|
"name": null,
|
||||||
|
"domain_id": null,
|
||||||
|
"domain_name": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronRbacPolicy(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
|
policy_id=dict(),
|
||||||
|
object_id=dict(), # ID of the object that this RBAC policy affects.
|
||||||
|
object_type=dict(choices=['security_group', 'qos_policy', 'network']), # Type of the object that this RBAC policy affects.
|
||||||
|
target_project_id=dict(), # The ID of the project this RBAC will be enforced.
|
||||||
|
project_id=dict(), # The owner project ID.
|
||||||
|
action=dict(choices=['access_as_external', 'access_as_shared']), # Action for the RBAC policy.
|
||||||
|
state=dict(default='present', choices=['absent', 'present'])
|
||||||
|
)
|
||||||
|
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _delete_rbac_policy(self, policy):
|
||||||
|
"""
|
||||||
|
Delete an existing RBAC policy
|
||||||
|
returns: the "Changed" state
|
||||||
|
"""
|
||||||
|
|
||||||
|
if policy is None:
|
||||||
|
self.fail_json(msg='Must specify policy_id for delete')
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.conn.network.delete_rbac_policy(policy.id)
|
||||||
|
except self.sdk.exceptions.OpenStackCloudException as ex:
|
||||||
|
self.fail_json(msg='Failed to delete RBAC policy: {0}'.format(str(ex)))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _create_rbac_policy(self):
|
||||||
|
"""
|
||||||
|
Creates a new RBAC policy
|
||||||
|
returns: the "Changed" state of the RBAC policy
|
||||||
|
"""
|
||||||
|
|
||||||
|
object_id = self.params.get('object_id')
|
||||||
|
object_type = self.params.get('object_type')
|
||||||
|
target_project_id = self.params.get('target_project_id')
|
||||||
|
project_id = self.params.get('project_id')
|
||||||
|
action = self.params.get('action')
|
||||||
|
|
||||||
|
attributes = {
|
||||||
|
'object_id': object_id,
|
||||||
|
'object_type': object_type,
|
||||||
|
'target_project_id': target_project_id,
|
||||||
|
'project_id': project_id,
|
||||||
|
'action': action
|
||||||
|
}
|
||||||
|
|
||||||
|
if not all(attributes.values()):
|
||||||
|
self.fail_json(msg='Missing one or more required parameter for creating a RBAC policy')
|
||||||
|
|
||||||
|
try:
|
||||||
|
search_attributes = dict(attributes)
|
||||||
|
del search_attributes['object_id']
|
||||||
|
del search_attributes['target_project_id']
|
||||||
|
policies = self.conn.network.rbac_policies(**search_attributes)
|
||||||
|
for p in policies:
|
||||||
|
if p.object_id == object_id and p.target_project_id == target_project_id:
|
||||||
|
return (False, p)
|
||||||
|
|
||||||
|
# if no matching policy exists, attempt to create one
|
||||||
|
policy = self.conn.network.create_rbac_policy(**attributes)
|
||||||
|
except self.sdk.exceptions.OpenStackCloudException as ex:
|
||||||
|
self.fail_json(msg='Failed to create RBAC policy: {0}'.format(str(ex)))
|
||||||
|
|
||||||
|
return (True, policy)
|
||||||
|
|
||||||
|
def _update_rbac_policy(self, policy):
|
||||||
|
"""
|
||||||
|
Updates an existing RBAC policy
|
||||||
|
returns: the "Changed" state of the RBAC policy
|
||||||
|
"""
|
||||||
|
|
||||||
|
object_id = self.params.get('object_id')
|
||||||
|
object_type = self.params.get('object_type')
|
||||||
|
target_project_id = self.params.get('target_project_id')
|
||||||
|
project_id = self.params.get('project_id')
|
||||||
|
action = self.params.get('action')
|
||||||
|
|
||||||
|
allowed_attributes = {
|
||||||
|
'rbac_policy': policy.id,
|
||||||
|
'target_project_id': target_project_id
|
||||||
|
}
|
||||||
|
|
||||||
|
disallowed_attributes = {
|
||||||
|
'object_id': object_id,
|
||||||
|
'object_type': object_type,
|
||||||
|
'project_id': project_id,
|
||||||
|
'action': action
|
||||||
|
}
|
||||||
|
|
||||||
|
if not all(allowed_attributes.values()):
|
||||||
|
self.fail_json(msg='Missing one or more required parameter for updating a RBAC policy')
|
||||||
|
|
||||||
|
if any(disallowed_attributes.values()):
|
||||||
|
self.fail_json(msg='Cannot change disallowed parameters while updating a RBAC policy: ["object_id", "object_type", "project_id", "action"]')
|
||||||
|
|
||||||
|
try:
|
||||||
|
policy = self.conn.network.update_rbac_policy(**allowed_attributes)
|
||||||
|
except self.sdk.exceptions.OpenStackCloudException as ex:
|
||||||
|
self.fail_json(msg='Failed to update the RBAC policy: {0}'.format(str(ex)))
|
||||||
|
|
||||||
|
return (True, policy)
|
||||||
|
|
||||||
|
def _policy_state_change(self, policy):
|
||||||
|
state = self.params['state']
|
||||||
|
if state == 'present':
|
||||||
|
if not policy:
|
||||||
|
return True
|
||||||
|
if state == 'absent' and policy:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
policy_id = self.params.get('policy_id')
|
||||||
|
state = self.params.get('state')
|
||||||
|
|
||||||
|
if policy_id is not None:
|
||||||
|
try:
|
||||||
|
policy = self.conn.network.get_rbac_policy(policy_id)
|
||||||
|
except self.sdk.exceptions.ResourceNotFound:
|
||||||
|
policy = None
|
||||||
|
except self.sdk.exceptions.OpenStackCloudException as ex:
|
||||||
|
self.fail_json(msg='Failed to get RBAC policy: {0}'.format(str(ex)))
|
||||||
|
else:
|
||||||
|
policy = None
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(changed=self._policy_state_change(policy), policy=policy)
|
||||||
|
|
||||||
|
if state == 'absent':
|
||||||
|
if policy is None and policy_id:
|
||||||
|
self.exit_json(changed=False)
|
||||||
|
if policy_id is None:
|
||||||
|
self.fail_json(msg='Must specify policy_id when state is absent')
|
||||||
|
if policy is not None:
|
||||||
|
changed = self._delete_rbac_policy(policy)
|
||||||
|
self.exit_json(changed=changed)
|
||||||
|
# state == 'present'
|
||||||
|
else:
|
||||||
|
if policy is None:
|
||||||
|
(changed, new_policy) = self._create_rbac_policy()
|
||||||
|
else:
|
||||||
|
(changed, new_policy) = self._update_rbac_policy(policy)
|
||||||
|
|
||||||
|
self.exit_json(changed=changed, policy=new_policy)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = NeutronRbacPolicy()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -65,58 +65,55 @@ EXAMPLES = '''
|
|||||||
container: config
|
container: config
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def process_object(
|
class SwiftObjectModule(OpenStackModule):
|
||||||
cloud_obj, container, name, filename, container_access, **kwargs):
|
argument_spec = dict(
|
||||||
|
|
||||||
changed = False
|
|
||||||
container_obj = cloud_obj.get_container(container)
|
|
||||||
if kwargs['state'] == 'present':
|
|
||||||
if not container_obj:
|
|
||||||
container_obj = cloud_obj.create_container(container)
|
|
||||||
changed = True
|
|
||||||
if cloud_obj.get_container_access(container) != container_access:
|
|
||||||
cloud_obj.set_container_access(container, container_access)
|
|
||||||
changed = True
|
|
||||||
if name:
|
|
||||||
if cloud_obj.is_object_stale(container, name, filename):
|
|
||||||
cloud_obj.create_object(container, name, filename)
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
if container_obj:
|
|
||||||
if name:
|
|
||||||
if cloud_obj.get_object_metadata(container, name):
|
|
||||||
cloud_obj.delete_object(container, name)
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
cloud_obj.delete_container(container)
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=False, default=None),
|
name=dict(required=False, default=None),
|
||||||
container=dict(required=True),
|
container=dict(required=True),
|
||||||
filename=dict(required=False, default=None),
|
filename=dict(required=False, default=None),
|
||||||
container_access=dict(default='private', choices=['private', 'public']),
|
container_access=dict(default='private', choices=['private', 'public']),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict()
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def process_object(
|
||||||
try:
|
self, container, name, filename, container_access, **kwargs
|
||||||
changed = process_object(cloud, **module.params)
|
):
|
||||||
|
changed = False
|
||||||
|
container_obj = self.conn.get_container(container)
|
||||||
|
if kwargs['state'] == 'present':
|
||||||
|
if not container_obj:
|
||||||
|
container_obj = self.conn.create_container(container)
|
||||||
|
changed = True
|
||||||
|
if self.conn.get_container_access(container) != container_access:
|
||||||
|
self.conn.set_container_access(container, container_access)
|
||||||
|
changed = True
|
||||||
|
if name:
|
||||||
|
if self.conn.is_object_stale(container, name, filename):
|
||||||
|
self.conn.create_object(container, name, filename)
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
if container_obj:
|
||||||
|
if name:
|
||||||
|
if self.conn.get_object_metadata(container, name):
|
||||||
|
self.conn.delete_object(container, name)
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
self.conn.delete_container(container)
|
||||||
|
changed = True
|
||||||
|
return changed
|
||||||
|
|
||||||
module.exit_json(changed=changed)
|
def run(self):
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
changed = self.process_object(**self.params)
|
||||||
module.fail_json(msg=str(e))
|
|
||||||
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = SwiftObjectModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
207
plugins/modules/object_container.py
Normal file
207
plugins/modules/object_container.py
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 by Open Telekom Cloud, operated by T-Systems International GmbH
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
DOCUMENTATION = '''
|
||||||
|
---
|
||||||
|
module: object_container
|
||||||
|
short_description: Manage Swift container.
|
||||||
|
author: OpenStack Ansible SIG
|
||||||
|
description:
|
||||||
|
- Manage Swift container.
|
||||||
|
options:
|
||||||
|
container:
|
||||||
|
description: Name of a container in Swift.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
metadata:
|
||||||
|
description:
|
||||||
|
- Key/value pairs to be set as metadata on the container.
|
||||||
|
- If a container doesn't exist, it will be created.
|
||||||
|
- Both custom and system metadata can be set.
|
||||||
|
- Custom metadata are keys and values defined by the user.
|
||||||
|
- The system metadata keys are content_type, content_encoding, content_disposition, delete_after,\
|
||||||
|
delete_at, is_content_type_detected
|
||||||
|
type: dict
|
||||||
|
required: false
|
||||||
|
keys:
|
||||||
|
description: Keys from 'metadata' to be deleted.
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
required: false
|
||||||
|
delete_with_all_objects:
|
||||||
|
description:
|
||||||
|
- Whether the container should be deleted with all objects or not.
|
||||||
|
- Without this parameter set to "true", an attempt to delete a container that contains objects will fail.
|
||||||
|
type: bool
|
||||||
|
default: False
|
||||||
|
required: false
|
||||||
|
state:
|
||||||
|
description: Whether resource should be present or absent.
|
||||||
|
default: 'present'
|
||||||
|
choices: ['present', 'absent']
|
||||||
|
type: str
|
||||||
|
requirements:
|
||||||
|
- "python >= 3.6"
|
||||||
|
- "openstacksdk"
|
||||||
|
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- openstack.cloud.openstack
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = '''
|
||||||
|
container:
|
||||||
|
description: Specifies the container.
|
||||||
|
returned: On success when C(state=present)
|
||||||
|
type: dict
|
||||||
|
sample:
|
||||||
|
{
|
||||||
|
"bytes": 5449,
|
||||||
|
"bytes_used": 5449,
|
||||||
|
"content_type": null,
|
||||||
|
"count": 1,
|
||||||
|
"id": "otc",
|
||||||
|
"if_none_match": null,
|
||||||
|
"is_content_type_detected": null,
|
||||||
|
"is_newest": null,
|
||||||
|
"meta_temp_url_key": null,
|
||||||
|
"meta_temp_url_key_2": null,
|
||||||
|
"name": "otc",
|
||||||
|
"object_count": 1,
|
||||||
|
"read_ACL": null,
|
||||||
|
"sync_key": null,
|
||||||
|
"sync_to": null,
|
||||||
|
"timestamp": null,
|
||||||
|
"versions_location": null,
|
||||||
|
"write_ACL": null
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = '''
|
||||||
|
# Create empty container
|
||||||
|
- openstack.cloud.object_container:
|
||||||
|
container: "new-container"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
# Set metadata for container
|
||||||
|
- openstack.cloud.object_container:
|
||||||
|
container: "new-container"
|
||||||
|
metadata: "Cache-Control='no-cache'"
|
||||||
|
|
||||||
|
# Delete some keys from metadata of a container
|
||||||
|
- openstack.cloud.object_container:
|
||||||
|
container: "new-container"
|
||||||
|
keys:
|
||||||
|
- content_type
|
||||||
|
|
||||||
|
# Delete container
|
||||||
|
- openstack.cloud.object_container:
|
||||||
|
container: "new-container"
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
# Delete container and its objects
|
||||||
|
- openstack.cloud.object_container:
|
||||||
|
container: "new-container"
|
||||||
|
delete_with_all_objects: true
|
||||||
|
state: absent
|
||||||
|
'''
|
||||||
|
|
||||||
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerModule(OpenStackModule):
|
||||||
|
|
||||||
|
argument_spec = dict(
|
||||||
|
container=dict(type='str', required=True),
|
||||||
|
metadata=dict(type='dict', required=False),
|
||||||
|
keys=dict(type='list', required=False, elements='str', no_log=False),
|
||||||
|
state=dict(type='str', required=False, default='present', choices=['present', 'absent']),
|
||||||
|
delete_with_all_objects=dict(type='bool', default=False, required=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
def create(self, container):
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
if self._container_exist(container):
|
||||||
|
self.exit_json(changed=False)
|
||||||
|
|
||||||
|
container_data = self.conn.object_store.create_container(name=container).to_dict()
|
||||||
|
container_data.pop('location')
|
||||||
|
data['container'] = container_data
|
||||||
|
self.exit_json(changed=True, **data)
|
||||||
|
|
||||||
|
def delete(self, container):
|
||||||
|
|
||||||
|
delete_with_all_objects = self.params['delete_with_all_objects']
|
||||||
|
|
||||||
|
changed = False
|
||||||
|
if self._container_exist(container):
|
||||||
|
objects = []
|
||||||
|
for raw in self.conn.object_store.objects(container):
|
||||||
|
dt = raw.to_dict()
|
||||||
|
dt.pop('location')
|
||||||
|
objects.append(dt)
|
||||||
|
if len(objects) > 0:
|
||||||
|
if delete_with_all_objects:
|
||||||
|
for obj in objects:
|
||||||
|
self.conn.object_store.delete_object(container=container, obj=obj['id'])
|
||||||
|
else:
|
||||||
|
self.fail_json(msg="Container has objects")
|
||||||
|
self.conn.object_store.delete_container(container=container)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
self.exit(changed=changed)
|
||||||
|
|
||||||
|
def set_metadata(self, container, metadata):
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
if not self._container_exist(container):
|
||||||
|
new_container = self.conn.object_store.create_container(name=container).to_dict()
|
||||||
|
|
||||||
|
new_container = self.conn.object_store.set_container_metadata(container, **metadata).to_dict()
|
||||||
|
new_container.pop('location')
|
||||||
|
data['container'] = new_container
|
||||||
|
self.exit(changed=True, **data)
|
||||||
|
|
||||||
|
def delete_metadata(self, container, keys):
|
||||||
|
|
||||||
|
if not self._container_exist(container):
|
||||||
|
self.fail_json(msg="Container doesn't exist")
|
||||||
|
|
||||||
|
self.conn.object_store.delete_container_metadata(container=container, keys=keys)
|
||||||
|
self.exit(changed=True)
|
||||||
|
|
||||||
|
def _container_exist(self, container):
|
||||||
|
try:
|
||||||
|
self.conn.object_store.get_container_metadata(container)
|
||||||
|
return True
|
||||||
|
except self.sdk.exceptions.ResourceNotFound:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
container = self.params['container']
|
||||||
|
state = self.params['state']
|
||||||
|
metadata = self.params['metadata']
|
||||||
|
keys = self.params['keys']
|
||||||
|
|
||||||
|
if state == 'absent':
|
||||||
|
self.delete(container)
|
||||||
|
if metadata:
|
||||||
|
self.set_metadata(container, metadata)
|
||||||
|
if keys:
|
||||||
|
self.delete_metadata(container, keys)
|
||||||
|
|
||||||
|
self.create(container)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = ContainerModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -119,6 +119,10 @@ options:
|
|||||||
description:
|
description:
|
||||||
- Whether to enable or disable the port security on the network.
|
- Whether to enable or disable the port security on the network.
|
||||||
type: bool
|
type: bool
|
||||||
|
binding_profile:
|
||||||
|
description:
|
||||||
|
- Binding profile dict that the port should be created with.
|
||||||
|
type: dict
|
||||||
requirements:
|
requirements:
|
||||||
- "python >= 3.6"
|
- "python >= 3.6"
|
||||||
- "openstacksdk"
|
- "openstacksdk"
|
||||||
@@ -199,6 +203,20 @@ EXAMPLES = '''
|
|||||||
name: port1
|
name: port1
|
||||||
network: foo
|
network: foo
|
||||||
vnic_type: direct
|
vnic_type: direct
|
||||||
|
|
||||||
|
# Create a port with binding profile
|
||||||
|
- openstack.cloud.port:
|
||||||
|
state: present
|
||||||
|
auth:
|
||||||
|
auth_url: https://identity.example.com
|
||||||
|
username: admin
|
||||||
|
password: admin
|
||||||
|
project_name: admin
|
||||||
|
name: port1
|
||||||
|
network: foo
|
||||||
|
binding_profile:
|
||||||
|
"pci_slot": "0000:03:11.1"
|
||||||
|
"physical_network": "provider"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURN = '''
|
RETURN = '''
|
||||||
@@ -246,12 +264,14 @@ port_security_enabled:
|
|||||||
description: Port security state on the network.
|
description: Port security state on the network.
|
||||||
returned: success
|
returned: success
|
||||||
type: bool
|
type: bool
|
||||||
|
binding:profile:
|
||||||
|
description: Port binded profile
|
||||||
|
returned: success
|
||||||
|
type: dict
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
from ansible.module_utils.basic import missing_required_lib
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
@@ -264,120 +284,8 @@ except ImportError:
|
|||||||
HAS_ORDEREDDICT = False
|
HAS_ORDEREDDICT = False
|
||||||
|
|
||||||
|
|
||||||
def _needs_update(module, port, cloud):
|
class NetworkPortModule(OpenStackModule):
|
||||||
"""Check for differences in the updatable values.
|
argument_spec = dict(
|
||||||
|
|
||||||
NOTE: We don't currently allow name updates.
|
|
||||||
"""
|
|
||||||
compare_simple = ['admin_state_up',
|
|
||||||
'mac_address',
|
|
||||||
'device_owner',
|
|
||||||
'device_id',
|
|
||||||
'binding:vnic_type',
|
|
||||||
'port_security_enabled']
|
|
||||||
compare_list_dict = ['allowed_address_pairs',
|
|
||||||
'extra_dhcp_opts']
|
|
||||||
compare_list = ['security_groups']
|
|
||||||
|
|
||||||
for key in compare_simple:
|
|
||||||
if module.params[key] is not None and module.params[key] != port[key]:
|
|
||||||
return True
|
|
||||||
for key in compare_list:
|
|
||||||
if (
|
|
||||||
module.params[key] is not None
|
|
||||||
and set(module.params[key]) != set(port[key])
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
|
|
||||||
for key in compare_list_dict:
|
|
||||||
if not module.params[key]:
|
|
||||||
if not port[key]:
|
|
||||||
return True
|
|
||||||
|
|
||||||
# sort dicts in list
|
|
||||||
port_ordered = [OrderedDict(sorted(d.items())) for d in port[key]]
|
|
||||||
param_ordered = [OrderedDict(sorted(d.items())) for d in module.params[key]]
|
|
||||||
|
|
||||||
for d in param_ordered:
|
|
||||||
if d not in port_ordered:
|
|
||||||
return True
|
|
||||||
|
|
||||||
for d in port_ordered:
|
|
||||||
if d not in param_ordered:
|
|
||||||
return True
|
|
||||||
|
|
||||||
# NOTE: if port was created or updated with 'no_security_groups=True',
|
|
||||||
# subsequent updates without 'no_security_groups' flag or
|
|
||||||
# 'no_security_groups=False' and no specified 'security_groups', will not
|
|
||||||
# result in an update to the port where the default security group is
|
|
||||||
# applied.
|
|
||||||
if module.params['no_security_groups'] and port['security_groups'] != []:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if module.params['fixed_ips'] is not None:
|
|
||||||
for item in module.params['fixed_ips']:
|
|
||||||
if 'ip_address' in item:
|
|
||||||
# if ip_address in request does not match any in existing port,
|
|
||||||
# update is required.
|
|
||||||
if not any(match['ip_address'] == item['ip_address']
|
|
||||||
for match in port['fixed_ips']):
|
|
||||||
return True
|
|
||||||
if 'subnet_id' in item:
|
|
||||||
return True
|
|
||||||
for item in port['fixed_ips']:
|
|
||||||
# if ip_address in existing port does not match any in request,
|
|
||||||
# update is required.
|
|
||||||
if not any(match.get('ip_address') == item['ip_address']
|
|
||||||
for match in module.params['fixed_ips']):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(module, port, cloud):
|
|
||||||
state = module.params['state']
|
|
||||||
if state == 'present':
|
|
||||||
if not port:
|
|
||||||
return True
|
|
||||||
return _needs_update(module, port, cloud)
|
|
||||||
if state == 'absent' and port:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _compose_port_args(module, cloud):
|
|
||||||
port_kwargs = {}
|
|
||||||
optional_parameters = ['name',
|
|
||||||
'fixed_ips',
|
|
||||||
'admin_state_up',
|
|
||||||
'mac_address',
|
|
||||||
'security_groups',
|
|
||||||
'allowed_address_pairs',
|
|
||||||
'extra_dhcp_opts',
|
|
||||||
'device_owner',
|
|
||||||
'device_id',
|
|
||||||
'binding:vnic_type',
|
|
||||||
'port_security_enabled']
|
|
||||||
for optional_param in optional_parameters:
|
|
||||||
if module.params[optional_param] is not None:
|
|
||||||
port_kwargs[optional_param] = module.params[optional_param]
|
|
||||||
|
|
||||||
if module.params['no_security_groups']:
|
|
||||||
port_kwargs['security_groups'] = []
|
|
||||||
|
|
||||||
return port_kwargs
|
|
||||||
|
|
||||||
|
|
||||||
def get_security_group_id(module, cloud, security_group_name_or_id):
|
|
||||||
security_group = cloud.get_security_group(security_group_name_or_id)
|
|
||||||
if not security_group:
|
|
||||||
module.fail_json(msg="Security group: %s, was not found"
|
|
||||||
% security_group_name_or_id)
|
|
||||||
return security_group['id']
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
network=dict(required=False),
|
network=dict(required=False),
|
||||||
name=dict(required=False),
|
name=dict(required=False),
|
||||||
fixed_ips=dict(type='list', default=None, elements='dict'),
|
fixed_ips=dict(type='list', default=None, elements='dict'),
|
||||||
@@ -393,81 +301,197 @@ def main():
|
|||||||
vnic_type=dict(default=None,
|
vnic_type=dict(default=None,
|
||||||
choices=['normal', 'direct', 'direct-physical',
|
choices=['normal', 'direct', 'direct-physical',
|
||||||
'macvtap', 'baremetal', 'virtio-forwarder']),
|
'macvtap', 'baremetal', 'virtio-forwarder']),
|
||||||
port_security_enabled=dict(default=None, type='bool')
|
port_security_enabled=dict(default=None, type='bool'),
|
||||||
|
binding_profile=dict(default=None, type='dict')
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
['no_security_groups', 'security_groups'],
|
['no_security_groups', 'security_groups'],
|
||||||
]
|
],
|
||||||
|
supports_check_mode=True
|
||||||
)
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec,
|
def _needs_update(self, port):
|
||||||
supports_check_mode=True,
|
"""Check for differences in the updatable values.
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
if not HAS_ORDEREDDICT:
|
NOTE: We don't currently allow name updates.
|
||||||
module.fail_json(msg=missing_required_lib('ordereddict'))
|
"""
|
||||||
|
compare_simple = ['admin_state_up',
|
||||||
|
'mac_address',
|
||||||
|
'device_owner',
|
||||||
|
'device_id',
|
||||||
|
'binding:vnic_type',
|
||||||
|
'port_security_enabled',
|
||||||
|
'binding:profile']
|
||||||
|
compare_list_dict = ['allowed_address_pairs',
|
||||||
|
'extra_dhcp_opts']
|
||||||
|
compare_list = ['security_groups']
|
||||||
|
|
||||||
name = module.params['name']
|
for key in compare_simple:
|
||||||
state = module.params['state']
|
if self.params[key] is not None and self.params[key] != port[key]:
|
||||||
|
return True
|
||||||
|
for key in compare_list:
|
||||||
|
if (
|
||||||
|
self.params[key] is not None
|
||||||
|
and set(self.params[key]) != set(port[key])
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
for key in compare_list_dict:
|
||||||
try:
|
if not self.params[key]:
|
||||||
if module.params['security_groups']:
|
if port.get(key):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.params[key]:
|
||||||
|
if not port.get(key):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# sort dicts in list
|
||||||
|
port_ordered = [OrderedDict(sorted(d.items())) for d in port[key]]
|
||||||
|
param_ordered = [OrderedDict(sorted(d.items())) for d in self.params[key]]
|
||||||
|
|
||||||
|
for d in param_ordered:
|
||||||
|
if d not in port_ordered:
|
||||||
|
return True
|
||||||
|
|
||||||
|
for d in port_ordered:
|
||||||
|
if d not in param_ordered:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# NOTE: if port was created or updated with 'no_security_groups=True',
|
||||||
|
# subsequent updates without 'no_security_groups' flag or
|
||||||
|
# 'no_security_groups=False' and no specified 'security_groups', will not
|
||||||
|
# result in an update to the port where the default security group is
|
||||||
|
# applied.
|
||||||
|
if self.params['no_security_groups'] and port['security_groups'] != []:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.params['fixed_ips'] is not None:
|
||||||
|
for item in self.params['fixed_ips']:
|
||||||
|
if 'ip_address' in item:
|
||||||
|
# if ip_address in request does not match any in existing port,
|
||||||
|
# update is required.
|
||||||
|
if not any(match['ip_address'] == item['ip_address']
|
||||||
|
for match in port['fixed_ips']):
|
||||||
|
return True
|
||||||
|
if 'subnet_id' in item:
|
||||||
|
return True
|
||||||
|
for item in port['fixed_ips']:
|
||||||
|
# if ip_address in existing port does not match any in request,
|
||||||
|
# update is required.
|
||||||
|
if not any(match.get('ip_address') == item['ip_address']
|
||||||
|
for match in self.params['fixed_ips']):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _system_state_change(self, port):
|
||||||
|
state = self.params['state']
|
||||||
|
if state == 'present':
|
||||||
|
if not port:
|
||||||
|
return True
|
||||||
|
return self._needs_update(port)
|
||||||
|
if state == 'absent' and port:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _compose_port_args(self):
|
||||||
|
port_kwargs = {}
|
||||||
|
optional_parameters = ['name',
|
||||||
|
'fixed_ips',
|
||||||
|
'admin_state_up',
|
||||||
|
'mac_address',
|
||||||
|
'security_groups',
|
||||||
|
'allowed_address_pairs',
|
||||||
|
'extra_dhcp_opts',
|
||||||
|
'device_owner',
|
||||||
|
'device_id',
|
||||||
|
'binding:vnic_type',
|
||||||
|
'port_security_enabled',
|
||||||
|
'binding:profile']
|
||||||
|
for optional_param in optional_parameters:
|
||||||
|
if self.params[optional_param] is not None:
|
||||||
|
port_kwargs[optional_param] = self.params[optional_param]
|
||||||
|
|
||||||
|
if self.params['no_security_groups']:
|
||||||
|
port_kwargs['security_groups'] = []
|
||||||
|
|
||||||
|
return port_kwargs
|
||||||
|
|
||||||
|
def get_security_group_id(self, security_group_name_or_id):
|
||||||
|
security_group = self.conn.get_security_group(security_group_name_or_id)
|
||||||
|
if not security_group:
|
||||||
|
self.fail_json(msg="Security group: %s, was not found"
|
||||||
|
% security_group_name_or_id)
|
||||||
|
return security_group['id']
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if not HAS_ORDEREDDICT:
|
||||||
|
self.fail_json(msg=missing_required_lib('ordereddict'))
|
||||||
|
|
||||||
|
name = self.params['name']
|
||||||
|
state = self.params['state']
|
||||||
|
|
||||||
|
if self.params['security_groups']:
|
||||||
# translate security_groups to UUID's if names where provided
|
# translate security_groups to UUID's if names where provided
|
||||||
module.params['security_groups'] = [
|
self.params['security_groups'] = [
|
||||||
get_security_group_id(module, cloud, v)
|
self.get_security_group_id(v)
|
||||||
for v in module.params['security_groups']
|
for v in self.params['security_groups']
|
||||||
]
|
]
|
||||||
|
|
||||||
# Neutron API accept 'binding:vnic_type' as an argument
|
# Neutron API accept 'binding:vnic_type' as an argument
|
||||||
# for the port type.
|
# for the port type.
|
||||||
module.params['binding:vnic_type'] = module.params.pop('vnic_type')
|
self.params['binding:vnic_type'] = self.params.pop('vnic_type')
|
||||||
|
# Neutron API accept 'binding:profile' as an argument
|
||||||
|
# for the port binding profile type.
|
||||||
|
self.params['binding:profile'] = self.params.pop('binding_profile')
|
||||||
|
|
||||||
port = None
|
port = None
|
||||||
network_id = None
|
network_id = None
|
||||||
if name:
|
if name:
|
||||||
port = cloud.get_port(name)
|
port = self.conn.get_port(name)
|
||||||
|
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(module, port, cloud))
|
self.exit_json(changed=self._system_state_change(port))
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if not port:
|
if not port:
|
||||||
network = module.params['network']
|
network = self.params['network']
|
||||||
if not network:
|
if not network:
|
||||||
module.fail_json(
|
self.fail_json(
|
||||||
msg="Parameter 'network' is required in Port Create"
|
msg="Parameter 'network' is required in Port Create"
|
||||||
)
|
)
|
||||||
port_kwargs = _compose_port_args(module, cloud)
|
port_kwargs = self._compose_port_args()
|
||||||
network_object = cloud.get_network(network)
|
network_object = self.conn.get_network(network)
|
||||||
|
|
||||||
if network_object:
|
if network_object:
|
||||||
network_id = network_object['id']
|
network_id = network_object['id']
|
||||||
else:
|
else:
|
||||||
module.fail_json(
|
self.fail_json(
|
||||||
msg="Specified network was not found."
|
msg="Specified network was not found."
|
||||||
)
|
)
|
||||||
|
|
||||||
port = cloud.create_port(network_id, **port_kwargs)
|
port = self.conn.create_port(network_id, **port_kwargs)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
if _needs_update(module, port, cloud):
|
if self._needs_update(port):
|
||||||
port_kwargs = _compose_port_args(module, cloud)
|
port_kwargs = self._compose_port_args()
|
||||||
port = cloud.update_port(port['id'], **port_kwargs)
|
port = self.conn.update_port(port['id'], **port_kwargs)
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed, id=port['id'], port=port)
|
self.exit_json(changed=changed, id=port['id'], port=port)
|
||||||
|
|
||||||
if state == 'absent':
|
if state == 'absent':
|
||||||
if port:
|
if port:
|
||||||
cloud.delete_port(port['id'])
|
self.conn.delete_port(port['id'])
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = NetworkPortModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -179,38 +179,31 @@ openstack_ports:
|
|||||||
sample: "51fce036d7984ba6af4f6c849f65ef00"
|
sample: "51fce036d7984ba6af4f6c849f65ef00"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class NetworkPortInfoModule(OpenStackModule):
|
||||||
argument_spec = openstack_full_argument_spec(
|
argument_spec = dict(
|
||||||
port=dict(required=False),
|
port=dict(required=False),
|
||||||
filters=dict(type='dict', required=False),
|
filters=dict(type='dict', required=False),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
supports_check_mode=True
|
||||||
is_old_facts = module._name == 'openstack.cloud.port_facts'
|
)
|
||||||
if is_old_facts:
|
|
||||||
module.deprecate("The 'openstack.cloud.port_facts' module has been renamed to 'openstack.cloud.port_info', "
|
|
||||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
|
||||||
|
|
||||||
port = module.params.get('port')
|
deprecated_names = ('openstack.cloud.port_facts')
|
||||||
filters = module.params.get('filters')
|
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def run(self):
|
||||||
try:
|
port = self.params.get('port')
|
||||||
ports = cloud.search_ports(port, filters)
|
filters = self.params.get('filters')
|
||||||
if is_old_facts:
|
|
||||||
module.exit_json(changed=False, ansible_facts=dict(
|
|
||||||
openstack_ports=ports))
|
|
||||||
else:
|
|
||||||
module.exit_json(changed=False, openstack_ports=ports)
|
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
ports = self.conn.search_ports(port, filters)
|
||||||
module.fail_json(msg=str(e))
|
self.exit_json(changed=False, openstack_ports=ports)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = NetworkPortInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ options:
|
|||||||
- Additional properties to be associated with this project. Requires
|
- Additional properties to be associated with this project. Requires
|
||||||
openstacksdk>0.45.
|
openstacksdk>0.45.
|
||||||
type: dict
|
type: dict
|
||||||
default: {}
|
required: false
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- Should the resource be present or absent.
|
- Should the resource be present or absent.
|
||||||
@@ -99,133 +99,121 @@ project:
|
|||||||
sample: True
|
sample: True
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _needs_update(module, project):
|
class IdentityProjectModule(OpenStackModule):
|
||||||
keys = ('description', 'enabled')
|
argument_spec = dict(
|
||||||
for key in keys:
|
|
||||||
if module.params[key] is not None and module.params[key] != project.get(key):
|
|
||||||
return True
|
|
||||||
|
|
||||||
properties = module.params['properties']
|
|
||||||
if properties:
|
|
||||||
project_properties = project.get('properties')
|
|
||||||
for k, v in properties.items():
|
|
||||||
if v is not None and v != project_properties[k]:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(module, project):
|
|
||||||
state = module.params['state']
|
|
||||||
if state == 'present':
|
|
||||||
if project is None:
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
if _needs_update(module, project):
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
elif state == 'absent':
|
|
||||||
if project is None:
|
|
||||||
changed = False
|
|
||||||
else:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
return changed
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
description=dict(required=False, default=None),
|
description=dict(required=False),
|
||||||
domain_id=dict(required=False, default=None, aliases=['domain']),
|
domain_id=dict(required=False, aliases=['domain']),
|
||||||
properties=dict(type='dict', default={}),
|
properties=dict(required=False, type='dict', min_ver='0.45.1'),
|
||||||
enabled=dict(default=True, type='bool'),
|
enabled=dict(default=True, type='bool'),
|
||||||
state=dict(default='present', choices=['absent', 'present'])
|
state=dict(default='present', choices=['absent', 'present'])
|
||||||
)
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
module_kwargs = openstack_module_kwargs()
|
supports_check_mode=True
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
**module_kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
name = module.params['name']
|
def _needs_update(self, project):
|
||||||
description = module.params['description']
|
keys = ('description', 'enabled')
|
||||||
domain = module.params.get('domain_id')
|
for key in keys:
|
||||||
enabled = module.params['enabled']
|
if self.params[key] is not None and self.params[key] != project.get(key):
|
||||||
properties = module.params['properties']
|
return True
|
||||||
state = module.params['state']
|
|
||||||
|
|
||||||
min_version = None
|
properties = self.params['properties']
|
||||||
|
if properties:
|
||||||
|
project_properties = project.get('properties')
|
||||||
|
for k, v in properties.items():
|
||||||
|
if v is not None and (k not in project_properties or v != project_properties[k]):
|
||||||
|
return True
|
||||||
|
|
||||||
if properties:
|
return False
|
||||||
min_version = '0.45.1'
|
|
||||||
|
def _system_state_change(self, project):
|
||||||
|
state = self.params['state']
|
||||||
|
if state == 'present':
|
||||||
|
if project is None:
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
if self._needs_update(project):
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
elif state == 'absent':
|
||||||
|
changed = project is not None
|
||||||
|
|
||||||
|
return changed
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
name = self.params['name']
|
||||||
|
description = self.params['description']
|
||||||
|
domain = self.params['domain_id']
|
||||||
|
enabled = self.params['enabled']
|
||||||
|
properties = self.params['properties'] or {}
|
||||||
|
state = self.params['state']
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module, min_version)
|
|
||||||
try:
|
|
||||||
if domain:
|
if domain:
|
||||||
try:
|
try:
|
||||||
# We assume admin is passing domain id
|
# We assume admin is passing domain id
|
||||||
dom = cloud.get_domain(domain)['id']
|
dom = self.conn.get_domain(domain)['id']
|
||||||
domain = dom
|
domain = dom
|
||||||
except Exception:
|
except Exception:
|
||||||
# If we fail, maybe admin is passing a domain name.
|
# If we fail, maybe admin is passing a domain name.
|
||||||
# Note that domains have unique names, just like id.
|
# Note that domains have unique names, just like id.
|
||||||
try:
|
try:
|
||||||
dom = cloud.search_domains(filters={'name': domain})[0]['id']
|
dom = self.conn.search_domains(filters={'name': domain})[0]['id']
|
||||||
domain = dom
|
domain = dom
|
||||||
except Exception:
|
except Exception:
|
||||||
# Ok, let's hope the user is non-admin and passing a sane id
|
# Ok, let's hope the user is non-admin and passing a sane id
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if domain:
|
if domain:
|
||||||
project = cloud.get_project(name, domain_id=domain)
|
project = self.conn.get_project(name, domain_id=domain)
|
||||||
else:
|
else:
|
||||||
project = cloud.get_project(name)
|
project = self.conn.get_project(name)
|
||||||
|
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(module, project))
|
self.exit_json(changed=self._system_state_change(project))
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if project is None:
|
if project is None:
|
||||||
project = cloud.create_project(
|
project = self.conn.create_project(
|
||||||
name=name, description=description,
|
name=name, description=description,
|
||||||
domain_id=domain,
|
domain_id=domain,
|
||||||
enabled=enabled)
|
enabled=enabled)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
project = cloud.update_project(
|
project = self.conn.update_project(
|
||||||
project['id'], description=description,
|
project['id'],
|
||||||
enabled=enabled, **properties)
|
description=description,
|
||||||
|
enabled=enabled,
|
||||||
|
**properties)
|
||||||
else:
|
else:
|
||||||
if _needs_update(module, project):
|
if self._needs_update(project):
|
||||||
project = cloud.update_project(
|
project = self.conn.update_project(
|
||||||
project['id'], description=description,
|
project['id'],
|
||||||
enabled=enabled, **properties)
|
description=description,
|
||||||
|
enabled=enabled,
|
||||||
|
**properties)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
module.exit_json(changed=changed, project=project)
|
self.exit_json(changed=changed, project=project)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if project is None:
|
if project is None:
|
||||||
changed = False
|
changed = False
|
||||||
else:
|
else:
|
||||||
cloud.delete_project(project['id'])
|
self.conn.delete_project(project['id'])
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=e.message, extra_data=e.extra_data)
|
def main():
|
||||||
|
module = IdentityProjectModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -91,49 +91,41 @@ flavor:
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class IdentityProjectAccess(OpenStackModule):
|
||||||
argument_spec = openstack_full_argument_spec(
|
argument_spec = dict(
|
||||||
state=dict(required=False, default='present',
|
state=dict(required=False, default='present',
|
||||||
choices=['absent', 'present']),
|
choices=['absent', 'present']),
|
||||||
|
|
||||||
target_project_id=dict(required=True, type='str'),
|
target_project_id=dict(required=True, type='str'),
|
||||||
resource_type=dict(required=True, type='str'),
|
resource_type=dict(required=True, type='str'),
|
||||||
resource_name=dict(required=True, type='str'),
|
resource_name=dict(required=True, type='str'),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
required_if=[
|
required_if=[
|
||||||
('state', 'present', ['target_project_id'])
|
('state', 'present', ['target_project_id'])
|
||||||
],
|
]
|
||||||
**module_kwargs)
|
)
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def run(self):
|
||||||
|
state = self.params['state']
|
||||||
|
resource_name = self.params['resource_name']
|
||||||
|
resource_type = self.params['resource_type']
|
||||||
|
target_project_id = self.params['target_project_id']
|
||||||
|
|
||||||
state = module.params['state']
|
|
||||||
resource_name = module.params['resource_name']
|
|
||||||
resource_type = module.params['resource_type']
|
|
||||||
target_project_id = module.params['target_project_id']
|
|
||||||
|
|
||||||
try:
|
|
||||||
if resource_type == 'nova_flavor':
|
if resource_type == 'nova_flavor':
|
||||||
# returns Munch({'NAME_ATTR': 'name',
|
# returns Munch({'NAME_ATTR': 'name',
|
||||||
# 'tenant_id': u'37e55da59ec842649d84230f3a24eed5',
|
# 'tenant_id': u'37e55da59ec842649d84230f3a24eed5',
|
||||||
# 'HUMAN_ID': False,
|
# 'HUMAN_ID': False,
|
||||||
# 'flavor_id': u'6d4d37b9-0480-4a8c-b8c9-f77deaad73f9',
|
# 'flavor_id': u'6d4d37b9-0480-4a8c-b8c9-f77deaad73f9',
|
||||||
# 'request_ids': [], 'human_id': None}),
|
# 'request_ids': [], 'human_id': None}),
|
||||||
_get_resource = cloud.get_flavor
|
_get_resource = self.conn.get_flavor
|
||||||
_list_resource_access = cloud.list_flavor_access
|
_list_resource_access = self.conn.list_flavor_access
|
||||||
_add_resource_access = cloud.add_flavor_access
|
_add_resource_access = self.conn.add_flavor_access
|
||||||
_remove_resource_access = cloud.remove_flavor_access
|
_remove_resource_access = self.conn.remove_flavor_access
|
||||||
elif resource_type == 'cinder_volume_type':
|
elif resource_type == 'cinder_volume_type':
|
||||||
# returns [Munch({
|
# returns [Munch({
|
||||||
# 'project_id': u'178cdb9955b047eea7afbe582038dc94',
|
# 'project_id': u'178cdb9955b047eea7afbe582038dc94',
|
||||||
@@ -141,41 +133,43 @@ def main():
|
|||||||
# 'human_id': None,
|
# 'human_id': None,
|
||||||
# 'HUMAN_ID': False},
|
# 'HUMAN_ID': False},
|
||||||
# 'id': u'd5573023-b290-42c8-b232-7c5ca493667f'}),
|
# 'id': u'd5573023-b290-42c8-b232-7c5ca493667f'}),
|
||||||
_get_resource = cloud.get_volume_type
|
_get_resource = self.conn.get_volume_type
|
||||||
_list_resource_access = cloud.get_volume_type_access
|
_list_resource_access = self.conn.get_volume_type_access
|
||||||
_add_resource_access = cloud.add_volume_type_access
|
_add_resource_access = self.conn.add_volume_type_access
|
||||||
_remove_resource_access = cloud.remove_volume_type_access
|
_remove_resource_access = self.conn.remove_volume_type_access
|
||||||
else:
|
else:
|
||||||
module.exit_json(changed=False,
|
self.exit_json(
|
||||||
resource_name=resource_name,
|
changed=False,
|
||||||
resource_type=resource_type,
|
resource_name=resource_name,
|
||||||
error="Not implemented.")
|
resource_type=resource_type,
|
||||||
|
error="Not implemented.")
|
||||||
|
|
||||||
resource = _get_resource(resource_name)
|
resource = _get_resource(resource_name)
|
||||||
if not resource:
|
if not resource:
|
||||||
module.exit_json(changed=False,
|
self.exit_json(
|
||||||
resource_name=resource_name,
|
changed=False,
|
||||||
resource_type=resource_type,
|
resource_name=resource_name,
|
||||||
error="Not found.")
|
resource_type=resource_type,
|
||||||
|
error="Not found.")
|
||||||
resource_id = getattr(resource, 'id', resource['id'])
|
resource_id = getattr(resource, 'id', resource['id'])
|
||||||
# _list_resource_access returns a list of dicts containing 'project_id'
|
# _list_resource_access returns a list of dicts containing 'project_id'
|
||||||
acls = _list_resource_access(resource_id)
|
acls = _list_resource_access(resource_id)
|
||||||
|
|
||||||
if not all(acl.get('project_id') for acl in acls):
|
if not all(acl.get('project_id') for acl in acls):
|
||||||
module.exit_json(changed=False,
|
self.exit_json(
|
||||||
resource_name=resource_name,
|
changed=False,
|
||||||
resource_type=resource_type,
|
resource_name=resource_name,
|
||||||
error="Missing project_id in resource output.")
|
resource_type=resource_type,
|
||||||
|
error="Missing project_id in resource output.")
|
||||||
allowed_tenants = [acl['project_id'] for acl in acls]
|
allowed_tenants = [acl['project_id'] for acl in acls]
|
||||||
|
|
||||||
changed_access = any((
|
changed_access = any((
|
||||||
state == 'present' and target_project_id not in allowed_tenants,
|
state == 'present' and target_project_id not in allowed_tenants,
|
||||||
state == 'absent' and target_project_id in allowed_tenants
|
state == 'absent' and target_project_id in allowed_tenants
|
||||||
))
|
))
|
||||||
if module.check_mode or not changed_access:
|
if self.ansible.check_mode or not changed_access:
|
||||||
module.exit_json(changed=changed_access,
|
self.exit_json(
|
||||||
resource=resource,
|
changed=changed_access, resource=resource, id=resource_id)
|
||||||
id=resource_id)
|
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
_add_resource_access(
|
_add_resource_access(
|
||||||
@@ -186,12 +180,13 @@ def main():
|
|||||||
resource_id, target_project_id
|
resource_id, target_project_id
|
||||||
)
|
)
|
||||||
|
|
||||||
module.exit_json(changed=True,
|
self.exit_json(
|
||||||
resource=resource,
|
changed=True, resource=resource, id=resource_id)
|
||||||
id=resource_id)
|
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), **module.params)
|
def main():
|
||||||
|
module = IdentityProjectAccess()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -99,58 +99,57 @@ openstack_projects:
|
|||||||
type: bool
|
type: bool
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, openstack_cloud_from_module
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class IdentityProjectInfoModule(OpenStackModule):
|
||||||
|
deprecated_names = ('project_facts', 'openstack.cloud.project_facts')
|
||||||
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
argument_spec = dict(
|
||||||
name=dict(required=False, default=None),
|
name=dict(required=False),
|
||||||
domain=dict(required=False, default=None),
|
domain=dict(required=False),
|
||||||
filters=dict(required=False, type='dict', default=None),
|
filters=dict(required=False, type='dict'),
|
||||||
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
)
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec)
|
def run(self):
|
||||||
is_old_facts = module._name == 'openstack.cloud.project_facts'
|
name = self.params['name']
|
||||||
if is_old_facts:
|
domain = self.params['domain']
|
||||||
module.deprecate("The 'openstack.cloud.project_facts' module has been renamed to 'openstack.cloud.project_info', "
|
filters = self.params['filters']
|
||||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
is_old_facts = self.module_name == 'openstack.cloud.project_facts'
|
||||||
|
|
||||||
sdk, opcloud = openstack_cloud_from_module(module)
|
|
||||||
try:
|
|
||||||
name = module.params['name']
|
|
||||||
domain = module.params['domain']
|
|
||||||
filters = module.params['filters']
|
|
||||||
|
|
||||||
if domain:
|
if domain:
|
||||||
try:
|
try:
|
||||||
# We assume admin is passing domain id
|
# We assume admin is passing domain id
|
||||||
dom = opcloud.get_domain(domain)['id']
|
dom = self.conn.get_domain(domain)['id']
|
||||||
domain = dom
|
domain = dom
|
||||||
except Exception:
|
except Exception:
|
||||||
# If we fail, maybe admin is passing a domain name.
|
# If we fail, maybe admin is passing a domain name.
|
||||||
# Note that domains have unique names, just like id.
|
# Note that domains have unique names, just like id.
|
||||||
dom = opcloud.search_domains(filters={'name': domain})
|
dom = self.conn.search_domains(filters={'name': domain})
|
||||||
if dom:
|
if dom:
|
||||||
domain = dom[0]['id']
|
domain = dom[0]['id']
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg='Domain name or ID does not exist')
|
self.fail_json(msg='Domain name or ID does not exist')
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = {}
|
filters = {}
|
||||||
|
|
||||||
filters['domain_id'] = domain
|
filters['domain_id'] = domain
|
||||||
|
|
||||||
projects = opcloud.search_projects(name, filters)
|
projects = self.conn.search_projects(name, filters)
|
||||||
if is_old_facts:
|
if is_old_facts:
|
||||||
module.exit_json(changed=False, ansible_facts=dict(
|
self.exit_json(changed=False, ansible_facts=dict(
|
||||||
openstack_projects=projects))
|
openstack_projects=projects))
|
||||||
else:
|
else:
|
||||||
module.exit_json(changed=False, openstack_projects=projects)
|
self.exit_json(changed=False, openstack_projects=projects)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityProjectInfoModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ options:
|
|||||||
loadbalancer:
|
loadbalancer:
|
||||||
description: Number of load balancers to allow.
|
description: Number of load balancers to allow.
|
||||||
type: int
|
type: int
|
||||||
|
metadata_items:
|
||||||
|
description: Number of metadata items allowed per instance.
|
||||||
|
type: int
|
||||||
network:
|
network:
|
||||||
description: Number of networks to allow.
|
description: Number of networks to allow.
|
||||||
type: int
|
type: int
|
||||||
@@ -183,6 +186,7 @@ EXAMPLES = '''
|
|||||||
instances: "{{ item.instances }}"
|
instances: "{{ item.instances }}"
|
||||||
key_pairs: "{{ item.key_pairs }}"
|
key_pairs: "{{ item.key_pairs }}"
|
||||||
loadbalancer: "{{ item.loadbalancer }}"
|
loadbalancer: "{{ item.loadbalancer }}"
|
||||||
|
metadata_items: "{{ item.metadata_items }}"
|
||||||
per_volume_gigabytes: "{{ item.per_volume_gigabytes }}"
|
per_volume_gigabytes: "{{ item.per_volume_gigabytes }}"
|
||||||
pool: "{{ item.pool }}"
|
pool: "{{ item.pool }}"
|
||||||
port: "{{ item.port }}"
|
port: "{{ item.port }}"
|
||||||
@@ -256,117 +260,11 @@ openstack_quotas:
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import traceback
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
KEYSTONEAUTH1_IMP_ERR = None
|
|
||||||
try:
|
|
||||||
from keystoneauth1 import exceptions as ksa_exceptions
|
|
||||||
HAS_KEYSTONEAUTH1 = True
|
|
||||||
except ImportError:
|
|
||||||
KEYSTONEAUTH1_IMP_ERR = traceback.format_exc()
|
|
||||||
HAS_KEYSTONEAUTH1 = False
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
|
||||||
openstack_full_argument_spec,
|
|
||||||
openstack_cloud_from_module,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_volume_quotas(cloud, project):
|
class QuotaModule(OpenStackModule):
|
||||||
|
argument_spec = dict(
|
||||||
return cloud.get_volume_quotas(project)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_network_quotas(cloud, project):
|
|
||||||
|
|
||||||
return cloud.get_network_quotas(project)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_compute_quotas(cloud, project):
|
|
||||||
|
|
||||||
return cloud.get_compute_quotas(project)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_quotas(sdk, module, cloud, project):
|
|
||||||
|
|
||||||
quota = {}
|
|
||||||
try:
|
|
||||||
quota['volume'] = _get_volume_quotas(cloud, project)
|
|
||||||
except ksa_exceptions.EndpointNotFound:
|
|
||||||
module.warn("No public endpoint for volumev2 service was found. Ignoring volume quotas.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
quota['network'] = _get_network_quotas(cloud, project)
|
|
||||||
except ksa_exceptions.EndpointNotFound:
|
|
||||||
module.warn("No public endpoint for network service was found. Ignoring network quotas.")
|
|
||||||
|
|
||||||
quota['compute'] = _get_compute_quotas(cloud, project)
|
|
||||||
|
|
||||||
for quota_type in quota.keys():
|
|
||||||
quota[quota_type] = _scrub_results(quota[quota_type])
|
|
||||||
|
|
||||||
return quota
|
|
||||||
|
|
||||||
|
|
||||||
def _scrub_results(quota):
|
|
||||||
|
|
||||||
filter_attr = [
|
|
||||||
'HUMAN_ID',
|
|
||||||
'NAME_ATTR',
|
|
||||||
'human_id',
|
|
||||||
'request_ids',
|
|
||||||
'x_openstack_request_ids',
|
|
||||||
]
|
|
||||||
|
|
||||||
for attr in filter_attr:
|
|
||||||
if attr in quota:
|
|
||||||
del quota[attr]
|
|
||||||
|
|
||||||
return quota
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change_details(module, project_quota_output):
|
|
||||||
|
|
||||||
quota_change_request = {}
|
|
||||||
changes_required = False
|
|
||||||
|
|
||||||
for quota_type in project_quota_output.keys():
|
|
||||||
for quota_option in project_quota_output[quota_type].keys():
|
|
||||||
if quota_option in module.params and module.params[quota_option] is not None:
|
|
||||||
if project_quota_output[quota_type][quota_option] != module.params[quota_option]:
|
|
||||||
changes_required = True
|
|
||||||
|
|
||||||
if quota_type not in quota_change_request:
|
|
||||||
quota_change_request[quota_type] = {}
|
|
||||||
|
|
||||||
quota_change_request[quota_type][quota_option] = module.params[quota_option]
|
|
||||||
|
|
||||||
return (changes_required, quota_change_request)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(module, project_quota_output):
|
|
||||||
"""
|
|
||||||
Determine if changes are required to the current project quota.
|
|
||||||
|
|
||||||
This is done by comparing the current project_quota_output against
|
|
||||||
the desired quota settings set on the module params.
|
|
||||||
"""
|
|
||||||
|
|
||||||
changes_required, quota_change_request = _system_state_change_details(
|
|
||||||
module,
|
|
||||||
project_quota_output
|
|
||||||
)
|
|
||||||
|
|
||||||
if changes_required:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
backup_gigabytes=dict(required=False, type='int', default=None),
|
backup_gigabytes=dict(required=False, type='int', default=None),
|
||||||
@@ -381,8 +279,9 @@ def main():
|
|||||||
injected_files=dict(required=False, type='int', default=None),
|
injected_files=dict(required=False, type='int', default=None),
|
||||||
injected_path_size=dict(required=False, type='int', default=None),
|
injected_path_size=dict(required=False, type='int', default=None),
|
||||||
instances=dict(required=False, type='int', default=None),
|
instances=dict(required=False, type='int', default=None),
|
||||||
key_pairs=dict(required=False, type='int', default=None),
|
key_pairs=dict(required=False, type='int', default=None, no_log=False),
|
||||||
loadbalancer=dict(required=False, type='int', default=None),
|
loadbalancer=dict(required=False, type='int', default=None),
|
||||||
|
metadata_items=dict(required=False, type='int', default=None),
|
||||||
network=dict(required=False, type='int', default=None),
|
network=dict(required=False, type='int', default=None),
|
||||||
per_volume_gigabytes=dict(required=False, type='int', default=None),
|
per_volume_gigabytes=dict(required=False, type='int', default=None),
|
||||||
pool=dict(required=False, type='int', default=None),
|
pool=dict(required=False, type='int', default=None),
|
||||||
@@ -404,16 +303,89 @@ def main():
|
|||||||
volumes_types=dict(required=False, type='dict', default={})
|
volumes_types=dict(required=False, type='dict', default={})
|
||||||
)
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec,
|
module_kwargs = dict(
|
||||||
supports_check_mode=True
|
supports_check_mode=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if not HAS_KEYSTONEAUTH1:
|
def _get_volume_quotas(self, project):
|
||||||
module.fail_json(msg=missing_required_lib("keystoneauth1"), exception=KEYSTONEAUTH1_IMP_ERR)
|
return self.conn.get_volume_quotas(project)
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
def _get_network_quotas(self, project):
|
||||||
try:
|
return self.conn.get_network_quotas(project)
|
||||||
cloud_params = dict(module.params)
|
|
||||||
|
def _get_compute_quotas(self, project):
|
||||||
|
return self.conn.get_compute_quotas(project)
|
||||||
|
|
||||||
|
def _get_quotas(self, project):
|
||||||
|
quota = {}
|
||||||
|
try:
|
||||||
|
quota['volume'] = self._get_volume_quotas(project)
|
||||||
|
except Exception:
|
||||||
|
self.warn("No public endpoint for volumev2 service was found. Ignoring volume quotas.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
quota['network'] = self._get_network_quotas(project)
|
||||||
|
except Exception:
|
||||||
|
self.warn("No public endpoint for network service was found. Ignoring network quotas.")
|
||||||
|
|
||||||
|
quota['compute'] = self._get_compute_quotas(project)
|
||||||
|
|
||||||
|
for quota_type in quota.keys():
|
||||||
|
quota[quota_type] = self._scrub_results(quota[quota_type])
|
||||||
|
|
||||||
|
return quota
|
||||||
|
|
||||||
|
def _scrub_results(self, quota):
|
||||||
|
filter_attr = [
|
||||||
|
'HUMAN_ID',
|
||||||
|
'NAME_ATTR',
|
||||||
|
'human_id',
|
||||||
|
'request_ids',
|
||||||
|
'x_openstack_request_ids',
|
||||||
|
]
|
||||||
|
|
||||||
|
for attr in filter_attr:
|
||||||
|
if attr in quota:
|
||||||
|
del quota[attr]
|
||||||
|
|
||||||
|
return quota
|
||||||
|
|
||||||
|
def _system_state_change_details(self, project_quota_output):
|
||||||
|
quota_change_request = {}
|
||||||
|
changes_required = False
|
||||||
|
|
||||||
|
for quota_type in project_quota_output.keys():
|
||||||
|
for quota_option in project_quota_output[quota_type].keys():
|
||||||
|
if quota_option in self.params and self.params[quota_option] is not None:
|
||||||
|
if project_quota_output[quota_type][quota_option] != self.params[quota_option]:
|
||||||
|
changes_required = True
|
||||||
|
|
||||||
|
if quota_type not in quota_change_request:
|
||||||
|
quota_change_request[quota_type] = {}
|
||||||
|
|
||||||
|
quota_change_request[quota_type][quota_option] = self.params[quota_option]
|
||||||
|
|
||||||
|
return (changes_required, quota_change_request)
|
||||||
|
|
||||||
|
def _system_state_change(self, project_quota_output):
|
||||||
|
"""
|
||||||
|
Determine if changes are required to the current project quota.
|
||||||
|
|
||||||
|
This is done by comparing the current project_quota_output against
|
||||||
|
the desired quota settings set on the module params.
|
||||||
|
"""
|
||||||
|
|
||||||
|
changes_required, quota_change_request = self._system_state_change_details(
|
||||||
|
project_quota_output
|
||||||
|
)
|
||||||
|
|
||||||
|
if changes_required:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
cloud_params = dict(self.params)
|
||||||
|
|
||||||
# In order to handle the different volume types we update module params after.
|
# In order to handle the different volume types we update module params after.
|
||||||
dynamic_types = [
|
dynamic_types = [
|
||||||
@@ -423,20 +395,19 @@ def main():
|
|||||||
]
|
]
|
||||||
|
|
||||||
for dynamic_type in dynamic_types:
|
for dynamic_type in dynamic_types:
|
||||||
for k, v in module.params[dynamic_type].items():
|
for k, v in self.params[dynamic_type].items():
|
||||||
module.params[k] = int(v)
|
self.params[k] = int(v)
|
||||||
|
|
||||||
# Get current quota values
|
# Get current quota values
|
||||||
project_quota_output = _get_quotas(
|
project_quota_output = self._get_quotas(cloud_params['name'])
|
||||||
sdk, module, cloud, cloud_params['name'])
|
|
||||||
changes_required = False
|
changes_required = False
|
||||||
|
|
||||||
if module.params['state'] == "absent":
|
if self.params['state'] == "absent":
|
||||||
# If a quota state is set to absent we should assume there will be changes.
|
# If a quota state is set to absent we should assume there will be changes.
|
||||||
# The default quota values are not accessible so we can not determine if
|
# The default quota values are not accessible so we can not determine if
|
||||||
# no changes will occur or not.
|
# no changes will occur or not.
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=True)
|
self.exit_json(changed=True)
|
||||||
|
|
||||||
# Calling delete_network_quotas when a quota has not been set results
|
# Calling delete_network_quotas when a quota has not been set results
|
||||||
# in an error, according to the sdk docs it should return the
|
# in an error, according to the sdk docs it should return the
|
||||||
@@ -447,49 +418,48 @@ def main():
|
|||||||
neutron_msg2 = "could not be found"
|
neutron_msg2 = "could not be found"
|
||||||
|
|
||||||
for quota_type in project_quota_output.keys():
|
for quota_type in project_quota_output.keys():
|
||||||
quota_call = getattr(cloud, 'delete_%s_quotas' % (quota_type))
|
quota_call = getattr(self.conn, 'delete_%s_quotas' % (quota_type))
|
||||||
try:
|
try:
|
||||||
quota_call(cloud_params['name'])
|
quota_call(cloud_params['name'])
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
except Exception as e:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
if error_msg.find(neutron_msg1) > -1 and error_msg.find(neutron_msg2) > -1:
|
if error_msg.find(neutron_msg1) > -1 and error_msg.find(neutron_msg2) > -1:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
self.fail_json(msg=str(e), extra_data=e.extra_data)
|
||||||
|
|
||||||
project_quota_output = _get_quotas(
|
project_quota_output = self._get_quotas(cloud_params['name'])
|
||||||
sdk, module, cloud, cloud_params['name'])
|
|
||||||
changes_required = True
|
changes_required = True
|
||||||
|
|
||||||
elif module.params['state'] == "present":
|
elif self.params['state'] == "present":
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(module, project_quota_output))
|
self.exit_json(changed=self._system_state_change(
|
||||||
|
project_quota_output))
|
||||||
|
|
||||||
changes_required, quota_change_request = _system_state_change_details(
|
changes_required, quota_change_request = self._system_state_change_details(
|
||||||
module,
|
|
||||||
project_quota_output
|
project_quota_output
|
||||||
)
|
)
|
||||||
|
|
||||||
if changes_required:
|
if changes_required:
|
||||||
for quota_type in quota_change_request.keys():
|
for quota_type in quota_change_request.keys():
|
||||||
quota_call = getattr(cloud, 'set_%s_quotas' % (quota_type))
|
quota_call = getattr(self.conn, 'set_%s_quotas' % (quota_type))
|
||||||
quota_call(cloud_params['name'], **quota_change_request[quota_type])
|
quota_call(cloud_params['name'], **quota_change_request[quota_type])
|
||||||
|
|
||||||
# Get quota state post changes for validation
|
# Get quota state post changes for validation
|
||||||
project_quota_update = _get_quotas(
|
project_quota_update = self._get_quotas(cloud_params['name'])
|
||||||
sdk, module, cloud, cloud_params['name'])
|
|
||||||
|
|
||||||
if project_quota_output == project_quota_update:
|
if project_quota_output == project_quota_update:
|
||||||
module.fail_json(msg='Could not apply quota update')
|
self.fail_json(msg='Could not apply quota update')
|
||||||
|
|
||||||
project_quota_output = project_quota_update
|
project_quota_output = project_quota_update
|
||||||
|
|
||||||
module.exit_json(changed=changes_required,
|
self.exit_json(
|
||||||
openstack_quotas=project_quota_output
|
changed=changes_required, openstack_quotas=project_quota_output)
|
||||||
)
|
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
def main():
|
||||||
|
module = QuotaModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ EXAMPLES = '''
|
|||||||
state: present
|
state: present
|
||||||
zone: example.net.
|
zone: example.net.
|
||||||
name: www.example.net.
|
name: www.example.net.
|
||||||
|
recordset_type: "a"
|
||||||
|
records: ['10.1.1.1']
|
||||||
ttl: 7200
|
ttl: 7200
|
||||||
|
|
||||||
# Delete recordset named "www.example.net."
|
# Delete recordset named "www.example.net."
|
||||||
@@ -120,29 +122,11 @@ recordset:
|
|||||||
sample: ['10.0.0.1']
|
sample: ['10.0.0.1']
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(state, records, description, ttl, recordset):
|
class DnsRecordsetModule(OpenStackModule):
|
||||||
if state == 'present':
|
argument_spec = dict(
|
||||||
if recordset is None:
|
|
||||||
return True
|
|
||||||
if records is not None and recordset['records'] != records:
|
|
||||||
return True
|
|
||||||
if description is not None and recordset['description'] != description:
|
|
||||||
return True
|
|
||||||
if ttl is not None and recordset['ttl'] != ttl:
|
|
||||||
return True
|
|
||||||
if state == 'absent' and recordset:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
zone=dict(required=True),
|
zone=dict(required=True),
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
recordset_type=dict(required=False, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
|
recordset_type=dict(required=False, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
|
||||||
@@ -152,85 +136,102 @@ def main():
|
|||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs()
|
module_kwargs = dict(
|
||||||
module = AnsibleModule(argument_spec,
|
required_if=[
|
||||||
required_if=[
|
('state', 'present',
|
||||||
('state', 'present',
|
['recordset_type', 'records'])],
|
||||||
['recordset_type', 'records'])],
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
module.module_min_sdk_version = '0.28.0'
|
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)
|
def _system_state_change(self, state, records, description, ttl, recordset):
|
||||||
recordsets = cloud.search_recordsets(zone, name_or_id=name)
|
if state == 'present':
|
||||||
|
if recordset is None:
|
||||||
|
return True
|
||||||
|
if records is not None and recordset['records'] != records:
|
||||||
|
return True
|
||||||
|
if description is not None and recordset['description'] != description:
|
||||||
|
return True
|
||||||
|
if ttl is not None and recordset['ttl'] != ttl:
|
||||||
|
return True
|
||||||
|
if state == 'absent' and recordset:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
if recordsets:
|
def run(self):
|
||||||
recordset = recordsets[0]
|
zone = self.params.get('zone')
|
||||||
try:
|
name = self.params.get('name')
|
||||||
recordset_id = recordset['id']
|
state = self.params.get('state')
|
||||||
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 state == 'present':
|
recordsets = self.conn.search_recordsets(zone, name_or_id=name)
|
||||||
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 recordsets:
|
||||||
if description:
|
recordset = recordsets[0]
|
||||||
kwargs['description'] = description
|
try:
|
||||||
kwargs['records'] = records
|
recordset_id = recordset['id']
|
||||||
|
except KeyError as e:
|
||||||
|
self.fail_json(msg=str(e))
|
||||||
|
else:
|
||||||
|
# recordsets is filtered by type and should never be more than 1 return
|
||||||
|
recordset = None
|
||||||
|
|
||||||
if module.check_mode:
|
if state == 'present':
|
||||||
module.exit_json(changed=_system_state_change(state,
|
recordset_type = self.params.get('recordset_type').upper()
|
||||||
records, description,
|
records = self.params.get('records')
|
||||||
ttl, recordset))
|
description = self.params.get('description')
|
||||||
|
ttl = self.params.get('ttl')
|
||||||
|
|
||||||
if recordset is None:
|
kwargs = {}
|
||||||
if ttl:
|
if description:
|
||||||
kwargs['ttl'] = ttl
|
kwargs['description'] = description
|
||||||
|
kwargs['records'] = records
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(
|
||||||
|
changed=self._system_state_change(
|
||||||
|
state, records, description, ttl, recordset))
|
||||||
|
|
||||||
|
if recordset is None:
|
||||||
|
if ttl:
|
||||||
|
kwargs['ttl'] = ttl
|
||||||
|
else:
|
||||||
|
kwargs['ttl'] = 300
|
||||||
|
|
||||||
|
recordset = self.conn.create_recordset(
|
||||||
|
zone=zone, name=name, recordset_type=recordset_type,
|
||||||
|
**kwargs)
|
||||||
|
changed = True
|
||||||
else:
|
else:
|
||||||
kwargs['ttl'] = 300
|
|
||||||
|
|
||||||
recordset = cloud.create_recordset(
|
if ttl:
|
||||||
zone=zone, name=name, recordset_type=recordset_type,
|
kwargs['ttl'] = ttl
|
||||||
**kwargs)
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
|
|
||||||
if ttl:
|
pre_update_recordset = recordset
|
||||||
kwargs['ttl'] = ttl
|
changed = self._system_state_change(
|
||||||
|
state, records, description, ttl, pre_update_recordset)
|
||||||
|
if changed:
|
||||||
|
recordset = self.conn.update_recordset(
|
||||||
|
zone=zone, name_or_id=recordset_id, **kwargs)
|
||||||
|
|
||||||
pre_update_recordset = recordset
|
self.exit_json(changed=changed, 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)
|
|
||||||
|
|
||||||
module.exit_json(changed=changed, recordset=recordset)
|
elif state == 'absent':
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(changed=self._system_state_change(
|
||||||
|
state, None, None, None, recordset))
|
||||||
|
|
||||||
elif state == 'absent':
|
if recordset is None:
|
||||||
if module.check_mode:
|
changed = False
|
||||||
module.exit_json(changed=_system_state_change(state,
|
else:
|
||||||
None, None,
|
self.conn.delete_recordset(zone, recordset_id)
|
||||||
None, recordset))
|
changed = True
|
||||||
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
if recordset is None:
|
|
||||||
changed = False
|
def main():
|
||||||
else:
|
module = DnsRecordsetModule()
|
||||||
cloud.delete_recordset(zone, recordset_id)
|
module()
|
||||||
changed = True
|
|
||||||
module.exit_json(changed=changed)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -72,35 +72,11 @@ RETURN = '''
|
|||||||
#
|
#
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(state, assignment):
|
class IdentityRoleAssignmentModule(OpenStackModule):
|
||||||
if state == 'present' and not assignment:
|
argument_spec = dict(
|
||||||
return True
|
|
||||||
elif state == 'absent' and assignment:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _build_kwargs(user, group, project, domain):
|
|
||||||
kwargs = {}
|
|
||||||
if user:
|
|
||||||
kwargs['user'] = user
|
|
||||||
if group:
|
|
||||||
kwargs['group'] = group
|
|
||||||
if project:
|
|
||||||
kwargs['project'] = project
|
|
||||||
if domain:
|
|
||||||
kwargs['domain'] = domain
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
role=dict(required=True),
|
role=dict(required=True),
|
||||||
user=dict(required=False),
|
user=dict(required=False),
|
||||||
group=dict(required=False),
|
group=dict(required=False),
|
||||||
@@ -109,92 +85,111 @@ def main():
|
|||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
|
|
||||||
module_kwargs = openstack_module_kwargs(
|
module_kwargs = dict(
|
||||||
required_one_of=[
|
required_one_of=[
|
||||||
['user', 'group']
|
['user', 'group']
|
||||||
])
|
],
|
||||||
module = AnsibleModule(argument_spec,
|
supports_check_mode=True
|
||||||
supports_check_mode=True,
|
)
|
||||||
**module_kwargs)
|
|
||||||
|
|
||||||
role = module.params.get('role')
|
def _system_state_change(self, state, assignment):
|
||||||
user = module.params.get('user')
|
if state == 'present' and not assignment:
|
||||||
group = module.params.get('group')
|
return True
|
||||||
project = module.params.get('project')
|
elif state == 'absent' and assignment:
|
||||||
domain = module.params.get('domain')
|
return True
|
||||||
state = module.params.get('state')
|
return False
|
||||||
|
|
||||||
|
def _build_kwargs(self, user, group, project, domain):
|
||||||
|
kwargs = {}
|
||||||
|
if user:
|
||||||
|
kwargs['user'] = user
|
||||||
|
if group:
|
||||||
|
kwargs['group'] = group
|
||||||
|
if project:
|
||||||
|
kwargs['project'] = project
|
||||||
|
if domain:
|
||||||
|
kwargs['domain'] = domain
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
role = self.params.get('role')
|
||||||
|
user = self.params.get('user')
|
||||||
|
group = self.params.get('group')
|
||||||
|
project = self.params.get('project')
|
||||||
|
domain = self.params.get('domain')
|
||||||
|
state = self.params.get('state')
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
|
||||||
try:
|
|
||||||
filters = {}
|
filters = {}
|
||||||
domain_id = None
|
domain_id = None
|
||||||
|
|
||||||
r = cloud.get_role(role)
|
r = self.conn.get_role(role)
|
||||||
if r is None:
|
if r is None:
|
||||||
module.fail_json(msg="Role %s is not valid" % role)
|
self.fail_json(msg="Role %s is not valid" % role)
|
||||||
filters['role'] = r['id']
|
filters['role'] = r['id']
|
||||||
|
|
||||||
if domain:
|
if domain:
|
||||||
d = cloud.get_domain(name_or_id=domain)
|
d = self.conn.get_domain(name_or_id=domain)
|
||||||
if d is None:
|
if d is None:
|
||||||
module.fail_json(msg="Domain %s is not valid" % domain)
|
self.fail_json(msg="Domain %s is not valid" % domain)
|
||||||
filters['domain'] = d['id']
|
filters['domain'] = d['id']
|
||||||
domain_id = d['id']
|
domain_id = d['id']
|
||||||
if user:
|
if user:
|
||||||
if domain:
|
if domain:
|
||||||
u = cloud.get_user(user, domain_id=filters['domain'])
|
u = self.conn.get_user(user, domain_id=filters['domain'])
|
||||||
else:
|
else:
|
||||||
u = cloud.get_user(user)
|
u = self.conn.get_user(user)
|
||||||
|
|
||||||
if u is None:
|
if u is None:
|
||||||
module.fail_json(msg="User %s is not valid" % user)
|
self.fail_json(msg="User %s is not valid" % user)
|
||||||
filters['user'] = u['id']
|
filters['user'] = u['id']
|
||||||
if group:
|
if group:
|
||||||
if domain:
|
if domain:
|
||||||
g = cloud.get_group(group, domain_id=filters['domain'])
|
g = self.conn.get_group(group, domain_id=filters['domain'])
|
||||||
else:
|
else:
|
||||||
g = cloud.get_group(group)
|
g = self.conn.get_group(group)
|
||||||
if g is None:
|
if g is None:
|
||||||
module.fail_json(msg="Group %s is not valid" % group)
|
self.fail_json(msg="Group %s is not valid" % group)
|
||||||
filters['group'] = g['id']
|
filters['group'] = g['id']
|
||||||
if project:
|
if project:
|
||||||
if domain:
|
if domain:
|
||||||
p = cloud.get_project(project, domain_id=filters['domain'])
|
p = self.conn.get_project(project, domain_id=filters['domain'])
|
||||||
# OpenStack won't allow us to use both a domain and project as
|
# OpenStack won't allow us to use both a domain and project as
|
||||||
# filter. Once we identified the project (using the domain as
|
# filter. Once we identified the project (using the domain as
|
||||||
# a filter criteria), we need to remove the domain itself from
|
# a filter criteria), we need to remove the domain itself from
|
||||||
# the filters list.
|
# the filters list.
|
||||||
domain_id = filters.pop('domain')
|
domain_id = filters.pop('domain')
|
||||||
else:
|
else:
|
||||||
p = cloud.get_project(project)
|
p = self.conn.get_project(project)
|
||||||
|
|
||||||
if p is None:
|
if p is None:
|
||||||
module.fail_json(msg="Project %s is not valid" % project)
|
self.fail_json(msg="Project %s is not valid" % project)
|
||||||
filters['project'] = p['id']
|
filters['project'] = p['id']
|
||||||
|
|
||||||
assignment = cloud.list_role_assignments(filters=filters)
|
assignment = self.conn.list_role_assignments(filters=filters)
|
||||||
|
|
||||||
if module.check_mode:
|
if self.ansible.check_mode:
|
||||||
module.exit_json(changed=_system_state_change(state, assignment))
|
self.exit_json(changed=self._system_state_change(state, assignment))
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if not assignment:
|
if not assignment:
|
||||||
kwargs = _build_kwargs(user, group, project, domain_id)
|
kwargs = self._build_kwargs(user, group, project, domain_id)
|
||||||
cloud.grant_role(role, **kwargs)
|
self.conn.grant_role(role, **kwargs)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if assignment:
|
if assignment:
|
||||||
kwargs = _build_kwargs(user, group, project, domain_id)
|
kwargs = self._build_kwargs(user, group, project, domain_id)
|
||||||
cloud.revoke_role(role, **kwargs)
|
self.conn.revoke_role(role, **kwargs)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e))
|
def main():
|
||||||
|
module = IdentityRoleAssignmentModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -211,13 +211,7 @@ router:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
import itertools
|
||||||
|
|
||||||
ROUTER_INTERFACE_OWNERS = set([
|
|
||||||
'network:router_interface',
|
|
||||||
'network:router_interface_distributed',
|
|
||||||
'network:ha_router_replicated_interface'
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class RouterModule(OpenStackModule):
|
class RouterModule(OpenStackModule):
|
||||||
@@ -232,14 +226,17 @@ class RouterModule(OpenStackModule):
|
|||||||
project=dict(default=None)
|
project=dict(default=None)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _router_internal_interfaces(self, router):
|
def _get_subnet_ids_from_ports(self, ports):
|
||||||
for port in self.conn.list_router_interfaces(router, 'internal'):
|
return [fixed_ip['subnet_id'] for fixed_ip in
|
||||||
if port['device_owner'] in ROUTER_INTERFACE_OWNERS:
|
itertools.chain.from_iterable(port['fixed_ips'] for port in ports if 'fixed_ips' in port)]
|
||||||
yield port
|
|
||||||
|
|
||||||
def _needs_update(self, router, network, internal_subnet_ids, internal_port_ids, filters=None):
|
def _needs_update(self, router, net,
|
||||||
"""Decide if the given router needs an update.
|
missing_port_ids,
|
||||||
"""
|
requested_subnet_ids,
|
||||||
|
existing_subnet_ids,
|
||||||
|
router_ifs_cfg,
|
||||||
|
filters=None):
|
||||||
|
"""Decide if the given router needs an update."""
|
||||||
if router['admin_state_up'] != self.params['admin_state_up']:
|
if router['admin_state_up'] != self.params['admin_state_up']:
|
||||||
return True
|
return True
|
||||||
if router['external_gateway_info']:
|
if router['external_gateway_info']:
|
||||||
@@ -247,68 +244,76 @@ class RouterModule(OpenStackModule):
|
|||||||
if self.params['enable_snat'] is not None:
|
if self.params['enable_snat'] is not None:
|
||||||
if router['external_gateway_info'].get('enable_snat', True) != self.params['enable_snat']:
|
if router['external_gateway_info'].get('enable_snat', True) != self.params['enable_snat']:
|
||||||
return True
|
return True
|
||||||
if network:
|
if net:
|
||||||
if not router['external_gateway_info']:
|
if not router['external_gateway_info']:
|
||||||
return True
|
return True
|
||||||
elif router['external_gateway_info']['network_id'] != network['id']:
|
elif router['external_gateway_info']['network_id'] != net['id']:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# check external interfaces
|
# check if external_fixed_ip has to be added
|
||||||
if self.params['external_fixed_ips']:
|
for external_fixed_ip in router_ifs_cfg['external_fixed_ips']:
|
||||||
for new_iface in self.params['external_fixed_ips']:
|
exists = False
|
||||||
subnet = self.conn.get_subnet(new_iface['subnet'], filters)
|
|
||||||
exists = False
|
|
||||||
|
|
||||||
# compare the requested interface with existing, looking for an existing match
|
# compare the requested interface with existing, looking for an existing match
|
||||||
for existing_iface in router['external_gateway_info']['external_fixed_ips']:
|
for existing_if in router['external_gateway_info']['external_fixed_ips']:
|
||||||
if existing_iface['subnet_id'] == subnet['id']:
|
if existing_if['subnet_id'] == external_fixed_ip['subnet_id']:
|
||||||
if 'ip' in new_iface:
|
if 'ip' in external_fixed_ip:
|
||||||
if existing_iface['ip_address'] == new_iface['ip']:
|
if existing_if['ip_address'] == external_fixed_ip['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 if external_fixed_ip has to be removed
|
||||||
|
if router_ifs_cfg['external_fixed_ips']:
|
||||||
|
for external_fixed_ip in router['external_gateway_info']['external_fixed_ips']:
|
||||||
|
obsolete = True
|
||||||
|
|
||||||
|
# compare the existing interface with requested, looking for an requested match
|
||||||
|
for requested_if in router_ifs_cfg['external_fixed_ips']:
|
||||||
|
if external_fixed_ip['subnet_id'] == requested_if['subnet_id']:
|
||||||
|
if 'ip' in requested_if:
|
||||||
|
if external_fixed_ip['ip_address'] == requested_if['ip']:
|
||||||
# both subnet id and ip address match
|
# both subnet id and ip address match
|
||||||
exists = True
|
obsolete = False
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# only the subnet was given, so ip doesn't matter
|
# only the subnet was given, so ip doesn't matter
|
||||||
exists = True
|
obsolete = False
|
||||||
break
|
break
|
||||||
|
|
||||||
# this interface isn't present on the existing router
|
# this interface isn't present on the existing router
|
||||||
if not exists:
|
if obsolete:
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
# check internal interfaces
|
# no external fixed ips requested
|
||||||
if self.params['interfaces']:
|
if router['external_gateway_info'] \
|
||||||
existing_subnet_ids = []
|
and router['external_gateway_info']['external_fixed_ips'] \
|
||||||
for port in self._router_internal_interfaces(router):
|
and len(router['external_gateway_info']['external_fixed_ips']) > 1:
|
||||||
if 'fixed_ips' in port:
|
# but router has several external fixed ips
|
||||||
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 True
|
||||||
|
|
||||||
return False
|
# check if internal port has to be added
|
||||||
|
if router_ifs_cfg['internal_ports_missing']:
|
||||||
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
|
return True
|
||||||
if state == 'present':
|
|
||||||
if not router:
|
if missing_port_ids:
|
||||||
return True
|
return True
|
||||||
return self._needs_update(router, network, internal_ids, internal_portids, filters)
|
|
||||||
|
# check if internal subnet has to be added or removed
|
||||||
|
if set(requested_subnet_ids) != set(existing_subnet_ids):
|
||||||
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _build_kwargs(self, router, network):
|
def _build_kwargs(self, router, net):
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'admin_state_up': self.params['admin_state_up'],
|
'admin_state_up': self.params['admin_state_up'],
|
||||||
}
|
}
|
||||||
@@ -318,8 +323,8 @@ class RouterModule(OpenStackModule):
|
|||||||
else:
|
else:
|
||||||
kwargs['name'] = self.params['name']
|
kwargs['name'] = self.params['name']
|
||||||
|
|
||||||
if network:
|
if net:
|
||||||
kwargs['ext_gateway_net_id'] = network['id']
|
kwargs['ext_gateway_net_id'] = net['id']
|
||||||
# can't send enable_snat unless we have a network
|
# can't send enable_snat unless we have a network
|
||||||
if self.params.get('enable_snat') is not None:
|
if self.params.get('enable_snat') is not None:
|
||||||
kwargs['enable_snat'] = self.params['enable_snat']
|
kwargs['enable_snat'] = self.params['enable_snat']
|
||||||
@@ -332,56 +337,83 @@ class RouterModule(OpenStackModule):
|
|||||||
if 'ip' in iface:
|
if 'ip' in iface:
|
||||||
d['ip_address'] = iface['ip']
|
d['ip_address'] = iface['ip']
|
||||||
kwargs['ext_fixed_ips'].append(d)
|
kwargs['ext_fixed_ips'].append(d)
|
||||||
|
else:
|
||||||
|
# no external fixed ips requested
|
||||||
|
if router \
|
||||||
|
and router['external_gateway_info'] \
|
||||||
|
and router['external_gateway_info']['external_fixed_ips'] \
|
||||||
|
and len(router['external_gateway_info']['external_fixed_ips']) > 1:
|
||||||
|
# but router has several external fixed ips
|
||||||
|
# keep first external fixed ip only
|
||||||
|
fip = router['external_gateway_info']['external_fixed_ips'][0]
|
||||||
|
kwargs['ext_fixed_ips'] = [fip]
|
||||||
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def _validate_subnets(self, filters=None):
|
def _build_router_interface_config(self, filters=None):
|
||||||
external_subnet_ids = []
|
external_fixed_ips = []
|
||||||
internal_subnet_ids = []
|
internal_subnets = []
|
||||||
internal_port_ids = []
|
internal_ports = []
|
||||||
existing_port_ips = []
|
internal_ports_missing = []
|
||||||
|
|
||||||
|
# Build external interface configuration
|
||||||
if self.params['external_fixed_ips']:
|
if self.params['external_fixed_ips']:
|
||||||
for iface in self.params['external_fixed_ips']:
|
for iface in self.params['external_fixed_ips']:
|
||||||
subnet = self.conn.get_subnet(iface['subnet'])
|
subnet = self.conn.get_subnet(iface['subnet'], filters)
|
||||||
if not subnet:
|
if not subnet:
|
||||||
self.fail_json(msg='subnet %s not found' % iface['subnet'])
|
self.fail(msg='subnet %s not found' % iface['subnet'])
|
||||||
external_subnet_ids.append(subnet['id'])
|
new_external_fixed_ip = {'subnet_name': subnet.name, 'subnet_id': subnet.id}
|
||||||
|
if 'ip' in iface:
|
||||||
|
new_external_fixed_ip['ip'] = iface['ip']
|
||||||
|
external_fixed_ips.append(new_external_fixed_ip)
|
||||||
|
|
||||||
|
# Build internal interface configuration
|
||||||
if self.params['interfaces']:
|
if self.params['interfaces']:
|
||||||
|
internal_ips = []
|
||||||
for iface in self.params['interfaces']:
|
for iface in self.params['interfaces']:
|
||||||
if isinstance(iface, str):
|
if isinstance(iface, str):
|
||||||
subnet = self.conn.get_subnet(iface, filters)
|
subnet = self.conn.get_subnet(iface, filters)
|
||||||
if not subnet:
|
if not subnet:
|
||||||
self.fail(msg='subnet %s not found' % iface)
|
self.fail(msg='subnet %s not found' % iface)
|
||||||
internal_subnet_ids.append(subnet['id'])
|
internal_subnets.append(subnet)
|
||||||
|
|
||||||
elif isinstance(iface, dict):
|
elif isinstance(iface, dict):
|
||||||
subnet = self.conn.get_subnet(iface['subnet'], filters)
|
subnet = self.conn.get_subnet(iface['subnet'], filters)
|
||||||
if not subnet:
|
if not subnet:
|
||||||
self.fail(msg='subnet %s not found' % iface['subnet'])
|
self.fail(msg='subnet %s not found' % iface['subnet'])
|
||||||
|
|
||||||
net = self.conn.get_network(iface['net'])
|
net = self.conn.get_network(iface['net'])
|
||||||
if not net:
|
if not net:
|
||||||
self.fail(msg='net %s not found' % iface['net'])
|
self.fail(msg='net %s not found' % iface['net'])
|
||||||
|
|
||||||
if "portip" not in iface:
|
if "portip" not in iface:
|
||||||
internal_subnet_ids.append(subnet['id'])
|
# portip not set, add any ip from subnet
|
||||||
|
internal_subnets.append(subnet)
|
||||||
elif not iface['portip']:
|
elif not iface['portip']:
|
||||||
self.fail(msg='put an ip in portip or remove it from list to assign default port to router')
|
# portip is set but has invalid value
|
||||||
|
self.fail(msg='put an ip in portip or remove it from list to assign default port to router')
|
||||||
else:
|
else:
|
||||||
|
# portip has valid value
|
||||||
|
# look for ports whose fixed_ips.ip_address matchs portip
|
||||||
for existing_port in self.conn.list_ports(filters={'network_id': net.id}):
|
for existing_port in self.conn.list_ports(filters={'network_id': net.id}):
|
||||||
for fixed_ip in existing_port['fixed_ips']:
|
for fixed_ip in existing_port['fixed_ips']:
|
||||||
if iface['portip'] == fixed_ip['ip_address']:
|
if iface['portip'] == fixed_ip['ip_address']:
|
||||||
internal_port_ids.append(existing_port.id)
|
# portip exists in net already
|
||||||
existing_port_ips.append(fixed_ip['ip_address'])
|
internal_ports.append(existing_port)
|
||||||
if iface['portip'] not in existing_port_ips:
|
internal_ips.append(fixed_ip['ip_address'])
|
||||||
p = self.conn.create_port(network_id=net.id, fixed_ips=[
|
if iface['portip'] not in internal_ips:
|
||||||
{
|
# no port with portip exists hence create a new port
|
||||||
'ip_address': iface['portip'],
|
internal_ports_missing.append({
|
||||||
'subnet_id': subnet.id
|
'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
|
return {
|
||||||
|
'external_fixed_ips': external_fixed_ips,
|
||||||
|
'internal_subnets': internal_subnets,
|
||||||
|
'internal_ports': internal_ports,
|
||||||
|
'internal_ports_missing': internal_ports_missing
|
||||||
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
@@ -391,7 +423,7 @@ class RouterModule(OpenStackModule):
|
|||||||
project = self.params['project']
|
project = self.params['project']
|
||||||
|
|
||||||
if self.params['external_fixed_ips'] and not network:
|
if self.params['external_fixed_ips'] and not network:
|
||||||
self.fail_json(msg='network is required when supplying external_fixed_ips')
|
self.fail(msg='network is required when supplying external_fixed_ips')
|
||||||
|
|
||||||
if project is not None:
|
if project is not None:
|
||||||
proj = self.conn.get_project(project)
|
proj = self.conn.get_project(project)
|
||||||
@@ -412,67 +444,125 @@ class RouterModule(OpenStackModule):
|
|||||||
|
|
||||||
# Validate and cache the subnet IDs so we can avoid duplicate checks
|
# Validate and cache the subnet IDs so we can avoid duplicate checks
|
||||||
# and expensive API calls.
|
# and expensive API calls.
|
||||||
external_ids, subnet_internal_ids, internal_portids = self._validate_subnets(filters)
|
router_ifs_cfg = self._build_router_interface_config(filters)
|
||||||
|
requested_subnet_ids = [subnet.id for subnet in router_ifs_cfg['internal_subnets']] + \
|
||||||
|
self._get_subnet_ids_from_ports(router_ifs_cfg['internal_ports'])
|
||||||
|
requested_port_ids = [i['id'] for i in router_ifs_cfg['internal_ports']]
|
||||||
|
|
||||||
|
if router:
|
||||||
|
router_ifs_internal = self.conn.list_router_interfaces(router, 'internal')
|
||||||
|
existing_subnet_ids = self._get_subnet_ids_from_ports(router_ifs_internal)
|
||||||
|
obsolete_subnet_ids = set(existing_subnet_ids) - set(requested_subnet_ids)
|
||||||
|
existing_port_ids = [i['id'] for i in router_ifs_internal]
|
||||||
|
|
||||||
|
else:
|
||||||
|
router_ifs_internal = []
|
||||||
|
existing_subnet_ids = []
|
||||||
|
obsolete_subnet_ids = []
|
||||||
|
existing_port_ids = []
|
||||||
|
|
||||||
|
missing_port_ids = set(requested_port_ids) - set(existing_port_ids)
|
||||||
|
|
||||||
if self.ansible.check_mode:
|
if self.ansible.check_mode:
|
||||||
self.exit_json(
|
# Check if the system state would be changed
|
||||||
changed=self._system_state_change(router, net, subnet_internal_ids, internal_portids, filters)
|
if state == 'absent' and router:
|
||||||
)
|
changed = True
|
||||||
|
elif state == 'absent' and not router:
|
||||||
|
changed = False
|
||||||
|
elif state == 'present' and not router:
|
||||||
|
changed = True
|
||||||
|
else: # if state == 'present' and router
|
||||||
|
changed = self._needs_update(router, net,
|
||||||
|
missing_port_ids,
|
||||||
|
requested_subnet_ids,
|
||||||
|
existing_subnet_ids,
|
||||||
|
router_ifs_cfg,
|
||||||
|
filters)
|
||||||
|
self.exit_json(changed=changed)
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
if not router:
|
if not router:
|
||||||
|
changed = True
|
||||||
|
|
||||||
kwargs = self._build_kwargs(router, net)
|
kwargs = self._build_kwargs(router, net)
|
||||||
if project_id:
|
if project_id:
|
||||||
kwargs['project_id'] = project_id
|
kwargs['project_id'] = project_id
|
||||||
router = self.conn.create_router(**kwargs)
|
router = self.conn.create_router(**kwargs)
|
||||||
for int_s_id in subnet_internal_ids:
|
|
||||||
self.conn.add_router_interface(router, subnet_id=int_s_id)
|
# add interface by subnet id, because user did not specify a port id
|
||||||
# add interface by port id as well
|
for subnet in router_ifs_cfg['internal_subnets']:
|
||||||
for int_p_id in internal_portids:
|
self.conn.add_router_interface(router, subnet_id=subnet.id)
|
||||||
self.conn.add_router_interface(router, port_id=int_p_id)
|
|
||||||
changed = True
|
# add interface by port id if user did specify a valid port id
|
||||||
|
for port in router_ifs_cfg['internal_ports']:
|
||||||
|
self.conn.add_router_interface(router, port_id=port.id)
|
||||||
|
|
||||||
|
# add port and interface if user did specify an ip address but port is missing yet
|
||||||
|
for missing_internal_port in router_ifs_cfg['internal_ports_missing']:
|
||||||
|
p = self.conn.create_port(**missing_internal_port)
|
||||||
|
if p:
|
||||||
|
self.conn.add_router_interface(router, port_id=p.id)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if self._needs_update(router, net, subnet_internal_ids, internal_portids, filters):
|
if self._needs_update(router, net,
|
||||||
|
missing_port_ids,
|
||||||
|
requested_subnet_ids,
|
||||||
|
existing_subnet_ids,
|
||||||
|
router_ifs_cfg,
|
||||||
|
filters):
|
||||||
|
changed = True
|
||||||
kwargs = self._build_kwargs(router, net)
|
kwargs = self._build_kwargs(router, net)
|
||||||
updated_router = self.conn.update_router(**kwargs)
|
updated_router = self.conn.update_router(**kwargs)
|
||||||
|
|
||||||
# Protect against update_router() not actually
|
# Protect against update_router() not actually updating the router.
|
||||||
# updating the router.
|
|
||||||
if not updated_router:
|
if not updated_router:
|
||||||
changed = False
|
changed = False
|
||||||
|
else:
|
||||||
# On a router update, if any internal interfaces were supplied,
|
|
||||||
# just detach all existing internal interfaces and attach the new.
|
|
||||||
if internal_portids or subnet_internal_ids:
|
|
||||||
router = updated_router
|
router = updated_router
|
||||||
ports = self._router_internal_interfaces(router)
|
|
||||||
for port in ports:
|
# delete internal subnets i.e. ports
|
||||||
self.conn.remove_router_interface(router, port_id=port['id'])
|
if obsolete_subnet_ids:
|
||||||
if internal_portids:
|
for port in router_ifs_internal:
|
||||||
external_ids, subnet_internal_ids, internal_portids = self._validate_subnets(filters)
|
if 'fixed_ips' in port:
|
||||||
for int_p_id in internal_portids:
|
for fip in port['fixed_ips']:
|
||||||
self.conn.add_router_interface(router, port_id=int_p_id)
|
if fip['subnet_id'] in obsolete_subnet_ids:
|
||||||
changed = True
|
self.conn.remove_router_interface(router, port_id=port['id'])
|
||||||
if subnet_internal_ids:
|
changed = True
|
||||||
for s_id in subnet_internal_ids:
|
|
||||||
self.conn.add_router_interface(router, subnet_id=s_id)
|
# add new internal interface by subnet id, because user did not specify a port id
|
||||||
|
for subnet in router_ifs_cfg['internal_subnets']:
|
||||||
|
if subnet.id not in existing_subnet_ids:
|
||||||
|
self.conn.add_router_interface(router, subnet_id=subnet.id)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
# add new internal interface by port id if user did specify a valid port id
|
||||||
|
for port_id in missing_port_ids:
|
||||||
|
self.conn.add_router_interface(router, port_id=port_id)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
self.exit(changed=changed, router=router, id=router['id'])
|
# add new port and new internal interface if user did specify an ip address but port is missing yet
|
||||||
|
for missing_internal_port in router_ifs_cfg['internal_ports_missing']:
|
||||||
|
p = self.conn.create_port(**missing_internal_port)
|
||||||
|
if p:
|
||||||
|
self.conn.add_router_interface(router, port_id=p.id)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
self.exit_json(changed=changed, router=router)
|
||||||
|
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
if not router:
|
if not router:
|
||||||
self.exit(changed=False)
|
self.exit_json(changed=False)
|
||||||
else:
|
else:
|
||||||
# We need to detach all internal interfaces on a router before
|
# We need to detach all internal interfaces on a router
|
||||||
# we will be allowed to delete it.
|
# before we will be allowed to delete it. Deletion can
|
||||||
ports = self._router_internal_interfaces(router)
|
# still fail if e.g. floating ips are attached to the
|
||||||
router_id = router['id']
|
# router.
|
||||||
for port in ports:
|
for port in router_ifs_internal:
|
||||||
self.conn.remove_router_interface(router, port_id=port['id'])
|
self.conn.remove_router_interface(router, port_id=port['id'])
|
||||||
self.conn.delete_router(router_id)
|
self.conn.delete_router(router['id'])
|
||||||
self.exit_json(changed=True)
|
self.exit_json(changed=True, router=router)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -155,6 +155,9 @@ class RouterInfoModule(OpenStackModule):
|
|||||||
name=dict(required=False, default=None),
|
name=dict(required=False, default=None),
|
||||||
filters=dict(required=False, type='dict', default=None)
|
filters=dict(required=False, type='dict', default=None)
|
||||||
)
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
|
|||||||
196
plugins/modules/security_group_info.py
Normal file
196
plugins/modules/security_group_info.py
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
#!/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')
|
||||||
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
@@ -19,8 +19,7 @@ options:
|
|||||||
type: str
|
type: str
|
||||||
protocol:
|
protocol:
|
||||||
description:
|
description:
|
||||||
- IP protocols ANY TCP UDP ICMP 112 (VRRP) 132 (SCTP)
|
- IP protocols ANY TCP UDP ICMP and others, also number in range 0-255
|
||||||
choices: ['any', 'tcp', 'udp', 'icmp', '112', '132', None]
|
|
||||||
type: str
|
type: str
|
||||||
port_range_min:
|
port_range_min:
|
||||||
description:
|
description:
|
||||||
@@ -203,7 +202,7 @@ def _ports_match(protocol, module_min, module_max, rule_min, rule_max):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Check if the user is supplying -1 for ICMP.
|
# Check if the user is supplying -1 for ICMP.
|
||||||
if protocol == 'icmp':
|
if protocol in ['icmp', 'ipv6-icmp']:
|
||||||
if module_min and int(module_min) == -1:
|
if module_min and int(module_min) == -1:
|
||||||
module_min = None
|
module_min = None
|
||||||
if module_max and int(module_max) == -1:
|
if module_max and int(module_max) == -1:
|
||||||
@@ -246,14 +245,11 @@ class SecurityGroupRuleModule(OpenStackModule):
|
|||||||
|
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
security_group=dict(required=True),
|
security_group=dict(required=True),
|
||||||
# NOTE(Shrews): None is an acceptable protocol value for
|
protocol=dict(type='str'),
|
||||||
# Neutron, but Nova will balk at this.
|
|
||||||
protocol=dict(default=None,
|
|
||||||
choices=[None, 'any', 'tcp', 'udp', 'icmp', '112', '132']),
|
|
||||||
port_range_min=dict(required=False, type='int'),
|
port_range_min=dict(required=False, type='int'),
|
||||||
port_range_max=dict(required=False, type='int'),
|
port_range_max=dict(required=False, type='int'),
|
||||||
remote_ip_prefix=dict(required=False, default=None),
|
remote_ip_prefix=dict(required=False),
|
||||||
remote_group=dict(required=False, default=None),
|
remote_group=dict(required=False),
|
||||||
ethertype=dict(default='IPv4',
|
ethertype=dict(default='IPv4',
|
||||||
choices=['IPv4', 'IPv6']),
|
choices=['IPv4', 'IPv6']),
|
||||||
direction=dict(default='ingress',
|
direction=dict(default='ingress',
|
||||||
|
|||||||
251
plugins/modules/security_group_rule_info.py
Normal file
251
plugins/modules/security_group_rule_info.py
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
#!/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'],
|
||||||
|
],
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
@@ -189,6 +189,10 @@ options:
|
|||||||
description:
|
description:
|
||||||
- Availability zone in which to create the server.
|
- Availability zone in which to create the server.
|
||||||
type: str
|
type: str
|
||||||
|
description:
|
||||||
|
description:
|
||||||
|
- Description of the server.
|
||||||
|
type: str
|
||||||
requirements:
|
requirements:
|
||||||
- "python >= 3.6"
|
- "python >= 3.6"
|
||||||
- "openstacksdk"
|
- "openstacksdk"
|
||||||
@@ -505,6 +509,7 @@ class ServerModule(OpenStackModule):
|
|||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
delete_fip=dict(default=False, type='bool'),
|
delete_fip=dict(default=False, type='bool'),
|
||||||
reuse_ips=dict(default=True, type='bool'),
|
reuse_ips=dict(default=True, type='bool'),
|
||||||
|
description=dict(default=None, type='str'),
|
||||||
)
|
)
|
||||||
module_kwargs = dict(
|
module_kwargs = dict(
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
@@ -608,7 +613,8 @@ class ServerModule(OpenStackModule):
|
|||||||
)
|
)
|
||||||
for optional_param in (
|
for optional_param in (
|
||||||
'key_name', 'availability_zone', 'network',
|
'key_name', 'availability_zone', 'network',
|
||||||
'scheduler_hints', 'volume_size', 'volumes'):
|
'scheduler_hints', 'volume_size', 'volumes',
|
||||||
|
'description'):
|
||||||
if self.params[optional_param]:
|
if self.params[optional_param]:
|
||||||
bootkwargs[optional_param] = self.params[optional_param]
|
bootkwargs[optional_param] = self.params[optional_param]
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ options:
|
|||||||
- Perform the given action. The lock and unlock actions always return
|
- Perform the given action. The lock and unlock actions always return
|
||||||
changed as the servers API does not provide lock status.
|
changed as the servers API does not provide lock status.
|
||||||
choices: [stop, start, pause, unpause, lock, unlock, suspend, resume,
|
choices: [stop, start, pause, unpause, lock, unlock, suspend, resume,
|
||||||
rebuild]
|
rebuild, shelve, shelve_offload, unshelve]
|
||||||
type: str
|
type: str
|
||||||
required: true
|
required: true
|
||||||
image:
|
image:
|
||||||
@@ -70,18 +70,43 @@ EXAMPLES = '''
|
|||||||
|
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
|
|
||||||
|
# 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',
|
_action_map = {'stop': ['SHUTOFF'],
|
||||||
'start': 'ACTIVE',
|
'start': ['ACTIVE'],
|
||||||
'pause': 'PAUSED',
|
'pause': ['PAUSED'],
|
||||||
'unpause': 'ACTIVE',
|
'unpause': ['ACTIVE'],
|
||||||
'lock': 'ACTIVE', # API doesn't show lock/unlock status
|
'lock': ['ACTIVE'], # API doesn't show lock/unlock status
|
||||||
'unlock': 'ACTIVE',
|
'unlock': ['ACTIVE'],
|
||||||
'suspend': 'SUSPENDED',
|
'suspend': ['SUSPENDED'],
|
||||||
'resume': 'ACTIVE',
|
'resume': ['ACTIVE'],
|
||||||
'rebuild': 'ACTIVE'}
|
'rebuild': ['ACTIVE'],
|
||||||
|
'shelve': ['SHELVED_OFFLOADED', 'SHELVED'],
|
||||||
|
'shelve_offload': ['SHELVED_OFFLOADED'],
|
||||||
|
'unshelve': ['ACTIVE']}
|
||||||
|
|
||||||
_admin_actions = ['pause', 'unpause', 'suspend', 'resume', 'lock', 'unlock']
|
_admin_actions = ['pause', 'unpause', 'suspend', 'resume', 'lock', 'unlock', 'shelve_offload']
|
||||||
|
|
||||||
|
|
||||||
class ServerActionModule(OpenStackModule):
|
class ServerActionModule(OpenStackModule):
|
||||||
@@ -92,9 +117,9 @@ class ServerActionModule(OpenStackModule):
|
|||||||
action=dict(required=True, type='str',
|
action=dict(required=True, type='str',
|
||||||
choices=['stop', 'start', 'pause', 'unpause',
|
choices=['stop', 'start', 'pause', 'unpause',
|
||||||
'lock', 'unlock', 'suspend', 'resume',
|
'lock', 'unlock', 'suspend', 'resume',
|
||||||
'rebuild']),
|
'rebuild', 'shelve', 'shelve_offload', 'unshelve']),
|
||||||
image=dict(required=False, type='str'),
|
image=dict(required=False, type='str'),
|
||||||
admin_password=dict(required=False, type='str'),
|
admin_password=dict(required=False, type='str', no_log=True),
|
||||||
)
|
)
|
||||||
module_kwargs = dict(
|
module_kwargs = dict(
|
||||||
required_if=[('action', 'rebuild', ['image'])],
|
required_if=[('action', 'rebuild', ['image'])],
|
||||||
@@ -128,6 +153,9 @@ class ServerActionModule(OpenStackModule):
|
|||||||
def _execute_server_action(self, os_server):
|
def _execute_server_action(self, os_server):
|
||||||
if self.params['action'] == 'rebuild':
|
if self.params['action'] == 'rebuild':
|
||||||
return self._rebuild_server(os_server)
|
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"
|
action_name = self.params['action'] + "_server"
|
||||||
try:
|
try:
|
||||||
func_name = getattr(self.conn.compute, action_name)
|
func_name = getattr(self.conn.compute, action_name)
|
||||||
@@ -154,23 +182,38 @@ class ServerActionModule(OpenStackModule):
|
|||||||
admin_password=self.params['admin_password']
|
admin_password=self.params['admin_password']
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.conn.compute.post(
|
self._action(os_server, json={'rebuild': {'imageRef': image['id']}})
|
||||||
'/servers/{server_id}/action'.format(
|
|
||||||
server_id=os_server['id']),
|
def _action(self, os_server, json):
|
||||||
json={'rebuild': {'imageRef': image['id']}})
|
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):
|
def _wait(self, os_server):
|
||||||
"""Wait for the server to reach the desired state for the given action."""
|
"""Wait for the server to reach the desired state for the given action."""
|
||||||
# Using Server object for wait_for_server function
|
# Using Server object for wait_for_server function
|
||||||
server = self.conn.compute.find_server(self.params['server'])
|
server = self.conn.compute.find_server(self.params['server'])
|
||||||
self.conn.compute.wait_for_server(
|
states = _action_map[self.params['action']]
|
||||||
server,
|
|
||||||
status=_action_map[self.params['action']],
|
try:
|
||||||
wait=self.params['timeout'])
|
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):
|
def __system_state_change(self, os_server):
|
||||||
"""Check if system state would change."""
|
"""Check if system state would change."""
|
||||||
return os_server.status != _action_map[self.params['action']]
|
return os_server.status not in _action_map[self.params['action']]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -96,70 +96,66 @@ user_id:
|
|||||||
type: str
|
type: str
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
|
|
||||||
openstack_module_kwargs,
|
|
||||||
openstack_cloud_from_module)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_change(state, server_group):
|
class ServerGroupModule(OpenStackModule):
|
||||||
if state == 'present' and not server_group:
|
argument_spec = dict(
|
||||||
return True
|
|
||||||
if state == 'absent' and server_group:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = openstack_full_argument_spec(
|
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
policies=dict(required=False, type='list', elements='str'),
|
policies=dict(required=False, type='list', elements='str'),
|
||||||
state=dict(default='present', choices=['absent', 'present']),
|
state=dict(default='present', choices=['absent', 'present']),
|
||||||
)
|
)
|
||||||
module_kwargs = openstack_module_kwargs()
|
|
||||||
module = AnsibleModule(
|
module_kwargs = dict(
|
||||||
argument_spec,
|
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
**module_kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
name = module.params['name']
|
def _system_state_change(self, state, server_group):
|
||||||
policies = module.params['policies']
|
if state == 'present' and not server_group:
|
||||||
state = module.params['state']
|
return True
|
||||||
|
if state == 'absent' and server_group:
|
||||||
|
return True
|
||||||
|
|
||||||
sdk, cloud = openstack_cloud_from_module(module)
|
return False
|
||||||
try:
|
|
||||||
server_group = cloud.get_server_group(name)
|
|
||||||
|
|
||||||
if module.check_mode:
|
def run(self):
|
||||||
module.exit_json(
|
name = self.params['name']
|
||||||
changed=_system_state_change(state, server_group)
|
policies = self.params['policies']
|
||||||
|
state = self.params['state']
|
||||||
|
|
||||||
|
server_group = self.conn.get_server_group(name)
|
||||||
|
|
||||||
|
if self.ansible.check_mode:
|
||||||
|
self.exit_json(
|
||||||
|
changed=self._system_state_change(state, server_group)
|
||||||
)
|
)
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
if not server_group:
|
if not server_group:
|
||||||
if not policies:
|
if not policies:
|
||||||
module.fail_json(
|
self.fail_json(
|
||||||
msg="Parameter 'policies' is required in Server Group "
|
msg="Parameter 'policies' is required in Server Group "
|
||||||
"Create"
|
"Create"
|
||||||
)
|
)
|
||||||
server_group = cloud.create_server_group(name, policies)
|
server_group = self.conn.create_server_group(name, policies)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
module.exit_json(
|
self.exit_json(
|
||||||
changed=changed,
|
changed=changed,
|
||||||
id=server_group['id'],
|
id=server_group['id'],
|
||||||
server_group=server_group
|
server_group=server_group
|
||||||
)
|
)
|
||||||
if state == 'absent':
|
if state == 'absent':
|
||||||
if server_group:
|
if server_group:
|
||||||
cloud.delete_server_group(server_group['id'])
|
self.conn.delete_server_group(server_group['id'])
|
||||||
changed = True
|
changed = True
|
||||||
module.exit_json(changed=changed)
|
self.exit_json(changed=changed)
|
||||||
except sdk.exceptions.OpenStackCloudException as e:
|
|
||||||
module.fail_json(msg=str(e), extra_data=e.extra_data)
|
|
||||||
|
def main():
|
||||||
|
module = ServerGroupModule()
|
||||||
|
module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ class ServerInfoModule(OpenStackModule):
|
|||||||
filters=dict(required=False, type='dict', default=None),
|
filters=dict(required=False, type='dict', default=None),
|
||||||
all_projects=dict(required=False, type='bool', default=False),
|
all_projects=dict(required=False, type='bool', default=False),
|
||||||
)
|
)
|
||||||
|
module_kwargs = dict(
|
||||||
|
supports_check_mode=True
|
||||||
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user