mirror of
https://opendev.org/openstack/ansible-collections-openstack.git
synced 2026-03-27 14:03:03 +00:00
Compare commits
111 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b03f918ac | ||
|
|
c435002734 | ||
|
|
e8bba38e2e | ||
|
|
058bd87f2a | ||
|
|
7772bf125d | ||
|
|
17e78e5173 | ||
|
|
0e9a6f26c2 | ||
|
|
8dfcd17731 | ||
|
|
39bb4909ee | ||
|
|
ce60e71bde | ||
|
|
b579d03968 | ||
|
|
8743f24c4b | ||
|
|
7a9837dfb5 | ||
|
|
26b53e78b2 | ||
|
|
b8c2310963 | ||
|
|
915a78d7af | ||
|
|
60c39d495f | ||
|
|
2052a47324 | ||
|
|
3ecd3b6e64 | ||
|
|
c4a296c07c | ||
|
|
6b58d28a4e | ||
|
|
620956c61d | ||
|
|
970fb2489c | ||
|
|
8cc678acc1 | ||
|
|
75558c5c2e | ||
|
|
b4bde6af5c | ||
|
|
b935f21f44 | ||
|
|
3c047406dc | ||
|
|
be8965c7fc | ||
|
|
acf64a1f72 | ||
|
|
a4894337d4 | ||
|
|
0b0a80796f | ||
|
|
c63ff6fbc8 | ||
|
|
4f8f6ffaf4 | ||
|
|
0204bbeede | ||
|
|
5626a8d4c9 | ||
|
|
a3bb143f34 | ||
|
|
9c16ee4df3 | ||
|
|
a70a4c3424 | ||
|
|
708ed756ca | ||
|
|
c83884e5c8 | ||
|
|
655ed21ffa | ||
|
|
e64211213a | ||
|
|
39676b664a | ||
|
|
220f2b7dca | ||
|
|
0af7a252bd | ||
|
|
e8f9457893 | ||
|
|
29831685d8 | ||
|
|
82311440b5 | ||
|
|
57012cbaa3 | ||
|
|
5b453c62d1 | ||
|
|
8e1f1d6475 | ||
|
|
4b9e2295e0 | ||
|
|
5bb8312171 | ||
|
|
b3ac841442 | ||
|
|
37c52c321d | ||
|
|
6edc70f965 | ||
|
|
cc8cd08c03 | ||
|
|
b572bf8ae9 | ||
|
|
b3e0d610ea | ||
|
|
291e8b8640 | ||
|
|
f70a50e363 | ||
|
|
befcc4353d | ||
|
|
8a91352a38 | ||
|
|
1d7fd25ac0 | ||
|
|
44fa06cba1 | ||
|
|
26bc8a0666 | ||
|
|
19d0562551 | ||
|
|
07c3ed0c17 | ||
|
|
8708167b5f | ||
|
|
a9565779b5 | ||
|
|
5c2069c47d | ||
|
|
583df2a8a9 | ||
|
|
0f532d10f3 | ||
|
|
87858ab976 | ||
|
|
09c3e4bdc9 | ||
|
|
ebffbe4fe8 | ||
|
|
cbcfce2e23 | ||
|
|
406558dae9 | ||
|
|
c8d89f81a5 | ||
|
|
c0e1f56894 | ||
|
|
a031968f80 | ||
|
|
2e78559cc1 | ||
|
|
bf939a4ce0 | ||
|
|
24d6f36602 | ||
|
|
dd9cdde3d8 | ||
|
|
da4a68c188 | ||
|
|
a7a190f3c0 | ||
|
|
ce73c9db34 | ||
|
|
0e102b1411 | ||
|
|
9f58d54721 | ||
|
|
79d7827d17 | ||
|
|
ba9aa9967f | ||
|
|
617e8fb552 | ||
|
|
5abf89d805 | ||
|
|
0599d05103 | ||
|
|
9c28af7d12 | ||
|
|
bcb5d18492 | ||
|
|
7aa626377b | ||
|
|
0a7889b9a2 | ||
|
|
a1b920742f | ||
|
|
bd55e1f905 | ||
|
|
20329c0329 | ||
|
|
bdf472a53f | ||
|
|
b7fb23b097 | ||
|
|
6ed02eff2d | ||
|
|
cb396cf03d | ||
|
|
20c2633ea3 | ||
|
|
6569e07023 | ||
|
|
031475d42e | ||
|
|
5e2ab3d8c3 |
415
.zuul.yaml
415
.zuul.yaml
@@ -1,14 +1,32 @@
|
||||
# yamllint disable
|
||||
---
|
||||
# Keep parent jobs in sync between branches to avoid issues e.g. with job scheduling. Zuul CI will search in master
|
||||
# branch first when collecting job variants during job freeze which can have unwanted side effects. For example, when
|
||||
# parent job *-base has been changed in stable/1.0.0 branch, Zuul could still use *-base variants from master branch
|
||||
# during job freeze on child jobs such as *-ussuri-ansible-2.11 etc.
|
||||
#
|
||||
# Do not share job definitions with the job.branches attribute across multiple branches. Do not define jobs which are
|
||||
# specific to other branches, except for parent jobs which are shared across branches. For example, to not add a job
|
||||
# which is specific for the stable/1.0.0 branch to the .zuul.yaml in master branch. In particular do not use the
|
||||
# job.branches directive on a job which will be copied to multiple branches. When you have multiple copies of a job with
|
||||
# the job.branches attribute, Zuul CI could pick any of the job definitions which might not be the one you expected.
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack
|
||||
name: ansible-collections-openstack-functional-devstack-base
|
||||
parent: openstacksdk-functional-devstack
|
||||
# Do not restrict branches in base jobs because else Zuul would not find a matching
|
||||
# parent job variant during job freeze when child jobs are on other branches.
|
||||
post-run: ci/playbooks/postlog.yaml
|
||||
description: |
|
||||
Run openstack collections functional tests against a master devstack
|
||||
using master of openstacksdk with latest ansible release
|
||||
Run openstack collections functional tests against a devstack
|
||||
# Do not set job.override-checkout or job.required-projects.override-checkout in base job because
|
||||
# else Zuul will use this branch when matching variants for parent jobs during job freeze
|
||||
required-projects:
|
||||
- openstack/ansible-collections-openstack
|
||||
- openstack/designate
|
||||
# openstack/devstack is required through parent job openstacksdk-functional-devstack
|
||||
# openstack/os-client-config is required through parent job openstacksdk-functional-devstack
|
||||
# openstack/openstacksdk is required through parent job openstacksdk-functional-devstack
|
||||
irrelevant-files: &ignore_files
|
||||
- changelogs/.*
|
||||
- galaxy.*
|
||||
@@ -29,19 +47,24 @@
|
||||
designate: https://opendev.org/openstack/designate
|
||||
devstack_services:
|
||||
designate: true
|
||||
neutron-dns: true
|
||||
zuul_copy_output:
|
||||
'{{ devstack_log_dir }}/test_output.log': 'logs'
|
||||
extensions_to_txt:
|
||||
log: true
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-octavia
|
||||
parent: ansible-collections-openstack-functional-devstack
|
||||
name: ansible-collections-openstack-functional-devstack-octavia-base
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
# Do not restrict branches in base jobs because else Zuul would not find a matching
|
||||
# parent job variant during job freeze when child jobs are on other branches.
|
||||
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.
|
||||
Run openstack collections functional tests against a devstack with Octavia plugin enabled
|
||||
pre-run: ci/playbooks/get_amphora_tarball.yaml
|
||||
# Do not set job.override-checkout or job.required-projects.override-checkout in base job because
|
||||
# else Zuul will use this branch when matching variants for parent jobs during job freeze
|
||||
required-projects:
|
||||
- openstack/octavia
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.12
|
||||
files:
|
||||
- ^ci/roles/loadbalancer/.*$
|
||||
- ^plugins/modules/lb_health_monitor.py
|
||||
@@ -51,12 +74,13 @@
|
||||
- ^plugins/modules/loadbalancer.py
|
||||
vars:
|
||||
configure_swap_size: 8192
|
||||
tox_envlist: ansible
|
||||
tox_install_siblings: false
|
||||
devstack_plugins:
|
||||
designate: https://opendev.org/openstack/designate
|
||||
octavia: https://opendev.org/openstack/octavia
|
||||
devstack_services:
|
||||
designate: true
|
||||
neutron-dns: true
|
||||
octavia: true
|
||||
o-api: true
|
||||
o-cw: true
|
||||
@@ -68,62 +92,98 @@
|
||||
OCTAVIA_AMP_IMAGE_NAME: "test-only-amphora-x64-haproxy-ubuntu-bionic"
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-releases
|
||||
parent: ansible-collections-openstack-functional-devstack
|
||||
name: ansible-collections-openstack-functional-devstack-octavia
|
||||
parent: ansible-collections-openstack-functional-devstack-octavia-base
|
||||
branches: stable/1.0.0
|
||||
description: |
|
||||
Run openstack collections functional tests against a master devstack
|
||||
using releases of openstacksdk and latest ansible release
|
||||
with Octavia plugin enabled, using 0.*.* releases of openstacksdk
|
||||
and latest ansible release. Run it only on Load Balancer changes.
|
||||
required-projects:
|
||||
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/devstack
|
||||
override-checkout: master
|
||||
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/openstacksdk
|
||||
override-checkout: master
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
tox_constraints_file: '{{ ansible_user_dir }}/{{ zuul.project.src_dir }}/tests/constraints-openstacksdk-0.x.x.txt'
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-releases
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
branches: stable/1.0.0
|
||||
description: |
|
||||
Run openstack collections functional tests against a master devstack
|
||||
using 0.*.* releases of openstacksdk and latest ansible release
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.9
|
||||
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/devstack
|
||||
override-checkout: master
|
||||
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/openstacksdk
|
||||
override-checkout: master
|
||||
vars:
|
||||
tox_constraints_file: '{{ ansible_user_dir }}/{{ zuul.project.src_dir }}/tests/constraints-openstacksdk-0.x.x.txt'
|
||||
tox_install_siblings: false
|
||||
|
||||
# Job with Ansible 2.9 for checking backward compatibility
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-ansible-2.9
|
||||
parent: ansible-collections-openstack-functional-devstack
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
branches: stable/1.0.0
|
||||
description: |
|
||||
Run openstack collections functional tests against a master devstack
|
||||
using master of openstacksdk and stable 2.9 branch of ansible
|
||||
using 0.*.* releases of openstacksdk and stable 2.9 branch of ansible
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.9
|
||||
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/devstack
|
||||
override-checkout: master
|
||||
- name: openstack/openstacksdk
|
||||
# Yoga has the latest SDK release of the 0.*.* series atm
|
||||
override-checkout: stable/yoga
|
||||
vars:
|
||||
tox_envlist: ansible-2.9
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-ansible-2.11
|
||||
parent: ansible-collections-openstack-functional-devstack
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
branches: stable/1.0.0
|
||||
description: |
|
||||
Run openstack collections functional tests against a master devstack
|
||||
using master of openstacksdk and stable 2.12 branch of ansible
|
||||
using 0.*.* releases of openstacksdk and stable 2.12 branch of ansible
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.11
|
||||
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/devstack
|
||||
override-checkout: master
|
||||
- name: openstack/openstacksdk
|
||||
# Yoga has the latest SDK release of the 0.*.* series atm
|
||||
override-checkout: stable/yoga
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
tox_envlist: ansible-2.11
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-ansible-2.12
|
||||
parent: ansible-collections-openstack-functional-devstack
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
branches: stable/1.0.0
|
||||
description: |
|
||||
Run openstack collections functional tests against a master devstack
|
||||
using master of openstacksdk and stable 2.12 branch of ansible
|
||||
using 0.*.* releases of openstacksdk and stable 2.12 branch of ansible
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.12
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||
parent: ansible-collections-openstack-functional-devstack
|
||||
description: |
|
||||
Run openstack collections functional tests against a master devstack
|
||||
using master of openstacksdk and devel branch of ansible
|
||||
voting: false
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: devel
|
||||
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/devstack
|
||||
override-checkout: master
|
||||
- name: openstack/openstacksdk
|
||||
# Yoga has the latest SDK release of the 0.*.* series atm
|
||||
override-checkout: stable/yoga
|
||||
vars:
|
||||
tox_envlist: ansible-2.12
|
||||
|
||||
@@ -131,130 +191,187 @@
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-xena-ansible-2.12
|
||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
|
||||
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
|
||||
# will not match that job when it collects job variants.
|
||||
#
|
||||
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
|
||||
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
|
||||
# jobs when collecting variants for parent jobs.
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
description: |
|
||||
Run openstack collections functional tests against a xena devstack
|
||||
using xena brach of openstacksdk and stable 2.12 branch of ansible
|
||||
voting: true
|
||||
using xena branch of openstacksdk and stable 2.12 branch of ansible
|
||||
branches: stable/1.0.0
|
||||
override-checkout: stable/xena
|
||||
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.12
|
||||
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/xena branch
|
||||
name: openstack/ansible-collections-openstack
|
||||
override-checkout: stable/1.0.0
|
||||
- # Choose parent devstack job from stable/xena branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/xena
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/xena
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
tox_envlist: ansible-2.12
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
|
||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
|
||||
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
|
||||
# will not match that job when it collects job variants.
|
||||
#
|
||||
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
|
||||
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
|
||||
# jobs when collecting variants for parent jobs.
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
description: |
|
||||
Run openstack collections functional tests against a wallaby devstack
|
||||
using wallaby brach of openstacksdk and stable 2.12 branch of ansible
|
||||
voting: true
|
||||
using wallaby branch of openstacksdk and stable 2.12 branch of ansible
|
||||
branches: stable/1.0.0
|
||||
override-checkout: stable/wallaby
|
||||
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.12
|
||||
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/wallaby branch
|
||||
name: openstack/ansible-collections-openstack
|
||||
override-checkout: stable/1.0.0
|
||||
- # Choose parent devstack job from stable/wallaby branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/wallaby
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/wallaby
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
tox_envlist: ansible-2.12
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
|
||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
|
||||
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
|
||||
# will not match that job when it collects job variants.
|
||||
#
|
||||
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
|
||||
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
|
||||
# jobs when collecting variants for parent jobs.
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
description: |
|
||||
Run openstack collections functional tests against a victoria devstack
|
||||
using victoria brach of openstacksdk and stable 2.12 branch of ansible
|
||||
voting: true
|
||||
using victoria branch of openstacksdk and stable 2.12 branch of ansible
|
||||
branches: stable/1.0.0
|
||||
override-checkout: stable/victoria
|
||||
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.12
|
||||
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/victoria branch
|
||||
name: openstack/ansible-collections-openstack
|
||||
override-checkout: stable/1.0.0
|
||||
- # Choose parent devstack job from stable/victoria branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/victoria
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/victoria
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
|
||||
tox_envlist: ansible-2.12
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
|
||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
|
||||
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
|
||||
# will not match that job when it collects job variants.
|
||||
#
|
||||
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
|
||||
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
|
||||
# jobs when collecting variants for parent jobs.
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
description: |
|
||||
Run openstack collections functional tests against a ussuri devstack
|
||||
using ussuri brach of openstacksdk and stable 2.11 branch of ansible
|
||||
voting: true
|
||||
using ussuri branch of openstacksdk and stable 2.11 branch of ansible
|
||||
branches: stable/1.0.0
|
||||
override-checkout: stable/ussuri
|
||||
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.11
|
||||
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/ussuri branch
|
||||
name: openstack/ansible-collections-openstack
|
||||
override-checkout: stable/1.0.0
|
||||
- # Choose parent devstack job from stable/ussuri branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/ussuri
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/ussuri
|
||||
- name: openstack/os-client-config
|
||||
override-checkout: stable/ussuri
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
tox_envlist: ansible-2.11
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-train-ansible-2.11
|
||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
|
||||
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
|
||||
# will not match that job when it collects job variants.
|
||||
#
|
||||
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
|
||||
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
|
||||
# jobs when collecting variants for parent jobs.
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
description: |
|
||||
Run openstack collections functional tests against a train devstack
|
||||
using train brach of openstacksdk and stable 2.11 branch of ansible
|
||||
voting: true
|
||||
using train branch of openstacksdk and stable 2.11 branch of ansible
|
||||
branches: stable/1.0.0
|
||||
override-checkout: stable/train
|
||||
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.11
|
||||
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/train branch
|
||||
name: openstack/ansible-collections-openstack
|
||||
override-checkout: stable/1.0.0
|
||||
- # Choose parent devstack job from stable/train branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/train
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/train
|
||||
- name: openstack/os-client-config
|
||||
override-checkout: stable/train
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.11
|
||||
parent: ansible-collections-openstack-functional-devstack-ansible-devel
|
||||
description: |
|
||||
Run openstack collections functional tests against a queens devstack
|
||||
using master branch of openstacksdk and stable 2.11 branch of ansible
|
||||
voting: true
|
||||
override-checkout: stable/queens
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.11
|
||||
- name: openstack/openstacksdk
|
||||
# Run queens with highest possible py2 version of SDK
|
||||
override-checkout: stable/train
|
||||
vars:
|
||||
tox_envlist: ansible
|
||||
tox_envlist: ansible-2.11
|
||||
|
||||
# Linters
|
||||
- job:
|
||||
name: openstack-tox-linters-ansible-devel
|
||||
name: openstack-tox-linters-ansible
|
||||
parent: openstack-tox-linters
|
||||
nodeset: ubuntu-focal
|
||||
description: |
|
||||
Run openstack collections linter tests using the devel branch of ansible
|
||||
# non-voting because we can't prevent ansible devel from breaking us
|
||||
voting: false
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: devel
|
||||
vars:
|
||||
tox_envlist: linters-2.12
|
||||
python_version: 3.8
|
||||
bindep_profile: test py38
|
||||
# override tox_constraints_file from parent job
|
||||
tox_constraints_file: '{{ ansible_user_dir }}/{{ zuul.project.src_dir }}/tests/constraints-none.txt'
|
||||
tox_envlist: linters
|
||||
tox_install_siblings: true
|
||||
|
||||
- job:
|
||||
name: openstack-tox-linters-ansible-devel
|
||||
parent: openstack-tox-linters-ansible
|
||||
nodeset: ubuntu-jammy
|
||||
description: |
|
||||
Run openstack collections linter tests using the devel branch of ansible
|
||||
# non-voting because we can't prevent ansible devel from breaking us
|
||||
voting: false
|
||||
vars:
|
||||
python_version: '3.10'
|
||||
bindep_profile: test py310
|
||||
|
||||
- job:
|
||||
name: openstack-tox-linters-ansible-2.12
|
||||
parent: openstack-tox-linters
|
||||
nodeset: ubuntu-focal
|
||||
parent: openstack-tox-linters-ansible
|
||||
description: |
|
||||
Run openstack collections linter tests using the 2.12 branch of ansible
|
||||
voting: true
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.12
|
||||
@@ -263,61 +380,114 @@
|
||||
python_version: 3.8
|
||||
bindep_profile: test py38
|
||||
|
||||
- job:
|
||||
name: openstack-tox-linters-ansible-2.9
|
||||
parent: openstack-tox-linters
|
||||
nodeset: ubuntu-bionic
|
||||
description: |
|
||||
Run openstack collections linter tests using the 2.9 branch of ansible
|
||||
voting: true
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.9
|
||||
vars:
|
||||
tox_envlist: linters-2.9
|
||||
|
||||
# Cross-checks with other projects
|
||||
- job:
|
||||
name: bifrost-collections-src
|
||||
parent: bifrost-integration-tinyipa-ubuntu-focal
|
||||
override-checkout: stable/yoga
|
||||
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
|
||||
required-projects:
|
||||
- openstack/ansible-collections-openstack
|
||||
|
||||
- # always use existing branch when collecting parent job variants, refer to git blame for rationale.
|
||||
name: openstack/bifrost
|
||||
# Yoga has the latest SDK release of the 0.*.* series atm
|
||||
override-checkout: stable/yoga
|
||||
- job:
|
||||
name: bifrost-keystone-collections-src
|
||||
parent: bifrost-integration-tinyipa-keystone-ubuntu-focal
|
||||
override-checkout: stable/yoga
|
||||
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
|
||||
required-projects:
|
||||
- openstack/ansible-collections-openstack
|
||||
- # always use existing branch when collecting parent job variants, refer to git blame for rationale.
|
||||
name: openstack/bifrost
|
||||
# Yoga has the latest SDK release of the 0.*.* series atm
|
||||
override-checkout: stable/yoga
|
||||
|
||||
# TripleO jobs
|
||||
- job:
|
||||
name: tripleo-ci-centos-8-standalone-osa
|
||||
parent: tripleo-ci-centos-8-standalone
|
||||
vars:
|
||||
name: tripleo-ci-centos-8-standalone-osa-pre-zed
|
||||
parent: tripleo-ci-centos-8-standalone-build
|
||||
# Do not restrict branches in base jobs because else Zuul would not find a matching
|
||||
# parent job variant during job freeze when child jobs are on other branches.
|
||||
vars: &tripleo_vars
|
||||
consumer_job: false
|
||||
build_container_images: true
|
||||
# Define branch_override in child jobs, e.g. branch_override: stable/yoga
|
||||
force_non_periodic: true
|
||||
required-projects: &tripleo_required_projects
|
||||
- # Required for TripleO Quickstart to install current patchset of the collection
|
||||
# Ref.: https://opendev.org/openstack/tripleo-quickstart/src/commit/b48d869e14de40444d69a107a0b718b5f721e912/quickstart.sh#L123
|
||||
openstack/ansible-collections-openstack
|
||||
- # always use master branch when collecting parent job variants, refer to git blame for rationale.
|
||||
name: openstack/tripleo-ci
|
||||
override-checkout: master
|
||||
irrelevant-files: &tripleo_irrelevant_files
|
||||
- .*molecule.*
|
||||
- ^.*\.md$
|
||||
- ^.*\.rst$
|
||||
- ^changelogs/.*$
|
||||
- ^contrib/.*$
|
||||
- ^docs/.*$
|
||||
- ^meta/.*$
|
||||
- ^requirements.*$
|
||||
- ^setup.*$
|
||||
- ^tests/.*$
|
||||
- ^tools/.*$
|
||||
- tox.ini
|
||||
# Run only on files used in TripleO
|
||||
files: &ooo_files
|
||||
files: &tripleo_files
|
||||
- ^.zuul.yaml$
|
||||
- ^plugins/module_utils/openstack.*$
|
||||
- ^plugins/modules/catalog_service.*$
|
||||
- ^plugins/modules/compute_flavor.*$
|
||||
- ^plugins/modules/endpoint.*$
|
||||
- ^plugins/modules/identity_domain.*$
|
||||
- ^plugins/modules/identity_domain_info.*$
|
||||
- ^plugins/modules/identity_role.*$
|
||||
- ^plugins/modules/identity_user.*$
|
||||
- ^plugins/modules/image.*$
|
||||
- ^plugins/modules/keypair.*$
|
||||
- ^plugins/modules/network.*$
|
||||
- ^plugins/modules/project.*$
|
||||
- ^plugins/modules/role_assignment.*$
|
||||
- ^plugins/modules/router.*$
|
||||
- ^plugins/modules/stack.*$
|
||||
- ^plugins/module_utils/openstack.*$
|
||||
- ^plugins/modules/subnet.*$
|
||||
|
||||
- job:
|
||||
name: tripleo-ci-centos-8-standalone-train-osa
|
||||
parent: tripleo-ci-centos-8-standalone-osa
|
||||
voting: false
|
||||
name: tripleo-ci-centos-9-standalone-osa-pre-zed
|
||||
parent: tripleo-ci-centos-9-standalone-build
|
||||
# Do not restrict branches in base jobs because else Zuul would not find a matching
|
||||
# parent job variant during job freeze when child jobs are on other branches.
|
||||
vars: *tripleo_vars
|
||||
required-projects: *tripleo_required_projects
|
||||
irrelevant-files: *tripleo_irrelevant_files
|
||||
files: *tripleo_files
|
||||
|
||||
- job:
|
||||
name: tripleo-ci-centos-8-standalone-osa-train
|
||||
parent: tripleo-ci-centos-8-standalone-osa-pre-zed
|
||||
branches: stable/1.0.0
|
||||
override-checkout: stable/train
|
||||
vars:
|
||||
branch_override: stable/train
|
||||
|
||||
- job:
|
||||
name: tripleo-ci-centos-8-standalone-osa-wallaby
|
||||
parent: tripleo-ci-centos-8-standalone-osa-pre-zed
|
||||
branches: stable/1.0.0
|
||||
override-checkout: stable/wallaby
|
||||
vars:
|
||||
branch_override: stable/wallaby
|
||||
|
||||
- job:
|
||||
name: tripleo-ci-centos-9-standalone-osa-wallaby
|
||||
parent: tripleo-ci-centos-9-standalone-osa-pre-zed
|
||||
branches: stable/1.0.0
|
||||
override-checkout: stable/wallaby
|
||||
vars:
|
||||
branch_override: stable/wallaby
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-release
|
||||
parent: base
|
||||
@@ -347,15 +517,10 @@
|
||||
- tox-pep8
|
||||
- openstack-tox-linters-ansible-devel
|
||||
- openstack-tox-linters-ansible-2.12
|
||||
- openstack-tox-linters-ansible-2.9
|
||||
- ansible-collections-openstack-functional-devstack:
|
||||
- ansible-collections-openstack-functional-devstack-releases:
|
||||
dependencies: &deps_unit_lint
|
||||
- tox-pep8
|
||||
- openstack-tox-linters-ansible-2.9
|
||||
- openstack-tox-linters-ansible-2.12
|
||||
|
||||
- ansible-collections-openstack-functional-devstack-releases:
|
||||
dependencies: *deps_unit_lint
|
||||
- ansible-collections-openstack-functional-devstack-ansible-2.9:
|
||||
dependencies: *deps_unit_lint
|
||||
- ansible-collections-openstack-functional-devstack-ansible-2.12:
|
||||
@@ -380,10 +545,9 @@
|
||||
dependencies: *deps_unit_lint
|
||||
irrelevant-files: *ignore_files
|
||||
|
||||
- tripleo-ci-centos-8-standalone-osa:
|
||||
- tripleo-ci-centos-8-standalone-osa-wallaby:
|
||||
dependencies: *deps_unit_lint
|
||||
|
||||
- tripleo-ci-centos-8-standalone-train-osa:
|
||||
- tripleo-ci-centos-9-standalone-osa-wallaby:
|
||||
voting: false
|
||||
dependencies: *deps_unit_lint
|
||||
|
||||
@@ -391,23 +555,19 @@
|
||||
jobs:
|
||||
- tox-pep8
|
||||
- openstack-tox-linters-ansible-2.12
|
||||
- openstack-tox-linters-ansible-2.9
|
||||
- ansible-collections-openstack-functional-devstack
|
||||
- ansible-collections-openstack-functional-devstack-releases
|
||||
- ansible-collections-openstack-functional-devstack-ansible-2.9
|
||||
- ansible-collections-openstack-functional-devstack-ansible-2.12
|
||||
# - ansible-collections-openstack-functional-devstack-ansible-2.9
|
||||
# - ansible-collections-openstack-functional-devstack-ansible-2.12
|
||||
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
|
||||
- ansible-collections-openstack-functional-devstack-xena-ansible-2.12
|
||||
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
|
||||
- ansible-collections-openstack-functional-devstack-octavia
|
||||
- tripleo-ci-centos-8-standalone-osa
|
||||
- tripleo-ci-centos-8-standalone-osa-wallaby
|
||||
|
||||
periodic:
|
||||
jobs:
|
||||
- openstack-tox-linters-ansible-devel
|
||||
- openstack-tox-linters-ansible-2.12
|
||||
- openstack-tox-linters-ansible-2.9
|
||||
- ansible-collections-openstack-functional-devstack
|
||||
- ansible-collections-openstack-functional-devstack-releases
|
||||
- ansible-collections-openstack-functional-devstack-ansible-2.9
|
||||
- ansible-collections-openstack-functional-devstack-ansible-2.12
|
||||
@@ -416,17 +576,18 @@
|
||||
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
|
||||
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
|
||||
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
|
||||
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11
|
||||
- bifrost-collections-src
|
||||
- bifrost-keystone-collections-src
|
||||
- ansible-collections-openstack-functional-devstack-octavia
|
||||
- tripleo-ci-centos-9-standalone-osa-wallaby
|
||||
- tripleo-ci-centos-8-standalone-osa-wallaby
|
||||
|
||||
experimental:
|
||||
jobs:
|
||||
- tripleo-ci-centos-8-standalone-osa-train
|
||||
- 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:
|
||||
|
||||
173
CHANGELOG.rst
173
CHANGELOG.rst
@@ -5,6 +5,179 @@ Openstack Cloud Ansilbe modules Release Notes
|
||||
.. contents:: Topics
|
||||
|
||||
|
||||
v1.10.0
|
||||
=======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Enable logging of openstacksdk activities and warn users about incompatible openstacksdk releases when using inventory plugin
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Add SDK logging option for openstack ansible collections
|
||||
- Don't use deprecated distutils from python 3.10
|
||||
- Ensure openstacksdk compatibility in inventory plugin
|
||||
- Lowered maximum OpenStack SDK version to 0.98.999 in inventory plugin
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection 2.0.0 or later which is currently under development.
|
||||
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior to 0.99.0 only.
|
||||
|
||||
v1.9.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Bugfix in keypair module
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Do not remove trailing spaces when reading public key in keypair module
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection 2.0.0 or later which is currently under development.
|
||||
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior to 0.99.0 only.
|
||||
|
||||
v1.9.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This release will enforce openstacksdk<0.99.0, has a dozen modules refactored and several bugs fixed.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Added support for specifying a maximum version of the OpenStack SDK
|
||||
- Constrain filters in compute_service_info to SDK >= 0.53.0
|
||||
- Drop username from return values of identity_user_info
|
||||
- Fix logic in routers_info
|
||||
- Fixed return value disable{d,s}_reason in compute_service_info module
|
||||
- Fixed return values in compute_service_info module again
|
||||
- Follow up to bump of minimum required OpenStack SDK release to SDK 0.36.0 (Train)
|
||||
- Lowered maximum OpenStack SDK version to 0.98.999
|
||||
- Move dns zone info to use proxy layer
|
||||
- Refactored catalog_service module
|
||||
- Refactored endpoint module
|
||||
- Refactored host_aggregate module
|
||||
- Refactored identity_domain_info module
|
||||
- Refactored identity_group_info module
|
||||
- Refactored identity_role module
|
||||
- Refactored identity_role_info module
|
||||
- Refactored identity_user module
|
||||
- Refactored identity_user_info module
|
||||
- Refactored image_info module
|
||||
- Refactored keypair_info module
|
||||
- Refactored recordset module
|
||||
- Refactored role_assignment module
|
||||
- Set owner in image module
|
||||
- Support description in sg-rule creation
|
||||
- Warn users about us breaking backward compatibility
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection 2.0.0 or later which is currently under development.
|
||||
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior to 0.99.0 only.
|
||||
|
||||
v1.8.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Subnet pool module and bugfixes
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Add 'all_projects' to server_action module
|
||||
- Add subnet pool module
|
||||
- Bumped minimum required OpenStack SDK release to SDK 0.36.0 (Train)
|
||||
- Changed compute_flavor_info module to use OpenStack SDK's proxy layer
|
||||
- Dropped deprecated return values in floating_ip_info and assert remaining fields
|
||||
- Fix ansible-lint issues for the newest version
|
||||
- Fix assertion after stack deletion
|
||||
- Handle aggregate host list set to None
|
||||
- Reenabled check-import.sh which tests imports to Ansible Galaxy
|
||||
- Remove old, unsupported parameters from documentation in image_info module
|
||||
- Router - Remove unneeded 'filter' parameter
|
||||
- Updated return value docs of compute_service_info module
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- openstack.cloud.subnet_pool - Create or Delete subnet pools from OpenStack.
|
||||
|
||||
v1.7.2
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Bugfixes
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix collection guidelines
|
||||
|
||||
v1.7.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Bugfixes
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- lb_member - Add monitor_[address,port] parameter
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- openstack_inventory - Fix documentation
|
||||
- quota - Fix description of volumes_types parameter
|
||||
|
||||
v1.7.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
New modules for Ironic and bugfixes
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- openstack_inventory - Adds use_name variable
|
||||
- port - Add dns_[name,domain] to the port module
|
||||
- project - Remove project properties tests and support
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- identity_user_info - Fix identity user lookup with a domain
|
||||
- keystone_domain - Move identity domain to use proxy layer
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- openstack.cloud.baremetal_node_info - Retrieve information about Bare Metal nodes from OpenStack an object.
|
||||
- openstack.cloud.baremetal_port - Create, Update, Remove ironic ports from OpenStack
|
||||
- openstack.cloud.baremetal_port_info - Retrieve information about Bare Metal ports from OpenStack an object.
|
||||
|
||||
v1.6.0
|
||||
======
|
||||
|
||||
|
||||
37
README.md
37
README.md
@@ -2,11 +2,44 @@
|
||||
|
||||
# Ansible Collection: openstack.cloud
|
||||
|
||||
|
||||
This repo hosts the `openstack.cloud` Ansible Collection.
|
||||
|
||||
The collection includes the Openstack modules and plugins supported by Openstack community to help the management of Openstack infrastructure.
|
||||
|
||||
## Breaking backward compatibility :warning:
|
||||
|
||||
Dear contributors and users of the Ansible OpenStack collection!
|
||||
Our codebase has been split into two separate release series:
|
||||
|
||||
* `2.x.x` releases of Ansible OpenStack collection are compatible with OpenStack SDK `1.x.x` and its release candidates
|
||||
`0.99.x` *only* (OpenStack Zed and later). Our `master` branch tracks our `2.x.x` releases.
|
||||
* `1.x.x` releases of Ansible OpenStack collection are compatible with OpenStack SDK `0.x.x` prior to `0.99.0` *only*
|
||||
(OpenStack Yoga and earlier). Our `stable/1.0.0` branch tracks our `1.x.x` releases.
|
||||
|
||||
Both branches will be developed in parallel for the time being. Patches from `master` will be backported to
|
||||
`stable/1.0.0` on a best effort basis but expect new features to be introduced in our `master` branch only.
|
||||
Contributions are welcome for both branches!
|
||||
Differences between both branches are mainly renamed and sometimes dropped module return values. We try to keep our
|
||||
module parameters backward compatible by offering aliases but e.g. the semantics of `filters` parameters in `*_info`
|
||||
modules have changed due to updates in the OpenStack SDK.
|
||||
|
||||
Our decision to break backward compatibility was not taken lightly. OpenStack SDK's first major release (`1.0.0` and its
|
||||
release candidates `0.99.x`) has streamlined and improved large parts of its codebase. For example, its Connection
|
||||
interface now consistently uses the Resource interfaces under the hood. This required breaking changes from older SDK
|
||||
releases though. The Ansible OpenStack collection is heavily based on OpenStack SDK. With OpenStack SDK becoming
|
||||
backward incompatible, so does our Ansible OpenStack collection. We simply lack the devpower to maintain a backward
|
||||
compatible interface in Ansible OpenStack collection across several SDK releases.
|
||||
|
||||
Our first `2.0.0` release is currently under development and we still have a long way to go. If you use modules of the
|
||||
Ansible OpenStack collection and want to join us in porting them to the upcoming OpenStack SDK, please contact us!
|
||||
Ping Jakob Meng <mail@jakobmeng.de> (jm1) or Rafael Castillo <rcastill@redhat.com> (rcastillo) and we will give you a
|
||||
quick introduction. We are also hanging around on `irc.oftc.net/#openstack-ansible-sig` and `irc.oftc.net/#oooq` 😎
|
||||
|
||||
We have extensive documentation on [why, what and how we are adopting and reviewing the new modules](
|
||||
https://hackmd.io/szgyWa5qSUOWw3JJBXLmOQ?view), [how to set up a working DevStack environment for hacking on the
|
||||
collection](https://hackmd.io/PI10x-iCTBuO09duvpeWgQ?view) and, most importantly, [a list of modules where we are
|
||||
coordinating our porting efforts](https://hackmd.io/7NtovjRkRn-tKraBXfz9jw?view).
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
### Installing dependencies
|
||||
@@ -15,7 +48,7 @@ For using the Openstack Cloud collection firstly you need to install `ansible` a
|
||||
For example with pip:
|
||||
|
||||
```bash
|
||||
pip install ansible openstacksdk
|
||||
pip install "ansible>=2.9" "openstacksdk>=0.36,<0.99.0"
|
||||
```
|
||||
|
||||
OpenStackSDK has to be available to Ansible and to the Python interpreter on the host, where Ansible executes the module (target host).
|
||||
|
||||
@@ -70,6 +70,21 @@ releases:
|
||||
name: volume_info
|
||||
namespace: ''
|
||||
release_date: '2020-08-17'
|
||||
1.10.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Add SDK logging option for openstack ansible collections
|
||||
- Don't use deprecated distutils from python 3.10
|
||||
- Ensure openstacksdk compatibility in inventory plugin
|
||||
- Lowered maximum OpenStack SDK version to 0.98.999 in inventory plugin
|
||||
known_issues:
|
||||
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection
|
||||
2.0.0 or later which is currently under development.
|
||||
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior
|
||||
to 0.99.0 only.
|
||||
release_summary: Enable logging of openstacksdk activities and warn users about
|
||||
incompatible openstacksdk releases when using inventory plugin
|
||||
release_date: '2022-10-04'
|
||||
1.2.0:
|
||||
changes:
|
||||
minor_changes:
|
||||
@@ -283,3 +298,109 @@ releases:
|
||||
name: neutron_rbac_policy
|
||||
namespace: ''
|
||||
release_date: '2022-01-13'
|
||||
1.7.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- identity_user_info - Fix identity user lookup with a domain
|
||||
- keystone_domain - Move identity domain to use proxy layer
|
||||
minor_changes:
|
||||
- openstack_inventory - Adds use_name variable
|
||||
- port - Add dns_[name,domain] to the port module
|
||||
- project - Remove project properties tests and support
|
||||
release_summary: New modules for Ironic and bugfixes
|
||||
modules:
|
||||
- description: Retrieve information about Bare Metal nodes from OpenStack an object.
|
||||
name: baremetal_node_info
|
||||
namespace: ''
|
||||
- description: Create, Update, Remove ironic ports from OpenStack
|
||||
name: baremetal_port
|
||||
namespace: ''
|
||||
- description: Retrieve information about Bare Metal ports from OpenStack an object.
|
||||
name: baremetal_port_info
|
||||
namespace: ''
|
||||
release_date: '2022-02-15'
|
||||
1.7.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- openstack_inventory - Fix documentation
|
||||
- quota - Fix description of volumes_types parameter
|
||||
minor_changes:
|
||||
- lb_member - Add monitor_[address,port] parameter
|
||||
release_summary: Bugfixes
|
||||
release_date: '2022-03-08'
|
||||
1.7.2:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Fix collection guidelines
|
||||
release_summary: Bugfixes
|
||||
release_date: '2022-03-10'
|
||||
1.8.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Add 'all_projects' to server_action module
|
||||
- Add subnet pool module
|
||||
- Bumped minimum required OpenStack SDK release to SDK 0.36.0 (Train)
|
||||
- Changed compute_flavor_info module to use OpenStack SDK's proxy layer
|
||||
- Dropped deprecated return values in floating_ip_info and assert remaining
|
||||
fields
|
||||
- Fix ansible-lint issues for the newest version
|
||||
- Fix assertion after stack deletion
|
||||
- Handle aggregate host list set to None
|
||||
- Reenabled check-import.sh which tests imports to Ansible Galaxy
|
||||
- Remove old, unsupported parameters from documentation in image_info module
|
||||
- Router - Remove unneeded 'filter' parameter
|
||||
- Updated return value docs of compute_service_info module
|
||||
release_summary: Subnet pool module and bugfixes
|
||||
modules:
|
||||
- description: Create or Delete subnet pools from OpenStack.
|
||||
name: subnet_pool
|
||||
namespace: ''
|
||||
release_date: '2022-04-08'
|
||||
1.9.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Added support for specifying a maximum version of the OpenStack SDK
|
||||
- Constrain filters in compute_service_info to SDK >= 0.53.0
|
||||
- Drop username from return values of identity_user_info
|
||||
- Fix logic in routers_info
|
||||
- Fixed return value disable{d,s}_reason in compute_service_info module
|
||||
- Fixed return values in compute_service_info module again
|
||||
- Follow up to bump of minimum required OpenStack SDK release to SDK 0.36.0
|
||||
(Train)
|
||||
- Lowered maximum OpenStack SDK version to 0.98.999
|
||||
- Move dns zone info to use proxy layer
|
||||
- Refactored catalog_service module
|
||||
- Refactored endpoint module
|
||||
- Refactored host_aggregate module
|
||||
- Refactored identity_domain_info module
|
||||
- Refactored identity_group_info module
|
||||
- Refactored identity_role module
|
||||
- Refactored identity_role_info module
|
||||
- Refactored identity_user module
|
||||
- Refactored identity_user_info module
|
||||
- Refactored image_info module
|
||||
- Refactored keypair_info module
|
||||
- Refactored recordset module
|
||||
- Refactored role_assignment module
|
||||
- Set owner in image module
|
||||
- Support description in sg-rule creation
|
||||
- Warn users about us breaking backward compatibility
|
||||
known_issues:
|
||||
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection
|
||||
2.0.0 or later which is currently under development.
|
||||
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior
|
||||
to 0.99.0 only.
|
||||
release_summary: This release will enforce openstacksdk<0.99.0, has a dozen
|
||||
modules refactored and several bugs fixed.
|
||||
release_date: '2022-08-25'
|
||||
1.9.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Do not remove trailing spaces when reading public key in keypair module
|
||||
known_issues:
|
||||
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection
|
||||
2.0.0 or later which is currently under development.
|
||||
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior
|
||||
to 0.99.0 only.
|
||||
release_summary: Bugfix in keypair module
|
||||
release_date: '2022-09-08'
|
||||
|
||||
8
ci/playbooks/postlog.yaml
Normal file
8
ci/playbooks/postlog.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
- hosts: all
|
||||
tasks:
|
||||
- zuul_return:
|
||||
data:
|
||||
zuul:
|
||||
artifacts:
|
||||
- name: Test log
|
||||
url: controller/logs/test_output_log.txt
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
- hosts: all
|
||||
vars:
|
||||
collection_path: "{{ ansible_user_dir }}/{{ zuul.projects['opendev.org/openstack/ansible-collections-openstack'].src_dir }}"
|
||||
collection_path: "{{ ansible_user_dir }}/{{ zuul.project.src_dir }}"
|
||||
build_collection_path: /tmp/collection_built/
|
||||
ansible_galaxy_path: "~/.local/bin/ansible-galaxy"
|
||||
|
||||
@@ -63,12 +63,21 @@
|
||||
url = {{ ansible_galaxy_info.url }}
|
||||
token = {{ ansible_galaxy_info.token }}
|
||||
|
||||
- name: Get content of galaxy.yml
|
||||
slurp:
|
||||
src: "{{ collection_path }}/galaxy.yml"
|
||||
register: galaxy_vars
|
||||
|
||||
- name: Parse yaml into variable
|
||||
set_fact:
|
||||
galaxy_yaml: "{{ galaxy_vars['content'] | b64decode | from_yaml }}"
|
||||
|
||||
- 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
|
||||
{{ build_collection_path }}/{{ galaxy_yaml.namespace }}-{{ galaxy_yaml.name }}-{{ version_tag }}.tar.gz
|
||||
|
||||
always:
|
||||
- name: Shred ansible-galaxy credentials
|
||||
|
||||
94
ci/roles/catalog_service/tasks/main.yml
Normal file
94
ci/roles/catalog_service/tasks/main.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
- name: Delete service test
|
||||
openstack.cloud.catalog_service:
|
||||
cloud: "{{ cloud }}"
|
||||
service_type: test
|
||||
name: test
|
||||
state: absent
|
||||
register: service_delete
|
||||
|
||||
- name: Assert changed is set to false
|
||||
assert:
|
||||
that:
|
||||
- not service_delete.changed
|
||||
|
||||
- name: Create a service for test
|
||||
openstack.cloud.catalog_service:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "test_service"
|
||||
state: present
|
||||
service_type: test_type
|
||||
description: "Test service"
|
||||
register: service_test
|
||||
|
||||
- name: Verify returned values
|
||||
assert:
|
||||
that:
|
||||
- item in service_test.service
|
||||
loop:
|
||||
- description
|
||||
- id
|
||||
- enabled
|
||||
- name
|
||||
- service_type
|
||||
- type
|
||||
|
||||
- name: Check if the service test was created successfully
|
||||
openstack.cloud.catalog_service:
|
||||
cloud: "{{ cloud }}"
|
||||
service_type: test
|
||||
name: test
|
||||
register: service_created
|
||||
|
||||
- name: Verify returned values
|
||||
assert:
|
||||
that:
|
||||
- item in service_created.service
|
||||
loop:
|
||||
- description
|
||||
- id
|
||||
- enabled
|
||||
- name
|
||||
- type
|
||||
- service_type
|
||||
|
||||
- name: Update service test
|
||||
openstack.cloud.catalog_service:
|
||||
cloud: "{{ cloud }}"
|
||||
service_type: test
|
||||
description: "A new description"
|
||||
is_enabled: False
|
||||
name: test
|
||||
register: service_test
|
||||
|
||||
- name: Check if description and enabled were updated
|
||||
assert:
|
||||
that:
|
||||
- service_test.service.description == "A new description"
|
||||
- not (service_test.service.enabled|bool)
|
||||
|
||||
- name: Delete service test
|
||||
openstack.cloud.catalog_service:
|
||||
cloud: "{{ cloud }}"
|
||||
service_type: test
|
||||
name: test
|
||||
state: absent
|
||||
register: service_deleted
|
||||
|
||||
- name: Verify if service was deleted
|
||||
assert:
|
||||
that:
|
||||
- service_deleted.changed
|
||||
|
||||
- name: Delete service test again
|
||||
openstack.cloud.catalog_service:
|
||||
cloud: "{{ cloud }}"
|
||||
service_type: test
|
||||
name: test
|
||||
state: absent
|
||||
register: service_deleted
|
||||
|
||||
- name: Assert changed is set to false
|
||||
assert:
|
||||
that:
|
||||
- not service_deleted.changed
|
||||
33
ci/roles/compute_flavor_info/tasks/main.yml
Normal file
33
ci/roles/compute_flavor_info/tasks/main.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
- name: List flavors
|
||||
openstack.cloud.compute_flavor_info:
|
||||
cloud: "{{ cloud }}"
|
||||
|
||||
- name: List flavors with filter
|
||||
openstack.cloud.compute_flavor_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "m1.tiny"
|
||||
register: flavor_name
|
||||
|
||||
- name: Check output of list flavors with filter
|
||||
assert:
|
||||
that:
|
||||
- flavor_name.openstack_flavors | length == 1
|
||||
|
||||
- name: Assert fields on SDK 0.*
|
||||
when: sdk_version is version(1.0, '<')
|
||||
assert:
|
||||
that:
|
||||
- '["name", "description", "disk", "is_public", "ram",
|
||||
"vcpus", "swap", "ephemeral", "is_disabled", "rxtx_factor", "id",
|
||||
"extra_specs"] | difference(flavor_name.openstack_flavors.0.keys())
|
||||
| length == 0'
|
||||
|
||||
- name: Assert fields on SDK 1.*
|
||||
when: sdk_version is version(1.0, '>=')
|
||||
assert:
|
||||
that:
|
||||
- '["name", "original_name", "description", "disk", "is_public", "ram",
|
||||
"vcpus", "swap", "ephemeral", "is_disabled", "rxtx_factor", "id",
|
||||
"extra_specs"] | difference(flavor_name.openstack_flavors.0.keys())
|
||||
| length == 0'
|
||||
@@ -18,53 +18,6 @@
|
||||
|
||||
- debug: var=updated_dns_zone
|
||||
|
||||
- name: Create a recordset
|
||||
openstack.cloud.recordset:
|
||||
cloud: "{{ cloud }}"
|
||||
zone: "{{ updated_dns_zone.zone.name }}"
|
||||
name: "{{ recordset_name }}"
|
||||
recordset_type: "a"
|
||||
records: "{{ records }}"
|
||||
register: recordset
|
||||
|
||||
- name: Verify recordset info
|
||||
assert:
|
||||
that:
|
||||
- recordset["recordset"].name == recordset_name
|
||||
- recordset["recordset"].zone_name == dns_zone.zone.name
|
||||
- recordset["recordset"].records == records
|
||||
|
||||
- name: Update a recordset
|
||||
openstack.cloud.recordset:
|
||||
cloud: "{{ cloud }}"
|
||||
zone: "{{ updated_dns_zone.zone.name }}"
|
||||
name: "{{ recordset_name }}"
|
||||
recordset_type: "a"
|
||||
records: "{{ updated_records }}"
|
||||
description: "new test recordset"
|
||||
register: updated_recordset
|
||||
|
||||
- name: Verify recordset info
|
||||
assert:
|
||||
that:
|
||||
- updated_recordset["recordset"].zone_name == dns_zone.zone.name
|
||||
- updated_recordset["recordset"].name == recordset_name
|
||||
- updated_recordset["recordset"].records == updated_records
|
||||
|
||||
- name: Delete recordset
|
||||
openstack.cloud.recordset:
|
||||
cloud: "{{ cloud }}"
|
||||
zone: "{{ updated_dns_zone.zone.name }}"
|
||||
name: "{{ recordset.recordset.name }}"
|
||||
state: absent
|
||||
register: deleted_recordset
|
||||
|
||||
- name: Verify recordset deletion
|
||||
assert:
|
||||
that:
|
||||
- deleted_recordset is successful
|
||||
- deleted_recordset is changed
|
||||
|
||||
- name: Delete dns zone
|
||||
openstack.cloud.dns_zone:
|
||||
cloud: "{{ cloud }}"
|
||||
|
||||
@@ -34,6 +34,14 @@
|
||||
- zone is not changed
|
||||
- zone.zones | length == 1
|
||||
|
||||
- name: Assert keys exist
|
||||
assert:
|
||||
that:
|
||||
- '["action", "attributes", "created_at", "description", "email",
|
||||
"links", "masters", "name", "pool_id", "project_id", "serial",
|
||||
"status", "ttl", "type", "updated_at", "id"] |
|
||||
difference(zone.zones.0.keys()) | length == 0'
|
||||
|
||||
- name: Drop created dns zone
|
||||
openstack.cloud.dns_zone:
|
||||
cloud: "{{ cloud }}"
|
||||
|
||||
65
ci/roles/endpoint/tasks/main.yml
Normal file
65
ci/roles/endpoint/tasks/main.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
- name: Create a service for compute
|
||||
openstack.cloud.endpoint:
|
||||
cloud: "{{ cloud }}"
|
||||
service: nova
|
||||
endpoint_interface: internal
|
||||
url: http://controller:9292
|
||||
region: RegionOne
|
||||
state: present
|
||||
register: endpoint_test
|
||||
|
||||
- name: Ensure service was created
|
||||
assert:
|
||||
that:
|
||||
- endpoint_test.endpoint.id is defined
|
||||
|
||||
- name: Ensure service have the proper endpoint
|
||||
assert:
|
||||
that:
|
||||
- endpoint_test.endpoint.url == "http://controller:9292"
|
||||
|
||||
- name: Create service for compute again
|
||||
openstack.cloud.endpoint:
|
||||
cloud: "{{ cloud }}"
|
||||
service: nova
|
||||
endpoint_interface: internal
|
||||
url: http://controller:9292
|
||||
region: RegionOne
|
||||
state: present
|
||||
register: endpoint_again
|
||||
|
||||
- name: Ensure changed is false
|
||||
assert:
|
||||
that:
|
||||
- not endpoint_again.changed
|
||||
|
||||
- name: Update endpoint url
|
||||
openstack.cloud.endpoint:
|
||||
cloud: "{{ cloud }}"
|
||||
service: nova
|
||||
endpoint_interface: internal
|
||||
url: http://controller:9393
|
||||
region: RegionOne
|
||||
state: present
|
||||
register: endpoint_updated
|
||||
|
||||
- name: Ensure endpoint was updated
|
||||
assert:
|
||||
that:
|
||||
- endpoint_updated.endpoint.url == "http://controller:9393"
|
||||
|
||||
- name: Delete endpoint
|
||||
openstack.cloud.endpoint:
|
||||
cloud: "{{ cloud }}"
|
||||
service: nova
|
||||
endpoint_interface: internal
|
||||
url: http://controller:9393
|
||||
region: RegionOne
|
||||
state: absent
|
||||
register: endpoint_deleted
|
||||
|
||||
- name: Ensure endpoint was deleted
|
||||
assert:
|
||||
that:
|
||||
- endpoint_deleted.changed
|
||||
@@ -9,3 +9,13 @@
|
||||
that:
|
||||
- fips is success
|
||||
- fips is not changed
|
||||
|
||||
- name: assert fields
|
||||
when: fips.floating_ips|length > 0
|
||||
assert:
|
||||
that:
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- '["created_at", "description", "dns_domain", "dns_name", "fixed_ip_address", "floating_ip_address",
|
||||
"floating_network_id", "id", "name", "port_details", "port_id", "project_id", "qos_policy_id",
|
||||
"revision_number", "router_id", "status", "subnet_id", "tags", "updated_at"]|
|
||||
difference(fips.floating_ips.0.keys())|length == 0'
|
||||
|
||||
10
ci/roles/host_aggregate/defaults/main.yml
Normal file
10
ci/roles/host_aggregate/defaults/main.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
# Parameter deleted has been renamed to is_deleted in openstacksdk 0.52.0,
|
||||
# hence we cannot test with this list here.
|
||||
# Ref.: https://github.com/openstack/openstacksdk/commit/b60915aab3ee0348f3e3cc8aa548f94d2a68b7eb
|
||||
expected_fields:
|
||||
- availability_zone
|
||||
- hosts
|
||||
- id
|
||||
- location
|
||||
- metadata
|
||||
- name
|
||||
99
ci/roles/host_aggregate/tasks/main.yml
Normal file
99
ci/roles/host_aggregate/tasks/main.yml
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
- name: ensure aggregate doesn't exist before tests
|
||||
openstack.cloud.host_aggregate:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: test_aggregate
|
||||
register: aggregate
|
||||
|
||||
- block:
|
||||
- name: create aggregate
|
||||
openstack.cloud.host_aggregate:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: test_aggregate
|
||||
hosts:
|
||||
- "{{ ansible_hostname }}"
|
||||
register: aggregate
|
||||
|
||||
- name: assert aggregate is changed
|
||||
assert:
|
||||
that: aggregate is changed
|
||||
|
||||
- name: assert aggregate fields
|
||||
assert:
|
||||
that: item in aggregate.aggregate
|
||||
loop: "{{ expected_fields }}"
|
||||
|
||||
- block:
|
||||
- name: recreate aggregate
|
||||
openstack.cloud.host_aggregate:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: test_aggregate
|
||||
hosts:
|
||||
- "{{ ansible_hostname }}"
|
||||
register: aggregate
|
||||
|
||||
- name: assert aggregate is not changed
|
||||
assert:
|
||||
that: aggregate is not changed
|
||||
|
||||
- name: assert aggregate fields
|
||||
assert:
|
||||
that: item in aggregate.aggregate
|
||||
loop: "{{ expected_fields }}"
|
||||
|
||||
- block:
|
||||
- name: update aggregate
|
||||
openstack.cloud.host_aggregate:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: test_aggregate
|
||||
metadata:
|
||||
ssd: "true"
|
||||
hosts:
|
||||
- "{{ ansible_hostname }}"
|
||||
register: aggregate
|
||||
|
||||
- name: assert aggregate is changed
|
||||
assert:
|
||||
that: aggregate is changed
|
||||
|
||||
- name: assert aggregate fields
|
||||
assert:
|
||||
that: item in aggregate.aggregate
|
||||
loop: "{{ expected_fields }}"
|
||||
|
||||
- block:
|
||||
- name: purge hosts
|
||||
openstack.cloud.host_aggregate:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: test_aggregate
|
||||
hosts: []
|
||||
purge_hosts: true
|
||||
register: aggregate
|
||||
|
||||
- name: assert hosts were purged
|
||||
assert:
|
||||
that:
|
||||
- aggregate is changed
|
||||
- aggregate.aggregate.hosts | length == 0
|
||||
|
||||
- name: assert aggregate fields
|
||||
assert:
|
||||
that: item in aggregate.aggregate
|
||||
loop: "{{ expected_fields }}"
|
||||
|
||||
- block:
|
||||
- name: delete aggregate
|
||||
openstack.cloud.host_aggregate:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: test_aggregate
|
||||
register: aggregate
|
||||
|
||||
- name: assert aggregate is changed
|
||||
assert:
|
||||
that: aggregate is changed
|
||||
8
ci/roles/identity_domain_info/defaults/main.yml
Normal file
8
ci/roles/identity_domain_info/defaults/main.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
domain_name: domain_info_test_domain
|
||||
unexistent_domain_name: domain_info_unexistent_domain
|
||||
disabled_domain_name: test_domain_disabled
|
||||
domain_info_fields:
|
||||
- description
|
||||
- id
|
||||
- enabled
|
||||
- name
|
||||
72
ci/roles/identity_domain_info/tasks/main.yml
Normal file
72
ci/roles/identity_domain_info/tasks/main.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
- block:
|
||||
- name: Ensure domain does not exist
|
||||
openstack.cloud.identity_domain:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ unexistent_domain_name }}"
|
||||
- name: Get unexistent domain
|
||||
openstack.cloud.identity_domain_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ unexistent_domain_name }}"
|
||||
register: domain_info
|
||||
- name: Assert no results returned
|
||||
assert:
|
||||
that: not domain_info.openstack_domains
|
||||
|
||||
|
||||
- block:
|
||||
- name: Ensure domain exists
|
||||
openstack.cloud.identity_domain:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ domain_name }}"
|
||||
description: "test description"
|
||||
register: domain
|
||||
- name: Get domain
|
||||
openstack.cloud.identity_domain_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ domain_name }}"
|
||||
register: domain_info
|
||||
- name: Assert one result exists
|
||||
assert:
|
||||
that: domain_info.openstack_domains | length == 1
|
||||
- name: Assert fields are present
|
||||
assert:
|
||||
that: item in domain_info.openstack_domains[0]
|
||||
loop: "{{ domain_info_fields }}"
|
||||
- name: Assert returned value
|
||||
assert:
|
||||
that:
|
||||
- domain_info.openstack_domains[0].description == domain.domain.description
|
||||
|
||||
- block:
|
||||
- name: Get all domains
|
||||
openstack.cloud.identity_domain_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: domain_info
|
||||
|
||||
- block:
|
||||
- name: Ensure disabled domain exists
|
||||
openstack.cloud.identity_domain:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ disabled_domain_name }}"
|
||||
enabled: false
|
||||
description: "test description"
|
||||
register: domain
|
||||
- name: Get filtered domains
|
||||
openstack.cloud.identity_domain_info:
|
||||
cloud: "{{ cloud }}"
|
||||
filters:
|
||||
enabled: true
|
||||
register: domain_info
|
||||
- name: Assert at least one result
|
||||
assert:
|
||||
that: domain_info.openstack_domains | length >= 1
|
||||
- name: Assert returned value
|
||||
assert:
|
||||
that: item.enabled == true
|
||||
loop: "{{ domain_info.openstack_domains }}"
|
||||
|
||||
|
||||
74
ci/roles/identity_group_info/tasks/main.yml
Normal file
74
ci/roles/identity_group_info/tasks/main.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
- name: List group by domain_id
|
||||
openstack.cloud.identity_group_info:
|
||||
cloud: "{{ cloud }}"
|
||||
domain: default
|
||||
register: group_domain
|
||||
|
||||
- name: Assert groups were returned
|
||||
assert:
|
||||
that:
|
||||
- group_domain.openstack_groups | length > 0
|
||||
- group_domain.openstack_groups[0].domain_id == 'default'
|
||||
- group_domain.openstack_groups[0].id is defined
|
||||
- group_domain.openstack_groups[0].description is defined
|
||||
- group_domain.openstack_groups[0].name is defined
|
||||
|
||||
- name: List group by domain_id and group
|
||||
openstack.cloud.identity_group_info:
|
||||
cloud: "{{ cloud }}"
|
||||
domain: default
|
||||
name: admins
|
||||
register: groups_info
|
||||
|
||||
- name: Assert groups by domain_id and grouph returned
|
||||
assert:
|
||||
that:
|
||||
- groups_info.openstack_groups | length > 0
|
||||
- groups_info.openstack_groups[0].domain_id == 'default'
|
||||
- groups_info.openstack_groups[0].id is defined
|
||||
- groups_info.openstack_groups[0].description is defined
|
||||
- groups_info.openstack_groups[0].name is defined
|
||||
|
||||
- name: List group by filter
|
||||
openstack.cloud.identity_group_info:
|
||||
cloud: "{{ cloud }}"
|
||||
domain: default
|
||||
filters:
|
||||
name: admins
|
||||
register: groups_filter
|
||||
|
||||
- name: Assert group by filter returned
|
||||
assert:
|
||||
that:
|
||||
- groups_filter.openstack_groups | length > 0
|
||||
- groups_filter.openstack_groups[0].domain_id == 'default'
|
||||
- groups_filter.openstack_groups[0].id is defined
|
||||
- groups_filter.openstack_groups[0].description is defined
|
||||
- groups_filter.openstack_groups[0].name is defined
|
||||
|
||||
- name: Verify returned values of group info
|
||||
assert:
|
||||
that:
|
||||
- item in groups_info.openstack_groups[0]
|
||||
loop:
|
||||
- description
|
||||
- domain_id
|
||||
- id
|
||||
- name
|
||||
|
||||
- name: List group by group name
|
||||
openstack.cloud.identity_group_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: admins
|
||||
register: groups_name
|
||||
|
||||
- name: Assert group by name returned
|
||||
assert:
|
||||
that:
|
||||
- groups_name.openstack_groups | length > 0
|
||||
- groups_name.openstack_groups[0].domain_id == 'default'
|
||||
- groups_name.openstack_groups[0].id is defined
|
||||
- groups_name.openstack_groups[0].description is defined
|
||||
- groups_name.openstack_groups[0].name is defined
|
||||
- groups_name.openstack_groups[0].name == 'admins'
|
||||
5
ci/roles/identity_role/defaults/main.yml
Normal file
5
ci/roles/identity_role/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
role_name: ansible_keystone_role
|
||||
expected_fields:
|
||||
- domain_id
|
||||
- id
|
||||
- name
|
||||
83
ci/roles/identity_role/tasks/main.yml
Normal file
83
ci/roles/identity_role/tasks/main.yml
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
- name: Cleanup before tests
|
||||
block:
|
||||
- openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ role_name }}"
|
||||
|
||||
- block:
|
||||
- name: Delete unexistent role
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ role_name }}"
|
||||
register: role
|
||||
- name: Assert role didn't change
|
||||
assert:
|
||||
that: role is not changed
|
||||
|
||||
- block:
|
||||
- name: Create keystone role
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ role_name }}"
|
||||
register: role
|
||||
- name: Try to get role
|
||||
openstack.cloud.identity_role_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ role_name }}"
|
||||
register: roles
|
||||
- name: Assert role found
|
||||
assert:
|
||||
that:
|
||||
- roles.openstack_roles | length == 1
|
||||
- name: Assert role changed
|
||||
assert:
|
||||
that: role is changed
|
||||
- name: Assert return fields
|
||||
assert:
|
||||
that: item in role['role']
|
||||
loop: "{{ expected_fields }}"
|
||||
- name: Assert return value
|
||||
assert:
|
||||
that: role['role']['name'] == role_name
|
||||
- name: Assert retrieved values
|
||||
assert:
|
||||
that: roles.openstack_roles[0].name == role_name
|
||||
|
||||
- block:
|
||||
- name: Create existing keystone role
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ role_name }}"
|
||||
register: role
|
||||
- name: Assert role not changed
|
||||
assert:
|
||||
that: role is not changed
|
||||
- name: Assert return fields
|
||||
assert:
|
||||
that: item in role['role']
|
||||
loop: "{{ expected_fields }}"
|
||||
|
||||
- block:
|
||||
- name: Delete keystone role
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ role_name }}"
|
||||
register: role
|
||||
- name: Assert role changed
|
||||
assert:
|
||||
that: role is changed
|
||||
- name: Try to get role
|
||||
openstack.cloud.identity_role_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ role_name }}"
|
||||
register: roles
|
||||
- name: Assert no role found
|
||||
assert:
|
||||
that:
|
||||
- roles.openstack_roles | length == 0
|
||||
62
ci/roles/identity_role_info/tasks/main.yml
Normal file
62
ci/roles/identity_role_info/tasks/main.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
- name: Ensure role does not exist before tests
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: test_role
|
||||
|
||||
- name: Get unexistent role
|
||||
openstack.cloud.identity_role_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: test_role
|
||||
register: roleinfo
|
||||
|
||||
- debug:
|
||||
var: roleinfo
|
||||
|
||||
- name: Assert that no results were returned
|
||||
assert:
|
||||
that: not roleinfo.openstack_roles
|
||||
|
||||
- name: Create keystone role
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: test_role
|
||||
|
||||
- name: Create second role
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: test_role2
|
||||
|
||||
- name: Get role by name
|
||||
openstack.cloud.identity_role_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: test_role
|
||||
register: roleinfo
|
||||
|
||||
- debug:
|
||||
var: roleinfo
|
||||
|
||||
- name: Assert that only one result was returned
|
||||
assert:
|
||||
that: roleinfo.openstack_roles | length == 1
|
||||
|
||||
- name: Assert that roleinfo has fields
|
||||
assert:
|
||||
that: item in roleinfo.openstack_roles[0]
|
||||
loop:
|
||||
- domain_id
|
||||
- id
|
||||
- name
|
||||
|
||||
- name: Post-test cleanup
|
||||
block:
|
||||
- name: Clean up roles
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ item }}"
|
||||
loop:
|
||||
- test_role
|
||||
- test_role2
|
||||
9
ci/roles/identity_user/defaults/main.yml
Normal file
9
ci/roles/identity_user/defaults/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
os_identity_user_fields:
|
||||
- default_project_id
|
||||
- description
|
||||
- domain_id
|
||||
- email
|
||||
- enabled
|
||||
- id
|
||||
- name
|
||||
- username
|
||||
197
ci/roles/identity_user/tasks/main.yml
Normal file
197
ci/roles/identity_user/tasks/main.yml
Normal file
@@ -0,0 +1,197 @@
|
||||
---
|
||||
- name: setup
|
||||
block:
|
||||
- name: Delete user before running tests
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ item }}"
|
||||
loop:
|
||||
- ansible_user
|
||||
- ansible_user2
|
||||
register: user
|
||||
|
||||
- block:
|
||||
- name: Delete unexistent user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
||||
register: user
|
||||
|
||||
- name: Ensure user was not changed
|
||||
assert:
|
||||
that: user is not changed
|
||||
|
||||
- block:
|
||||
- name: Create a user without a password
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
email: ansible.user@nowhere.net
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- name: Ensure user was changed
|
||||
assert:
|
||||
that: user is changed
|
||||
|
||||
- name: Ensure user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- name: Fail when update_password is always but no password specified
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
update_password: always
|
||||
email: ansible.user@nowhere.net
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that: user.msg == "update_password is always but a password value is missing"
|
||||
|
||||
- name: Delete user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
||||
|
||||
- block:
|
||||
- name: Create user with a password
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: ansible.user@nowhere.net
|
||||
update_password: on_create
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- name: Assert user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- block:
|
||||
- name: Create identical user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: ansible.user@nowhere.net
|
||||
update_password: on_create
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- name: Assert user was not changed
|
||||
assert:
|
||||
that: user is not changed
|
||||
|
||||
- name: Assert user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- block:
|
||||
- name: Update user with password
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret2
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: user
|
||||
|
||||
- name: Ensure user was changed
|
||||
assert:
|
||||
that: user is changed
|
||||
|
||||
- name: Ensure user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- name: Update user without password and update_password set to always
|
||||
block:
|
||||
- openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
update_password: always
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: user
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that: user.msg == "update_password is always but a password value is missing"
|
||||
|
||||
- block:
|
||||
- name: Ensure user with update_password set to on_create
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
update_password: on_create
|
||||
password: secret3
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: user
|
||||
|
||||
- name: Ensure user was not changed
|
||||
assert:
|
||||
that: user is not changed
|
||||
|
||||
- block:
|
||||
- name: Ensure user with update_password set to always
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
update_password: always
|
||||
password: secret3
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: user
|
||||
|
||||
- name: Ensure user was changed
|
||||
assert:
|
||||
that: user is changed
|
||||
|
||||
- block:
|
||||
- name: Create user without a password
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user2
|
||||
password: secret
|
||||
email: ansible.user2@nowhere.net
|
||||
update_password: on_create
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- name: Assert user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- block:
|
||||
- name: Delete user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
||||
|
||||
- name: Ensure user was changed
|
||||
assert:
|
||||
that: user is changed
|
||||
9
ci/roles/identity_user_info/defaults/main.yml
Normal file
9
ci/roles/identity_user_info/defaults/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
os_expected_user_info_fields:
|
||||
- default_project_id
|
||||
- description
|
||||
- domain_id
|
||||
- email
|
||||
- enabled
|
||||
- id
|
||||
- name
|
||||
- username
|
||||
69
ci/roles/identity_user_info/tasks/main.yml
Normal file
69
ci/roles/identity_user_info/tasks/main.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
- name: Ensure user does not exist before tests
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
||||
|
||||
- block:
|
||||
- name: Get unexistent user
|
||||
openstack.cloud.identity_user_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_user
|
||||
register: userinfo
|
||||
- name: Ensure nothing was returned
|
||||
assert:
|
||||
that: not userinfo.openstack_users
|
||||
|
||||
- block:
|
||||
- name: Create user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: ansible.user@nowhere.net
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
- name: Create second user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user2
|
||||
password: secret
|
||||
email: ansible.user2@nowhere.net
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
- name: Get first user info
|
||||
openstack.cloud.identity_user_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_user
|
||||
register: userinfo
|
||||
- name: Assert only one result exists
|
||||
assert:
|
||||
that: "{{ userinfo.openstack_users | length }} == 1"
|
||||
- name: Assert userinfo has fields
|
||||
assert:
|
||||
that: item in userinfo.openstack_users[0]
|
||||
loop: "{{ os_expected_user_info_fields }}"
|
||||
|
||||
- block:
|
||||
- name: Get all users
|
||||
openstack.cloud.identity_user_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: userinfo
|
||||
- name: Assert results were returned
|
||||
assert:
|
||||
that: "{{ userinfo.openstack_users | length }} > 0"
|
||||
|
||||
- name: Post-test cleanup
|
||||
block:
|
||||
- name: Ensure users do not exist
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ item }}"
|
||||
loop:
|
||||
- ansible_user
|
||||
- ansible_user2
|
||||
@@ -25,8 +25,8 @@
|
||||
- name: Verify image info
|
||||
assert:
|
||||
that:
|
||||
- "image_info_result.openstack_image.name == image_name"
|
||||
- "image_info_result.openstack_image.tags | sort == image_tags | sort"
|
||||
- "image_info_result.openstack_images[0].name == image_name"
|
||||
- "image_info_result.openstack_images[0].tags | sort == image_tags | sort"
|
||||
|
||||
- name: Delete raw image (defaults)
|
||||
openstack.cloud.image:
|
||||
@@ -57,11 +57,6 @@
|
||||
state: absent
|
||||
name: "{{ image_name }}"
|
||||
|
||||
- name: Delete test image file
|
||||
file:
|
||||
name: "{{ tmp_file.stdout }}"
|
||||
state: absent
|
||||
|
||||
- name: Try to get details of deleted image
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
@@ -71,4 +66,83 @@
|
||||
- name: Verify image is deleted
|
||||
assert:
|
||||
that:
|
||||
- not deleted_image_info_result.openstack_image
|
||||
- not deleted_image_info_result.openstack_images
|
||||
|
||||
- name: Create owner project
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: image_owner_project
|
||||
description: Project owning test image
|
||||
domain_id: default
|
||||
enabled: True
|
||||
register: owner_project
|
||||
|
||||
- name: Create raw image (owner by project name)
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ image_name }}"
|
||||
filename: "{{ tmp_file.stdout }}"
|
||||
disk_format: raw
|
||||
tags: "{{ image_tags }}"
|
||||
project: image_owner_project
|
||||
register: image
|
||||
|
||||
- name: Get details of created image (owner by project name)
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
image: "{{ image_name }}"
|
||||
register: image_info_result
|
||||
|
||||
- name: Verify image owner (owner by project name)
|
||||
assert:
|
||||
that:
|
||||
- image_info_result.openstack_images[0].owner == owner_project.project.id
|
||||
|
||||
- name: Delete raw image (owner by project name)
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ image_name }}"
|
||||
|
||||
- name: Create raw image (owner by project name and domain name)
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ image_name }}"
|
||||
filename: "{{ tmp_file.stdout }}"
|
||||
disk_format: raw
|
||||
tags: "{{ image_tags }}"
|
||||
project: image_owner_project
|
||||
project_domain: default
|
||||
register: image
|
||||
|
||||
- name: Get details of created image (owner by project name and domain name)
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
image: "{{ image_name }}"
|
||||
register: image_info_result
|
||||
|
||||
- name: Verify image owner (owner by project name and domain name)
|
||||
assert:
|
||||
that:
|
||||
- image_info_result.openstack_images[0].owner == owner_project.project.id
|
||||
|
||||
- name: Delete raw image (owner by project name and domain name)
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ image_name }}"
|
||||
|
||||
- name: Delete owner project
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: image_owner_project
|
||||
domain_id: default
|
||||
|
||||
- name: Delete test image file
|
||||
file:
|
||||
name: "{{ tmp_file.stdout }}"
|
||||
state: absent
|
||||
|
||||
23
ci/roles/image_info/defaults/main.yml
Normal file
23
ci/roles/image_info/defaults/main.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
expected_fields:
|
||||
- checksum
|
||||
- container_format
|
||||
- created_at
|
||||
- direct_url
|
||||
- disk_format
|
||||
- file
|
||||
- id
|
||||
- locations
|
||||
- metadata
|
||||
- min_disk
|
||||
- min_ram
|
||||
- name
|
||||
- os_hidden
|
||||
- owner
|
||||
- properties
|
||||
- schema
|
||||
- size
|
||||
- status
|
||||
- tags
|
||||
- updated_at
|
||||
- virtual_size
|
||||
- visibility
|
||||
11
ci/roles/image_info/tasks/main.yml
Normal file
11
ci/roles/image_info/tasks/main.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: List all images # This will list at least the default cirros image of devstack
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: image_list_result
|
||||
|
||||
- name: Assert fields
|
||||
assert:
|
||||
that:
|
||||
- item in image_list_result.openstack_images.0.keys()
|
||||
loop: "{{ expected_fields }}"
|
||||
@@ -1 +1,11 @@
|
||||
keypair_name: shade_keypair
|
||||
expected_fields:
|
||||
- created_at
|
||||
- fingerprint
|
||||
- id
|
||||
- is_deleted
|
||||
- name
|
||||
- private_key
|
||||
- public_key
|
||||
- type
|
||||
- user_id
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
---
|
||||
- name: Create keypair (non-existing)
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: present
|
||||
register:
|
||||
keypair
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: present
|
||||
register: keypair
|
||||
|
||||
- name: Get list of keypairs
|
||||
- name: Get list of all keypairs
|
||||
openstack.cloud.keypair_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: keypairs_all
|
||||
|
||||
- name: Get list of keypairs with filter
|
||||
openstack.cloud.keypair_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
@@ -18,17 +22,35 @@
|
||||
that:
|
||||
- keypairs['openstack_keypairs']|length == 1
|
||||
|
||||
- name: Assert fields
|
||||
assert:
|
||||
that:
|
||||
- item in keypairs.openstack_keypairs.0.keys()
|
||||
loop: "{{ expected_fields }}"
|
||||
|
||||
# This assert verifies that Ansible is capable serializing data returned by SDK
|
||||
- name: Ensure private key is returned
|
||||
- name: Ensure public key is returned
|
||||
assert:
|
||||
that:
|
||||
- keypair.key.public_key is defined and keypair.key.public_key
|
||||
|
||||
- name: Create another keypair
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}-2"
|
||||
state: present
|
||||
|
||||
- name: Delete keypair (non-existing)
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: absent
|
||||
cloud: "{{ cloud }}"
|
||||
name: "non-existing"
|
||||
state: absent
|
||||
|
||||
- name: Delete keypair
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: absent
|
||||
|
||||
- name: Get list of keypairs
|
||||
openstack.cloud.keypair_info:
|
||||
@@ -41,18 +63,24 @@
|
||||
that:
|
||||
- keypairs['openstack_keypairs']|length == 0
|
||||
|
||||
- name: Delete another keypair
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}-2"
|
||||
state: absent
|
||||
|
||||
- name: Generate test key file
|
||||
user:
|
||||
name: "{{ ansible_env.USER }}"
|
||||
generate_ssh_key: yes
|
||||
ssh_key_file: .ssh/shade_id_rsa
|
||||
name: "{{ ansible_env.USER }}"
|
||||
generate_ssh_key: yes
|
||||
ssh_key_file: .ssh/shade_id_rsa
|
||||
|
||||
- name: Create keypair (file)
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: present
|
||||
public_key_file: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: present
|
||||
public_key_file: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
|
||||
|
||||
- name: Get list of keypairs
|
||||
openstack.cloud.keypair_info:
|
||||
@@ -67,9 +95,9 @@
|
||||
|
||||
- name: Delete keypair (file)
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: absent
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: absent
|
||||
|
||||
- name: Get list of keypairs
|
||||
openstack.cloud.keypair_info:
|
||||
@@ -84,10 +112,10 @@
|
||||
|
||||
- name: Create keypair (key)
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: present
|
||||
public_key: "{{ lookup('file', '~/.ssh/shade_id_rsa.pub') }}"
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: present
|
||||
public_key: "{{ lookup('file', '~/.ssh/shade_id_rsa.pub') }}"
|
||||
|
||||
- name: Get list of keypairs
|
||||
openstack.cloud.keypair_info:
|
||||
@@ -102,9 +130,9 @@
|
||||
|
||||
- name: Delete keypair (key)
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: absent
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ keypair_name }}"
|
||||
state: absent
|
||||
|
||||
- name: Get list of keypairs
|
||||
openstack.cloud.keypair_info:
|
||||
@@ -119,10 +147,10 @@
|
||||
|
||||
- name: Delete test key pub file
|
||||
file:
|
||||
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
|
||||
state: absent
|
||||
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
|
||||
state: absent
|
||||
|
||||
- name: Delete test key pvt file
|
||||
file:
|
||||
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa"
|
||||
state: absent
|
||||
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa"
|
||||
state: absent
|
||||
|
||||
@@ -1,19 +1,36 @@
|
||||
---
|
||||
- name: Create keystone domain
|
||||
openstack.cloud.identity_domain:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ domain_name }}"
|
||||
description: "test description"
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ domain_name }}"
|
||||
description: "test description"
|
||||
register: os_domain
|
||||
|
||||
- name: Test output
|
||||
assert:
|
||||
that:
|
||||
- "'domain' in os_domain"
|
||||
- os_domain.domain.name == "{{ domain_name }}"
|
||||
- >-
|
||||
('enabled' in os_domain.domain.keys() and os_domain.domain['enabled']|bool) or
|
||||
('is_enabled' in os_domain.domain and os_domain.domain['is_enabled']|bool)
|
||||
- os_domain.domain.description == "test description"
|
||||
|
||||
- name: Update keystone domain
|
||||
openstack.cloud.identity_domain:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ domain_name }}"
|
||||
description: "updated description"
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ domain_name }}"
|
||||
description: "updated description"
|
||||
register: os_domain_updated
|
||||
|
||||
- name: Test output
|
||||
assert:
|
||||
that:
|
||||
- os_domain_updated.domain.description == "updated description"
|
||||
|
||||
- name: Delete keystone domain
|
||||
openstack.cloud.identity_domain:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ domain_name }}"
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ domain_name }}"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
role_name: ansible_keystone_role
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
- name: Create keystone role
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ role_name }}"
|
||||
|
||||
- name: List keystone roles
|
||||
openstack.cloud.identity_role_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: roles
|
||||
|
||||
- name: Check roles
|
||||
assert:
|
||||
that:
|
||||
- roles.openstack_roles | length > 0
|
||||
- "'{{ role_name }}' in (roles.openstack_roles | map(attribute='name') | list)"
|
||||
|
||||
- name: List keystone roles by name
|
||||
openstack.cloud.identity_role_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ role_name}}"
|
||||
register: roles1
|
||||
|
||||
- name: Check roles
|
||||
assert:
|
||||
that:
|
||||
- roles1.openstack_roles | length == 1
|
||||
- roles1.openstack_roles[0]['name'] == role_name
|
||||
|
||||
- name: Delete keystone role
|
||||
openstack.cloud.identity_role:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ role_name }}"
|
||||
1
ci/roles/logging/defaults/main.yaml
Normal file
1
ci/roles/logging/defaults/main.yaml
Normal file
@@ -0,0 +1 @@
|
||||
sdk_log_file_path: "{{ playbook_dir }}/sdk.log"
|
||||
20
ci/roles/logging/tasks/main.yaml
Normal file
20
ci/roles/logging/tasks/main.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
- name: Trigger flavor listing
|
||||
openstack.cloud.compute_flavor_info:
|
||||
cloud: "{{ cloud }}"
|
||||
sdk_log_path: "{{ sdk_log_file_path }}"
|
||||
sdk_log_level: "DEBUG"
|
||||
|
||||
- name: Check log file presence
|
||||
ansible.builtin.stat:
|
||||
path: "{{ sdk_log_file_path }}"
|
||||
register: sdk_log_file
|
||||
|
||||
- name: Assert
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "sdk_log_file.stat.exists"
|
||||
|
||||
- name: Debug log file content
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('ansible.builtin.file', sdk_log_file_path) }}"
|
||||
@@ -3,13 +3,31 @@
|
||||
- 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"
|
||||
- name: Assert fields on OpenStack SDK before 0.53
|
||||
when: sdk_version is version(0.53, '<')
|
||||
assert:
|
||||
that:
|
||||
- '["availability_zone", "binary", "disables_reason",
|
||||
"host", "name", "state", "status", "id"] |
|
||||
difference(result.openstack_compute_services.0.keys()) | length == 0'
|
||||
|
||||
- name: Assert fields on OpenStack SDK 0.53 and later
|
||||
when: sdk_version is version(0.53, '>=')
|
||||
assert:
|
||||
that:
|
||||
- '["availability_zone", "binary", "disabled_reason", "is_forced_down",
|
||||
"host", "name", "state", "status", "updated_at", "id"] |
|
||||
difference(result.openstack_compute_services.0.keys()) | length == 0'
|
||||
|
||||
- name: Filter compute services. Supported since OpenStack SDK 0.53.0 (Wallaby).
|
||||
when: sdk_version is version(0.53, '>=')
|
||||
block:
|
||||
- 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"
|
||||
|
||||
@@ -41,4 +41,4 @@
|
||||
- assert:
|
||||
that:
|
||||
- stacks is defined
|
||||
- stacks['stacks']|length == 0
|
||||
- (stacks['stacks']|length == 0) or (stacks['stacks'][0]['status'] == 'DELETE_COMPLETE')
|
||||
|
||||
@@ -60,6 +60,26 @@
|
||||
state: absent
|
||||
name: "{{ port_name }}"
|
||||
|
||||
- name: Create port (with dns_name, dns_domain)
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ port_name }}"
|
||||
network: "{{ network_name }}"
|
||||
fixed_ips:
|
||||
- ip_address: 10.5.5.69
|
||||
dns_name: "dns-port-name"
|
||||
dns_domain: "example.com."
|
||||
register: port
|
||||
|
||||
- debug: var=port
|
||||
|
||||
- name: Delete port (with dns name,domain)
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ port_name }}"
|
||||
|
||||
- name: Create port (with allowed_address_pairs and extra_dhcp_opts)
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
@@ -101,7 +121,7 @@
|
||||
|
||||
- name: Assert binding:profile exists in created port
|
||||
assert:
|
||||
that: "port.port['binding:profile']"
|
||||
that: "port.port['binding_profile']"
|
||||
|
||||
- debug: var=port
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
dummy_value: 'test-value'
|
||||
dummy_value_updated: 'test-value-updated'
|
||||
@@ -1,142 +0,0 @@
|
||||
---
|
||||
- name: 'Create project with properties - CHECK_MODE'
|
||||
check_mode: yes
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: dummy description
|
||||
domain_id: default
|
||||
enabled: True
|
||||
properties:
|
||||
dummy_key: '{{ dummy_value }}'
|
||||
register: create_project_cm
|
||||
- assert:
|
||||
that:
|
||||
- create_project_cm is successful
|
||||
- create_project_cm is changed
|
||||
|
||||
- name: 'Create project with properties'
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: dummy description
|
||||
domain_id: default
|
||||
enabled: True
|
||||
properties:
|
||||
dummy_key: '{{ dummy_value }}'
|
||||
register: create_project
|
||||
- assert:
|
||||
that:
|
||||
- create_project is successful
|
||||
- create_project is changed
|
||||
- '"project" in create_project'
|
||||
- '"dummy_key" in create_project["project"]'
|
||||
- create_project["project"].dummy_key == dummy_value
|
||||
|
||||
- name: 'Create project with properties (retry - no change) - CHECK_MODE'
|
||||
check_mode: yes
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: dummy description
|
||||
domain_id: default
|
||||
enabled: True
|
||||
properties:
|
||||
dummy_key: '{{ dummy_value }}'
|
||||
register: create_project_retry_cm
|
||||
- assert:
|
||||
that:
|
||||
- create_project_retry_cm is successful
|
||||
- create_project_retry_cm is not changed
|
||||
|
||||
- name: 'Create project with properties (retry - no change)'
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: dummy description
|
||||
domain_id: default
|
||||
enabled: True
|
||||
properties:
|
||||
dummy_key: '{{ dummy_value }}'
|
||||
register: create_project_retry
|
||||
- assert:
|
||||
that:
|
||||
- create_project_retry is successful
|
||||
- create_project_retry is not changed
|
||||
- '"project" in create_project_retry'
|
||||
- '"dummy_key" in create_project_retry["project"]'
|
||||
- create_project_retry["project"].dummy_key == dummy_value
|
||||
|
||||
- name: 'Update project with properties - CHECK_MODE'
|
||||
check_mode: yes
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: new description
|
||||
properties:
|
||||
dummy_key: '{{ dummy_value_updated }}'
|
||||
register: updated_project_cm
|
||||
- assert:
|
||||
that:
|
||||
- updated_project_cm is successful
|
||||
- updated_project_cm is changed
|
||||
|
||||
- name: 'Update project with properties'
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: new description
|
||||
properties:
|
||||
dummy_key: '{{ dummy_value_updated }}'
|
||||
register: updated_project
|
||||
- assert:
|
||||
that:
|
||||
- updated_project is successful
|
||||
- updated_project is changed
|
||||
- '"project" in updated_project'
|
||||
- '"dummy_key" in updated_project["project"]'
|
||||
- updated_project["project"].dummy_key == dummy_value_updated
|
||||
|
||||
- name: 'Update project with properties (retry - no change) - CHECK_MODE'
|
||||
check_mode: yes
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: new description
|
||||
properties:
|
||||
dummy_key: '{{ dummy_value_updated }}'
|
||||
register: updated_project_retry_cm
|
||||
- assert:
|
||||
that:
|
||||
- updated_project_retry_cm is successful
|
||||
- updated_project_retry_cm is not changed
|
||||
|
||||
- name: 'Update project with properties (retry - no change)'
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: new description
|
||||
properties:
|
||||
dummy_key: '{{ dummy_value_updated }}'
|
||||
register: updated_project_retry
|
||||
- assert:
|
||||
that:
|
||||
- updated_project_retry is successful
|
||||
- updated_project_retry is not changed
|
||||
- '"project" in updated_project_retry'
|
||||
- '"dummy_key" in updated_project_retry["project"]'
|
||||
- updated_project_retry["project"].dummy_key == dummy_value_updated
|
||||
|
||||
- name: Delete project with properties
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_project
|
||||
19
ci/roles/recordset/defaults/main.yml
Normal file
19
ci/roles/recordset/defaults/main.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
dns_zone_name: test.dns.zone.
|
||||
recordset_name: testrecordset.test.dns.zone.
|
||||
records: ['10.0.0.0']
|
||||
updated_records: ['10.1.1.1']
|
||||
|
||||
recordset_fields:
|
||||
- action
|
||||
- created_at
|
||||
- description
|
||||
- id
|
||||
- links
|
||||
- name
|
||||
- project_id
|
||||
- records
|
||||
- status
|
||||
- ttl
|
||||
- type
|
||||
- zone_id
|
||||
- zone_name
|
||||
112
ci/roles/recordset/tasks/main.yml
Normal file
112
ci/roles/recordset/tasks/main.yml
Normal file
@@ -0,0 +1,112 @@
|
||||
- name: Ensure DNS zone not present before tests
|
||||
openstack.cloud.dns_zone:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ dns_zone_name }}"
|
||||
zone_type: "primary"
|
||||
email: test@example.net
|
||||
state: absent
|
||||
|
||||
- name: Ensure dns zone
|
||||
openstack.cloud.dns_zone:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ dns_zone_name }}"
|
||||
zone_type: "primary"
|
||||
email: test@example.net
|
||||
register: dns_zone
|
||||
|
||||
- name: Create a recordset
|
||||
openstack.cloud.recordset:
|
||||
cloud: "{{ cloud }}"
|
||||
zone: "{{ dns_zone.zone.name }}"
|
||||
name: "{{ recordset_name }}"
|
||||
recordset_type: "a"
|
||||
records: "{{ records }}"
|
||||
register: recordset
|
||||
|
||||
- name: Verify recordset info
|
||||
assert:
|
||||
that:
|
||||
- recordset is changed
|
||||
- recordset["recordset"].name == recordset_name
|
||||
- recordset["recordset"].zone_name == dns_zone.zone.name
|
||||
- recordset["recordset"].records == records
|
||||
|
||||
- name: Assert recordset fields
|
||||
assert:
|
||||
that: item in recordset.recordset
|
||||
loop: "{{ recordset_fields }}"
|
||||
|
||||
- name: Create identical recordset
|
||||
openstack.cloud.recordset:
|
||||
cloud: "{{ cloud }}"
|
||||
zone: "{{ dns_zone.zone.name }}"
|
||||
name: "{{ recordset_name }}"
|
||||
recordset_type: "a"
|
||||
records: "{{ records }}"
|
||||
register: recordset
|
||||
|
||||
- name: Assert recordset not changed
|
||||
assert:
|
||||
that:
|
||||
- recordset is not changed
|
||||
|
||||
- name: Assert recordset fields
|
||||
assert:
|
||||
that: item in recordset.recordset
|
||||
loop: "{{ recordset_fields }}"
|
||||
|
||||
- name: Update a recordset
|
||||
openstack.cloud.recordset:
|
||||
cloud: "{{ cloud }}"
|
||||
zone: "{{ dns_zone.zone.name }}"
|
||||
name: "{{ recordset_name }}"
|
||||
recordset_type: "a"
|
||||
records: "{{ updated_records }}"
|
||||
description: "new test recordset"
|
||||
register: recordset
|
||||
|
||||
- name: Verify recordset info
|
||||
assert:
|
||||
that:
|
||||
- recordset is changed
|
||||
- recordset["recordset"].zone_name == dns_zone.zone.name
|
||||
- recordset["recordset"].name == recordset_name
|
||||
- recordset["recordset"].records == updated_records
|
||||
|
||||
- name: Assert recordset fields
|
||||
assert:
|
||||
that: item in recordset.recordset
|
||||
loop: "{{ recordset_fields }}"
|
||||
|
||||
- name: Delete recordset
|
||||
openstack.cloud.recordset:
|
||||
cloud: "{{ cloud }}"
|
||||
zone: "{{ dns_zone.zone.name }}"
|
||||
name: "{{ recordset.recordset.name }}"
|
||||
state: absent
|
||||
register: deleted_recordset
|
||||
|
||||
- name: Verify recordset deletion
|
||||
assert:
|
||||
that:
|
||||
- deleted_recordset is successful
|
||||
- deleted_recordset is changed
|
||||
|
||||
- name: Delete unexistent recordset
|
||||
openstack.cloud.recordset:
|
||||
cloud: "{{ cloud }}"
|
||||
zone: "{{ dns_zone.zone.name }}"
|
||||
name: "{{ recordset.recordset.name }}"
|
||||
state: absent
|
||||
register: deleted_recordset
|
||||
|
||||
- name: Verify recordset deletion
|
||||
assert:
|
||||
that:
|
||||
- deleted_recordset is not changed
|
||||
|
||||
- name: Delete dns zone
|
||||
openstack.cloud.dns_zone:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ dns_zone.zone.name }}"
|
||||
state: absent
|
||||
47
ci/roles/role_assignment/tasks/main.yml
Normal file
47
ci/roles/role_assignment/tasks/main.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
- name: Create project
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_project
|
||||
description: dummy description
|
||||
domain_id: default
|
||||
enabled: True
|
||||
register: project
|
||||
|
||||
- name: Grant an admin role on the user admin in the project ansible_project
|
||||
openstack.cloud.role_assignment:
|
||||
cloud: "{{ cloud }}"
|
||||
domain: default
|
||||
project: ansible_project
|
||||
role: admin
|
||||
user: admin
|
||||
|
||||
- name: Grant an admin role on the user admin in the project ansible_project again
|
||||
openstack.cloud.role_assignment:
|
||||
cloud: "{{ cloud }}"
|
||||
domain: default
|
||||
project: ansible_project
|
||||
role: admin
|
||||
user: admin
|
||||
register: grant_again
|
||||
|
||||
- name: Ensure grant again doesn't change anything
|
||||
assert:
|
||||
that:
|
||||
- not grant_again.changed
|
||||
|
||||
- name: Revoke the admin role on the user admin in the project ansible_project
|
||||
openstack.cloud.role_assignment:
|
||||
cloud: "{{ cloud }}"
|
||||
domain: default
|
||||
project: ansible_project
|
||||
role: admin
|
||||
state: absent
|
||||
user: admin
|
||||
|
||||
- name: Delete project
|
||||
openstack.cloud.project:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_project
|
||||
@@ -1,5 +1,8 @@
|
||||
server_network: private
|
||||
server_name: ansible_server
|
||||
server_alt_network: private_alt
|
||||
server_alt_subnet: subnet_alt
|
||||
server_alt_name: ansible_server_alt
|
||||
flavor: m1.tiny
|
||||
floating_ip_pool_name: public
|
||||
boot_volume_size: 5
|
||||
|
||||
@@ -124,12 +124,8 @@
|
||||
terminate_volume: true
|
||||
wait: true
|
||||
register: server
|
||||
tags:
|
||||
- object
|
||||
|
||||
- debug: var=server
|
||||
tags:
|
||||
- object
|
||||
|
||||
- name: Delete server with volume
|
||||
openstack.cloud.server:
|
||||
@@ -137,9 +133,6 @@
|
||||
state: absent
|
||||
name: "{{ server_name }}"
|
||||
wait: true
|
||||
tags:
|
||||
- object
|
||||
|
||||
- name: Create a minimal server
|
||||
openstack.cloud.server:
|
||||
cloud: "{{ cloud }}"
|
||||
|
||||
@@ -518,3 +518,95 @@
|
||||
that:
|
||||
- info24.openstack_servers.0.status == 'ACTIVE'
|
||||
- server is not changed
|
||||
|
||||
- name: Create network for alternate server
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud_alt }}"
|
||||
name: "{{ server_alt_network }}"
|
||||
state: present
|
||||
|
||||
- name: Create subnet for alternate server
|
||||
openstack.cloud.subnet:
|
||||
cloud: "{{ cloud_alt }}"
|
||||
network_name: "{{ server_alt_network }}"
|
||||
name: "{{ server_alt_subnet }}"
|
||||
state: present
|
||||
cidr: 192.168.0.0/24
|
||||
|
||||
- name: Create server in alternate project
|
||||
openstack.cloud.server:
|
||||
cloud: "{{ cloud_alt }}"
|
||||
state: present
|
||||
name: "{{ server_alt_name }}"
|
||||
image: "{{ image }}"
|
||||
flavor: "{{ flavor }}"
|
||||
network: "{{ server_alt_network }}"
|
||||
auto_floating_ip: false
|
||||
wait: true
|
||||
register: server_alt
|
||||
|
||||
- name: Get info about server in alternate project
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud_alt }}"
|
||||
server: "{{ server_alt_name }}"
|
||||
register: info25
|
||||
|
||||
- name: Ensure status for server in alternate project is ACTIVE
|
||||
assert:
|
||||
that:
|
||||
- info25.openstack_servers.0.status == 'ACTIVE'
|
||||
|
||||
- name: Try to stop server in alternate project
|
||||
openstack.cloud.server_action:
|
||||
cloud: "{{ cloud }}"
|
||||
server: "{{ server_alt_name }}"
|
||||
action: stop
|
||||
wait: true
|
||||
ignore_errors: true
|
||||
register: server_alt
|
||||
|
||||
- name: Ensure server was not stopped
|
||||
assert:
|
||||
that:
|
||||
- server_alt is failed
|
||||
- server_alt.msg == "Could not find server {{ server_alt_name }}"
|
||||
|
||||
- name: Stop server in alternate project with all_projects=true
|
||||
openstack.cloud.server_action:
|
||||
cloud: "{{ cloud }}"
|
||||
server: "{{ server_alt_name }}"
|
||||
action: stop
|
||||
wait: true
|
||||
all_projects: True
|
||||
register: server_alt
|
||||
|
||||
- name: Get info about server in alternate project
|
||||
openstack.cloud.server_info:
|
||||
cloud: "{{ cloud_alt }}"
|
||||
server: "{{ server_alt_name }}"
|
||||
register: info26
|
||||
|
||||
- name: Ensure status for server is SHUTOFF
|
||||
assert:
|
||||
that:
|
||||
- info26.openstack_servers.0.status == 'SHUTOFF'
|
||||
- server_alt is changed
|
||||
|
||||
- name: Delete server in alternate project
|
||||
openstack.cloud.server:
|
||||
cloud: "{{ cloud_alt }}"
|
||||
state: absent
|
||||
name: "{{ server_alt_name }}"
|
||||
wait: true
|
||||
|
||||
- name: Delete subnet for alternate server
|
||||
openstack.cloud.subnet:
|
||||
cloud: "{{ cloud_alt }}"
|
||||
name: "{{ server_alt_subnet }}"
|
||||
state: absent
|
||||
|
||||
- name: Delete network for alternate server
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud_alt }}"
|
||||
name: "{{ server_alt_network }}"
|
||||
state: absent
|
||||
|
||||
5
ci/roles/subnet_pool/defaults/main.yml
Normal file
5
ci/roles/subnet_pool/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
subnet_pool_name: "subnet_pool"
|
||||
address_scope_name: "address_scope"
|
||||
default_prefix_length: 24
|
||||
minimum_prefix_length: 10
|
||||
maximum_prefix_length: 30
|
||||
73
ci/roles/subnet_pool/tasks/main.yaml
Normal file
73
ci/roles/subnet_pool/tasks/main.yaml
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
- name: Create address_scope
|
||||
openstack.cloud.address_scope:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ address_scope_name }}"
|
||||
shared: False
|
||||
ip_version: "4"
|
||||
register: create_address_scope
|
||||
|
||||
- name: Create subnet pool
|
||||
openstack.cloud.subnet_pool:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ subnet_pool_name }}"
|
||||
shared: False
|
||||
address_scope: "{{ address_scope_name }}"
|
||||
prefixes:
|
||||
- 192.168.0.0/24
|
||||
register: create_subnet_pool
|
||||
|
||||
- name: Verify subnet pool
|
||||
assert:
|
||||
that:
|
||||
- create_subnet_pool is successful
|
||||
- create_subnet_pool is changed
|
||||
- create_subnet_pool.subnet_pool.name == subnet_pool_name
|
||||
- create_subnet_pool.subnet_pool.is_shared == False
|
||||
- create_subnet_pool.subnet_pool.is_default == False
|
||||
- create_subnet_pool.subnet_pool.address_scope_id == create_address_scope.address_scope.id
|
||||
- create_subnet_pool.subnet_pool.prefixes == ['192.168.0.0/24']
|
||||
|
||||
|
||||
- name: Update subnet pool
|
||||
openstack.cloud.subnet_pool:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ subnet_pool_name }}"
|
||||
address_scope: "{{ address_scope_name }}"
|
||||
shared: False
|
||||
default_prefix_length: "{{ default_prefix_length }}"
|
||||
minimum_prefix_length: "{{ minimum_prefix_length }}"
|
||||
maximum_prefix_length: "{{ maximum_prefix_length }}"
|
||||
description: "test"
|
||||
prefixes:
|
||||
- 192.168.0.0/24
|
||||
- 192.168.1.0/24
|
||||
register: update_subnet_pool
|
||||
|
||||
- name: Verify updated subnet pool
|
||||
assert:
|
||||
that:
|
||||
- update_subnet_pool is successful
|
||||
- update_subnet_pool is changed
|
||||
- update_subnet_pool.subnet_pool.name == subnet_pool_name
|
||||
- update_subnet_pool.subnet_pool.is_shared == False
|
||||
- update_subnet_pool.subnet_pool.is_default == False
|
||||
- update_subnet_pool.subnet_pool.address_scope_id == create_address_scope.address_scope.id
|
||||
- update_subnet_pool.subnet_pool.prefixes == ['192.168.0.0/23']
|
||||
- update_subnet_pool.subnet_pool.description == 'test'
|
||||
- update_subnet_pool.subnet_pool.default_prefix_length == default_prefix_length
|
||||
- update_subnet_pool.subnet_pool.minimum_prefix_length == minimum_prefix_length
|
||||
- update_subnet_pool.subnet_pool.maximum_prefix_length == maximum_prefix_length
|
||||
|
||||
- name: Delete created subnet pool
|
||||
openstack.cloud.subnet_pool:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ subnet_pool_name }}"
|
||||
state: absent
|
||||
|
||||
|
||||
- name: Delete created address scope
|
||||
openstack.cloud.address_scope:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ address_scope_name }}"
|
||||
state: absent
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
- name: Create user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: ansible.user@nowhere.net
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- debug: var=user
|
||||
|
||||
- name: Update user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: updateduser
|
||||
|
||||
- debug: var=updateduser
|
||||
|
||||
- name: Delete user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
||||
@@ -6,20 +6,22 @@
|
||||
#
|
||||
# tox -e ansible [TAG ...]
|
||||
# or
|
||||
# tox -e ansible -- -c cloudX [TAG ...]
|
||||
# tox -e ansible -- -c cloudX -u cloudY [TAG ...]
|
||||
# or to use the development version of Ansible:
|
||||
# tox -e ansible -- -d -c cloudX [TAG ...]
|
||||
# tox -e ansible -- -d -c cloudX -u cloudY [TAG ...]
|
||||
#
|
||||
# USAGE:
|
||||
# run-ansible-tests.sh -e ENVDIR [-d] [-c CLOUD] [TAG ...]
|
||||
# run-ansible-tests.sh -e ENVDIR [-d] [-c CLOUD] [-u CLOUD_ALT] [TAG ...]
|
||||
#
|
||||
# PARAMETERS:
|
||||
# -d Use Ansible source repo development branch.
|
||||
# -e ENVDIR Directory of the tox environment to use for testing.
|
||||
# -c CLOUD Name of the cloud to use for testing.
|
||||
# Defaults to "devstack-admin".
|
||||
# [TAG ...] Optional list of space-separated tags to control which
|
||||
# modules are tested.
|
||||
# -d Use Ansible source repo development branch.
|
||||
# -e ENVDIR Directory of the tox environment to use for testing.
|
||||
# -c CLOUD Name of the cloud to use for testing.
|
||||
# Defaults to "devstack-admin".
|
||||
# -u CLOUD_ALT Name of another cloud to use for testing.
|
||||
# Defaults to "devstack-alt".
|
||||
# [TAG ...] Optional list of space-separated tags to control which
|
||||
# modules are tested.
|
||||
#
|
||||
# EXAMPLES:
|
||||
# # Run all Ansible tests
|
||||
@@ -31,14 +33,16 @@
|
||||
set -ex
|
||||
|
||||
CLOUD="devstack-admin"
|
||||
CLOUD_ALT="devstack-alt"
|
||||
ENVDIR=
|
||||
USE_DEV=0
|
||||
|
||||
while getopts "c:de:" opt
|
||||
while getopts "c:de:u:" opt
|
||||
do
|
||||
case $opt in
|
||||
d) USE_DEV=1 ;;
|
||||
c) CLOUD=${OPTARG} ;;
|
||||
u) CLOUD_ALT=${OPTARG} ;;
|
||||
e) ENVDIR=${OPTARG} ;;
|
||||
?) echo "Invalid option: -${OPTARG}"
|
||||
exit 1;;
|
||||
@@ -131,8 +135,9 @@ fi
|
||||
SDK_VER=$(python -c "import openstack; print(openstack.version.__version__)")
|
||||
pushd ci/
|
||||
# run tests
|
||||
set -o pipefail
|
||||
ANSIBLE_COLLECTIONS_PATHS=$TEST_COLLECTIONS_PATHS ansible-playbook \
|
||||
-vvv ./run-collection.yml \
|
||||
-e "sdk_version=${SDK_VER} cloud=${CLOUD} image=${IMAGE} ${ANSIBLE_VARS}" \
|
||||
${tag_opt}
|
||||
-e "sdk_version=${SDK_VER} cloud=${CLOUD} cloud_alt=${CLOUD_ALT} image=${IMAGE} ${ANSIBLE_VARS}" \
|
||||
${tag_opt} 2>&1 | sudo tee /opt/stack/logs/test_output.log
|
||||
popd
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
roles:
|
||||
- { role: address_scope, tags: address_scope }
|
||||
- { role: auth, tags: auth }
|
||||
- { role: catalog_service, tags: catalog_service }
|
||||
- { role: client_config, tags: client_config }
|
||||
- { role: dns_zone_info, tags: dns_zone_info }
|
||||
- role: object_container
|
||||
@@ -15,8 +16,17 @@
|
||||
- role: dns
|
||||
tags: dns
|
||||
when: sdk_version is version(0.28, '>=')
|
||||
- { role: endpoint, tags: endpoint }
|
||||
- { role: floating_ip_info, tags: floating_ip_info }
|
||||
- { role: host_aggregate, tags: host_aggregate }
|
||||
- { role: identity_domain_info, tags: identity_domain_info }
|
||||
- { role: identity_group_info, tags: identity_group_info }
|
||||
- { role: identity_user, tags: identity_user }
|
||||
- { role: identity_user_info, tags: identity_user_info }
|
||||
- { role: identity_role, tags: identity_role }
|
||||
- { role: identity_role_info, tags: identity_role_info }
|
||||
- { role: image, tags: image }
|
||||
- { role: image_info, tags: image_info }
|
||||
- { role: keypair, tags: keypair }
|
||||
- { role: keystone_domain, tags: keystone_domain }
|
||||
- role: keystone_mapping
|
||||
@@ -28,27 +38,28 @@
|
||||
- role: keystone_federation_protocol
|
||||
tags: keystone_federation_protocol
|
||||
when: sdk_version is version(0.44, '>=')
|
||||
- { role: keystone_role, tags: keystone_role }
|
||||
- { role: logging, tags: logging }
|
||||
- { role: network, tags: network }
|
||||
- role: neutron_rbac
|
||||
tags:
|
||||
- rbac
|
||||
- neutron_rbac
|
||||
- { role: nova_flavor, tags: nova_flavor }
|
||||
- role: compute_flavor_info
|
||||
tags: nova_flavor
|
||||
- role: nova_services
|
||||
tags: nova_services
|
||||
when: sdk_version is version(0.44, '>=')
|
||||
- { role: object, tags: object }
|
||||
- { role: port, tags: port }
|
||||
- { role: project, tags: project }
|
||||
- role: project_properties
|
||||
tags: project_properties
|
||||
when: sdk_version is version("0.45.01", '>')
|
||||
- { role: recordset, tags: recordset }
|
||||
- { role: role_assignment, tags: role_assignment }
|
||||
- { role: router, tags: router }
|
||||
- { role: security_group, tags: security_group }
|
||||
- { role: server, tags: server }
|
||||
- { role: subnet, tags: subnet }
|
||||
- { role: user, tags: user }
|
||||
- { role: subnet_pool, tags: subnet_pool }
|
||||
- { role: user_group, tags: user_group }
|
||||
- { role: user_role, tags: user_role }
|
||||
- { role: volume, tags: volume }
|
||||
|
||||
@@ -13,7 +13,7 @@ Naming
|
||||
------
|
||||
|
||||
* This is a collection named ``openstack.cloud``. There is no need for further namespace prefixing.
|
||||
* Name any module that a cloud consumer would expect to use after the logical resource it manages:
|
||||
* Name any module that a cloud consumer would expect to use after the logical resource it manages:
|
||||
``server`` not ``nova``. This naming convention acknowledges that the end user does not care
|
||||
which service manages the resource - that is a deployment detail. For example cloud consumers may
|
||||
not know whether their floating IPs are managed by Nova or Neutron.
|
||||
@@ -24,6 +24,7 @@ Interface
|
||||
* If the resource being managed has an id, it should be returned.
|
||||
* If the resource being managed has an associated object more complex than
|
||||
an id, it should also be returned.
|
||||
* Return format shall be a dictionary or list
|
||||
|
||||
Interoperability
|
||||
----------------
|
||||
@@ -53,7 +54,7 @@ Libraries
|
||||
* All complex cloud interaction or interoperability code should be housed in
|
||||
the `openstacksdk <https://opendev.org/openstack/openstacksdk>`_
|
||||
library.
|
||||
* All OpenStack API interactions should happen via the openstacksdk and not via
|
||||
* All OpenStack API interactions should happen via the openstackSDK and not via
|
||||
OpenStack Client libraries. The OpenStack Client libraries do no have end
|
||||
users as a primary audience, they are for intra-server communication.
|
||||
* All modules should be registered in ``meta/action_groups.yml`` for enabling the
|
||||
|
||||
@@ -33,4 +33,4 @@ build_ignore:
|
||||
- ansible_collections_openstack.egg-info
|
||||
- contrib
|
||||
- changelogs
|
||||
version: 1.6.0
|
||||
version: 1.10.0
|
||||
|
||||
@@ -18,7 +18,7 @@ build_ignore:
|
||||
- ci
|
||||
- galaxy.yml.in
|
||||
- setup.cfg
|
||||
- test-requirements.txt
|
||||
- test-requirements*
|
||||
- tests
|
||||
- tools
|
||||
- tox.ini
|
||||
@@ -29,7 +29,7 @@ build_ignore:
|
||||
- importer_result.json
|
||||
- .tox
|
||||
- .env
|
||||
- .vscode
|
||||
- ansible_collections_openstack.egg-info
|
||||
- contrib
|
||||
- changelogs/.plugin-cache.yaml
|
||||
- changelogs/fragments
|
||||
- changelogs
|
||||
|
||||
@@ -9,6 +9,9 @@ action_groups:
|
||||
- baremetal_node
|
||||
- baremetal_node_action
|
||||
- baremetal_node_action
|
||||
- baremetal_node_info
|
||||
- baremetal_port_info
|
||||
- baremetal_port
|
||||
- catalog_endpoint
|
||||
- catalog_service
|
||||
- catalog_service
|
||||
@@ -96,6 +99,7 @@ action_groups:
|
||||
- stack
|
||||
- subnet
|
||||
- subnets_info
|
||||
- subnet_pool
|
||||
- volume
|
||||
- volume_backup
|
||||
- volume_backup_info
|
||||
|
||||
@@ -89,9 +89,18 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
type: str
|
||||
sdk_log_path:
|
||||
description:
|
||||
- Path to the logfile of the OpenStackSDK. If empty no log is written
|
||||
type: str
|
||||
sdk_log_level:
|
||||
description: Log level of the OpenStackSDK
|
||||
type: str
|
||||
default: INFO
|
||||
choices: [INFO, DEBUG]
|
||||
requirements:
|
||||
- python >= 3.6
|
||||
- openstacksdk >= 0.12.0
|
||||
- openstacksdk >= 0.36, < 0.99.0
|
||||
notes:
|
||||
- The standard OpenStack environment variables, such as C(OS_USERNAME)
|
||||
may be used instead of providing explicit values.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2012, Marco Vito Moscaritolo <marco@agavee.com>
|
||||
# Copyright (c) 2013, Jesse Keating <jesse.keating@rackspace.com>
|
||||
# Copyright (c) 2015, Hewlett-Packard Development Company, L.P.
|
||||
@@ -9,11 +10,8 @@
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
name: openstack
|
||||
plugin_type: inventory
|
||||
author: OpenStack Ansible SIG
|
||||
short_description: OpenStack inventory source
|
||||
requirements:
|
||||
- "openstacksdk >= 0.28"
|
||||
description:
|
||||
- Get inventory hosts from OpenStack clouds
|
||||
- Uses openstack.(yml|yaml) YAML configuration file to configure the inventory plugin
|
||||
@@ -26,7 +24,7 @@ options:
|
||||
show_all:
|
||||
description: toggles showing all vms vs only those with a working IP
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: false
|
||||
inventory_hostname:
|
||||
description: |
|
||||
What to register as the inventory hostname.
|
||||
@@ -42,6 +40,13 @@ options:
|
||||
- name
|
||||
- uuid
|
||||
default: "name"
|
||||
use_names:
|
||||
description: |
|
||||
Use the host's 'name' instead of 'interface_ip' for the 'ansible_host' and
|
||||
'ansible_ssh_host' facts. This might be desired when using jump or
|
||||
bastion hosts and the name is the FQDN of the host.
|
||||
type: bool
|
||||
default: false
|
||||
expand_hostvars:
|
||||
description: |
|
||||
Run extra commands on each host to fill in additional
|
||||
@@ -50,7 +55,7 @@ options:
|
||||
(Note, the default value of this is opposite from the default
|
||||
old openstack.py inventory script's option expand_hostvars)
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: false
|
||||
private:
|
||||
description: |
|
||||
Use the private interface of each server, if it has one, as
|
||||
@@ -58,12 +63,13 @@ options:
|
||||
running ansible inside a server in the cloud and would rather
|
||||
communicate to your servers over the private network.
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: false
|
||||
only_clouds:
|
||||
description: |
|
||||
List of clouds from clouds.yaml to use, instead of using
|
||||
the whole list.
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
fail_on_errors:
|
||||
description: |
|
||||
@@ -74,12 +80,12 @@ options:
|
||||
default value of this is opposite from the old openstack.py
|
||||
inventory script's option fail_on_errors)
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: false
|
||||
all_projects:
|
||||
description: |
|
||||
Lists servers from all projects
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: false
|
||||
clouds_yaml_path:
|
||||
description: |
|
||||
Override path to clouds.yaml file. If this value is given it
|
||||
@@ -88,6 +94,7 @@ options:
|
||||
/etc/ansible/openstack.yml to the regular locations documented
|
||||
at https://docs.openstack.org/os-client-config/latest/user/configuration.html#config-files
|
||||
type: list
|
||||
elements: str
|
||||
env:
|
||||
- name: OS_CLIENT_CONFIG_FILE
|
||||
compose:
|
||||
@@ -102,7 +109,9 @@ options:
|
||||
description: Automatically create groups from host variables.
|
||||
type: bool
|
||||
default: true
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.28, < 0.99.0"
|
||||
extends_documentation_fragment:
|
||||
- inventory_cache
|
||||
- constructed
|
||||
@@ -125,6 +134,9 @@ import logging
|
||||
from ansible.errors import AnsibleParserError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
||||
from ansible.utils.display import Display
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
||||
ensure_compatibility
|
||||
)
|
||||
|
||||
display = Display()
|
||||
os_logger = logging.getLogger("openstack")
|
||||
@@ -166,6 +178,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
elif not HAS_SDK:
|
||||
msg = "openstacksdk is required for the OpenStack inventory plugin. OpenStack inventory sources will be skipped."
|
||||
|
||||
if not msg:
|
||||
try:
|
||||
ensure_compatibility(sdk.version.__version__)
|
||||
except ImportError as e:
|
||||
msg = ("Incompatible openstacksdk library found: {error}."
|
||||
.format(error=str(e)))
|
||||
|
||||
if msg:
|
||||
display.vvvv(msg)
|
||||
raise AnsibleParserError(msg)
|
||||
@@ -237,6 +256,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
expand_hostvars = self._config_data.get('expand_hostvars', False)
|
||||
fail_on_errors = self._config_data.get('fail_on_errors', False)
|
||||
all_projects = self._config_data.get('all_projects', False)
|
||||
self.use_names = self._config_data.get('use_names', False)
|
||||
|
||||
source_data = []
|
||||
try:
|
||||
@@ -363,10 +383,20 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
def _append_hostvars(self, hostvars, groups, current_host,
|
||||
server, namegroup=False):
|
||||
hostvars[current_host] = dict(
|
||||
ansible_ssh_host=server['interface_ip'],
|
||||
ansible_host=server['interface_ip'],
|
||||
openstack=server)
|
||||
if not self.use_names:
|
||||
hostvars[current_host] = dict(
|
||||
ansible_ssh_host=server['interface_ip'],
|
||||
ansible_host=server['interface_ip'],
|
||||
openstack=server,
|
||||
)
|
||||
|
||||
if self.use_names:
|
||||
hostvars[current_host] = dict(
|
||||
ansible_ssh_host=server['name'],
|
||||
ansible_host=server['name'],
|
||||
openstack=server,
|
||||
)
|
||||
|
||||
self.inventory.add_host(current_host)
|
||||
|
||||
if self.get_option('legacy_groups'):
|
||||
|
||||
@@ -29,7 +29,16 @@
|
||||
|
||||
import abc
|
||||
import copy
|
||||
from distutils.version import StrictVersion
|
||||
from ansible.module_utils.six import raise_from
|
||||
try:
|
||||
from ansible.module_utils.compat.version import StrictVersion
|
||||
except ImportError:
|
||||
try:
|
||||
from distutils.version import StrictVersion
|
||||
except ImportError as exc:
|
||||
raise_from(ImportError('To use this plugin or module with ansible-core'
|
||||
' < 2.11, you need to use Python < 3.12 with '
|
||||
'distutils.version present'), exc)
|
||||
import importlib
|
||||
import os
|
||||
|
||||
@@ -67,7 +76,41 @@ OVERRIDES = {'os_client_config': 'config',
|
||||
|
||||
CUSTOM_VAR_PARAMS = ['min_ver', 'max_ver']
|
||||
|
||||
MINIMUM_SDK_VERSION = '0.12.0'
|
||||
MINIMUM_SDK_VERSION = '0.36.0'
|
||||
MAXIMUM_SDK_VERSION = '0.98.999'
|
||||
|
||||
|
||||
def ensure_compatibility(version, min_version=None, max_version=None):
|
||||
""" Raises ImportError if the specified version does not
|
||||
meet the minimum and maximum version requirements"""
|
||||
|
||||
if min_version and MINIMUM_SDK_VERSION:
|
||||
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
|
||||
StrictVersion(min_version))
|
||||
elif MINIMUM_SDK_VERSION:
|
||||
min_version = StrictVersion(MINIMUM_SDK_VERSION)
|
||||
|
||||
if max_version and MAXIMUM_SDK_VERSION:
|
||||
max_version = min(StrictVersion(MAXIMUM_SDK_VERSION),
|
||||
StrictVersion(max_version))
|
||||
elif MAXIMUM_SDK_VERSION:
|
||||
max_version = StrictVersion(MAXIMUM_SDK_VERSION)
|
||||
|
||||
if min_version and StrictVersion(version) < min_version:
|
||||
raise ImportError(
|
||||
"Version MUST be >={min_version} and <={max_version}, but"
|
||||
" {version} is smaller than minimum version {min_version}"
|
||||
.format(version=version,
|
||||
min_version=min_version,
|
||||
max_version=max_version))
|
||||
|
||||
if max_version and StrictVersion(version) > max_version:
|
||||
raise ImportError(
|
||||
"Version MUST be >={min_version} and <={max_version}, but"
|
||||
" {version} is larger than maximum version {max_version}"
|
||||
.format(version=version,
|
||||
min_version=min_version,
|
||||
max_version=max_version))
|
||||
|
||||
|
||||
def openstack_argument_spec():
|
||||
@@ -130,6 +173,9 @@ def openstack_full_argument_spec(**kwargs):
|
||||
interface=dict(
|
||||
default='public', choices=['public', 'internal', 'admin'],
|
||||
aliases=['endpoint_type']),
|
||||
sdk_log_path=dict(default=None, type='str'),
|
||||
sdk_log_level=dict(
|
||||
default='INFO', type='str', choices=['INFO', 'DEBUG']),
|
||||
)
|
||||
# Filter out all our custom parameters before passing to AnsibleModule
|
||||
kwargs_copy = copy.deepcopy(kwargs)
|
||||
@@ -152,25 +198,20 @@ def openstack_module_kwargs(**kwargs):
|
||||
|
||||
|
||||
# for compatibility with old versions
|
||||
def openstack_cloud_from_module(module, min_version=None):
|
||||
def openstack_cloud_from_module(module, min_version=None, max_version=None):
|
||||
try:
|
||||
# Due to the name shadowing we should import other way
|
||||
sdk = importlib.import_module('openstack')
|
||||
sdk_version = importlib.import_module('openstack.version')
|
||||
except ImportError:
|
||||
module.fail_json(msg='openstacksdk is required for this module')
|
||||
|
||||
if min_version:
|
||||
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
|
||||
StrictVersion(min_version))
|
||||
else:
|
||||
min_version = StrictVersion(MINIMUM_SDK_VERSION)
|
||||
|
||||
if StrictVersion(sdk_version.__version__) < min_version:
|
||||
try:
|
||||
ensure_compatibility(sdk.version.__version__,
|
||||
min_version, max_version)
|
||||
except ImportError as e:
|
||||
module.fail_json(
|
||||
msg="To utilize this module, the installed version of "
|
||||
"the openstacksdk library MUST be >={min_version}.".format(
|
||||
min_version=min_version))
|
||||
msg="Incompatible openstacksdk library found: {error}."
|
||||
.format(error=str(e)))
|
||||
|
||||
cloud_config = module.params.pop('cloud', None)
|
||||
try:
|
||||
@@ -244,6 +285,7 @@ class OpenStackModule:
|
||||
argument_spec = {}
|
||||
module_kwargs = {}
|
||||
module_min_sdk_version = None
|
||||
module_max_sdk_version = None
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize Openstack base class.
|
||||
@@ -264,6 +306,7 @@ class OpenStackModule:
|
||||
self.warn = self.ansible.warn
|
||||
self.sdk, self.conn = self.openstack_cloud_from_module()
|
||||
self.check_deprecated_names()
|
||||
self.setup_sdk_logging()
|
||||
|
||||
def log(self, msg):
|
||||
"""Prints log message to system log.
|
||||
@@ -283,6 +326,16 @@ class OpenStackModule:
|
||||
self.ansible.log(
|
||||
" ".join(['[DEBUG]', msg]))
|
||||
|
||||
def setup_sdk_logging(self):
|
||||
log_path = self.params.get('sdk_log_path')
|
||||
if log_path is not None:
|
||||
log_level = self.params.get('sdk_log_level')
|
||||
self.sdk.enable_logging(
|
||||
debug=True if log_level == 'DEBUG' else False,
|
||||
http_debug=True if log_level == 'DEBUG' else False,
|
||||
path=log_path
|
||||
)
|
||||
|
||||
def check_deprecated_names(self):
|
||||
"""Check deprecated module names if `deprecated_names` variable is set.
|
||||
"""
|
||||
@@ -301,23 +354,18 @@ class OpenStackModule:
|
||||
try:
|
||||
# Due to the name shadowing we should import other way
|
||||
sdk = importlib.import_module('openstack')
|
||||
sdk_version_lib = importlib.import_module('openstack.version')
|
||||
self.sdk_version = sdk_version_lib.__version__
|
||||
self.sdk_version = sdk.version.__version__
|
||||
except ImportError:
|
||||
self.fail_json(msg='openstacksdk is required for this module')
|
||||
|
||||
# Fail if the available SDK version doesn't meet the minimum version
|
||||
# requirements
|
||||
if self.module_min_sdk_version:
|
||||
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
|
||||
StrictVersion(self.module_min_sdk_version))
|
||||
else:
|
||||
min_version = StrictVersion(MINIMUM_SDK_VERSION)
|
||||
if StrictVersion(self.sdk_version) < min_version:
|
||||
self.fail(
|
||||
msg="To utilize this module, the installed version of "
|
||||
"the openstacksdk library MUST be >={min_version}.".format(
|
||||
min_version=min_version))
|
||||
try:
|
||||
ensure_compatibility(self.sdk_version,
|
||||
self.module_min_sdk_version,
|
||||
self.module_max_sdk_version)
|
||||
except ImportError as e:
|
||||
self.fail_json(
|
||||
msg="Incompatible openstacksdk library found: {error}."
|
||||
.format(error=str(e)))
|
||||
|
||||
# Fail if there are set unsupported for this version parameters
|
||||
# New parameters should NOT use 'default' but rely on SDK defaults
|
||||
|
||||
@@ -30,7 +30,7 @@ options:
|
||||
ip_version:
|
||||
description:
|
||||
- The IP version of the subnet 4 or 6
|
||||
default: 4
|
||||
default: '4'
|
||||
type: str
|
||||
choices: ['4', '6']
|
||||
shared:
|
||||
|
||||
555
plugins/modules/baremetal_node_info.py
Normal file
555
plugins/modules/baremetal_node_info.py
Normal file
@@ -0,0 +1,555 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021 by Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: baremetal_node_info
|
||||
short_description: Retrieve information about Bare Metal nodes from OpenStack
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Retrieve information about Bare Metal nodes from OpenStack.
|
||||
options:
|
||||
node:
|
||||
description:
|
||||
- Name or globally unique identifier (UUID) to identify the host.
|
||||
type: str
|
||||
mac:
|
||||
description:
|
||||
- Unique mac address that is used to attempt to identify the host.
|
||||
type: str
|
||||
ironic_url:
|
||||
description:
|
||||
- If noauth mode is utilized, this is required to be set to the
|
||||
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
|
||||
settings set to None.
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Gather information about all baremeal nodes
|
||||
- openstack.cloud.baremetal_node_info:
|
||||
cloud: "devstack"
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.baremetal_nodes }}"
|
||||
# Gather information about a baremeal node
|
||||
- openstack.cloud.baremetal_node_info:
|
||||
cloud: "devstack"
|
||||
node: "00000000-0000-0000-0000-000000000002"
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.baremetal_nodes }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
baremetal_nodes:
|
||||
description: Bare Metal node list. A subset of the dictionary keys
|
||||
listed below may be returned, depending on your cloud
|
||||
provider.
|
||||
returned: always, but can be null
|
||||
type: complex
|
||||
contains:
|
||||
allocation_uuid:
|
||||
description: The UUID of the allocation associated with the node.
|
||||
If not null, will be the same as instance_uuid (the
|
||||
opposite is not always true). Unlike instance_uuid,
|
||||
this field is read-only. Please use the Allocation API
|
||||
to remove allocations.
|
||||
returned: success
|
||||
type: str
|
||||
automated_clean:
|
||||
description: Indicates whether the node will perform automated
|
||||
clean or not.
|
||||
returned: success
|
||||
type: bool
|
||||
bios_interface:
|
||||
description: The bios interface to be used for this node.
|
||||
returned: success
|
||||
type: str
|
||||
boot_interface:
|
||||
description: The boot interface for a Node, e.g. "pxe".
|
||||
returned: success
|
||||
type: str
|
||||
boot_mode:
|
||||
description: The boot mode for a node, either "uefi" or "bios"
|
||||
returned: success
|
||||
type: str
|
||||
chassis_uuid:
|
||||
description: UUID of the chassis associated with this Node. May be
|
||||
empty or None.
|
||||
returned: success
|
||||
type: str
|
||||
clean_step:
|
||||
description: The current clean step.
|
||||
returned: success
|
||||
type: str
|
||||
conductor:
|
||||
description: The conductor currently servicing a node. This field
|
||||
is read-only.
|
||||
returned: success
|
||||
type: str
|
||||
conductor_group:
|
||||
description: The conductor group for a node. Case-insensitive
|
||||
string up to 255 characters, containing a-z, 0-9, _,
|
||||
-, and ..
|
||||
returned: success
|
||||
type: str
|
||||
console_enabled:
|
||||
description: Indicates whether console access is enabled or
|
||||
disabled on this node.
|
||||
returned: success
|
||||
type: bool
|
||||
console_interface:
|
||||
description: The console interface for a node, e.g. "no-console".
|
||||
returned: success
|
||||
type: str
|
||||
created_at:
|
||||
description: Bare Metal node created at timestamp.
|
||||
returned: success
|
||||
type: str
|
||||
deploy_interface:
|
||||
description: The deploy interface for a node, e.g. "direct".
|
||||
returned: success
|
||||
type: str
|
||||
deploy_step:
|
||||
description: The current deploy step.
|
||||
returned: success
|
||||
type: str
|
||||
driver:
|
||||
description: The name of the driver.
|
||||
returned: success
|
||||
type: str
|
||||
driver_info:
|
||||
description: All the metadata required by the driver to manage this
|
||||
Node. List of fields varies between drivers, and can
|
||||
be retrieved from the
|
||||
/v1/drivers/<DRIVER_NAME>/properties resource.
|
||||
returned: success
|
||||
type: dict
|
||||
driver_internal_info:
|
||||
description: Internal metadata set and stored by the Node's driver.
|
||||
returned: success
|
||||
type: dict
|
||||
extra:
|
||||
description: A set of one or more arbitrary metadata key and value
|
||||
pairs.
|
||||
returned: success
|
||||
type: dict
|
||||
fault:
|
||||
description: The fault indicates the active fault detected by
|
||||
ironic, typically the Node is in "maintenance mode".
|
||||
None means no fault has been detected by ironic.
|
||||
"power failure" indicates ironic failed to retrieve
|
||||
power state from this node. There are other possible
|
||||
types, e.g., "clean failure" and "rescue abort
|
||||
failure".
|
||||
returned: success
|
||||
type: str
|
||||
id:
|
||||
description: The UUID for the resource.
|
||||
returned: success
|
||||
type: str
|
||||
inspect_interface:
|
||||
description: The interface used for node inspection.
|
||||
returned: success
|
||||
type: str
|
||||
instance_info:
|
||||
description: Information used to customize the deployed image. May
|
||||
include root partition size, a base 64 encoded config
|
||||
drive, and other metadata. Note that this field is
|
||||
erased automatically when the instance is deleted
|
||||
(this is done by requesting the Node provision state
|
||||
be changed to DELETED).
|
||||
returned: success
|
||||
type: dict
|
||||
instance_uuid:
|
||||
description: UUID of the Nova instance associated with this Node.
|
||||
returned: success
|
||||
type: str
|
||||
last_error:
|
||||
description: Any error from the most recent (last) transaction that
|
||||
started but failed to finish.
|
||||
returned: success
|
||||
type: str
|
||||
maintenance:
|
||||
description: Whether or not this Node is currently in "maintenance
|
||||
mode". Setting a Node into maintenance mode removes it
|
||||
from the available resource pool and halts some
|
||||
internal automation. This can happen manually (eg, via
|
||||
an API request) or automatically when Ironic detects a
|
||||
hardware fault that prevents communication with the
|
||||
machine.
|
||||
returned: success
|
||||
type: bool
|
||||
maintenance_reason:
|
||||
description: User-settable description of the reason why this Node
|
||||
was placed into maintenance mode
|
||||
returned: success
|
||||
type: str
|
||||
management_interface:
|
||||
description: Interface for out-of-band node management.
|
||||
returned: success
|
||||
type: str
|
||||
name:
|
||||
description: Human-readable identifier for the Node resource. May
|
||||
be undefined. Certain words are reserved.
|
||||
returned: success
|
||||
type: str
|
||||
network_interface:
|
||||
description: Which Network Interface provider to use when plumbing
|
||||
the network connections for this Node.
|
||||
returned: success
|
||||
type: str
|
||||
owner:
|
||||
description: A string or UUID of the tenant who owns the object.
|
||||
returned: success
|
||||
type: str
|
||||
portgroups:
|
||||
description: List of ironic portgroups on this node.
|
||||
returned: success
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
address:
|
||||
description: Physical hardware address of this Portgroup,
|
||||
typically the hardware MAC address.
|
||||
returned: success
|
||||
type: str
|
||||
created_at:
|
||||
description: The UTC date and time when the resource was
|
||||
created, ISO 8601 format.
|
||||
returned: success
|
||||
type: str
|
||||
extra:
|
||||
description: A set of one or more arbitrary metadata key and
|
||||
value pairs.
|
||||
returned: success
|
||||
type: dict
|
||||
id:
|
||||
description: The UUID for the resource.
|
||||
returned: success
|
||||
type: str
|
||||
internal_info:
|
||||
description: Internal metadata set and stored by the Portgroup.
|
||||
This field is read-only.
|
||||
returned: success
|
||||
type: dict
|
||||
is_standalone_ports_supported:
|
||||
description: Indicates whether ports that are members of this
|
||||
portgroup can be used as stand-alone ports.
|
||||
returned: success
|
||||
type: bool
|
||||
mode:
|
||||
description: Mode of the port group. For possible values, refer
|
||||
to https://www.kernel.org/doc/Documentation/networking/bonding.txt.
|
||||
If not specified in a request to create a port
|
||||
group, it will be set to the value of the
|
||||
[DEFAULT]default_portgroup_mode configuration
|
||||
option. When set, can not be removed from the port
|
||||
group.
|
||||
returned: success
|
||||
type: str
|
||||
name:
|
||||
description: Human-readable identifier for the Portgroup
|
||||
resource. May be undefined.
|
||||
returned: success
|
||||
type: str
|
||||
node_id:
|
||||
description: UUID of the Node this resource belongs to.
|
||||
returned: success
|
||||
type: str
|
||||
ports:
|
||||
description: List of port UUID's of ports belonging to this
|
||||
portgroup.
|
||||
returned: success
|
||||
type: list
|
||||
properties:
|
||||
description: Key/value properties related to the port group's
|
||||
configuration.
|
||||
returned: success
|
||||
type: dict
|
||||
updated_at:
|
||||
description: The UTC date and time when the resource was
|
||||
updated, ISO 8601 format. May be "null".
|
||||
returned: success
|
||||
type: str
|
||||
ports:
|
||||
description: List of ironic ports on this node.
|
||||
returned: success
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
address:
|
||||
description: Physical hardware address of this network Port,
|
||||
typically the hardware MAC address.
|
||||
returned: success
|
||||
type: str
|
||||
created_at:
|
||||
description: The UTC date and time when the resource was
|
||||
created, ISO 8601 format.
|
||||
returned: success
|
||||
type: str
|
||||
extra:
|
||||
description: A set of one or more arbitrary metadata key and
|
||||
value pairs.
|
||||
returned: success
|
||||
type: dict
|
||||
id:
|
||||
description: The UUID for the resource.
|
||||
returned: success
|
||||
type: str
|
||||
internal_info:
|
||||
description: Internal metadata set and stored by the Port. This
|
||||
field is read-only.
|
||||
returned: success
|
||||
type: dict
|
||||
local_link_connection:
|
||||
description: The Port binding profile. If specified, must
|
||||
contain switch_id (only a MAC address or an
|
||||
OpenFlow based datapath_id of the switch are
|
||||
accepted in this field) and port_id (identifier of
|
||||
the physical port on the switch to which node's
|
||||
port is connected to) fields. switch_info is an
|
||||
optional string field to be used to store any
|
||||
vendor-specific information.
|
||||
returned: success
|
||||
type: dict
|
||||
name:
|
||||
description: The name of the resource.
|
||||
returned: success
|
||||
type: str
|
||||
node_uuid:
|
||||
description: UUID of the Node this resource belongs to.
|
||||
returned: success
|
||||
type: str
|
||||
physical_network:
|
||||
description: The name of the physical network to which a port
|
||||
is connected. May be empty.
|
||||
returned: success
|
||||
type: str
|
||||
portgroup_uuid:
|
||||
description: UUID of the Portgroup this resource belongs to.
|
||||
returned: success
|
||||
type: str
|
||||
pxe_enabled:
|
||||
description: Indicates whether PXE is enabled or disabled on
|
||||
the Port.
|
||||
returned: success
|
||||
type: str
|
||||
updated_at:
|
||||
description: The UTC date and time when the resource was
|
||||
updated, ISO 8601 format. May be "null".
|
||||
returned: success
|
||||
type: str
|
||||
uuid:
|
||||
description: The UUID for the resource.
|
||||
returned: success
|
||||
type: str
|
||||
power_interface:
|
||||
description: Interface used for performing power actions on the
|
||||
node, e.g. "ipmitool".
|
||||
returned: success
|
||||
type: str
|
||||
power_state:
|
||||
description: The current power state of this Node. Usually, "power
|
||||
on" or "power off", but may be "None" if Ironic is
|
||||
unable to determine the power state (eg, due to
|
||||
hardware failure).
|
||||
returned: success
|
||||
type: str
|
||||
properties:
|
||||
description: Physical characteristics of this Node. Populated by
|
||||
ironic-inspector during inspection. May be edited via
|
||||
the REST API at any time.
|
||||
returned: success
|
||||
type: dict
|
||||
protected:
|
||||
description: Whether the node is protected from undeploying,
|
||||
rebuilding and deletion.
|
||||
returned: success
|
||||
type: bool
|
||||
protected_reason:
|
||||
description: The reason the node is marked as protected.
|
||||
returned: success
|
||||
type: str
|
||||
provision_state:
|
||||
description: The current provisioning state of this Node.
|
||||
returned: success
|
||||
type: str
|
||||
raid_config:
|
||||
description: Represents the current RAID configuration of the node.
|
||||
Introduced with the cleaning feature.
|
||||
returned: success
|
||||
type: dict
|
||||
raid_interface:
|
||||
description: Interface used for configuring RAID on this node.
|
||||
returned: success
|
||||
type: str
|
||||
rescue_interface:
|
||||
description: The interface used for node rescue, e.g. "no-rescue".
|
||||
returned: success
|
||||
type: str
|
||||
reservation:
|
||||
description: The name of an Ironic Conductor host which is holding
|
||||
a lock on this node, if a lock is held. Usually
|
||||
"null", but this field can be useful for debugging.
|
||||
returned: success
|
||||
type: str
|
||||
resource_class:
|
||||
description: A string which can be used by external schedulers to
|
||||
identify this Node as a unit of a specific type of
|
||||
resource. For more details, see
|
||||
https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html
|
||||
returned: success
|
||||
type: str
|
||||
retired:
|
||||
description: Whether the node is retired and can hence no longer be
|
||||
provided, i.e. move from manageable to available, and
|
||||
will end up in manageable after cleaning (rather than
|
||||
available).
|
||||
returned: success
|
||||
type: bool
|
||||
retired_reason:
|
||||
description: The reason the node is marked as retired.
|
||||
returned: success
|
||||
type: str
|
||||
secure_boot:
|
||||
description: Indicates whether node is currently booted with
|
||||
secure_boot turned on.
|
||||
returned: success
|
||||
type: bool
|
||||
storage_interface:
|
||||
description: Interface used for attaching and detaching volumes on
|
||||
this node, e.g. "cinder".
|
||||
returned: success
|
||||
type: str
|
||||
target_power_state:
|
||||
description: If a power state transition has been requested, this
|
||||
field represents the requested (ie, "target") state,
|
||||
either "power on" or "power off".
|
||||
returned: success
|
||||
type: str
|
||||
target_provision_state:
|
||||
description: If a provisioning action has been requested, this
|
||||
field represents the requested (ie, "target") state.
|
||||
Note that a Node may go through several states during
|
||||
its transition to this target state. For instance,
|
||||
when requesting an instance be deployed to an
|
||||
AVAILABLE Node, the Node may go through the following
|
||||
state change progression, AVAILABLE -> DEPLOYING ->
|
||||
DEPLOYWAIT -> DEPLOYING -> ACTIVE
|
||||
returned: success
|
||||
type: str
|
||||
target_raid_config:
|
||||
description: Represents the requested RAID configuration of the
|
||||
node, which will be applied when the Node next
|
||||
transitions through the CLEANING state. Introduced
|
||||
with the cleaning feature.
|
||||
returned: success
|
||||
type: dict
|
||||
traits:
|
||||
description: List of traits for this node.
|
||||
returned: success
|
||||
type: list
|
||||
updated_at:
|
||||
description: Bare Metal node updated at timestamp.
|
||||
returned: success
|
||||
type: str
|
||||
uuid:
|
||||
description: The UUID for the resource.
|
||||
returned: success
|
||||
type: str
|
||||
vendor_interface:
|
||||
description: Interface for vendor-specific functionality on this
|
||||
node, e.g. "no-vendor".
|
||||
returned: success
|
||||
type: str
|
||||
'''
|
||||
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
|
||||
IronicModule,
|
||||
ironic_argument_spec,
|
||||
)
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
||||
openstack_module_kwargs,
|
||||
openstack_cloud_from_module
|
||||
)
|
||||
|
||||
|
||||
def cleanup_node_properties(machine, cloud):
|
||||
# states are links, not useful
|
||||
machine.pop('states', None)
|
||||
|
||||
for port in machine.ports:
|
||||
# links are not useful
|
||||
port.pop('links', None)
|
||||
# redundant, location is in on machine as well
|
||||
port.pop('location', None)
|
||||
|
||||
for portgroup in machine.portgroups:
|
||||
# links are not useful
|
||||
portgroup.pop('links', None)
|
||||
# redundant, location is in on machine as well
|
||||
portgroup.pop('location', None)
|
||||
# links to ports are not useful, replace with list of port uuid's
|
||||
portgroup['ports'] = [x.id for x in list(
|
||||
cloud.baremetal.ports(portgroup=portgroup['id']))]
|
||||
|
||||
|
||||
def get_ports_and_portgroups(cloud, machine):
|
||||
machine.ports = cloud.list_nics_for_machine(machine.uuid)
|
||||
machine.portgroups = [dict(x) for x in
|
||||
list(cloud.baremetal.port_groups(node=machine.uuid,
|
||||
details=True))]
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ironic_argument_spec(
|
||||
node=dict(required=False),
|
||||
mac=dict(required=False),
|
||||
)
|
||||
module_kwargs = openstack_module_kwargs()
|
||||
module_kwargs['supports_check_mode'] = True
|
||||
|
||||
module = IronicModule(argument_spec, **module_kwargs)
|
||||
|
||||
machine = None
|
||||
machines = list()
|
||||
|
||||
sdk, cloud = openstack_cloud_from_module(module)
|
||||
try:
|
||||
if module.params['node']:
|
||||
machine = cloud.get_machine(module.params['node'])
|
||||
elif module.params['mac']:
|
||||
machine = cloud.get_machine_by_mac(module.params['mac'])
|
||||
|
||||
# Fail if node not found
|
||||
if (module.params['node'] or module.params['mac']) and not machine:
|
||||
module.fail_json(msg='The baremetal node was not found')
|
||||
|
||||
if machine:
|
||||
machines.append(machine)
|
||||
else:
|
||||
machines = cloud.list_machines()
|
||||
|
||||
for machine in machines:
|
||||
get_ports_and_portgroups(cloud, machine)
|
||||
cleanup_node_properties(machine, cloud)
|
||||
|
||||
module.exit_json(changed=False, baremetal_nodes=machines)
|
||||
except sdk.exceptions.OpenStackCloudException as e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
373
plugins/modules/baremetal_port.py
Normal file
373
plugins/modules/baremetal_port.py
Normal file
@@ -0,0 +1,373 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
# Copyright (c) 2021 by Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: baremetal_port
|
||||
short_description: Create/Delete Bare Metal port Resources from OpenStack
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Create, Update and Remove ironic ports from OpenStack.
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Indicates desired state of the resource
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
type: str
|
||||
uuid:
|
||||
description:
|
||||
- globally unique identifier (UUID) to be given to the resource. Will
|
||||
be auto-generated if not specified.
|
||||
type: str
|
||||
node:
|
||||
description:
|
||||
- UUID or Name of the Node this resource belongs to.
|
||||
type: str
|
||||
address:
|
||||
description:
|
||||
- Physical hardware address of this network Port, typically the
|
||||
hardware MAC address.
|
||||
type: str
|
||||
portgroup:
|
||||
description:
|
||||
- UUID or Name of the Portgroup this resource belongs to.
|
||||
type: str
|
||||
local_link_connection:
|
||||
description:
|
||||
- The Port binding profile.
|
||||
type: dict
|
||||
suboptions:
|
||||
switch_id:
|
||||
description:
|
||||
- A MAC address or an OpenFlow based datapath_id of the switch.
|
||||
type: str
|
||||
port_id:
|
||||
description:
|
||||
- Identifier of the physical port on the switch to which node's
|
||||
port is connected to.
|
||||
type: str
|
||||
switch_info:
|
||||
description:
|
||||
- An optional string field to be used to store any vendor-specific
|
||||
information.
|
||||
type: str
|
||||
is_pxe_enabled:
|
||||
description:
|
||||
- Whether PXE should be enabled or disabled on the Port.
|
||||
type: bool
|
||||
physical_network:
|
||||
description:
|
||||
- The name of the physical network to which a port is connected.
|
||||
type: str
|
||||
extra:
|
||||
description:
|
||||
- A set of one or more arbitrary metadata key and value pairs.
|
||||
type: dict
|
||||
ironic_url:
|
||||
description:
|
||||
- If noauth mode is utilized, this is required to be set to the
|
||||
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
|
||||
settings set to None.
|
||||
type: str
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create Bare Metal port
|
||||
- name: Create Bare Metal port
|
||||
openstack.cloud.baremetal_port:
|
||||
cloud: devstack
|
||||
state: present
|
||||
node: bm-0
|
||||
address: fa:16:3e:aa:aa:aa
|
||||
pxe_enabled: True
|
||||
local_link_connection:
|
||||
switch_id: 0a:1b:2c:3d:4e:5f
|
||||
port_id: Ethernet3/1
|
||||
switch_info: switch1
|
||||
extra:
|
||||
something: extra
|
||||
physical_network: datacenter
|
||||
register: result
|
||||
# Delete Bare Metal port
|
||||
- name: Delete Bare Metal port
|
||||
openstack.cloud.baremetal_port:
|
||||
cloud: devstack
|
||||
state: absent
|
||||
address: fa:16:3e:aa:aa:aa
|
||||
register: result
|
||||
# Update Bare Metal port
|
||||
- name: Update Bare Metal port
|
||||
openstack.cloud.baremetal_port:
|
||||
cloud: devstack
|
||||
state: present
|
||||
uuid: 1a85ebca-22bf-42eb-ad9e-f640789b8098
|
||||
pxe_enabled: False
|
||||
local_link_connection:
|
||||
switch_id: a0:b1:c2:d3:e4:f5
|
||||
port_id: Ethernet4/12
|
||||
switch_info: switch2
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: Unique UUID of the port.
|
||||
returned: always, but can be null
|
||||
type: str
|
||||
result:
|
||||
description: A short text describing the result.
|
||||
returned: success
|
||||
type: str
|
||||
changes:
|
||||
description: Map showing from -> to values for properties that was changed
|
||||
after port update.
|
||||
returned: success
|
||||
type: dict
|
||||
port:
|
||||
description: A port dictionary, subset of the dictionary keys listed below
|
||||
may be returned, depending on your cloud provider.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
address:
|
||||
description: Physical hardware address of this network Port,
|
||||
typically the hardware MAC address.
|
||||
returned: success
|
||||
type: str
|
||||
created_at:
|
||||
description: Bare Metal port created at timestamp.
|
||||
returned: success
|
||||
type: str
|
||||
extra:
|
||||
description: A set of one or more arbitrary metadata key and value
|
||||
pairs.
|
||||
returned: success
|
||||
type: dict
|
||||
id:
|
||||
description: The UUID for the Baremetal Port resource.
|
||||
returned: success
|
||||
type: str
|
||||
internal_info:
|
||||
description: Internal metadata set and stored by the Port. This
|
||||
field is read-only.
|
||||
returned: success
|
||||
type: dict
|
||||
is_pxe_enabled:
|
||||
description: Whether PXE is enabled or disabled on the Port.
|
||||
returned: success
|
||||
type: bool
|
||||
local_link_connection:
|
||||
description: The Port binding profile. If specified, must contain
|
||||
switch_id (only a MAC address or an OpenFlow based
|
||||
datapath_id of the switch are accepted in this field
|
||||
and port_id (identifier of the physical port on the
|
||||
switch to which node's port is connected to) fields.
|
||||
switch_info is an optional string field to be used to
|
||||
store any vendor-specific information.
|
||||
returned: success
|
||||
type: dict
|
||||
location:
|
||||
description: Cloud location of this resource (cloud, project,
|
||||
region, zone)
|
||||
returned: success
|
||||
type: dict
|
||||
name:
|
||||
description: Bare Metal port name.
|
||||
returned: success
|
||||
type: str
|
||||
node_id:
|
||||
description: UUID of the Bare Metal Node this resource belongs to.
|
||||
returned: success
|
||||
type: str
|
||||
physical_network:
|
||||
description: The name of the physical network to which a port is
|
||||
connected.
|
||||
returned: success
|
||||
type: str
|
||||
port_group_id:
|
||||
description: UUID of the Portgroup this resource belongs to.
|
||||
returned: success
|
||||
type: str
|
||||
updated_at:
|
||||
description: Bare Metal port updated at timestamp.
|
||||
returned: success
|
||||
type: str
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
|
||||
IronicModule,
|
||||
ironic_argument_spec,
|
||||
)
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
||||
openstack_module_kwargs,
|
||||
openstack_cloud_from_module
|
||||
)
|
||||
|
||||
_PROP_TO_ATTR_MAP = {
|
||||
'pxe_enabled': 'is_pxe_enabled',
|
||||
'address': 'address',
|
||||
'extra': 'extra',
|
||||
'local_link_connection': 'local_link_connection',
|
||||
'physical_network': 'physical_network',
|
||||
'node_uuid': 'node_id',
|
||||
'portgroup_uuid': 'port_group_id',
|
||||
'uuid': 'id',
|
||||
}
|
||||
|
||||
|
||||
def find_port(module, cloud):
|
||||
port = None
|
||||
if module.params['uuid']:
|
||||
port = cloud.baremetal.find_port(module.params['uuid'])
|
||||
elif module.params['address']:
|
||||
ports = list(cloud.baremetal.ports(address=module.params['address'],
|
||||
details=True))
|
||||
if ports and len(ports) == 1:
|
||||
port = ports[0]
|
||||
elif len(ports) > 1:
|
||||
module.fail_json(
|
||||
msg="Multiple ports with address {address} found. A uuid must "
|
||||
"be defined in order to identify the correct port"
|
||||
.format(address=module.params['address']))
|
||||
|
||||
return port
|
||||
|
||||
|
||||
def add_port(module, cloud):
|
||||
port = find_port(module, cloud)
|
||||
if port:
|
||||
update_port(module, cloud, port=port)
|
||||
|
||||
if not module.params['node'] or not module.params['address']:
|
||||
module.fail_json(
|
||||
msg="A Bare Metal node (name or uuid) and an address is required "
|
||||
"to create a port")
|
||||
|
||||
machine = cloud.get_machine(module.params['node'])
|
||||
if not machine:
|
||||
module.fail_json(
|
||||
msg="Bare Metal node {node} could not be found".format(
|
||||
node=module.params['node']))
|
||||
|
||||
module.params['node_uuid'] = machine.id
|
||||
props = {k: module.params[k] for k in _PROP_TO_ATTR_MAP.keys()
|
||||
if k in module.params}
|
||||
port = cloud.baremetal.create_port(**props)
|
||||
port_dict = port.to_dict()
|
||||
port_dict.pop('links', None)
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
result="Port successfully created",
|
||||
changes=None,
|
||||
port=port_dict,
|
||||
id=port_dict['id'])
|
||||
|
||||
|
||||
def update_port(module, cloud, port=None):
|
||||
if not port:
|
||||
port = find_port(module, cloud)
|
||||
|
||||
if module.params['node']:
|
||||
machine = cloud.get_machine(module.params['node'])
|
||||
if machine:
|
||||
module.params['node_uuid'] = machine.id
|
||||
|
||||
old_props = {k: port[v] for k, v in _PROP_TO_ATTR_MAP.items()}
|
||||
new_props = {k: module.params[k] for k in _PROP_TO_ATTR_MAP.keys()
|
||||
if k in module.params and module.params[k] is not None}
|
||||
prop_diff = {k: new_props[k] for k in _PROP_TO_ATTR_MAP.keys()
|
||||
if k in new_props and old_props[k] != new_props[k]}
|
||||
|
||||
if not prop_diff:
|
||||
port_dict = port.to_dict()
|
||||
port_dict.pop('links', None)
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
result="No port update required",
|
||||
changes=None,
|
||||
port=port_dict,
|
||||
id=port_dict['id'])
|
||||
|
||||
port = cloud.baremetal.update_port(port.id, **prop_diff)
|
||||
port_dict = port.to_dict()
|
||||
port_dict.pop('links', None)
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
result="Port successfully updated",
|
||||
changes={k: {'to': new_props[k], 'from': old_props[k]}
|
||||
for k in prop_diff},
|
||||
port=port_dict,
|
||||
id=port_dict['id'])
|
||||
|
||||
|
||||
def remove_port(module, cloud):
|
||||
if not module.params['uuid'] and not module.params['address']:
|
||||
module.fail_json(
|
||||
msg="A uuid or an address value must be defined in order to "
|
||||
"remove a port.")
|
||||
if module.params['uuid']:
|
||||
port = cloud.baremetal.delete_port(module.params['uuid'])
|
||||
if not port:
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
result="Port not found",
|
||||
changes=None,
|
||||
id=module.params['uuid'])
|
||||
else:
|
||||
port = find_port(module, cloud)
|
||||
if not port:
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
result="Port not found",
|
||||
changes=None,
|
||||
id=None)
|
||||
port = cloud.baremetal.delete_port(port.id)
|
||||
|
||||
module.exit_json(
|
||||
changed=True,
|
||||
result="Port successfully removed",
|
||||
changes=None,
|
||||
id=port.id)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ironic_argument_spec(
|
||||
uuid=dict(required=False),
|
||||
node=dict(required=False),
|
||||
address=dict(required=False),
|
||||
portgroup=dict(required=False),
|
||||
local_link_connection=dict(required=False, type='dict'),
|
||||
is_pxe_enabled=dict(required=False, type='bool'),
|
||||
physical_network=dict(required=False),
|
||||
extra=dict(required=False, type='dict'),
|
||||
state=dict(required=False,
|
||||
default='present',
|
||||
choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
module_kwargs = openstack_module_kwargs()
|
||||
module = IronicModule(argument_spec, **module_kwargs)
|
||||
|
||||
module.params['pxe_enabled'] = module.params.pop('is_pxe_enabled', None)
|
||||
|
||||
sdk, cloud = openstack_cloud_from_module(module)
|
||||
try:
|
||||
if module.params['state'] == 'present':
|
||||
add_port(module, cloud)
|
||||
|
||||
if module.params['state'] == 'absent':
|
||||
remove_port(module, cloud)
|
||||
|
||||
except sdk.exceptions.OpenStackCloudException as e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
208
plugins/modules/baremetal_port_info.py
Normal file
208
plugins/modules/baremetal_port_info.py
Normal file
@@ -0,0 +1,208 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
# Copyright (c) 2021 by Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = '''
|
||||
module: baremetal_port_info
|
||||
short_description: Retrieve information about Bare Metal ports from OpenStack
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Retrieve information about Bare Metal ports from OpenStack.
|
||||
options:
|
||||
uuid:
|
||||
description:
|
||||
- Name or globally unique identifier (UUID) to identify the port.
|
||||
type: str
|
||||
address:
|
||||
description:
|
||||
- Physical hardware address of this network Port, typically the
|
||||
hardware MAC address.
|
||||
type: str
|
||||
node:
|
||||
description:
|
||||
- Name or globally unique identifier (UUID) to identify a Baremetal
|
||||
Node.
|
||||
type: str
|
||||
ironic_url:
|
||||
description:
|
||||
- If noauth mode is utilized, this is required to be set to the
|
||||
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
|
||||
settings set to None.
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Gather information about all baremetal ports
|
||||
- openstack.cloud.baremetal_port_info:
|
||||
cloud: devstack
|
||||
register: result
|
||||
# Gather information about a baremetal port by address
|
||||
- openstack.cloud.baremetal_port_info:
|
||||
cloud: devstack
|
||||
address: fa:16:3e:aa:aa:aa
|
||||
register: result
|
||||
# Gather information about a baremetal port by address
|
||||
- openstack.cloud.baremetal_port_info:
|
||||
cloud: devstack
|
||||
uuid: a2b6bd99-77b9-43f0-9ddc-826568e68dec
|
||||
register: result
|
||||
# Gather information about a baremetal ports associated with a baremetal node
|
||||
- openstack.cloud.baremetal_port_info:
|
||||
cloud: devstack
|
||||
node: bm-0
|
||||
register: result
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
baremetal_ports:
|
||||
description: Bare Metal port list. A subset of the dictionary keys
|
||||
listed below may be returned, depending on your cloud
|
||||
provider.
|
||||
returned: always, but can be null
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
address:
|
||||
description: Physical hardware address of this network Port,
|
||||
typically the hardware MAC address.
|
||||
returned: success
|
||||
type: str
|
||||
created_at:
|
||||
description: Bare Metal port created at timestamp.
|
||||
returned: success
|
||||
type: str
|
||||
extra:
|
||||
description: A set of one or more arbitrary metadata key and
|
||||
value pairs.
|
||||
returned: success
|
||||
type: dict
|
||||
id:
|
||||
description: The UUID for the Baremetal Port resource.
|
||||
returned: success
|
||||
type: str
|
||||
internal_info:
|
||||
description: Internal metadata set and stored by the Port. This
|
||||
field is read-only.
|
||||
returned: success
|
||||
type: dict
|
||||
is_pxe_enabled:
|
||||
description: Whether PXE is enabled or disabled on the Port.
|
||||
returned: success
|
||||
type: bool
|
||||
local_link_connection:
|
||||
description: The Port binding profile.
|
||||
returned: success
|
||||
type: dict
|
||||
contains:
|
||||
switch_id:
|
||||
description: A MAC address or an OpenFlow based datapath_id of
|
||||
the switch.
|
||||
type: str
|
||||
port_id:
|
||||
description: Identifier of the physical port on the switch to
|
||||
which node's port is connected to.
|
||||
type: str
|
||||
switch_info:
|
||||
description: An optional string field to be used to store any
|
||||
vendor-specific information.
|
||||
type: str
|
||||
location:
|
||||
description: Cloud location of this resource (cloud, project,
|
||||
region, zone)
|
||||
returned: success
|
||||
type: dict
|
||||
name:
|
||||
description: Bare Metal port name.
|
||||
returned: success
|
||||
type: str
|
||||
node_id:
|
||||
description: UUID of the Bare Metal Node this resource belongs to.
|
||||
returned: success
|
||||
type: str
|
||||
physical_network:
|
||||
description: The name of the physical network to which a port is
|
||||
connected.
|
||||
returned: success
|
||||
type: str
|
||||
port_group_id:
|
||||
description: UUID of the Portgroup this resource belongs to.
|
||||
returned: success
|
||||
type: str
|
||||
updated_at:
|
||||
description: Bare Metal port updated at timestamp.
|
||||
returned: success
|
||||
type: str
|
||||
'''
|
||||
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
|
||||
IronicModule,
|
||||
ironic_argument_spec,
|
||||
)
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
||||
openstack_module_kwargs,
|
||||
openstack_cloud_from_module
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ironic_argument_spec(
|
||||
uuid=dict(required=False),
|
||||
address=dict(required=False),
|
||||
node=dict(required=False),
|
||||
)
|
||||
module_kwargs = openstack_module_kwargs()
|
||||
module_kwargs['supports_check_mode'] = True
|
||||
module = IronicModule(argument_spec, **module_kwargs)
|
||||
|
||||
ports = list()
|
||||
sdk, cloud = openstack_cloud_from_module(module)
|
||||
try:
|
||||
if module.params['uuid']:
|
||||
port = cloud.baremetal.find_port(module.params['uuid'])
|
||||
if not port:
|
||||
module.fail_json(
|
||||
msg='Baremetal port with uuid {uuid} was not found'
|
||||
.format(uuid=module.params['uuid']))
|
||||
ports.append(port)
|
||||
|
||||
elif module.params['address']:
|
||||
ports = list(
|
||||
cloud.baremetal.ports(address=module.params['address'],
|
||||
details=True))
|
||||
if not ports:
|
||||
module.fail_json(
|
||||
msg='Baremetal port with address {address} was not found'
|
||||
.format(address=module.params['address']))
|
||||
|
||||
elif module.params['node']:
|
||||
machine = cloud.get_machine(module.params['node'])
|
||||
if not machine:
|
||||
module.fail_json(
|
||||
msg='Baremetal node {node} was not found'
|
||||
.format(node=module.params['node']))
|
||||
ports = list(
|
||||
cloud.baremetal.ports(node_uuid=machine.uuid, details=True))
|
||||
|
||||
else:
|
||||
ports = list(cloud.baremetal.ports(details=True))
|
||||
|
||||
# Convert ports to dictionaries and cleanup properties
|
||||
ports = [port.to_dict() for port in ports]
|
||||
for port in ports:
|
||||
# links are not useful
|
||||
port.pop('links', None)
|
||||
|
||||
module.exit_json(changed=False, baremetal_ports=ports)
|
||||
except sdk.exceptions.OpenStackCloudException as e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -26,11 +26,13 @@ options:
|
||||
- Is the service enabled
|
||||
type: bool
|
||||
default: 'yes'
|
||||
service_type:
|
||||
aliases: ['is_enabled']
|
||||
type:
|
||||
description:
|
||||
- The type of service
|
||||
required: true
|
||||
type: str
|
||||
aliases: ['service_type']
|
||||
state:
|
||||
description:
|
||||
- Should the resource be present or absent.
|
||||
@@ -51,14 +53,14 @@ EXAMPLES = '''
|
||||
cloud: mycloud
|
||||
state: present
|
||||
name: glance
|
||||
service_type: image
|
||||
type: image
|
||||
description: OpenStack Image Service
|
||||
# Delete a service
|
||||
- openstack.cloud.catalog_service:
|
||||
cloud: mycloud
|
||||
state: absent
|
||||
name: glance
|
||||
service_type: image
|
||||
type: image
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
@@ -75,6 +77,10 @@ service:
|
||||
description: Service name.
|
||||
type: str
|
||||
sample: "glance"
|
||||
type:
|
||||
description: Service type.
|
||||
type: str
|
||||
sample: "image"
|
||||
service_type:
|
||||
description: Service type.
|
||||
type: str
|
||||
@@ -100,9 +106,9 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
|
||||
class IdentityCatalogServiceModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
description=dict(default=None),
|
||||
enabled=dict(default=True, type='bool'),
|
||||
enabled=dict(default=True, aliases=['is_enabled'], type='bool'),
|
||||
name=dict(required=True),
|
||||
service_type=dict(required=True),
|
||||
type=dict(required=True, aliases=['service_type']),
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
)
|
||||
|
||||
@@ -111,11 +117,9 @@ class IdentityCatalogServiceModule(OpenStackModule):
|
||||
)
|
||||
|
||||
def _needs_update(self, service):
|
||||
if service.enabled != self.params['enabled']:
|
||||
return True
|
||||
if service.description is not None and \
|
||||
service.description != self.params['description']:
|
||||
return True
|
||||
for parameter in ('enabled', 'description', 'type'):
|
||||
if service[parameter] != self.params[parameter]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _system_state_change(self, service):
|
||||
@@ -135,33 +139,34 @@ class IdentityCatalogServiceModule(OpenStackModule):
|
||||
enabled = self.params['enabled']
|
||||
name = self.params['name']
|
||||
state = self.params['state']
|
||||
service_type = self.params['service_type']
|
||||
type = self.params['type']
|
||||
|
||||
services = self.conn.search_services(
|
||||
name_or_id=name, filters=dict(type=service_type))
|
||||
name_or_id=name, filters=(dict(type=type) if type else None))
|
||||
|
||||
service = None
|
||||
if len(services) > 1:
|
||||
self.fail_json(
|
||||
msg='Service name %s and type %s are not unique'
|
||||
% (name, service_type))
|
||||
% (name, type))
|
||||
elif len(services) == 1:
|
||||
service = services[0]
|
||||
else:
|
||||
service = None
|
||||
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(changed=self._system_state_change(service))
|
||||
|
||||
args = {'name': name, 'enabled': enabled, 'type': type}
|
||||
if description:
|
||||
args['description'] = description
|
||||
|
||||
if state == 'present':
|
||||
if service is None:
|
||||
service = self.conn.create_service(
|
||||
name=name, description=description, type=service_type, enabled=True)
|
||||
service = self.conn.create_service(**args)
|
||||
changed = True
|
||||
else:
|
||||
if self._needs_update(service):
|
||||
service = self.conn.update_service(
|
||||
service.id, name=name, type=service_type, enabled=enabled,
|
||||
description=description)
|
||||
service = self.conn.update_service(service,
|
||||
**args)
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
@@ -171,7 +176,7 @@ class IdentityCatalogServiceModule(OpenStackModule):
|
||||
if service is None:
|
||||
changed = False
|
||||
else:
|
||||
self.conn.delete_service(service.id)
|
||||
self.conn.identity.delete_service(service.id)
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
|
||||
|
||||
@@ -126,6 +126,29 @@ openstack_flavors:
|
||||
returned: success
|
||||
type: str
|
||||
sample: "tiny"
|
||||
description:
|
||||
description: Description of the flavor
|
||||
returned: success
|
||||
type: str
|
||||
sample: "Small flavor"
|
||||
is_disabled:
|
||||
description: Wether the flavor is enabled or not
|
||||
returned: success
|
||||
type: bool
|
||||
sample: False
|
||||
rxtx_factor:
|
||||
description: Factor to be multiplied by the rxtx_base property of
|
||||
the network it is attached to in order to have a
|
||||
different bandwidth cap.
|
||||
returned: success
|
||||
type: float
|
||||
sample: 1.0
|
||||
extra_specs:
|
||||
description: Optional parameters to configure different flavors
|
||||
options.
|
||||
returned: success
|
||||
type: dict
|
||||
sample: "{'hw_rng:allowed': True}"
|
||||
disk:
|
||||
description: Size of local disk, in GB.
|
||||
returned: success
|
||||
@@ -196,16 +219,22 @@ class ComputeFlavorInfoModule(OpenStackModule):
|
||||
filters['ephemeral'] = ephemeral
|
||||
|
||||
if name:
|
||||
flavors = self.conn.search_flavors(filters={'name': name})
|
||||
# extra_specs are exposed in the flavor representation since Rocky, so we do not
|
||||
# need get_extra_specs=True which is not available in OpenStack SDK 0.36 (Train)
|
||||
# Ref.: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html
|
||||
flavor = self.conn.compute.find_flavor(name)
|
||||
flavors = [flavor] if flavor else []
|
||||
|
||||
else:
|
||||
flavors = self.conn.list_flavors()
|
||||
flavors = list(self.conn.compute.flavors())
|
||||
if filters:
|
||||
flavors = self.conn.range_search(flavors, filters)
|
||||
|
||||
if limit is not None:
|
||||
flavors = flavors[:limit]
|
||||
|
||||
# Transform entries to dict
|
||||
flavors = [flavor.to_dict(computed=True) for flavor in flavors]
|
||||
self.exit_json(changed=False, openstack_flavors=flavors)
|
||||
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ description:
|
||||
options:
|
||||
binary:
|
||||
description:
|
||||
- Filter by service binary type
|
||||
- Filter by service binary type. Requires openstacksdk>=0.53.
|
||||
type: str
|
||||
host:
|
||||
description:
|
||||
- Filter by service host
|
||||
- Filter by service host. Requires openstacksdk>=0.53.
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
@@ -59,10 +59,26 @@ openstack_compute_services:
|
||||
description: The name of the host.
|
||||
returned: success
|
||||
type: str
|
||||
zone:
|
||||
disabled_reason:
|
||||
description: The reason why the service is disabled
|
||||
returned: success and OpenStack SDK >= 0.53
|
||||
type: str
|
||||
disables_reason:
|
||||
description: The reason why the service is disabled
|
||||
returned: success and OpenStack SDK < 0.53
|
||||
type: str
|
||||
availability_zone:
|
||||
description: The availability zone name.
|
||||
returned: success
|
||||
type: str
|
||||
is_forced_down:
|
||||
description: If the service has been forced down or nova-compute
|
||||
returned: success
|
||||
type: bool
|
||||
name:
|
||||
description: Service name
|
||||
returned: success
|
||||
type: str
|
||||
status:
|
||||
description: The status of the service. One of enabled or disabled.
|
||||
returned: success
|
||||
@@ -71,7 +87,7 @@ openstack_compute_services:
|
||||
description: The state of the service. One of up or down.
|
||||
returned: success
|
||||
type: str
|
||||
update:
|
||||
update_at:
|
||||
description: The date and time when the resource was updated
|
||||
returned: success
|
||||
type: str
|
||||
@@ -82,23 +98,18 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
|
||||
|
||||
class ComputeServiceInfoModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
binary=dict(required=False, default=None),
|
||||
host=dict(required=False, default=None),
|
||||
binary=dict(required=False, default=None, min_ver='0.53.0'),
|
||||
host=dict(required=False, default=None, min_ver='0.53.0'),
|
||||
)
|
||||
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
|
||||
filters = self.check_versioned(binary=self.params['binary'], host=self.params['host'])
|
||||
filters = {k: v for k, v in filters.items() if v is not None}
|
||||
services = self.conn.compute.services(**filters)
|
||||
services = list(services)
|
||||
services = [service.to_dict(computed=True) for service in services]
|
||||
self.exit_json(changed=False, openstack_compute_services=services)
|
||||
|
||||
|
||||
|
||||
@@ -161,12 +161,8 @@ class DnsZoneInfoModule(OpenStackModule):
|
||||
if ttl:
|
||||
kwargs['ttl'] = ttl
|
||||
|
||||
data = []
|
||||
|
||||
for raw in self.conn.dns.zones(**kwargs):
|
||||
dt = raw.to_dict()
|
||||
dt.pop('location')
|
||||
data.append(dt)
|
||||
data = [zone.to_dict(computed=False) for zone in
|
||||
self.conn.dns.zones(**kwargs)]
|
||||
|
||||
self.exit_json(zones=data, changed=False)
|
||||
|
||||
|
||||
@@ -81,26 +81,34 @@ endpoint:
|
||||
description: Endpoint ID.
|
||||
type: str
|
||||
sample: 3292f020780b4d5baf27ff7e1d224c44
|
||||
interface:
|
||||
description: Endpoint Interface.
|
||||
type: str
|
||||
sample: public
|
||||
enabled:
|
||||
description: Service status.
|
||||
type: bool
|
||||
sample: True
|
||||
links:
|
||||
description: Links for the endpoint
|
||||
type: str
|
||||
sample: http://controller/identity/v3/endpoints/123
|
||||
region:
|
||||
description: Region Name.
|
||||
description: Same as C(region_id). Deprecated.
|
||||
type: str
|
||||
sample: RegionOne
|
||||
region_id:
|
||||
description: Region ID.
|
||||
type: str
|
||||
sample: RegionOne
|
||||
service_id:
|
||||
description: Service ID.
|
||||
type: str
|
||||
sample: b91f1318f735494a825a55388ee118f3
|
||||
interface:
|
||||
description: Endpoint Interface.
|
||||
type: str
|
||||
sample: public
|
||||
url:
|
||||
description: Service URL.
|
||||
type: str
|
||||
sample: http://controller:9292
|
||||
enabled:
|
||||
description: Service status.
|
||||
type: bool
|
||||
sample: True
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
@@ -148,10 +156,11 @@ class IdentityEndpointModule(OpenStackModule):
|
||||
state = self.params['state']
|
||||
|
||||
service = self.conn.get_service(service_name_or_id)
|
||||
|
||||
if service is None and state == 'absent':
|
||||
self.exit_json(changed=False)
|
||||
|
||||
elif service is None and state == 'present':
|
||||
if service is None and state == 'present':
|
||||
self.fail_json(msg='Service %s does not exist' % service_name_or_id)
|
||||
|
||||
filters = dict(service_id=service.id, interface=interface)
|
||||
@@ -159,24 +168,27 @@ class IdentityEndpointModule(OpenStackModule):
|
||||
filters['region'] = region
|
||||
endpoints = self.conn.search_endpoints(filters=filters)
|
||||
|
||||
endpoint = None
|
||||
if len(endpoints) > 1:
|
||||
self.fail_json(msg='Service %s, interface %s and region %s are '
|
||||
'not unique' %
|
||||
(service_name_or_id, interface, region))
|
||||
elif len(endpoints) == 1:
|
||||
endpoint = endpoints[0]
|
||||
else:
|
||||
endpoint = None
|
||||
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(changed=self._system_state_change(endpoint))
|
||||
|
||||
if state == 'present':
|
||||
if endpoint is None:
|
||||
result = self.conn.create_endpoint(
|
||||
service_name_or_id=service, url=url, interface=interface,
|
||||
region=region, enabled=enabled)
|
||||
endpoint = result[0]
|
||||
args = {'url': url, 'interface': interface,
|
||||
'service_name_or_id': service.id, 'enabled': enabled,
|
||||
'region': region}
|
||||
endpoints = self.conn.create_endpoint(**args)
|
||||
# safe because endpoints contains a single item when url is
|
||||
# given to self.conn.create_endpoint()
|
||||
endpoint = endpoints[0]
|
||||
|
||||
changed = True
|
||||
else:
|
||||
if self._needs_update(endpoint):
|
||||
@@ -185,7 +197,8 @@ class IdentityEndpointModule(OpenStackModule):
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
self.exit_json(changed=changed, endpoint=endpoint)
|
||||
self.exit_json(changed=changed,
|
||||
endpoint=endpoint)
|
||||
|
||||
elif state == 'absent':
|
||||
if endpoint is None:
|
||||
|
||||
@@ -165,7 +165,6 @@ class FloatingIPInfoModule(OpenStackModule):
|
||||
router = self.params['router']
|
||||
status = self.params['status']
|
||||
|
||||
data = []
|
||||
query = {}
|
||||
if description:
|
||||
query['description'] = description
|
||||
@@ -194,15 +193,8 @@ class FloatingIPInfoModule(OpenStackModule):
|
||||
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
|
||||
)
|
||||
ips = [ip.to_dict(computed=False) for ip in self.conn.network.ips(**query)]
|
||||
self.exit_json(changed=False, floating_ips=ips)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -73,8 +73,36 @@ EXAMPLES = '''
|
||||
name: db_aggregate
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
|
||||
RETURN = r'''
|
||||
aggregate:
|
||||
description: A host aggregate resource.
|
||||
type: complex
|
||||
returned: On success, when I(state) is present
|
||||
contains:
|
||||
availability_zone:
|
||||
description: Availability zone of the aggregate
|
||||
type: str
|
||||
returned: always
|
||||
deleted:
|
||||
description: Whether or not the resource is deleted
|
||||
type: bool
|
||||
returned: always
|
||||
hosts:
|
||||
description: Hosts belonging to the aggregate
|
||||
type: str
|
||||
returned: always
|
||||
id:
|
||||
description: The UUID of the aggregate.
|
||||
type: str
|
||||
returned: always
|
||||
metadata:
|
||||
description: Metadata attached to the aggregate
|
||||
type: str
|
||||
returned: always
|
||||
name:
|
||||
description: Name of the aggregate
|
||||
type: str
|
||||
returned: always
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
@@ -94,14 +122,20 @@ class ComputeHostAggregateModule(OpenStackModule):
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
def _find_aggregate(self, name_or_id):
|
||||
aggregates = self.conn.search_aggregates(name_or_id=name_or_id)
|
||||
if len(aggregates) == 1:
|
||||
return aggregates[0]
|
||||
elif len(aggregates) == 0:
|
||||
return None
|
||||
raise Exception("Aggregate is not unique, this should be impossible")
|
||||
|
||||
def _needs_update(self, aggregate):
|
||||
new_metadata = (self.params['metadata'] or {})
|
||||
new_metadata = self.params['metadata'] or {}
|
||||
|
||||
if self.params['availability_zone'] is not None:
|
||||
new_metadata['availability_zone'] = self.params['availability_zone']
|
||||
|
||||
if self.params['name'] != aggregate.name:
|
||||
return True
|
||||
if self.params['hosts'] is not None:
|
||||
if self.params['purge_hosts']:
|
||||
if set(self.params['hosts']) != set(aggregate.hosts):
|
||||
@@ -110,11 +144,10 @@ class ComputeHostAggregateModule(OpenStackModule):
|
||||
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:
|
||||
|
||||
for param in ('availability_zone', 'metadata'):
|
||||
if self.params[param] is not None and \
|
||||
self.params[param] != aggregate[param]:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -135,16 +168,16 @@ class ComputeHostAggregateModule(OpenStackModule):
|
||||
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)
|
||||
hosts_to_add = set(hosts) - set(aggregate['hosts'] or [])
|
||||
for host in hosts_to_add:
|
||||
self.conn.add_host_to_aggregate(aggregate.id, host)
|
||||
|
||||
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)
|
||||
hosts_to_remove = set(aggregate["hosts"] or []) - set(hosts)
|
||||
for host in hosts_to_remove:
|
||||
self.conn.remove_host_from_aggregate(aggregate.id, host)
|
||||
|
||||
def run(self):
|
||||
name = self.params['name']
|
||||
@@ -157,18 +190,12 @@ class ComputeHostAggregateModule(OpenStackModule):
|
||||
if metadata is not None:
|
||||
metadata.pop('availability_zone', None)
|
||||
|
||||
aggregates = self.conn.search_aggregates(name_or_id=name)
|
||||
|
||||
if len(aggregates) == 1:
|
||||
aggregate = aggregates[0]
|
||||
elif len(aggregates) == 0:
|
||||
aggregate = None
|
||||
else:
|
||||
raise Exception("Should not happen")
|
||||
aggregate = self._find_aggregate(name)
|
||||
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(changed=self._system_state_change(aggregate))
|
||||
|
||||
changed = False
|
||||
if state == 'present':
|
||||
if aggregate is None:
|
||||
aggregate = self.conn.create_aggregate(
|
||||
@@ -177,32 +204,27 @@ class ComputeHostAggregateModule(OpenStackModule):
|
||||
if metadata:
|
||||
self.conn.set_aggregate_metadata(aggregate.id, metadata)
|
||||
changed = True
|
||||
else:
|
||||
if self._needs_update(aggregate):
|
||||
if availability_zone is not None:
|
||||
aggregate = self.conn.update_aggregate(
|
||||
aggregate.id, name=name,
|
||||
availability_zone=availability_zone)
|
||||
if metadata is not None:
|
||||
metas = metadata
|
||||
for i in (set(aggregate.metadata.keys()) - set(metadata.keys())):
|
||||
if i != 'availability_zone':
|
||||
metas[i] = None
|
||||
self.conn.set_aggregate_metadata(aggregate.id, metas)
|
||||
self._update_hosts(aggregate, hosts, purge_hosts)
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
self.exit_json(changed=changed)
|
||||
|
||||
elif state == 'absent':
|
||||
if aggregate is None:
|
||||
changed = False
|
||||
else:
|
||||
self._update_hosts(aggregate, [], True)
|
||||
self.conn.delete_aggregate(aggregate.id)
|
||||
elif self._needs_update(aggregate):
|
||||
if availability_zone is not None:
|
||||
aggregate = self.conn.update_aggregate(
|
||||
aggregate.id, name=name,
|
||||
availability_zone=availability_zone)
|
||||
if metadata is not None:
|
||||
metas = metadata
|
||||
for i in set(aggregate.metadata.keys() - set(metadata.keys())):
|
||||
if i != 'availability_zone':
|
||||
metas[i] = None
|
||||
self.conn.set_aggregate_metadata(aggregate.id, metas)
|
||||
self._update_hosts(aggregate, hosts, purge_hosts)
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
aggregate = self._find_aggregate(name)
|
||||
self.exit_json(changed=changed, aggregate=aggregate)
|
||||
|
||||
elif state == 'absent' and aggregate is not None:
|
||||
self._update_hosts(aggregate, [], True)
|
||||
self.conn.delete_aggregate(aggregate.id)
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -104,7 +104,8 @@ class IdentityDomainModule(OpenStackModule):
|
||||
if self.params['description'] is not None and \
|
||||
domain.description != self.params['description']:
|
||||
return True
|
||||
if domain.enabled != self.params['enabled']:
|
||||
if domain.get(
|
||||
"is_enabled", domain.get("enabled")) != self.params['enabled']:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -126,7 +127,7 @@ class IdentityDomainModule(OpenStackModule):
|
||||
enabled = self.params['enabled']
|
||||
state = self.params['state']
|
||||
|
||||
domains = self.conn.search_domains(filters=dict(name=name))
|
||||
domains = list(self.conn.identity.domains(name=name))
|
||||
|
||||
if len(domains) > 1:
|
||||
self.fail_json(msg='Domain name %s is not unique' % name)
|
||||
@@ -151,7 +152,10 @@ class IdentityDomainModule(OpenStackModule):
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
self.exit_json(changed=changed, domain=domain, id=domain.id)
|
||||
if hasattr(domain, "to_dict"):
|
||||
domain = domain.to_dict()
|
||||
domain.pop("location")
|
||||
self.exit_json(changed=changed, domain=domain, id=domain['id'])
|
||||
|
||||
elif state == 'absent':
|
||||
if domain is None:
|
||||
|
||||
@@ -18,7 +18,7 @@ options:
|
||||
type: str
|
||||
filters:
|
||||
description:
|
||||
- A dictionary of meta data to use for further filtering. Elements of
|
||||
- A dictionary of meta data to use for filtering. Elements of
|
||||
this dictionary may be additional dictionaries.
|
||||
type: dict
|
||||
requirements:
|
||||
@@ -61,7 +61,8 @@ RETURN = '''
|
||||
openstack_domains:
|
||||
description: has all the OpenStack information about domains
|
||||
returned: always, but can be null
|
||||
type: complex
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
id:
|
||||
description: Unique UUID.
|
||||
@@ -89,10 +90,8 @@ class IdentityDomainInfoModule(OpenStackModule):
|
||||
name=dict(required=False, default=None),
|
||||
filters=dict(required=False, type='dict', default=None),
|
||||
)
|
||||
|
||||
module_kwargs = dict(
|
||||
mutually_exclusive=[
|
||||
['name', 'filters'],
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
@@ -100,18 +99,14 @@ class IdentityDomainInfoModule(OpenStackModule):
|
||||
|
||||
def run(self):
|
||||
name = self.params['name']
|
||||
filters = self.params['filters']
|
||||
filters = self.params['filters'] or {}
|
||||
|
||||
args = {}
|
||||
if name:
|
||||
# Let's suppose user is passing domain ID
|
||||
try:
|
||||
domains = self.conn.get_domain(name)
|
||||
except Exception:
|
||||
domains = self.conn.search_domains(filters={'name': name})
|
||||
|
||||
else:
|
||||
domains = self.conn.search_domains(filters)
|
||||
args['name_or_id'] = name
|
||||
args['filters'] = filters
|
||||
|
||||
domains = self.conn.search_domains(**args)
|
||||
self.exit_json(changed=False, openstack_domains=domains)
|
||||
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ EXAMPLES = '''
|
||||
RETURN = '''
|
||||
openstack_groups:
|
||||
description: Dictionary describing all the matching groups.
|
||||
returned: always, but can be null
|
||||
returned: always, but can be an empty list
|
||||
type: complex
|
||||
contains:
|
||||
name:
|
||||
@@ -126,27 +126,19 @@ class IdentityGroupInfoModule(OpenStackModule):
|
||||
def run(self):
|
||||
name = self.params['name']
|
||||
domain = self.params['domain']
|
||||
filters = self.params['filters']
|
||||
filters = self.params['filters'] or {}
|
||||
|
||||
args = {}
|
||||
if domain:
|
||||
try:
|
||||
# We assume admin is passing domain id
|
||||
dom = self.conn.get_domain(domain)['id']
|
||||
domain = dom
|
||||
except Exception:
|
||||
# If we fail, maybe admin is passing a domain name.
|
||||
# Note that domains have unique names, just like id.
|
||||
dom = self.conn.search_domains(filters={'name': domain})
|
||||
if dom:
|
||||
domain = dom[0]['id']
|
||||
else:
|
||||
self.fail_json(msg='Domain name or ID does not exist')
|
||||
dom = self.conn.identity.find_domain(domain)
|
||||
if dom:
|
||||
args['domain_id'] = dom['id']
|
||||
else:
|
||||
self.fail_json(msg='Domain name or ID does not exist')
|
||||
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
groups = self.conn.search_groups(name, filters, domain_id=domain)
|
||||
self.exit_json(changed=False, groups=groups)
|
||||
groups = self.conn.search_groups(name, filters, **args)
|
||||
# groups is for backward (and forward) compatibility
|
||||
self.exit_json(changed=False, groups=groups, openstack_groups=groups)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -49,6 +49,10 @@ role:
|
||||
returned: On success when I(state) is 'present'.
|
||||
type: complex
|
||||
contains:
|
||||
domain_id:
|
||||
description: Domain to which the role belongs
|
||||
type: str
|
||||
sample: default
|
||||
id:
|
||||
description: Unique role ID.
|
||||
type: str
|
||||
@@ -88,20 +92,16 @@ class IdentityRoleModule(OpenStackModule):
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(changed=self._system_state_change(state, role))
|
||||
|
||||
changed = False
|
||||
if state == 'present':
|
||||
if role is None:
|
||||
role = self.conn.create_role(name)
|
||||
role = self.conn.create_role(name=name)
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
self.exit_json(changed=changed, role=role)
|
||||
elif state == 'absent':
|
||||
if role is None:
|
||||
changed = False
|
||||
else:
|
||||
self.conn.delete_role(name)
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
elif state == 'absent' and role is not None:
|
||||
self.conn.identity.delete_role(role['id'])
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: identity_role_info
|
||||
short_description: Retrive information about roles
|
||||
short_description: Retrieve 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
|
||||
- Domain ID which owns the role
|
||||
type: str
|
||||
required: false
|
||||
name:
|
||||
description:
|
||||
- List role speficied by name
|
||||
- Name or ID of the role
|
||||
type: str
|
||||
required: false
|
||||
|
||||
@@ -37,21 +37,19 @@ openstack_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:
|
||||
|
||||
contains:
|
||||
id:
|
||||
description: Unique ID for the role
|
||||
returned: success
|
||||
type: str
|
||||
name:
|
||||
description: Unique role name, within the owning domain.
|
||||
returned: success
|
||||
type: str
|
||||
domain_id:
|
||||
description: References the domain ID which owns the role.
|
||||
returned: success
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
@@ -75,23 +73,24 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
|
||||
|
||||
|
||||
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})
|
||||
params = {
|
||||
'domain_id': self.params['domain_id'],
|
||||
'name_or_id': self.params['name'],
|
||||
}
|
||||
params = {k: v for k, v in params.items() if v is not None}
|
||||
|
||||
roles = self.conn.search_roles(**params)
|
||||
self.exit_json(changed=False, openstack_roles=roles)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -26,6 +26,7 @@ options:
|
||||
update_password:
|
||||
required: false
|
||||
choices: ['always', 'on_create']
|
||||
default: on_create
|
||||
description:
|
||||
- C(always) will attempt to update password. C(on_create) will only
|
||||
set the password for newly created users.
|
||||
@@ -108,28 +109,46 @@ RETURN = '''
|
||||
user:
|
||||
description: Dictionary describing the user.
|
||||
returned: On success when I(state) is 'present'
|
||||
type: complex
|
||||
type: dict
|
||||
contains:
|
||||
default_project_id:
|
||||
description: User default project ID. Only present with Keystone >= v3.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "4427115787be45f08f0ec22a03bfc735"
|
||||
description:
|
||||
description: The description of this user
|
||||
returned: success
|
||||
type: str
|
||||
sample: "a user"
|
||||
domain_id:
|
||||
description: User domain ID. Only present with Keystone >= v3.
|
||||
returned: success
|
||||
type: str
|
||||
sample: "default"
|
||||
email:
|
||||
description: User email address
|
||||
returned: success
|
||||
type: str
|
||||
sample: "demo@example.com"
|
||||
id:
|
||||
description: User ID
|
||||
returned: success
|
||||
type: str
|
||||
sample: "f59382db809c43139982ca4189404650"
|
||||
enabled:
|
||||
description: Indicates whether the user is enabled
|
||||
type: bool
|
||||
name:
|
||||
description: User name
|
||||
description: Unique user name, within the owning domain
|
||||
returned: success
|
||||
type: str
|
||||
sample: "demouser"
|
||||
username:
|
||||
description: Username with Identity API v2 (OpenStack Pike or earlier) else Null
|
||||
returned: success
|
||||
type: str
|
||||
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
@@ -145,46 +164,37 @@ class IdentityUserModule(OpenStackModule):
|
||||
domain=dict(required=False, default=None),
|
||||
enabled=dict(default=True, type='bool'),
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
update_password=dict(default=None, choices=['always', 'on_create']),
|
||||
update_password=dict(default='on_create', choices=['always', 'on_create']),
|
||||
)
|
||||
|
||||
module_kwargs = dict()
|
||||
|
||||
def _needs_update(self, params_dict, user):
|
||||
for k in params_dict:
|
||||
if k not in ('password', 'update_password') and user[k] != params_dict[k]:
|
||||
# We don't get password back in the user object, so assume any supplied
|
||||
# password is a change.
|
||||
if k == 'password':
|
||||
return True
|
||||
if k == 'default_project':
|
||||
if user['default_project_id'] != params_dict['default_project']:
|
||||
return True
|
||||
else:
|
||||
continue
|
||||
if user[k] != params_dict[k]:
|
||||
return True
|
||||
|
||||
# We don't get password back in the user object, so assume any supplied
|
||||
# password is a change.
|
||||
if (
|
||||
params_dict['password'] is not None
|
||||
and params_dict['update_password'] == 'always'
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _get_domain_id(self, domain):
|
||||
try:
|
||||
# We assume admin is passing domain id
|
||||
domain_id = self.conn.get_domain(domain)['id']
|
||||
except Exception:
|
||||
# If we fail, maybe admin is passing a domain name.
|
||||
# Note that domains have unique names, just like id.
|
||||
try:
|
||||
domain_id = self.conn.search_domains(filters={'name': domain})[0]['id']
|
||||
except Exception:
|
||||
# Ok, let's hope the user is non-admin and passing a sane id
|
||||
domain_id = domain
|
||||
|
||||
return domain_id
|
||||
dom_obj = self.conn.identity.find_domain(domain)
|
||||
if dom_obj is None:
|
||||
# Ok, let's hope the user is non-admin and passing a sane id
|
||||
return domain
|
||||
return dom_obj.id
|
||||
|
||||
def _get_default_project_id(self, default_project, domain_id):
|
||||
project = self.conn.get_project(default_project, domain_id=domain_id)
|
||||
project = self.conn.identity.find_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):
|
||||
@@ -198,84 +208,50 @@ class IdentityUserModule(OpenStackModule):
|
||||
update_password = self.params['update_password']
|
||||
description = self.params['description']
|
||||
|
||||
domain_id = None
|
||||
if domain:
|
||||
domain_id = self._get_domain_id(domain)
|
||||
user = self.conn.get_user(name, domain_id=domain_id)
|
||||
else:
|
||||
domain_id = None
|
||||
user = self.conn.get_user(name)
|
||||
|
||||
changed = False
|
||||
if state == 'present':
|
||||
if update_password in ('always', 'on_create'):
|
||||
if not password:
|
||||
msg = "update_password is %s but a password value is missing" % update_password
|
||||
self.fail_json(msg=msg)
|
||||
default_project_id = None
|
||||
user_args = {
|
||||
'name': name,
|
||||
'email': email,
|
||||
'domain_id': domain_id,
|
||||
'description': description,
|
||||
'enabled': enabled,
|
||||
}
|
||||
if default_project:
|
||||
default_project_id = self._get_default_project_id(
|
||||
default_project, domain_id)
|
||||
user_args['default_project'] = default_project_id
|
||||
user_args = {k: v for k, v in user_args.items() if v is not None}
|
||||
|
||||
changed = False
|
||||
if user is None:
|
||||
if description is not None:
|
||||
user = self.conn.create_user(
|
||||
name=name, password=password, email=email,
|
||||
default_project=default_project_id, domain_id=domain_id,
|
||||
enabled=enabled, description=description)
|
||||
else:
|
||||
user = self.conn.create_user(
|
||||
name=name, password=password, email=email,
|
||||
default_project=default_project_id, domain_id=domain_id,
|
||||
enabled=enabled)
|
||||
if password:
|
||||
user_args['password'] = password
|
||||
|
||||
user = self.conn.create_user(**user_args)
|
||||
changed = True
|
||||
else:
|
||||
params_dict = {'email': email, 'enabled': enabled,
|
||||
'password': password,
|
||||
'update_password': update_password}
|
||||
if description is not None:
|
||||
params_dict['description'] = description
|
||||
if domain_id is not None:
|
||||
params_dict['domain_id'] = domain_id
|
||||
if default_project_id is not None:
|
||||
params_dict['default_project_id'] = default_project_id
|
||||
if update_password == 'always':
|
||||
if not password:
|
||||
self.fail_json(msg="update_password is always but a password value is missing")
|
||||
user_args['password'] = password
|
||||
|
||||
if self._needs_update(params_dict, user):
|
||||
if update_password == 'always':
|
||||
if description is not None:
|
||||
user = self.conn.update_user(
|
||||
user['id'], password=password, email=email,
|
||||
default_project=default_project_id,
|
||||
domain_id=domain_id, enabled=enabled, description=description)
|
||||
else:
|
||||
user = self.conn.update_user(
|
||||
user['id'], password=password, email=email,
|
||||
default_project=default_project_id,
|
||||
domain_id=domain_id, enabled=enabled)
|
||||
else:
|
||||
if description is not None:
|
||||
user = self.conn.update_user(
|
||||
user['id'], email=email,
|
||||
default_project=default_project_id,
|
||||
domain_id=domain_id, enabled=enabled, description=description)
|
||||
else:
|
||||
user = self.conn.update_user(
|
||||
user['id'], email=email,
|
||||
default_project=default_project_id,
|
||||
domain_id=domain_id, enabled=enabled)
|
||||
if self._needs_update(user_args, user):
|
||||
user = self.conn.update_user(user['id'], **user_args)
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
self.exit_json(changed=changed, user=user)
|
||||
|
||||
elif state == 'absent':
|
||||
if user is None:
|
||||
changed = False
|
||||
else:
|
||||
if domain:
|
||||
self.conn.delete_user(user['id'], domain_id=domain_id)
|
||||
else:
|
||||
self.conn.delete_user(user['id'])
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
self.exit_json(changed=changed, user=user)
|
||||
elif state == 'absent' and user is not None:
|
||||
self.conn.identity.delete_user(user['id'])
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -74,35 +74,40 @@ EXAMPLES = '''
|
||||
RETURN = '''
|
||||
openstack_users:
|
||||
description: has all the OpenStack information about users
|
||||
returned: always, but can be null
|
||||
type: complex
|
||||
returned: always
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
id:
|
||||
description: Unique UUID.
|
||||
returned: success
|
||||
type: str
|
||||
name:
|
||||
description: Name given to the user.
|
||||
returned: success
|
||||
type: str
|
||||
enabled:
|
||||
description: Flag to indicate if the user is enabled
|
||||
returned: success
|
||||
type: bool
|
||||
domain_id:
|
||||
description: Domain ID containing the user
|
||||
description: Username of the user.
|
||||
returned: success
|
||||
type: str
|
||||
default_project_id:
|
||||
description: Default project ID of the user
|
||||
returned: success
|
||||
type: str
|
||||
description:
|
||||
description: The description of this user
|
||||
returned: success
|
||||
type: str
|
||||
domain_id:
|
||||
description: Domain ID containing the user
|
||||
returned: success
|
||||
type: str
|
||||
email:
|
||||
description: Email of the user
|
||||
returned: success
|
||||
type: str
|
||||
enabled:
|
||||
description: Flag to indicate if the user is enabled
|
||||
returned: success
|
||||
type: bool
|
||||
username:
|
||||
description: Username of the user
|
||||
description: Username with Identity API v2 (OpenStack Pike or earlier) else Null
|
||||
returned: success
|
||||
type: str
|
||||
'''
|
||||
@@ -127,26 +132,15 @@ class IdentityUserInfoModule(OpenStackModule):
|
||||
domain = self.params['domain']
|
||||
filters = self.params['filters']
|
||||
|
||||
args = {}
|
||||
if domain:
|
||||
try:
|
||||
# We assume admin is passing domain id
|
||||
dom = self.conn.get_domain(domain)['id']
|
||||
domain = dom
|
||||
except Exception:
|
||||
# If we fail, maybe admin is passing a domain name.
|
||||
# Note that domains have unique names, just like id.
|
||||
dom = self.conn.search_domains(filters={'name': domain})
|
||||
if dom:
|
||||
domain = dom[0]['id']
|
||||
else:
|
||||
self.fail_json(msg='Domain name or ID does not exist')
|
||||
dom_obj = self.conn.identity.find_domain(domain)
|
||||
if dom_obj is None:
|
||||
self.fail_json(
|
||||
msg="Domain name or ID '{0}' does not exist".format(domain))
|
||||
args['domain_id'] = dom_obj.id
|
||||
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
filters['domain_id'] = domain
|
||||
|
||||
users = self.conn.search_users(name, filters)
|
||||
users = self.conn.search_users(name, filters, **args)
|
||||
self.exit_json(changed=False, openstack_users=users)
|
||||
|
||||
|
||||
|
||||
@@ -40,9 +40,15 @@ options:
|
||||
default: bare
|
||||
choices: ['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']
|
||||
type: str
|
||||
owner:
|
||||
project:
|
||||
description:
|
||||
- The owner of the image
|
||||
- The name or ID of the project owning the image
|
||||
type: str
|
||||
aliases: ['owner']
|
||||
project_domain:
|
||||
description:
|
||||
- The domain the project owning the image belongs to
|
||||
- May be used to identify a unique project when providing a name to the project argument and multiple projects with such name exist
|
||||
type: str
|
||||
min_disk:
|
||||
description:
|
||||
@@ -61,7 +67,7 @@ options:
|
||||
description:
|
||||
- Prevent image from being deleted
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: false
|
||||
filename:
|
||||
description:
|
||||
- The path to the file which has to be uploaded
|
||||
@@ -169,7 +175,8 @@ class ImageModule(OpenStackModule):
|
||||
disk_format=dict(default='qcow2',
|
||||
choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']),
|
||||
container_format=dict(default='bare', choices=['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']),
|
||||
owner=dict(type='str'),
|
||||
project=dict(type='str', aliases=['owner']),
|
||||
project_domain=dict(type='str'),
|
||||
min_disk=dict(type='int', default=0),
|
||||
min_ram=dict(type='int', default=0),
|
||||
is_public=dict(type='bool', default=False),
|
||||
@@ -202,6 +209,16 @@ class ImageModule(OpenStackModule):
|
||||
kwargs = {}
|
||||
if self.params['id'] is not None:
|
||||
kwargs['id'] = self.params['id']
|
||||
if self.params['project']:
|
||||
project_domain = {'id': None}
|
||||
if self.params['project_domain']:
|
||||
project_domain = self.conn.get_domain(name_or_id=self.params['project_domain'])
|
||||
if not project_domain or project_domain['id'] is None:
|
||||
self.fail(msg='Project domain %s could not be found' % self.params['project_domain'])
|
||||
project = self.conn.get_project(name_or_id=self.params['project'], domain_id=project_domain['id'])
|
||||
if not project:
|
||||
self.fail(msg='Project %s could not be found' % self.params['project'])
|
||||
kwargs['owner'] = project['id']
|
||||
image = self.conn.create_image(
|
||||
name=self.params['name'],
|
||||
filename=self.params['filename'],
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: image_info
|
||||
short_description: Retrieve information about an image within OpenStack.
|
||||
author: OpenStack Ansible SIG
|
||||
@@ -17,11 +18,12 @@ options:
|
||||
- Name or ID of the image
|
||||
required: false
|
||||
type: str
|
||||
properties:
|
||||
filters:
|
||||
description:
|
||||
- Dict of properties of the images used for query
|
||||
type: dict
|
||||
required: false
|
||||
aliases: ['properties']
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
@@ -43,7 +45,7 @@ EXAMPLES = '''
|
||||
|
||||
- name: Show openstack information
|
||||
debug:
|
||||
msg: "{{ result.openstack_image }}"
|
||||
msg: "{{ result.image }}"
|
||||
|
||||
# Show all available Openstack images
|
||||
- name: Retrieve all available Openstack images
|
||||
@@ -52,22 +54,22 @@ EXAMPLES = '''
|
||||
|
||||
- name: Show images
|
||||
debug:
|
||||
msg: "{{ result.openstack_image }}"
|
||||
msg: "{{ result.image }}"
|
||||
|
||||
# Show images matching requested properties
|
||||
- name: Retrieve images having properties with desired values
|
||||
openstack.cloud.image_facts:
|
||||
properties:
|
||||
filters:
|
||||
some_property: some_value
|
||||
OtherProp: OtherVal
|
||||
|
||||
- name: Show images
|
||||
debug:
|
||||
msg: "{{ result.openstack_image }}"
|
||||
msg: "{{ result.image }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
openstack_image:
|
||||
openstack_images:
|
||||
description: has all the openstack information about the image
|
||||
returned: always, but can be null
|
||||
type: complex
|
||||
@@ -88,14 +90,14 @@ openstack_image:
|
||||
description: Image created at timestamp.
|
||||
returned: success
|
||||
type: str
|
||||
deleted:
|
||||
description: Image deleted flag.
|
||||
returned: success
|
||||
type: bool
|
||||
container_format:
|
||||
description: Container format of the image.
|
||||
returned: success
|
||||
type: str
|
||||
direct_url:
|
||||
description: URL to access the image file kept in external store.
|
||||
returned: success
|
||||
type: str
|
||||
min_ram:
|
||||
description: Min amount of RAM required for this image.
|
||||
returned: success
|
||||
@@ -104,19 +106,39 @@ openstack_image:
|
||||
description: Disk format of the image.
|
||||
returned: success
|
||||
type: str
|
||||
file:
|
||||
description: The URL for the virtual machine image file.
|
||||
returned: success
|
||||
type: str
|
||||
os_hidden:
|
||||
description: Controls whether an image is displayed in the default image-list response
|
||||
returned: success
|
||||
type: bool
|
||||
locations:
|
||||
description: A list of URLs to access the image file in external store.
|
||||
returned: success
|
||||
type: str
|
||||
metadata:
|
||||
description: The location metadata.
|
||||
returned: success
|
||||
type: str
|
||||
schema:
|
||||
description: URL for the schema describing a virtual machine image.
|
||||
returned: success
|
||||
type: str
|
||||
updated_at:
|
||||
description: Image updated at timestamp.
|
||||
returned: success
|
||||
type: str
|
||||
properties:
|
||||
description: Additional properties associated with the image.
|
||||
virtual_size:
|
||||
description: The virtual size of the image.
|
||||
returned: success
|
||||
type: dict
|
||||
type: str
|
||||
min_disk:
|
||||
description: Min amount of disk space required for this image.
|
||||
returned: success
|
||||
type: int
|
||||
protected:
|
||||
is_protected:
|
||||
description: Image protected flag.
|
||||
returned: success
|
||||
type: bool
|
||||
@@ -128,12 +150,8 @@ openstack_image:
|
||||
description: Owner for the image.
|
||||
returned: success
|
||||
type: str
|
||||
is_public:
|
||||
description: Is public flag of the image.
|
||||
returned: success
|
||||
type: bool
|
||||
deleted_at:
|
||||
description: Image deleted at timestamp.
|
||||
visibility:
|
||||
description: Indicates who has access to the image.
|
||||
returned: success
|
||||
type: str
|
||||
size:
|
||||
@@ -145,7 +163,6 @@ openstack_image:
|
||||
returned: success
|
||||
type: list
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
|
||||
|
||||
@@ -155,20 +172,27 @@ class ImageInfoModule(OpenStackModule):
|
||||
|
||||
argument_spec = dict(
|
||||
image=dict(type='str', required=False),
|
||||
properties=dict(type='dict', required=False),
|
||||
filters=dict(type='dict', required=False, aliases=['properties']),
|
||||
)
|
||||
module_kwargs = dict(
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
def run(self):
|
||||
args = {
|
||||
'name_or_id': self.params['image'],
|
||||
'filters': self.params['filters'],
|
||||
}
|
||||
args = {k: v for k, v in args.items() if v is not None}
|
||||
images = self.conn.search_images(**args)
|
||||
|
||||
if self.params['image']:
|
||||
image = self.conn.get_image(self.params['image'])
|
||||
self.exit(changed=False, openstack_image=image)
|
||||
# for backward compatibility
|
||||
if 'name_or_id' in args:
|
||||
image = images[0] if images else None
|
||||
else:
|
||||
images = self.conn.search_images(filters=self.params['properties'])
|
||||
self.exit(changed=False, openstack_image=images)
|
||||
image = images
|
||||
|
||||
self.exit(changed=False, openstack_images=images, image=image)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -113,7 +113,7 @@ class KeyPairModule(OpenStackModule):
|
||||
|
||||
if self.params['public_key_file']:
|
||||
with open(self.params['public_key_file']) as public_key_fh:
|
||||
public_key = public_key_fh.read().rstrip()
|
||||
public_key = public_key_fh.read()
|
||||
|
||||
keypair = self.conn.get_keypair(name)
|
||||
|
||||
|
||||
@@ -54,7 +54,8 @@ RETURN = '''
|
||||
openstack_keypairs:
|
||||
description:
|
||||
- Lists keypairs that are associated with the account.
|
||||
type: complex
|
||||
type: list
|
||||
elements: dict
|
||||
returned: always
|
||||
contains:
|
||||
created_at:
|
||||
@@ -120,31 +121,15 @@ class KeyPairInfoModule(OpenStackModule):
|
||||
)
|
||||
|
||||
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)
|
||||
filters = {k: self.params[k] for k in
|
||||
['user_id', 'name', 'limit', 'marker']
|
||||
if self.params[k] is not None}
|
||||
keypairs = self.conn.search_keypairs(name_or_id=self.params['name'],
|
||||
filters=filters)
|
||||
# self.conn.search_keypairs() returned munch.Munch objects before Train
|
||||
result = [raw if isinstance(raw, dict) else raw.to_dict(computed=False)
|
||||
for raw in keypairs]
|
||||
self.exit(changed=False, openstack_keypairs=result)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -45,7 +45,7 @@ options:
|
||||
- The number of successful checks before changing the operating status of the member to ONLINE.
|
||||
max_retries_down:
|
||||
type: 'str'
|
||||
default: 3
|
||||
default: '3'
|
||||
description:
|
||||
- The number of allowed check failures before changing the operating status of the member to ERROR. A valid value is from 1 to 10. The default is 3.
|
||||
resp_timeout:
|
||||
@@ -60,7 +60,7 @@ options:
|
||||
type: bool
|
||||
expected_codes:
|
||||
type: 'str'
|
||||
default: 200
|
||||
default: '200'
|
||||
description:
|
||||
- The list of HTTP status codes expected in response from the member to declare it healthy. Specify one of the following values
|
||||
A single value, such as 200
|
||||
|
||||
@@ -51,6 +51,14 @@ options:
|
||||
into ACTIVE state.
|
||||
default: 180
|
||||
type: int
|
||||
monitor_address:
|
||||
description:
|
||||
- IP address used to monitor this member
|
||||
type: str
|
||||
monitor_port:
|
||||
description:
|
||||
- Port used to monitor this member
|
||||
type: int
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
@@ -141,6 +149,8 @@ class LoadbalancerMemberModule(OpenStackModule):
|
||||
address=dict(default=None),
|
||||
protocol_port=dict(default=80, type='int'),
|
||||
subnet_id=dict(default=None),
|
||||
monitor_address=dict(default=None),
|
||||
monitor_port=dict(default=None, type='int')
|
||||
)
|
||||
module_kwargs = dict()
|
||||
|
||||
@@ -191,7 +201,9 @@ class LoadbalancerMemberModule(OpenStackModule):
|
||||
address=self.params['address'],
|
||||
name=name,
|
||||
protocol_port=self.params['protocol_port'],
|
||||
subnet_id=self.params['subnet_id']
|
||||
subnet_id=self.params['subnet_id'],
|
||||
monitor_address=self.params['monitor_address'],
|
||||
monitor_port=self.params['monitor_port']
|
||||
)
|
||||
changed = True
|
||||
|
||||
|
||||
@@ -123,6 +123,14 @@ options:
|
||||
description:
|
||||
- Binding profile dict that the port should be created with.
|
||||
type: dict
|
||||
dns_name:
|
||||
description:
|
||||
- The dns name of the port ( only with dns-integration enabled )
|
||||
type: str
|
||||
dns_domain:
|
||||
description:
|
||||
- The dns domain of the port ( only with dns-integration enabled )
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
@@ -302,7 +310,9 @@ class NetworkPortModule(OpenStackModule):
|
||||
choices=['normal', 'direct', 'direct-physical',
|
||||
'macvtap', 'baremetal', 'virtio-forwarder']),
|
||||
port_security_enabled=dict(default=None, type='bool'),
|
||||
binding_profile=dict(default=None, type='dict')
|
||||
binding_profile=dict(default=None, type='dict'),
|
||||
dns_name=dict(type='str', default=None),
|
||||
dns_domain=dict(type='str', default=None)
|
||||
)
|
||||
|
||||
module_kwargs = dict(
|
||||
@@ -312,6 +322,13 @@ class NetworkPortModule(OpenStackModule):
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
def _is_dns_integration_enabled(self):
|
||||
""" Check if dns-integraton is enabled """
|
||||
for ext in self.conn.network.extensions():
|
||||
if ext.alias == 'dns-integration':
|
||||
return True
|
||||
return False
|
||||
|
||||
def _needs_update(self, port):
|
||||
"""Check for differences in the updatable values.
|
||||
|
||||
@@ -324,10 +341,18 @@ class NetworkPortModule(OpenStackModule):
|
||||
'binding:vnic_type',
|
||||
'port_security_enabled',
|
||||
'binding:profile']
|
||||
compare_dns = ['dns_name', 'dns_domain']
|
||||
compare_list_dict = ['allowed_address_pairs',
|
||||
'extra_dhcp_opts']
|
||||
compare_list = ['security_groups']
|
||||
|
||||
if self.conn.has_service('dns') and \
|
||||
self._is_dns_integration_enabled():
|
||||
for key in compare_dns:
|
||||
if self.params[key] is not None and \
|
||||
self.params[key] != port[key]:
|
||||
return True
|
||||
|
||||
for key in compare_simple:
|
||||
if self.params[key] is not None and self.params[key] != port[key]:
|
||||
return True
|
||||
@@ -410,6 +435,11 @@ class NetworkPortModule(OpenStackModule):
|
||||
'binding:vnic_type',
|
||||
'port_security_enabled',
|
||||
'binding:profile']
|
||||
|
||||
if self.conn.has_service('dns') and \
|
||||
self._is_dns_integration_enabled():
|
||||
optional_parameters.extend(['dns_name', 'dns_domain'])
|
||||
|
||||
for optional_param in optional_parameters:
|
||||
if self.params[optional_param] is not None:
|
||||
port_kwargs[optional_param] = self.params[optional_param]
|
||||
@@ -473,12 +503,14 @@ class NetworkPortModule(OpenStackModule):
|
||||
msg="Specified network was not found."
|
||||
)
|
||||
|
||||
port = self.conn.create_port(network_id, **port_kwargs)
|
||||
port_kwargs['network_id'] = network_id
|
||||
port = self.conn.network.create_port(**port_kwargs)
|
||||
changed = True
|
||||
else:
|
||||
if self._needs_update(port):
|
||||
port_kwargs = self._compose_port_args()
|
||||
port = self.conn.update_port(port['id'], **port_kwargs)
|
||||
port = self.conn.network.update_port(port['id'],
|
||||
**port_kwargs)
|
||||
changed = True
|
||||
self.exit_json(changed=changed, id=port['id'], port=port)
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ options:
|
||||
volumes_types:
|
||||
description:
|
||||
- Per-driver volume count quotas. Keys should be
|
||||
prefixed with C(gigabytes_) values should be ints.
|
||||
prefixed with C(volumes_) values should be ints.
|
||||
type: dict
|
||||
project:
|
||||
description: Unused, kept for compatability
|
||||
|
||||
@@ -12,42 +12,42 @@ description:
|
||||
updated. Only the I(records), I(description), and I(ttl) values
|
||||
can be updated.
|
||||
options:
|
||||
zone:
|
||||
description:
|
||||
description:
|
||||
- Zone managing the recordset
|
||||
required: true
|
||||
- Description of the recordset
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Name of the recordset. It must be ended with name of dns zone.
|
||||
required: true
|
||||
type: str
|
||||
recordset_type:
|
||||
description:
|
||||
- Recordset type
|
||||
- Required when I(state=present).
|
||||
choices: ['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']
|
||||
type: str
|
||||
records:
|
||||
description:
|
||||
- List of recordset definitions.
|
||||
- Required when I(state=present).
|
||||
type: list
|
||||
elements: str
|
||||
description:
|
||||
recordset_type:
|
||||
description:
|
||||
- Description of the recordset
|
||||
- Recordset type
|
||||
- Required when I(state=present).
|
||||
choices: ['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- TTL (Time To Live) value in seconds
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- Should the resource be present or absent.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- TTL (Time To Live) value in seconds
|
||||
type: int
|
||||
zone:
|
||||
description:
|
||||
- Name or ID of the zone which manages the recordset
|
||||
required: true
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
@@ -90,36 +90,73 @@ RETURN = '''
|
||||
recordset:
|
||||
description: Dictionary describing the recordset.
|
||||
returned: On success when I(state) is 'present'.
|
||||
type: complex
|
||||
type: dict
|
||||
contains:
|
||||
id:
|
||||
description: Unique recordset ID
|
||||
action:
|
||||
description: Current action in progress on the resource
|
||||
type: str
|
||||
sample: "c1c530a3-3619-46f3-b0f6-236927b2618c"
|
||||
name:
|
||||
description: Recordset name
|
||||
returned: always
|
||||
created_at:
|
||||
description: Timestamp when the zone was created
|
||||
type: str
|
||||
sample: "www.example.net."
|
||||
zone_id:
|
||||
description: Zone id
|
||||
type: str
|
||||
sample: 9508e177-41d8-434e-962c-6fe6ca880af7
|
||||
type:
|
||||
description: Recordset type
|
||||
type: str
|
||||
sample: "A"
|
||||
returned: always
|
||||
description:
|
||||
description: Recordset description
|
||||
type: str
|
||||
sample: "Test description"
|
||||
ttl:
|
||||
description: Zone TTL value
|
||||
type: int
|
||||
sample: 3600
|
||||
returned: always
|
||||
id:
|
||||
description: Unique recordset ID
|
||||
type: str
|
||||
sample: "c1c530a3-3619-46f3-b0f6-236927b2618c"
|
||||
links:
|
||||
description: Links related to the resource
|
||||
type: dict
|
||||
returned: always
|
||||
name:
|
||||
description: Recordset name
|
||||
type: str
|
||||
sample: "www.example.net."
|
||||
returned: always
|
||||
project_id:
|
||||
description: ID of the proect to which the recordset belongs
|
||||
type: str
|
||||
returned: always
|
||||
records:
|
||||
description: Recordset records
|
||||
type: list
|
||||
sample: ['10.0.0.1']
|
||||
returned: always
|
||||
status:
|
||||
description:
|
||||
- Recordset status
|
||||
- Valid values include `PENDING_CREATE`, `ACTIVE`,`PENDING_DELETE`,
|
||||
`ERROR`
|
||||
type: str
|
||||
returned: always
|
||||
ttl:
|
||||
description: Zone TTL value
|
||||
type: int
|
||||
sample: 3600
|
||||
returned: always
|
||||
type:
|
||||
description:
|
||||
- Recordset type
|
||||
- Valid values include `A`, `AAAA`, `MX`, `CNAME`, `TXT`, `NS`,
|
||||
`SSHFP`, `SPF`, `SRV`, `PTR`
|
||||
type: str
|
||||
sample: "A"
|
||||
returned: always
|
||||
zone_id:
|
||||
description: The id of the Zone which this recordset belongs to
|
||||
type: str
|
||||
sample: 9508e177-41d8-434e-962c-6fe6ca880af7
|
||||
returned: always
|
||||
zone_name:
|
||||
description: The name of the Zone which this recordset belongs to
|
||||
type: str
|
||||
sample: "example.com."
|
||||
returned: always
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
@@ -127,13 +164,13 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
|
||||
|
||||
class DnsRecordsetModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
zone=dict(required=True),
|
||||
name=dict(required=True),
|
||||
recordset_type=dict(required=False, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
|
||||
records=dict(required=False, type='list', elements='str'),
|
||||
description=dict(required=False, default=None),
|
||||
ttl=dict(required=False, type='int'),
|
||||
name=dict(required=True),
|
||||
records=dict(required=False, type='list', elements='str'),
|
||||
recordset_type=dict(required=False, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
ttl=dict(required=False, type='int'),
|
||||
zone=dict(required=True),
|
||||
)
|
||||
|
||||
module_kwargs = dict(
|
||||
@@ -145,88 +182,73 @@ class DnsRecordsetModule(OpenStackModule):
|
||||
|
||||
module_min_sdk_version = '0.28.0'
|
||||
|
||||
def _system_state_change(self, state, records, description, ttl, recordset):
|
||||
def _needs_update(self, params, recordset):
|
||||
for k in ('description', 'records', 'ttl'):
|
||||
if k not in params:
|
||||
continue
|
||||
if params[k] is not None and params[k] != recordset[k]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _system_state_change(self, state, recordset):
|
||||
if state == 'present':
|
||||
if recordset is None:
|
||||
return True
|
||||
if records is not None and recordset['records'] != records:
|
||||
return True
|
||||
if description is not None and recordset['description'] != description:
|
||||
return True
|
||||
if ttl is not None and recordset['ttl'] != ttl:
|
||||
return True
|
||||
kwargs = self._build_params()
|
||||
return self._needs_update(kwargs, recordset)
|
||||
if state == 'absent' and recordset:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _build_params(self):
|
||||
recordset_type = self.params['recordset_type']
|
||||
records = self.params['records']
|
||||
description = self.params['description']
|
||||
ttl = self.params['ttl']
|
||||
params = {
|
||||
'description': description,
|
||||
'records': records,
|
||||
'type': recordset_type.upper(),
|
||||
'ttl': ttl,
|
||||
}
|
||||
return {k: v for k, v in params.items() if v is not None}
|
||||
|
||||
def run(self):
|
||||
zone = self.params.get('zone')
|
||||
name = self.params.get('name')
|
||||
state = self.params.get('state')
|
||||
ttl = self.params.get('ttl')
|
||||
|
||||
recordsets = self.conn.search_recordsets(zone, name_or_id=name)
|
||||
|
||||
recordset = None
|
||||
if recordsets:
|
||||
recordset = recordsets[0]
|
||||
try:
|
||||
recordset_id = recordset['id']
|
||||
except KeyError as e:
|
||||
self.fail_json(msg=str(e))
|
||||
else:
|
||||
# recordsets is filtered by type and should never be more than 1 return
|
||||
recordset = None
|
||||
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(changed=self._system_state_change(state, recordset))
|
||||
|
||||
changed = False
|
||||
if state == 'present':
|
||||
recordset_type = self.params.get('recordset_type').upper()
|
||||
records = self.params.get('records')
|
||||
description = self.params.get('description')
|
||||
ttl = self.params.get('ttl')
|
||||
|
||||
kwargs = {}
|
||||
if description:
|
||||
kwargs['description'] = description
|
||||
kwargs['records'] = records
|
||||
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(
|
||||
changed=self._system_state_change(
|
||||
state, records, description, ttl, recordset))
|
||||
|
||||
kwargs = self._build_params()
|
||||
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)
|
||||
kwargs['ttl'] = ttl or 300
|
||||
type = kwargs.pop('type', None)
|
||||
if type is not None:
|
||||
kwargs['recordset_type'] = type
|
||||
recordset = self.conn.create_recordset(zone=zone, name=name,
|
||||
**kwargs)
|
||||
changed = True
|
||||
elif self._needs_update(kwargs, recordset):
|
||||
type = kwargs.pop('type', None)
|
||||
recordset = self.conn.update_recordset(zone, recordset['id'],
|
||||
**kwargs)
|
||||
changed = True
|
||||
else:
|
||||
|
||||
if ttl:
|
||||
kwargs['ttl'] = ttl
|
||||
|
||||
pre_update_recordset = recordset
|
||||
changed = self._system_state_change(
|
||||
state, records, description, ttl, pre_update_recordset)
|
||||
if changed:
|
||||
recordset = self.conn.update_recordset(
|
||||
zone=zone, name_or_id=recordset_id, **kwargs)
|
||||
|
||||
self.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))
|
||||
|
||||
if recordset is None:
|
||||
changed = False
|
||||
else:
|
||||
self.conn.delete_recordset(zone, recordset_id)
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
elif state == 'absent' and recordset is not None:
|
||||
self.conn.delete_recordset(zone, recordset['id'])
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -120,51 +120,42 @@ class IdentityRoleAssignmentModule(OpenStackModule):
|
||||
state = self.params.get('state')
|
||||
|
||||
filters = {}
|
||||
find_filters = {}
|
||||
domain_id = None
|
||||
|
||||
r = self.conn.get_role(role)
|
||||
r = self.conn.identity.find_role(role)
|
||||
if r is None:
|
||||
self.fail_json(msg="Role %s is not valid" % role)
|
||||
filters['role'] = r['id']
|
||||
|
||||
if domain:
|
||||
d = self.conn.get_domain(name_or_id=domain)
|
||||
d = self.conn.identity.find_domain(domain)
|
||||
if d is None:
|
||||
self.fail_json(msg="Domain %s is not valid" % domain)
|
||||
filters['domain'] = d['id']
|
||||
domain_id = d['id']
|
||||
find_filters['domain_id'] = domain_id
|
||||
if user:
|
||||
if domain:
|
||||
u = self.conn.get_user(user, domain_id=filters['domain'])
|
||||
else:
|
||||
u = self.conn.get_user(user)
|
||||
|
||||
u = self.conn.identity.find_user(user, **find_filters)
|
||||
if u is None:
|
||||
self.fail_json(msg="User %s is not valid" % user)
|
||||
filters['user'] = u['id']
|
||||
|
||||
if group:
|
||||
if domain:
|
||||
g = self.conn.get_group(group, domain_id=filters['domain'])
|
||||
else:
|
||||
g = self.conn.get_group(group)
|
||||
# self.conn.identity.find_group() does not accept
|
||||
# a domain_id argument in Train's openstacksdk
|
||||
g = self.conn.get_group(group, **find_filters)
|
||||
if g is None:
|
||||
self.fail_json(msg="Group %s is not valid" % group)
|
||||
filters['group'] = g['id']
|
||||
if project:
|
||||
if domain:
|
||||
p = self.conn.get_project(project, domain_id=filters['domain'])
|
||||
# OpenStack won't allow us to use both a domain and project as
|
||||
# filter. Once we identified the project (using the domain as
|
||||
# a filter criteria), we need to remove the domain itself from
|
||||
# the filters list.
|
||||
domain_id = filters.pop('domain')
|
||||
else:
|
||||
p = self.conn.get_project(project)
|
||||
|
||||
p = self.conn.identity.find_project(project, **find_filters)
|
||||
if p is None:
|
||||
self.fail_json(msg="Project %s is not valid" % project)
|
||||
filters['project'] = p['id']
|
||||
|
||||
# Keeping the self.conn.list_role_assignments because it calls directly
|
||||
# the identity.role_assignments and there are some logics for the
|
||||
# filters that won't worth rewrite here.
|
||||
assignment = self.conn.list_role_assignments(filters=filters)
|
||||
|
||||
if self.ansible.check_mode:
|
||||
@@ -172,6 +163,9 @@ class IdentityRoleAssignmentModule(OpenStackModule):
|
||||
|
||||
changed = False
|
||||
|
||||
# Both grant_role and revoke_role calls directly the proxy layer, and
|
||||
# has some logic that won't worth to rewrite here so keeping it is a
|
||||
# good idea
|
||||
if state == 'present':
|
||||
if not assignment:
|
||||
kwargs = self._build_kwargs(user, group, project, domain_id)
|
||||
|
||||
@@ -234,8 +234,7 @@ class RouterModule(OpenStackModule):
|
||||
missing_port_ids,
|
||||
requested_subnet_ids,
|
||||
existing_subnet_ids,
|
||||
router_ifs_cfg,
|
||||
filters=None):
|
||||
router_ifs_cfg):
|
||||
"""Decide if the given router needs an update."""
|
||||
if router['admin_state_up'] != self.params['admin_state_up']:
|
||||
return True
|
||||
@@ -476,8 +475,7 @@ class RouterModule(OpenStackModule):
|
||||
missing_port_ids,
|
||||
requested_subnet_ids,
|
||||
existing_subnet_ids,
|
||||
router_ifs_cfg,
|
||||
filters)
|
||||
router_ifs_cfg)
|
||||
self.exit_json(changed=changed)
|
||||
|
||||
if state == 'present':
|
||||
@@ -510,8 +508,7 @@ class RouterModule(OpenStackModule):
|
||||
missing_port_ids,
|
||||
requested_subnet_ids,
|
||||
existing_subnet_ids,
|
||||
router_ifs_cfg,
|
||||
filters):
|
||||
router_ifs_cfg):
|
||||
changed = True
|
||||
kwargs = self._build_kwargs(router, net)
|
||||
updated_router = self.conn.update_router(**kwargs)
|
||||
|
||||
@@ -49,6 +49,7 @@ options:
|
||||
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
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
@@ -178,7 +179,7 @@ class RouterInfoModule(OpenStackModule):
|
||||
'ip_address': ip_spec.get('ip_address'),
|
||||
'subnet_id': ip_spec.get('subnet_id')
|
||||
}
|
||||
interfaces_info.append(int_info)
|
||||
interfaces_info.append(int_info)
|
||||
router['interfaces_info'] = interfaces_info
|
||||
|
||||
self.exit(changed=False, openstack_routers=routers)
|
||||
|
||||
@@ -63,6 +63,11 @@ options:
|
||||
- Unique name or ID of the project.
|
||||
required: false
|
||||
type: str
|
||||
description:
|
||||
required: false
|
||||
description:
|
||||
- Description of the rule.
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
@@ -256,6 +261,7 @@ class SecurityGroupRuleModule(OpenStackModule):
|
||||
choices=['egress', 'ingress']),
|
||||
state=dict(default='present',
|
||||
choices=['absent', 'present']),
|
||||
description=dict(required=False, default=None),
|
||||
project=dict(default=None),
|
||||
)
|
||||
|
||||
@@ -349,10 +355,12 @@ class SecurityGroupRuleModule(OpenStackModule):
|
||||
kwargs = {}
|
||||
if project_id:
|
||||
kwargs['project_id'] = project_id
|
||||
rule = self.conn.create_security_group_rule(
|
||||
secgroup['id'],
|
||||
port_range_min=self.params['port_range_min'],
|
||||
port_range_max=self.params['port_range_max'],
|
||||
if self.params["description"] is not None:
|
||||
kwargs["description"] = self.params['description']
|
||||
rule = self.conn.network.create_security_group_rule(
|
||||
security_group_id=secgroup['id'],
|
||||
port_range_min=None if self.params['port_range_min'] == -1 else self.params['port_range_min'],
|
||||
port_range_max=None if self.params['port_range_max'] == -1 else self.params['port_range_max'],
|
||||
protocol=self.params['protocol'],
|
||||
remote_ip_prefix=self.params['remote_ip_prefix'],
|
||||
remote_group_id=remotegroup['id'],
|
||||
|
||||
@@ -46,6 +46,12 @@ options:
|
||||
description:
|
||||
- Admin password for server to rebuild
|
||||
type: str
|
||||
all_projects:
|
||||
description:
|
||||
- Whether to search for server in all projects or just the current
|
||||
auth scoped project.
|
||||
type: bool
|
||||
default: 'no'
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
@@ -120,6 +126,7 @@ class ServerActionModule(OpenStackModule):
|
||||
'rebuild', 'shelve', 'shelve_offload', 'unshelve']),
|
||||
image=dict(required=False, type='str'),
|
||||
admin_password=dict(required=False, type='str', no_log=True),
|
||||
all_projects=dict(required=False, type='bool', default=False),
|
||||
)
|
||||
module_kwargs = dict(
|
||||
required_if=[('action', 'rebuild', ['image'])],
|
||||
@@ -137,7 +144,10 @@ class ServerActionModule(OpenStackModule):
|
||||
|
||||
def _preliminary_checks(self):
|
||||
# Using Munch object for getting information about a server
|
||||
os_server = self.conn.get_server(self.params['server'])
|
||||
os_server = self.conn.get_server(
|
||||
self.params['server'],
|
||||
all_projects=self.params['all_projects'],
|
||||
)
|
||||
if not os_server:
|
||||
self.fail_json(msg='Could not find server %s' % self.params['server'])
|
||||
# check mode
|
||||
@@ -193,8 +203,9 @@ class ServerActionModule(OpenStackModule):
|
||||
|
||||
def _wait(self, os_server):
|
||||
"""Wait for the server to reach the desired state for the given action."""
|
||||
# Using Server object for wait_for_server function
|
||||
server = self.conn.compute.find_server(self.params['server'])
|
||||
# The wait_for_server function needs a Server object instead of the
|
||||
# Munch object returned by self.conn.get_server
|
||||
server = self.conn.compute.get_server(os_server['id'])
|
||||
states = _action_map[self.params['action']]
|
||||
|
||||
try:
|
||||
|
||||
@@ -39,7 +39,7 @@ options:
|
||||
ip_version:
|
||||
description:
|
||||
- The IP version of the subnet 4 or 6
|
||||
default: 4
|
||||
default: '4'
|
||||
type: str
|
||||
choices: ['4', '6']
|
||||
enable_dhcp:
|
||||
|
||||
345
plugins/modules/subnet_pool.py
Normal file
345
plugins/modules/subnet_pool.py
Normal file
@@ -0,0 +1,345 @@
|
||||
#!/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: subnet_pool
|
||||
short_description: Create or delete subnet pools from OpenStack
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Create or Delete subnet pools 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 subnet pool
|
||||
required: true
|
||||
type: str
|
||||
project:
|
||||
description:
|
||||
- Unique name or ID of the project.
|
||||
type: str
|
||||
prefixes:
|
||||
description:
|
||||
- Set subnet pool prefixes (in CIDR notation)
|
||||
type: list
|
||||
elements: str
|
||||
minimum_prefix_length:
|
||||
description:
|
||||
- The minimum prefix length that can be allocated from the subnet pool.
|
||||
required: False
|
||||
type: int
|
||||
maximum_prefix_length:
|
||||
description:
|
||||
- The maximum prefix length that can be allocated from the subnet pool.
|
||||
required: False
|
||||
type: int
|
||||
default_prefix_length:
|
||||
description:
|
||||
- The length of the prefix to allocate when the cidr or prefixlen attributes
|
||||
are omitted when creating a subnet
|
||||
type: int
|
||||
required: False
|
||||
address_scope:
|
||||
description:
|
||||
- Set address scope (ID or name) associated with the subnet pool
|
||||
type: str
|
||||
required: False
|
||||
is_default:
|
||||
description:
|
||||
- Whether this subnet pool is by default
|
||||
type: bool
|
||||
default: 'no'
|
||||
description:
|
||||
description: The subnet pool description
|
||||
type: str
|
||||
required: False
|
||||
default_quota:
|
||||
description:
|
||||
- A per-project quota on the prefix space that can be allocated
|
||||
from the subnet pool for project subnets
|
||||
required: False
|
||||
type: int
|
||||
shared:
|
||||
description:
|
||||
- Whether this subnet pool 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 subnet pool.
|
||||
- openstack.cloud.subnet_pool:
|
||||
cloud: mycloud
|
||||
state: present
|
||||
name: my_subnet_pool
|
||||
prefixes:
|
||||
- 10.10.10.0/24
|
||||
|
||||
# Create a subnet pool for a given project.
|
||||
- openstack.cloud.subnet_pool:
|
||||
cloud: mycloud
|
||||
state: present
|
||||
name: my_subnet_pool
|
||||
project: myproj
|
||||
prefixes:
|
||||
- 10.10.10.0/24
|
||||
|
||||
# Create a shared and default subnet pool in existing address scope
|
||||
- openstack.cloud.subnet_pool:
|
||||
cloud: mycloud
|
||||
state: present
|
||||
name: my_subnet_pool
|
||||
address_scope: my_adress_scope
|
||||
is_default: True
|
||||
default_quota: 10
|
||||
maximum_prefix_length: 32
|
||||
minimum_prefix_length: 8
|
||||
default_prefix_length: 24
|
||||
shared: True
|
||||
prefixes:
|
||||
- 10.10.10.0/8
|
||||
|
||||
# Delete subnet poool.
|
||||
- openstack.cloud.subnet_pool:
|
||||
cloud: mycloud
|
||||
state: absent
|
||||
name: my_subnet_pool
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
subnet_pool:
|
||||
description: Dictionary describing the subnet pool.
|
||||
returned: On success when I(state) is 'present'
|
||||
type: complex
|
||||
contains:
|
||||
id:
|
||||
description: Subnet Pool ID.
|
||||
type: str
|
||||
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
|
||||
name:
|
||||
description: Subnet Pool name.
|
||||
type: str
|
||||
sample: "my_subnet_pool"
|
||||
project_id:
|
||||
description: The ID of the project.
|
||||
type: str
|
||||
sample: "861174b82b43463c9edc5202aadc60ef"
|
||||
ip_version:
|
||||
description: The IP version of the subnet pool 4 or 6.
|
||||
type: int
|
||||
sample: 4
|
||||
is_shared:
|
||||
description: Indicates whether this subnet pool is shared across all projects.
|
||||
type: bool
|
||||
sample: false
|
||||
is_default:
|
||||
description: Indicates whether this is the default subnet pool.
|
||||
type: bool
|
||||
sample: false
|
||||
address_scope_id:
|
||||
description: The address scope ID.
|
||||
type: str
|
||||
sample: "861174b82b43463c9edc5202aadc60ef"
|
||||
created_at:
|
||||
description: Timestamp when the subnet pool was created.
|
||||
type: str
|
||||
sample: ""
|
||||
default_prefix_length:
|
||||
description:
|
||||
- The length of the prefix to allocate when the cidr or prefixlen
|
||||
attributes are omitted when creating a subnet
|
||||
type: int
|
||||
sample: 32
|
||||
default_quota:
|
||||
description:
|
||||
- The per-project quota on the prefix space that can be allocated
|
||||
from the subnet pool for project subnets.
|
||||
type: int
|
||||
sample: 22
|
||||
description:
|
||||
description: The subnet pool description.
|
||||
type: str
|
||||
sample: "My test subnet pool."
|
||||
maximum_prefix_length:
|
||||
description: The maximum prefix length that can be allocated from the subnet pool.
|
||||
type: int
|
||||
sample: 22
|
||||
minimum_prefix_length:
|
||||
description: The minimum prefix length that can be allocated from the subnet pool.
|
||||
type: int
|
||||
sample: 8
|
||||
prefixes:
|
||||
description: A list of subnet prefixes that are assigned to the subnet pool.
|
||||
type: list
|
||||
sample: ['10.10.20.0/24', '10.20.10.0/24']
|
||||
revision_number:
|
||||
description: Revision number of the subnet pool.
|
||||
type: int
|
||||
sample: 5
|
||||
updated_at:
|
||||
description: Timestamp when the subnet pool was last updated.
|
||||
type: str
|
||||
sample:
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
|
||||
|
||||
class SubnetPoolModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
name=dict(required=True),
|
||||
shared=dict(default=False, type='bool'),
|
||||
minimum_prefix_length=dict(default=None, type='int'),
|
||||
maximum_prefix_length=dict(default=None, type='int'),
|
||||
default_prefix_length=dict(default=None, type='int'),
|
||||
description=dict(default=None, type='str'),
|
||||
default_quota=dict(default=None, type='int'),
|
||||
prefixes=dict(type='list', elements='str'),
|
||||
is_default=dict(default=False, type='bool'),
|
||||
address_scope=dict(default=None),
|
||||
project=dict(default=None),
|
||||
extra_specs=dict(type='dict', default=dict())
|
||||
)
|
||||
|
||||
def _needs_update(self, subnet_pool):
|
||||
"""Check for differences in the updatable values.
|
||||
|
||||
NOTE: We don't currently allow name updates.
|
||||
"""
|
||||
compare_simple = ['is_default',
|
||||
'minimum_prefix_length',
|
||||
'maximum_prefix_length',
|
||||
'default_prefix_length',
|
||||
'description',
|
||||
'default_quota']
|
||||
compare_list = ['prefixes']
|
||||
|
||||
for key in compare_simple:
|
||||
if self.params[key] is not None and self.params[key] != subnet_pool[key]:
|
||||
return True
|
||||
for key in compare_list:
|
||||
if (
|
||||
self.params[key] is not None
|
||||
and set(self.params[key]) != set(subnet_pool[key])
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _system_state_change(self, subnet_pool, filters=None):
|
||||
"""Check if the system state would be changed."""
|
||||
state = self.params['state']
|
||||
if state == 'absent' and subnet_pool:
|
||||
return True
|
||||
if state == 'present':
|
||||
if not subnet_pool:
|
||||
return True
|
||||
return self._needs_update(subnet_pool, filters)
|
||||
return False
|
||||
|
||||
def _compose_subnet_pool_args(self):
|
||||
subnet_pool_kwargs = {}
|
||||
optional_parameters = ['name',
|
||||
'minimum_prefix_length',
|
||||
'maximum_prefix_length',
|
||||
'default_prefix_length',
|
||||
'description',
|
||||
'is_default',
|
||||
'default_quota',
|
||||
'prefixes']
|
||||
|
||||
for optional_param in optional_parameters:
|
||||
if self.params[optional_param] is not None:
|
||||
subnet_pool_kwargs[optional_param] = self.params[optional_param]
|
||||
|
||||
return subnet_pool_kwargs
|
||||
|
||||
def run(self):
|
||||
|
||||
state = self.params['state']
|
||||
name = self.params['name']
|
||||
project = self.params['project']
|
||||
address_scope = self.params['address_scope']
|
||||
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_id = None
|
||||
if address_scope is not None:
|
||||
address_scope = self.conn.network.find_address_scope(name_or_id=address_scope)
|
||||
if address_scope is None:
|
||||
self.fail(msg='AddressScope %s could not be found' % address_scope)
|
||||
address_scope_id = address_scope['id']
|
||||
subnet_pool = self.conn.network.find_subnet_pool(name_or_id=name)
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(
|
||||
changed=self._system_state_change(subnet_pool)
|
||||
)
|
||||
|
||||
if state == 'present':
|
||||
changed = False
|
||||
|
||||
if not subnet_pool:
|
||||
kwargs = self._compose_subnet_pool_args()
|
||||
kwargs['address_scope_id'] = address_scope_id
|
||||
kwargs['project_id'] = project_id
|
||||
kwargs['is_shared'] = self.params['shared']
|
||||
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)
|
||||
subnet_pool = self.conn.network.create_subnet_pool(**kwargs)
|
||||
changed = True
|
||||
else:
|
||||
if self._needs_update(subnet_pool):
|
||||
kwargs = self._compose_subnet_pool_args()
|
||||
subnet_pool = self.conn.network.update_subnet_pool(subnet_pool['id'], **kwargs)
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
self.exit_json(changed=changed, subnet_pool=subnet_pool, id=subnet_pool['id'])
|
||||
|
||||
elif state == 'absent':
|
||||
if not subnet_pool:
|
||||
self.exit(changed=False)
|
||||
else:
|
||||
self.conn.network.delete_subnet_pool(subnet_pool['id'])
|
||||
self.exit_json(changed=True)
|
||||
|
||||
|
||||
def main():
|
||||
module = SubnetPoolModule()
|
||||
module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1 +1 @@
|
||||
openstacksdk>=0.13
|
||||
openstacksdk>=0.36,<0.99.0
|
||||
|
||||
@@ -56,7 +56,16 @@ import collections
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from distutils.version import StrictVersion
|
||||
from ansible.module_utils.six import raise_from
|
||||
try:
|
||||
from ansible.module_utils.compat.version import StrictVersion
|
||||
except ImportError:
|
||||
try:
|
||||
from distutils.version import StrictVersion
|
||||
except ImportError as exc:
|
||||
raise_from(ImportError('To use this plugin or module with ansible-core'
|
||||
' < 2.11, you need to use Python < 3.12 with '
|
||||
'distutils.version present'), exc)
|
||||
from io import StringIO
|
||||
|
||||
import json
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
openstacksdk
|
||||
ansible<2.10
|
||||
pycodestyle
|
||||
flake8
|
||||
pylint
|
||||
voluptuous
|
||||
yamllint
|
||||
rstcheck
|
||||
ruamel.yaml
|
||||
#galaxy-importer # see https://review.opendev.org/#/c/743054
|
||||
tox
|
||||
@@ -1 +0,0 @@
|
||||
test-requirements-2.11.txt
|
||||
1
tests/constraints-none.txt
Normal file
1
tests/constraints-none.txt
Normal file
@@ -0,0 +1 @@
|
||||
# No constraints are defined by default
|
||||
2
tests/constraints-openstacksdk-0.x.x.txt
Normal file
2
tests/constraints-openstacksdk-0.x.x.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# 0.99.x are release candidates for the first major release of OpenStackSDK 1.x.x
|
||||
openstacksdk<0.99.0
|
||||
2
tests/constraints-openstacksdk-1.x.x.txt
Normal file
2
tests/constraints-openstacksdk-1.x.x.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# 0.99.x are release candidates for the first major release of OpenStackSDK 1.x.x
|
||||
openstacksdk>=0.99.0
|
||||
@@ -1,11 +1,11 @@
|
||||
openstacksdk
|
||||
ansible-core
|
||||
pycodestyle
|
||||
ansible-core>=2.11.0,<2.12.0
|
||||
flake8
|
||||
galaxy-importer
|
||||
openstacksdk
|
||||
pycodestyle
|
||||
pylint
|
||||
voluptuous
|
||||
yamllint
|
||||
rstcheck
|
||||
ruamel.yaml
|
||||
#galaxy-importer # see https://review.opendev.org/#/c/743054
|
||||
tox
|
||||
voluptuous
|
||||
yamllint
|
||||
@@ -1,11 +1,11 @@
|
||||
openstacksdk
|
||||
ansible-core
|
||||
pycodestyle
|
||||
ansible-core>=2.12.0,<2.13.0
|
||||
flake8
|
||||
galaxy-importer
|
||||
openstacksdk
|
||||
pycodestyle
|
||||
pylint
|
||||
voluptuous
|
||||
yamllint
|
||||
rstcheck
|
||||
ruamel.yaml
|
||||
#galaxy-importer # see https://review.opendev.org/#/c/743054
|
||||
tox
|
||||
voluptuous
|
||||
yamllint
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user