mirror of
https://opendev.org/openstack/ansible-collections-openstack.git
synced 2026-03-27 14:03:03 +00:00
Compare commits
19 Commits
1.9.1
...
stable/1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0458dd1a6 | ||
|
|
f448ac258d | ||
|
|
e4be201f20 | ||
|
|
bc16f70e1d | ||
|
|
dec8ecda5a | ||
|
|
dd383c010c | ||
|
|
acd0f01443 | ||
|
|
a4260040c6 | ||
|
|
ef5b217411 | ||
|
|
6592363c7c | ||
|
|
922032cb9f | ||
|
|
a11943d9b1 | ||
|
|
84160f6f78 | ||
|
|
1b03f918ac | ||
|
|
c435002734 | ||
|
|
e8bba38e2e | ||
|
|
058bd87f2a | ||
|
|
7772bf125d | ||
|
|
17e78e5173 |
248
.zuul.yaml
248
.zuul.yaml
@@ -36,11 +36,10 @@
|
||||
- .*\.rst
|
||||
- tools/run-ansible-sanity.sh
|
||||
- tests/sanity/.*
|
||||
- contrib/.*
|
||||
- .zuul.yaml
|
||||
vars:
|
||||
zuul_work_dir: src/opendev.org/openstack/ansible-collections-openstack
|
||||
tox_envlist: ansible
|
||||
tox_envlist: ansible_latest
|
||||
tox_install_siblings: true
|
||||
fetch_subunit: false
|
||||
devstack_plugins:
|
||||
@@ -53,6 +52,49 @@
|
||||
extensions_to_txt:
|
||||
log: true
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-magnum-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 devstack with Magnum plugin enabled
|
||||
# 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/magnum
|
||||
- openstack/python-magnumclient
|
||||
files:
|
||||
- ^ci/roles/coe_cluster/.*$
|
||||
- ^plugins/modules/coe_cluster.py
|
||||
- ^plugins/modules/coe_cluster_template.py
|
||||
timeout: 10800
|
||||
vars:
|
||||
devstack_localrc:
|
||||
# NOTE: extend default glance limit from 1GB
|
||||
GLANCE_LIMIT_IMAGE_SIZE_TOTAL: 5000
|
||||
devstack_plugins:
|
||||
magnum: https://opendev.org/openstack/magnum
|
||||
devstack_services:
|
||||
magnum-api: true
|
||||
magnum-cond: true
|
||||
# Disable swift and dependent c-bak service to support upload of .qcow2.xz image in the gate
|
||||
s-account: false
|
||||
s-container: false
|
||||
s-object: false
|
||||
s-proxy: false
|
||||
c-bak: false
|
||||
tox_extra_args: -vv --skip-missing-interpreters=false -- coe_cluster coe_cluster_template
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-magnum
|
||||
parent: ansible-collections-openstack-functional-devstack-magnum-base
|
||||
branches: master
|
||||
description: |
|
||||
Run openstack collections functional tests against a master devstack
|
||||
with Magnum plugin enabled, using master of openstacksdk and latest
|
||||
ansible release. Run it only on coe_cluster{,_template} changes.
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-octavia-base
|
||||
parent: ansible-collections-openstack-functional-devstack-base
|
||||
@@ -60,7 +102,6 @@
|
||||
# parent job variant during job freeze when child jobs are on other branches.
|
||||
description: |
|
||||
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:
|
||||
@@ -86,10 +127,6 @@
|
||||
o-cw: true
|
||||
o-hk: true
|
||||
o-hm: true
|
||||
devstack_localrc:
|
||||
OCTAVIA_AMP_IMAGE_FILE: "/tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
|
||||
OCTAVIA_AMP_IMAGE_SIZE: 3
|
||||
OCTAVIA_AMP_IMAGE_NAME: "test-only-amphora-x64-haproxy-ubuntu-bionic"
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-octavia
|
||||
@@ -140,14 +177,14 @@
|
||||
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
|
||||
- # Choose parent devstack job from stable/yoga branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/devstack
|
||||
override-checkout: master
|
||||
override-checkout: stable/yoga
|
||||
- name: openstack/openstacksdk
|
||||
# Yoga has the latest SDK release of the 0.*.* series atm
|
||||
override-checkout: stable/yoga
|
||||
vars:
|
||||
tox_envlist: ansible-2.9
|
||||
tox_envlist: ansible_2_9
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-ansible-2.11
|
||||
@@ -159,14 +196,14 @@
|
||||
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
|
||||
- # Choose parent devstack job from stable/yoga branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/devstack
|
||||
override-checkout: master
|
||||
override-checkout: stable/yoga
|
||||
- name: openstack/openstacksdk
|
||||
# Yoga has the latest SDK release of the 0.*.* series atm
|
||||
override-checkout: stable/yoga
|
||||
vars:
|
||||
tox_envlist: ansible-2.11
|
||||
tox_envlist: ansible_2_11
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-ansible-2.12
|
||||
@@ -178,14 +215,14 @@
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.12
|
||||
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
|
||||
- # Choose parent devstack job from stable/yoga branch instead of non-existing stable/1.0.0 branch
|
||||
name: openstack/devstack
|
||||
override-checkout: master
|
||||
override-checkout: stable/yoga
|
||||
- name: openstack/openstacksdk
|
||||
# Yoga has the latest SDK release of the 0.*.* series atm
|
||||
override-checkout: stable/yoga
|
||||
vars:
|
||||
tox_envlist: ansible-2.12
|
||||
tox_envlist: ansible_2_12
|
||||
|
||||
# Stable branches tests
|
||||
|
||||
@@ -214,10 +251,12 @@
|
||||
- # Choose parent devstack job from stable/xena branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/xena
|
||||
- name: openstack/designate
|
||||
override-checkout: stable/xena
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/xena
|
||||
vars:
|
||||
tox_envlist: ansible-2.12
|
||||
tox_envlist: ansible_2_12
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
|
||||
@@ -244,10 +283,12 @@
|
||||
- # Choose parent devstack job from stable/wallaby branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/wallaby
|
||||
- name: openstack/designate
|
||||
override-checkout: stable/wallaby
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/wallaby
|
||||
vars:
|
||||
tox_envlist: ansible-2.12
|
||||
tox_envlist: ansible_2_12
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
|
||||
@@ -274,10 +315,12 @@
|
||||
- # Choose parent devstack job from stable/victoria branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/victoria
|
||||
- name: openstack/designate
|
||||
override-checkout: stable/victoria
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/victoria
|
||||
vars:
|
||||
tox_envlist: ansible-2.12
|
||||
tox_envlist: ansible_2_12
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
|
||||
@@ -304,10 +347,12 @@
|
||||
- # Choose parent devstack job from stable/ussuri branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/ussuri
|
||||
- name: openstack/designate
|
||||
override-checkout: stable/ussuri
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/ussuri
|
||||
vars:
|
||||
tox_envlist: ansible-2.11
|
||||
tox_envlist: ansible_2_11
|
||||
|
||||
- job:
|
||||
name: ansible-collections-openstack-functional-devstack-train-ansible-2.11
|
||||
@@ -334,16 +379,17 @@
|
||||
- # Choose parent devstack job from stable/train branch
|
||||
name: openstack/devstack
|
||||
override-checkout: stable/train
|
||||
- name: openstack/designate
|
||||
override-checkout: train-eol
|
||||
- name: openstack/openstacksdk
|
||||
override-checkout: stable/train
|
||||
vars:
|
||||
tox_envlist: ansible-2.11
|
||||
tox_envlist: ansible_2_11
|
||||
|
||||
# Linters
|
||||
- job:
|
||||
name: openstack-tox-linters-ansible
|
||||
parent: openstack-tox-linters
|
||||
nodeset: ubuntu-focal
|
||||
description: |
|
||||
Run openstack collections linter tests using the devel branch of ansible
|
||||
required-projects:
|
||||
@@ -352,30 +398,33 @@
|
||||
vars:
|
||||
# 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_envlist: linters_latest
|
||||
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.8
|
||||
bindep_profile: test py38
|
||||
python_version: '3.10'
|
||||
bindep_profile: test py310
|
||||
|
||||
- job:
|
||||
name: openstack-tox-linters-ansible-2.12
|
||||
parent: openstack-tox-linters-ansible
|
||||
nodeset: ubuntu-focal
|
||||
description: |
|
||||
Run openstack collections linter tests using the 2.12 branch of ansible
|
||||
required-projects:
|
||||
- name: github.com/ansible/ansible
|
||||
override-checkout: stable-2.12
|
||||
vars:
|
||||
tox_envlist: linters-2.12
|
||||
ensure_tox_version: '<4'
|
||||
tox_envlist: linters_2_12
|
||||
python_version: 3.8
|
||||
bindep_profile: test py38
|
||||
|
||||
@@ -403,165 +452,24 @@
|
||||
# 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-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
|
||||
# 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: &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/modules/subnet.*$
|
||||
|
||||
- job:
|
||||
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
|
||||
run: ci/publish/publish_collection.yml
|
||||
secrets:
|
||||
- ansible_galaxy_info
|
||||
|
||||
- secret:
|
||||
name: ansible_galaxy_info
|
||||
data:
|
||||
url: https://galaxy.ansible.com
|
||||
token: !encrypted/pkcs1-oaep
|
||||
- lZFzfoCbuwqV1k6qRfl/VS7E+knUW7+zpg7BptrenK4n0g7UY0HtdVkYq0pV0Tj/LbhzG
|
||||
jHD0mehcV1iS6B7ORKg4criJkdDfEx09BD8z8yv0EleiIMmhlrCoMcY593OZMBtVbGi0D
|
||||
CwQtNO98QIsfZogChfLfvRNiBmUV98mEb/p6p3EtGx8J7qcAsqfWxc/CzB8GCleLAHHHT
|
||||
FuikMM03ZnV0ew7E+TPkHbzzPhBZOqS5HYF0HtgttHwIXdfIWp/XdTuEEk7uRRgYZ2Iao
|
||||
ifWRzoKaOQmhM++e1ydCqw9D4y9dZEFNMQLwSqcrvtb8cNwT1kl7SCFqYNE2lbutj4ne6
|
||||
PTBQRsKegMB4Y3ena14fNF6tCynvJLPhF/cjPH2Jhs+B19XQhWkL3TgiOY02W24YHwRcP
|
||||
+LdkM8inAvyVi3DEbEqdjBPO9OFJcBOKPlCdkGvuwdNCuEpEwctWs0gV3voflG2CDKzmJ
|
||||
wu9JJOAWnq/0l1WpuDqWreKeQ/BUGZC2Gb4xRAqofulgvhs4WuYoEccjH4EJFIZ90S1EP
|
||||
R/ZLadqZaEhmjwGM5sMWbBbjT23XsRgg0Tzt9m8DENYMuYDqkMdRbt2jYZa+32p4hyxVe
|
||||
Y6H/pqYq5b9uOzumnShaK4WlmkQyXcNPkoSlMC1h4OGvqX/WUixpI38jyMA5Tc=
|
||||
|
||||
- project:
|
||||
check:
|
||||
jobs:
|
||||
- tox-pep8
|
||||
- openstack-tox-linters-ansible-devel
|
||||
- openstack-tox-linters-ansible-2.12
|
||||
- ansible-collections-openstack-functional-devstack-releases:
|
||||
dependencies: &deps_unit_lint
|
||||
dependencies:
|
||||
- tox-pep8
|
||||
- openstack-tox-linters-ansible-2.12
|
||||
- ansible-collections-openstack-functional-devstack-ansible-2.9:
|
||||
dependencies: *deps_unit_lint
|
||||
- ansible-collections-openstack-functional-devstack-ansible-2.12:
|
||||
dependencies: *deps_unit_lint
|
||||
- ansible-collections-openstack-functional-devstack-ansible-devel:
|
||||
dependencies: *deps_unit_lint
|
||||
- ansible-collections-openstack-functional-devstack-xena-ansible-2.12:
|
||||
dependencies: *deps_unit_lint
|
||||
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12:
|
||||
dependencies: *deps_unit_lint
|
||||
- ansible-collections-openstack-functional-devstack-train-ansible-2.11:
|
||||
dependencies: *deps_unit_lint
|
||||
- ansible-collections-openstack-functional-devstack-octavia:
|
||||
dependencies: *deps_unit_lint
|
||||
|
||||
- bifrost-collections-src:
|
||||
voting: false
|
||||
dependencies: *deps_unit_lint
|
||||
irrelevant-files: *ignore_files
|
||||
- bifrost-keystone-collections-src:
|
||||
voting: false
|
||||
dependencies: *deps_unit_lint
|
||||
irrelevant-files: *ignore_files
|
||||
|
||||
- tripleo-ci-centos-8-standalone-osa-wallaby:
|
||||
dependencies: *deps_unit_lint
|
||||
- tripleo-ci-centos-9-standalone-osa-wallaby:
|
||||
voting: false
|
||||
dependencies: *deps_unit_lint
|
||||
|
||||
gate:
|
||||
jobs:
|
||||
- tox-pep8
|
||||
- openstack-tox-linters-ansible-2.12
|
||||
- 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-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-wallaby
|
||||
|
||||
periodic:
|
||||
jobs:
|
||||
@@ -577,13 +485,11 @@
|
||||
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
|
||||
- bifrost-collections-src
|
||||
- bifrost-keystone-collections-src
|
||||
- ansible-collections-openstack-functional-devstack-magnum
|
||||
- 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
|
||||
|
||||
@@ -5,6 +5,28 @@ 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
|
||||
======
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
.. _contributing:
|
||||
|
||||
=============================================
|
||||
Contributing to ansible-collections-openstack
|
||||
=============================================
|
||||
|
||||
If you're interested in contributing to the ansible-collections-openstack project,
|
||||
the following will help get you started.
|
||||
|
||||
Developer Workflow
|
||||
------------------
|
||||
|
||||
OpenStack uses OpenDev for it's development, and patches are submitted to
|
||||
`OpenDev Gerrit`_. Please read `DeveloperWorkflow`_ before sending your
|
||||
first patch for review.
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* https://wiki.openstack.org/wiki/How_To_Contribute
|
||||
* https://wiki.openstack.org/wiki/CLA
|
||||
|
||||
.. _OpenDev Gerrit: https://review.opendev.org/
|
||||
.. _DeveloperWorkflow: https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Project Hosting Details
|
||||
-----------------------
|
||||
|
||||
Bug tracker
|
||||
https://storyboard.openstack.org/#!/project/openstack/ansible-collections-openstack
|
||||
|
||||
Mailing list (prefix subjects with ``[ansible]`` for faster responses)
|
||||
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
|
||||
|
||||
Code Hosting
|
||||
https://opendev.org/openstack/ansible-collections-openstack
|
||||
|
||||
Code Review
|
||||
https://review.opendev.org/#/q/status:open+project:openstack/ansible-collections-openstack,n,z
|
||||
179
README.md
179
README.md
@@ -1,91 +1,75 @@
|
||||
[](http://zuul.opendev.org/t/openstack/builds?project=openstack%2Fansible-collections-openstack#)
|
||||
[](
|
||||
http://zuul.opendev.org/t/openstack/builds?project=openstack%2Fansible-collections-openstack)
|
||||
|
||||
# Ansible Collection: openstack.cloud
|
||||
# Ansible OpenStack Collection
|
||||
|
||||
This repo hosts the `openstack.cloud` Ansible Collection.
|
||||
Ansible OpenStack collection aka `openstack.cloud` provides Ansible modules and Ansible plugins for managing OpenStack
|
||||
clouds. It is supported and maintained by the OpenStack community.
|
||||
|
||||
The collection includes the Openstack modules and plugins supported by Openstack community to help the management of Openstack infrastructure.
|
||||
## Branches and Non Backward Compatibility ⚠️
|
||||
|
||||
## Breaking backward compatibility :warning:
|
||||
Our codebase has been split into two separate release series, `2.x.x` and `1.x.x`:
|
||||
|
||||
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][openstacksdk] `1.x.x` and its
|
||||
release candidates `0.99.0` and later *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][openstacksdk] `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.
|
||||
* `2.x.x` releases of Ansible OpenStack collection are not backward compatible to `1.x.x` releases ⚠️
|
||||
|
||||
* `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.
|
||||
For rationale and details please read our [branching docs](docs/branching.md). 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!
|
||||
|
||||
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.
|
||||
[openstacksdk]: https://opendev.org/openstack/openstacksdk
|
||||
|
||||
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.
|
||||
## Installation
|
||||
|
||||
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` 😎
|
||||
For using this collection, first you have to install both Python packages `ansible` and `openstacksdk` on your Ansible
|
||||
controller:
|
||||
|
||||
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
|
||||
|
||||
For using the Openstack Cloud collection firstly you need to install `ansible` and `openstacksdk` Python modules on your Ansible controller.
|
||||
For example with pip:
|
||||
|
||||
```bash
|
||||
```sh
|
||||
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).
|
||||
Please note, that under some circumstances Ansible might invoke a non-standard Python interpreter on the target host.
|
||||
Using Python version 3 is highly recommended for OpenstackSDK and strongly required from OpenstackSDK version 0.39.0.
|
||||
[OpenStack SDK][openstacksdk] has to be available on the Ansible host running the OpenStack modules. Depending on the
|
||||
Ansible playbook and roles you use, this host is not necessarily the Ansible controller. Sometimes Ansible might invoke
|
||||
a non-standard Python interpreter on the target Ansible host. Using Python 3.6 is required for modules in this
|
||||
collection.
|
||||
|
||||
---
|
||||
Always use the last stable version of [OpenStack SDK][openstacksdk] if possible, also when running against older
|
||||
OpenStack deployments. OpenStack SDK is backward compatible to older OpenStack deployments, so its safe to run last
|
||||
version of the SDK against older OpenStack clouds. The installed version of the OpenStack SDK does not have to match
|
||||
your OpenStack cloud, but it has to match the release series of this collection which you are using. For notes about
|
||||
our release series and branches please read the introduction above.
|
||||
|
||||
#### NOTE
|
||||
Before using this collection, you have to install it with `ansible-galaxy`:
|
||||
|
||||
OpenstackSDK is better to be the last stable version. It should NOT be installed on Openstack nodes,
|
||||
but rather on operators host (aka "Ansible controller"). OpenstackSDK from last version supports
|
||||
operations on all Openstack cloud versions. Therefore OpenstackSDK module version doesn't have to match
|
||||
Openstack cloud version usually.
|
||||
```sh
|
||||
ansible-galaxy collection install openstack.cloud
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Installing the Collection from Ansible Galaxy
|
||||
|
||||
Before using the Openstack Cloud collection, you need to install the collection with the `ansible-galaxy` CLI:
|
||||
|
||||
`ansible-galaxy collection install openstack.cloud`
|
||||
|
||||
You can also include it in a `requirements.yml` file and install it through `ansible-galaxy collection install -r requirements.yml` using the format:
|
||||
You can also include it in a `requirements.yml` file:
|
||||
|
||||
```yaml
|
||||
collections:
|
||||
- name: openstack.cloud
|
||||
```
|
||||
|
||||
### Playbooks
|
||||
And then install it with:
|
||||
|
||||
To use a module from the Openstack Cloud collection, please reference the full namespace, collection name, and module name that you want to use:
|
||||
```sh
|
||||
ansible-galaxy collection install -r requirements.yml
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To use a module from the OpenStack Cloud collection, please reference the full namespace, collection name, and module
|
||||
name that you want to use:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Using Openstack Cloud collection
|
||||
- name: Using OpenStack Cloud collection
|
||||
hosts: localhost
|
||||
tasks:
|
||||
- openstack.cloud.server:
|
||||
@@ -103,7 +87,7 @@ Or you can add the full namespace and collection name in the `collections` eleme
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Using Openstack Cloud collection
|
||||
- name: Using the Ansible OpenStack Collection
|
||||
hosts: localhost
|
||||
collections:
|
||||
- openstack.cloud
|
||||
@@ -116,49 +100,64 @@ Or you can add the full namespace and collection name in the `collections` eleme
|
||||
device: /dev/vdb
|
||||
```
|
||||
|
||||
### Usage
|
||||
[Ansible module defaults][ansible-module-defaults] are supported as well:
|
||||
|
||||
See the collection docs at Ansible site:
|
||||
```yaml
|
||||
---
|
||||
- module_defaults:
|
||||
group/openstack.cloud.openstack:
|
||||
cloud: devstack-admin
|
||||
#
|
||||
#
|
||||
# Listing modules individually is required for
|
||||
# backward compatibility with Ansible 2.9 only
|
||||
openstack.cloud.compute_flavor_info:
|
||||
cloud: devstack-admin
|
||||
openstack.cloud.server_info:
|
||||
cloud: devstack-admin
|
||||
block:
|
||||
- name: List compute flavors
|
||||
openstack.cloud.compute_flavor_info:
|
||||
|
||||
* [openstack.cloud collection docs (version released in Ansible package)](https://docs.ansible.com/ansible/latest/collections/openstack/cloud/index.html)
|
||||
- name: List servers
|
||||
openstack.cloud.server_info:
|
||||
```
|
||||
|
||||
* [openstack.cloud collection docs (devel version)](https://docs.ansible.com/ansible/devel/collections/openstack/cloud/index.html)
|
||||
[ansible-module-defaults]: https://docs.ansible.com/ansible/latest/user_guide/playbooks_module_defaults.html
|
||||
|
||||
## Documentation
|
||||
|
||||
See collection docs at Ansible's main page:
|
||||
|
||||
* [openstack.cloud collection docs (version released in Ansible package)](
|
||||
https://docs.ansible.com/ansible/latest/collections/openstack/cloud/index.html)
|
||||
|
||||
* [openstack.cloud collection docs (devel version)](
|
||||
https://docs.ansible.com/ansible/devel/collections/openstack/cloud/index.html)
|
||||
|
||||
## Contributing
|
||||
|
||||
For information on contributing, please see [CONTRIBUTING](https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/CONTRIBUTING.rst)
|
||||
Thank you for your interest in our Ansible OpenStack collection ☺️
|
||||
|
||||
There are many ways in which you can participate in the project, for example:
|
||||
|
||||
- Submit [bugs and feature requests](https://storyboard.openstack.org/#!/project/openstack/ansible-collections-openstack), and help us verify them
|
||||
- Submit and review source code changes in [Openstack Gerrit](https://review.opendev.org/#/q/project:openstack/ansible-collections-openstack)
|
||||
- Add new modules for Openstack Cloud
|
||||
- [Report and verify bugs and help with solving issues](
|
||||
https://storyboard.openstack.org/#!/project/openstack/ansible-collections-openstack).
|
||||
- [Submit and review patches](
|
||||
https://review.opendev.org/#/q/project:openstack/ansible-collections-openstack).
|
||||
- Follow OpenStack's [How To Contribute](https://wiki.openstack.org/wiki/How_To_Contribute) guide.
|
||||
|
||||
We work with [OpenDev Gerrit](https://review.opendev.org/), pull requests submitted through GitHub will be ignored.
|
||||
|
||||
## Testing and Development
|
||||
|
||||
If you want to develop new content for this collection or improve what is already here, the easiest way to work on the collection is to clone it into one of the configured [`COLLECTIONS_PATHS`](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#collections-paths), and work on it there.
|
||||
|
||||
### Testing with `ansible-test`
|
||||
|
||||
We use `ansible-test` for sanity:
|
||||
|
||||
```bash
|
||||
tox -e linters
|
||||
```
|
||||
|
||||
## More Information
|
||||
|
||||
TBD
|
||||
Please read our [Contributions and Development Guide](docs/contributing.md) (⚠️) and our [Review Guide](
|
||||
docs/reviewing.md) (⚠️) before sending your first patch. Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
## Communication
|
||||
|
||||
We have a dedicated Interest Group for Openstack Ansible modules.
|
||||
You can find other people interested in this in `#openstack-ansible-sig` on [OFTC IRC](https://www.oftc.net/).
|
||||
We have a Special Interest Group for the Ansible OpenStack collection. Join us in `#openstack-ansible-sig` on
|
||||
[OFTC IRC](https://www.oftc.net/) and ping Artem Goncharov <artem.goncharov@gmail.com> (gtema), Jakob Meng
|
||||
<mail@jakobmeng.de> (jm1) or Sagi Shnaidman <sshnaidm@redhat.com> (sshnaidm) 🍪
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0 or later
|
||||
|
||||
See [LICENCE](https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/COPYING) to see the full text.
|
||||
See [LICENCE](COPYING) to see the full text.
|
||||
|
||||
@@ -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:
|
||||
@@ -375,8 +390,8 @@ releases:
|
||||
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_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:
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
- hosts: all
|
||||
tasks:
|
||||
- name: Download amphora tarball
|
||||
get_url:
|
||||
url: "https://tarballs.openstack.org/octavia/test-images/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
|
||||
dest: /tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2
|
||||
5
ci/requirements.yml
Normal file
5
ci/requirements.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
collections:
|
||||
- ansible.posix
|
||||
- ansible.utils
|
||||
- community.general
|
||||
18
ci/roles/coe_cluster/defaults/main.yml
Normal file
18
ci/roles/coe_cluster/defaults/main.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
expected_fields:
|
||||
# Magnum might return more fields according to its documentation [0] but
|
||||
# openstacksdk normalizes coe cluster resources, moving most fields from
|
||||
# top level into a 'properties' field [1].
|
||||
# [0] https://docs.openstack.org/api-ref/container-infrastructure-management/#create-new-cluster
|
||||
# [1] https://opendev.org/openstack/openstacksdk/src/commit/d57c1fcab3b6cbe806cbae735fefa4983b200ab2/openstack/cloud/_normalize.py#L484
|
||||
- cluster_template_id
|
||||
- create_timeout
|
||||
- id
|
||||
- keypair
|
||||
- location
|
||||
- master_count
|
||||
- name
|
||||
- node_count
|
||||
- properties
|
||||
- stack_id
|
||||
- status
|
||||
- uuid
|
||||
181
ci/roles/coe_cluster/tasks/main.yml
Normal file
181
ci/roles/coe_cluster/tasks/main.yml
Normal file
@@ -0,0 +1,181 @@
|
||||
---
|
||||
- name: Create keypair
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_keypair
|
||||
state: present
|
||||
register: keypair
|
||||
|
||||
- name: List all images
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: images
|
||||
|
||||
- name: Identify Fedora CoreOS image id
|
||||
set_fact:
|
||||
image_id: "{{ images.images|community.general.json_query(query)|first }}"
|
||||
vars:
|
||||
query: "[?starts_with(name, 'fedora-coreos')].id"
|
||||
|
||||
- name: Create external network
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud }}"
|
||||
external: true
|
||||
name: ansible_external_network
|
||||
state: present
|
||||
register: external_network
|
||||
|
||||
- name: Create external subnet
|
||||
openstack.cloud.subnet:
|
||||
cidr: 10.6.6.0/24
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_external_subnet
|
||||
network_name: ansible_external_network
|
||||
state: present
|
||||
|
||||
- name: Create internal network
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_internal_network
|
||||
external: false
|
||||
|
||||
- name: Create internal subnet
|
||||
openstack.cloud.subnet:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
network_name: ansible_internal_network
|
||||
name: ansible_internal_subnet
|
||||
cidr: 10.7.7.0/24
|
||||
|
||||
- name: Create router
|
||||
openstack.cloud.router:
|
||||
cloud: "{{ cloud }}"
|
||||
external_fixed_ips:
|
||||
- subnet: ansible_external_subnet
|
||||
ip: 10.6.6.10
|
||||
interfaces:
|
||||
- net: ansible_internal_network
|
||||
subnet: ansible_internal_subnet
|
||||
portip: 10.7.7.1
|
||||
name: ansible_router
|
||||
network: ansible_external_network
|
||||
state: present
|
||||
|
||||
- name: Create Kubernetes cluster template
|
||||
openstack.cloud.coe_cluster_template:
|
||||
cloud: "{{ cloud }}"
|
||||
coe: kubernetes
|
||||
external_network_id: '{{ external_network.network.id }}'
|
||||
fixed_network: ansible_internal_network
|
||||
fixed_subnet: ansible_internal_subnet
|
||||
floating_ip_enabled: true
|
||||
image_id: '{{ image_id }}'
|
||||
keypair_id: '{{ keypair.keypair.id }}'
|
||||
name: k8s
|
||||
state: present
|
||||
register: coe_cluster_template
|
||||
|
||||
- name: Create Kubernetes cluster
|
||||
openstack.cloud.coe_cluster:
|
||||
cloud: "{{ cloud }}"
|
||||
cluster_template_id: "{{ coe_cluster_template.cluster_template.uuid }}"
|
||||
keypair: ansible_keypair
|
||||
name: k8s
|
||||
state: present
|
||||
# cluster creation takes longer than max tenant timeout of 10800
|
||||
wait: false
|
||||
register: coe_cluster
|
||||
|
||||
- name: Assert return values of coe_cluster module
|
||||
assert:
|
||||
that:
|
||||
# openstack.cloud.coe_cluster will only return 'id' on cluster creation when wait is false
|
||||
- "['id']|difference(coe_cluster.cluster.keys())|length == 0"
|
||||
|
||||
- name: Pause for 1 minutes to allow Magnum to create the Kubernetes cluster
|
||||
ansible.builtin.pause:
|
||||
minutes: 1
|
||||
|
||||
- name: Create Kubernetes cluster again
|
||||
openstack.cloud.coe_cluster:
|
||||
cloud: "{{ cloud }}"
|
||||
cluster_template_id: "{{ coe_cluster_template.cluster_template.uuid }}"
|
||||
keypair: ansible_keypair
|
||||
name: k8s
|
||||
state: present
|
||||
# cluster creation takes longer than max tenant timeout of 10800
|
||||
wait: false
|
||||
register: coe_cluster
|
||||
|
||||
- name: Assert return values of coe_cluster module
|
||||
assert:
|
||||
that:
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- expected_fields|difference(coe_cluster.cluster.keys())|length == 0
|
||||
|
||||
- name: Delete Kubernetes cluster
|
||||
openstack.cloud.coe_cluster:
|
||||
cloud: "{{ cloud }}"
|
||||
name: k8s
|
||||
state: absent
|
||||
register: coe_cluster
|
||||
|
||||
- name: Assert return values of coe_cluster module
|
||||
assert:
|
||||
that:
|
||||
- coe_cluster is changed
|
||||
|
||||
- name: Delete Kubernetes cluster again
|
||||
openstack.cloud.coe_cluster:
|
||||
cloud: "{{ cloud }}"
|
||||
name: k8s
|
||||
state: absent
|
||||
register: coe_cluster
|
||||
|
||||
- name: Assert return values of coe_cluster module
|
||||
assert:
|
||||
that:
|
||||
- coe_cluster is not changed
|
||||
|
||||
- name: Delete Kubernetes cluster template
|
||||
openstack.cloud.coe_cluster_template:
|
||||
cloud: "{{ cloud }}"
|
||||
name: k8s
|
||||
state: absent
|
||||
|
||||
- name: Delete router
|
||||
openstack.cloud.router:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_router
|
||||
state: absent
|
||||
|
||||
- name: Delete internal subnet
|
||||
openstack.cloud.subnet:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_subnet
|
||||
|
||||
- name: Delete internal network
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_internal_network
|
||||
|
||||
- name: Delete external subnet
|
||||
openstack.cloud.subnet:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_external_subnet
|
||||
state: absent
|
||||
|
||||
- name: Delete external network
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_external_network
|
||||
state: absent
|
||||
|
||||
- name: Delete keypair
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_keypair
|
||||
state: absent
|
||||
40
ci/roles/coe_cluster_template/defaults/main.yml
Normal file
40
ci/roles/coe_cluster_template/defaults/main.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
expected_fields:
|
||||
# Magnum might return more fields according to its documentation [0] but
|
||||
# openstacksdk normalizes coe cluster template resources, moving most
|
||||
# fields from top level into a 'properties' field [1].
|
||||
# [0] https://docs.openstack.org/api-ref/container-infrastructure-management/#create-new-cluster
|
||||
# [1] https://opendev.org/openstack/openstacksdk/src/commit/d57c1fcab3b6cbe806cbae735fefa4983b200ab2/openstack/cloud/_normalize.py#L522
|
||||
- apiserver_port
|
||||
- cluster_distro
|
||||
- coe
|
||||
- created_at
|
||||
- dns_nameserver
|
||||
- docker_volume_size
|
||||
- external_network_id
|
||||
- fixed_network
|
||||
- fixed_subnet
|
||||
- flavor_id
|
||||
- floating_ip_enabled
|
||||
- http_proxy
|
||||
- https_proxy
|
||||
- id
|
||||
- image_id
|
||||
- insecure_registry
|
||||
- is_public
|
||||
- is_registry_enabled
|
||||
- is_tls_disabled
|
||||
- keypair_id
|
||||
- labels
|
||||
- location
|
||||
- master_flavor_id
|
||||
- name
|
||||
- network_driver
|
||||
- no_proxy
|
||||
- properties
|
||||
- public
|
||||
- registry_enabled
|
||||
- server_type
|
||||
- tls_disabled
|
||||
- updated_at
|
||||
- uuid
|
||||
- volume_driver
|
||||
81
ci/roles/coe_cluster_template/tasks/main.yml
Normal file
81
ci/roles/coe_cluster_template/tasks/main.yml
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
- name: Create keypair
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_keypair
|
||||
state: present
|
||||
register: keypair
|
||||
|
||||
- name: List all images
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: images
|
||||
|
||||
- name: Identify Fedora CoreOS image id
|
||||
set_fact:
|
||||
image_id: "{{ images.images|community.general.json_query(query)|first }}"
|
||||
vars:
|
||||
query: "[?starts_with(name, 'fedora-coreos')].id"
|
||||
|
||||
- name: Create Kubernetes cluster template
|
||||
openstack.cloud.coe_cluster_template:
|
||||
cloud: "{{ cloud }}"
|
||||
coe: kubernetes
|
||||
floating_ip_enabled: false
|
||||
image_id: '{{ image_id }}'
|
||||
keypair_id: '{{ keypair.keypair.id }}'
|
||||
name: k8s
|
||||
state: present
|
||||
register: coe_cluster_template
|
||||
|
||||
- name: Assert return values of coe_cluster_template module
|
||||
assert:
|
||||
that:
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- expected_fields|difference(coe_cluster_template.cluster_template.keys())|length == 0
|
||||
|
||||
- name: Create Kubernetes cluster template again
|
||||
openstack.cloud.coe_cluster_template:
|
||||
cloud: "{{ cloud }}"
|
||||
coe: kubernetes
|
||||
floating_ip_enabled: false
|
||||
image_id: '{{ image_id }}'
|
||||
keypair_id: '{{ keypair.keypair.id }}'
|
||||
name: k8s
|
||||
state: present
|
||||
register: coe_cluster_template
|
||||
|
||||
- name: Assert return values of coe_cluster_template module
|
||||
assert:
|
||||
that:
|
||||
- coe_cluster_template is not changed
|
||||
|
||||
- name: Delete Kubernetes cluster template
|
||||
openstack.cloud.coe_cluster_template:
|
||||
cloud: "{{ cloud }}"
|
||||
name: k8s
|
||||
state: absent
|
||||
register: coe_cluster_template
|
||||
|
||||
- name: Assert return values of coe_cluster_template module
|
||||
assert:
|
||||
that:
|
||||
- coe_cluster_template is changed
|
||||
|
||||
- name: Delete Kubernetes cluster template again
|
||||
openstack.cloud.coe_cluster_template:
|
||||
cloud: "{{ cloud }}"
|
||||
name: k8s
|
||||
state: absent
|
||||
register: coe_cluster_template
|
||||
|
||||
- name: Assert return values of coe_cluster_template module
|
||||
assert:
|
||||
that:
|
||||
- coe_cluster_template is not changed
|
||||
|
||||
- name: Delete keypair
|
||||
openstack.cloud.keypair:
|
||||
cloud: "{{ cloud }}"
|
||||
name: ansible_keypair
|
||||
state: absent
|
||||
@@ -7,12 +7,12 @@
|
||||
openstack.cloud.compute_flavor_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "m1.tiny"
|
||||
register: flavor_name
|
||||
register: flavor
|
||||
|
||||
- name: Check output of list flavors with filter
|
||||
assert:
|
||||
that:
|
||||
- flavor_name.openstack_flavors | length == 1
|
||||
- flavor.openstack_flavors | length == 1
|
||||
|
||||
- name: Assert fields on SDK 0.*
|
||||
when: sdk_version is version(1.0, '<')
|
||||
@@ -20,7 +20,7 @@
|
||||
that:
|
||||
- '["name", "description", "disk", "is_public", "ram",
|
||||
"vcpus", "swap", "ephemeral", "is_disabled", "rxtx_factor", "id",
|
||||
"extra_specs"] | difference(flavor_name.openstack_flavors.0.keys())
|
||||
"extra_specs"] | difference(flavor.openstack_flavors.0.keys())
|
||||
| length == 0'
|
||||
|
||||
- name: Assert fields on SDK 1.*
|
||||
@@ -29,5 +29,5 @@
|
||||
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())
|
||||
"extra_specs"] | difference(flavor.openstack_flavors.0.keys())
|
||||
| length == 0'
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
---
|
||||
# Prepare environment
|
||||
- name: List all images
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: images
|
||||
|
||||
- name: Identify CirrOS image name
|
||||
set_fact:
|
||||
image_name: "{{ images.openstack_images|community.general.json_query(query)|first }}"
|
||||
vars:
|
||||
query: "[?starts_with(name, 'cirros')].name"
|
||||
|
||||
- name: Gather information about public network
|
||||
openstack.cloud.networks_info:
|
||||
cloud: "{{ cloud }}"
|
||||
@@ -143,7 +154,7 @@
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_server1
|
||||
image: "{{ image }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: m1.tiny
|
||||
nics:
|
||||
# one nic only else simple, first floating ip test does not work
|
||||
@@ -173,7 +184,7 @@
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_server2
|
||||
image: "{{ image }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: m1.tiny
|
||||
nics:
|
||||
- port-name: ansible_internal_port2
|
||||
|
||||
@@ -42,10 +42,12 @@
|
||||
register: userinfo
|
||||
- name: Assert only one result exists
|
||||
assert:
|
||||
that: "{{ userinfo.openstack_users | length }} == 1"
|
||||
that:
|
||||
- userinfo.openstack_users | length == 1
|
||||
- name: Assert userinfo has fields
|
||||
assert:
|
||||
that: item in userinfo.openstack_users[0]
|
||||
that:
|
||||
- item in userinfo.openstack_users[0]
|
||||
loop: "{{ os_expected_user_info_fields }}"
|
||||
|
||||
- block:
|
||||
@@ -55,7 +57,8 @@
|
||||
register: userinfo
|
||||
- name: Assert results were returned
|
||||
assert:
|
||||
that: "{{ userinfo.openstack_users | length }} > 0"
|
||||
that:
|
||||
- userinfo.openstack_users | length > 0
|
||||
|
||||
- name: Post-test cleanup
|
||||
block:
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
image_name: ansible_image
|
||||
image_tags:
|
||||
- test
|
||||
- ansible
|
||||
@@ -10,35 +10,37 @@
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ image_name }}"
|
||||
name: ansible_image
|
||||
filename: "{{ tmp_file.stdout }}"
|
||||
disk_format: raw
|
||||
tags: "{{ image_tags }}"
|
||||
tags:
|
||||
- test
|
||||
- ansible
|
||||
register: image
|
||||
|
||||
- name: Get details of created image
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
image: "{{ image_name }}"
|
||||
image: ansible_image
|
||||
register: image_info_result
|
||||
|
||||
- name: Verify image info
|
||||
assert:
|
||||
that:
|
||||
- "image_info_result.openstack_images[0].name == image_name"
|
||||
- "image_info_result.openstack_images[0].tags | sort == image_tags | sort"
|
||||
- image_info_result.openstack_images[0].name == "ansible_image"
|
||||
- image_info_result.openstack_images[0].tags | sort == ['test', 'ansible'] | sort
|
||||
|
||||
- name: Delete raw image (defaults)
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ image_name }}"
|
||||
name: ansible_image
|
||||
|
||||
- name: Create raw image (complex)
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ image_name }}"
|
||||
name: ansible_image
|
||||
filename: "{{ tmp_file.stdout }}"
|
||||
disk_format: raw
|
||||
is_public: True
|
||||
@@ -55,12 +57,12 @@
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ image_name }}"
|
||||
name: ansible_image
|
||||
|
||||
- name: Try to get details of deleted image
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
image: "{{ image_name }}"
|
||||
image: ansible_image
|
||||
register: deleted_image_info_result
|
||||
|
||||
- name: Verify image is deleted
|
||||
@@ -82,17 +84,19 @@
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ image_name }}"
|
||||
name: ansible_image
|
||||
filename: "{{ tmp_file.stdout }}"
|
||||
disk_format: raw
|
||||
tags: "{{ image_tags }}"
|
||||
tags:
|
||||
- test
|
||||
- ansible
|
||||
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 }}"
|
||||
image: ansible_image
|
||||
register: image_info_result
|
||||
|
||||
- name: Verify image owner (owner by project name)
|
||||
@@ -104,16 +108,18 @@
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ image_name }}"
|
||||
name: ansible_image
|
||||
|
||||
- name: Create raw image (owner by project name and domain name)
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ image_name }}"
|
||||
name: ansible_image
|
||||
filename: "{{ tmp_file.stdout }}"
|
||||
disk_format: raw
|
||||
tags: "{{ image_tags }}"
|
||||
tags:
|
||||
- test
|
||||
- ansible
|
||||
project: image_owner_project
|
||||
project_domain: default
|
||||
register: image
|
||||
@@ -121,7 +127,7 @@
|
||||
- name: Get details of created image (owner by project name and domain name)
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
image: "{{ image_name }}"
|
||||
image: ansible_image
|
||||
register: image_info_result
|
||||
|
||||
- name: Verify image owner (owner by project name and domain name)
|
||||
@@ -133,7 +139,7 @@
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ image_name }}"
|
||||
name: ansible_image
|
||||
|
||||
- name: Delete owner project
|
||||
openstack.cloud.project:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
assert:
|
||||
that:
|
||||
- "'domain' in os_domain"
|
||||
- os_domain.domain.name == "{{ domain_name }}"
|
||||
- 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)
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
|
||||
---
|
||||
- name: Download Amphora tarball
|
||||
get_url:
|
||||
url: "https://tarballs.openstack.org/octavia/test-images/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
|
||||
dest: /tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2
|
||||
|
||||
- name: Upload Amphora image for Octavia to test load balancers
|
||||
openstack.cloud.image:
|
||||
cloud: "{{ cloud }}"
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
filename: /tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2
|
||||
is_public: false
|
||||
name: test-only-amphora-x64-haproxy-ubuntu-bionic
|
||||
owner: service
|
||||
properties:
|
||||
hw_architecture: x86_64
|
||||
hw_rng_model: virtio
|
||||
state: present
|
||||
tags:
|
||||
- amphora
|
||||
|
||||
- name: Create network {{ network_name }} for LB
|
||||
openstack.cloud.network:
|
||||
cloud: "{{ cloud }}"
|
||||
|
||||
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) }}"
|
||||
@@ -108,30 +108,6 @@
|
||||
state: absent
|
||||
name: "{{ secgroup_name }}"
|
||||
|
||||
- name: Test port binding config (runs from train release sdk > 0.28)
|
||||
block:
|
||||
- name: Create port (with binding profile)
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ port_name }}"
|
||||
network: "{{ network_name }}"
|
||||
binding_profile: "{{ binding_profile }}"
|
||||
register: port
|
||||
|
||||
- name: Assert binding:profile exists in created port
|
||||
assert:
|
||||
that: "port.port['binding_profile']"
|
||||
|
||||
- debug: var=port
|
||||
|
||||
- name: Delete port (with binding profile)
|
||||
openstack.cloud.port:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ port_name }}"
|
||||
when: sdk_version is version(0.28, '>')
|
||||
|
||||
- name: Delete subnet
|
||||
openstack.cloud.subnet:
|
||||
cloud: "{{ cloud }}"
|
||||
|
||||
@@ -3,6 +3,6 @@ server_name: ansible_server
|
||||
server_alt_network: private_alt
|
||||
server_alt_subnet: subnet_alt
|
||||
server_alt_name: ansible_server_alt
|
||||
flavor: m1.tiny
|
||||
flavor_name: m1.tiny
|
||||
floating_ip_pool_name: public
|
||||
boot_volume_size: 5
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
---
|
||||
- name: List all images
|
||||
openstack.cloud.image_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: images
|
||||
|
||||
- name: Identify CirrOS image name
|
||||
set_fact:
|
||||
image_name: "{{ images.openstack_images|community.general.json_query(query)|first }}"
|
||||
vars:
|
||||
query: "[?starts_with(name, 'cirros')].name"
|
||||
|
||||
- name: Create server with meta as CSV
|
||||
openstack.cloud.server:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ server_name }}"
|
||||
image: "{{ image }}"
|
||||
flavor: "{{ flavor }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: "{{ flavor_name }}"
|
||||
network: "{{ server_network }}"
|
||||
auto_floating_ip: false
|
||||
meta: "key1=value1,key2=value2"
|
||||
@@ -46,8 +57,8 @@
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ server_name }}"
|
||||
image: "{{ image }}"
|
||||
flavor: "{{ flavor }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: "{{ flavor_name }}"
|
||||
auto_floating_ip: false
|
||||
network: "{{ server_network }}"
|
||||
meta:
|
||||
@@ -67,7 +78,7 @@
|
||||
- name: Check info about server name
|
||||
assert:
|
||||
that:
|
||||
info.openstack_servers[0].name == "{{ server_name }}"
|
||||
info.openstack_servers[0].name == server_name
|
||||
|
||||
- name: Delete server with meta as dict
|
||||
openstack.cloud.server:
|
||||
@@ -81,8 +92,8 @@
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ server_name }}"
|
||||
image: "{{ image }}"
|
||||
flavor: "{{ flavor }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: "{{ flavor_name }}"
|
||||
network: "{{ server_network }}"
|
||||
floating_ip_pools:
|
||||
- "{{ floating_ip_pool_name }}"
|
||||
@@ -101,7 +112,7 @@
|
||||
- name: Check info about server image name
|
||||
assert:
|
||||
that:
|
||||
info.openstack_servers[0].image.name == "{{ image }}"
|
||||
info.openstack_servers[0].image.name == image_name
|
||||
|
||||
- name: Delete server (FIP from pool/network)
|
||||
openstack.cloud.server:
|
||||
@@ -115,8 +126,8 @@
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ server_name }}"
|
||||
image: "{{ image }}"
|
||||
flavor: "{{ flavor }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: "{{ flavor_name }}"
|
||||
network: "{{ server_network }}"
|
||||
auto_floating_ip: false
|
||||
boot_from_volume: true
|
||||
@@ -133,13 +144,14 @@
|
||||
state: absent
|
||||
name: "{{ server_name }}"
|
||||
wait: true
|
||||
|
||||
- name: Create a minimal server
|
||||
openstack.cloud.server:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ server_name }}"
|
||||
image: "{{ image }}"
|
||||
flavor: "{{ flavor }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: "{{ flavor_name }}"
|
||||
network: "{{ server_network }}"
|
||||
auto_floating_ip: false
|
||||
wait: true
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: "{{ server_name }}"
|
||||
image: "{{ image }}"
|
||||
flavor: "{{ flavor }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: "{{ flavor_name }}"
|
||||
network: "{{ server_network }}"
|
||||
auto_floating_ip: false
|
||||
wait: true
|
||||
@@ -380,7 +380,7 @@
|
||||
openstack.cloud.server_action:
|
||||
cloud: "{{ cloud }}"
|
||||
server: "{{ server_name }}"
|
||||
image: "{{ image }}"
|
||||
image: "{{ image_name }}"
|
||||
action: rebuild
|
||||
wait: true
|
||||
register: server
|
||||
@@ -401,7 +401,7 @@
|
||||
openstack.cloud.server_action:
|
||||
cloud: "{{ cloud }}"
|
||||
server: "{{ server_name }}"
|
||||
image: "{{ image }}"
|
||||
image: "{{ image_name }}"
|
||||
action: rebuild
|
||||
wait: true
|
||||
admin_password: random
|
||||
@@ -538,8 +538,8 @@
|
||||
cloud: "{{ cloud_alt }}"
|
||||
state: present
|
||||
name: "{{ server_alt_name }}"
|
||||
image: "{{ image }}"
|
||||
flavor: "{{ flavor }}"
|
||||
image: "{{ image_name }}"
|
||||
flavor: "{{ flavor_name }}"
|
||||
network: "{{ server_alt_network }}"
|
||||
auto_floating_ip: false
|
||||
wait: true
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
- name: Check info
|
||||
assert:
|
||||
that:
|
||||
- info1.volumes | selectattr("id", "equalto", "{{ info.volumes.0.id }}") | list | length == 1
|
||||
- info1.volumes | selectattr("id", "equalto", info.volumes.0.id) | list | length == 1
|
||||
- info1.volumes.0.name == 'ansible_test'
|
||||
- info1.volumes.0.status == None
|
||||
|
||||
|
||||
114
ci/run-ansible-tests-collection.sh
Normal file → Executable file
114
ci/run-ansible-tests-collection.sh
Normal file → Executable file
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash
|
||||
#############################################################################
|
||||
# run-ansible-tests.sh
|
||||
#
|
||||
@@ -41,27 +42,39 @@ 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}"
|
||||
c) CLOUD=$OPTARG ;;
|
||||
u) CLOUD_ALT=$OPTARG ;;
|
||||
e) ENVDIR=$OPTARG ;;
|
||||
?) echo "Invalid option: -$OPTARG"
|
||||
exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z ${ENVDIR} ]
|
||||
then
|
||||
# Shift arguments read by getopts
|
||||
shift $((OPTIND-1))
|
||||
|
||||
# Remaining arguments are Ansible tags
|
||||
TAGS=$( echo "$*" | tr ' ' , )
|
||||
|
||||
if [ -z "$ENVDIR" ]; then
|
||||
echo "Option -e is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
shift $((OPTIND-1))
|
||||
TAGS=$( echo "$*" | tr ' ' , )
|
||||
if [ ! -d ci ]; then
|
||||
echo "Script must be run from collection's root directory"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Install collections before dealing with Ansible virtual environments
|
||||
if [[ -z "$PIP_INSTALL" ]]; then
|
||||
# Install Ansible collections before dealing with virtual environments for Ansible
|
||||
|
||||
# Install collections used in ci
|
||||
ansible-galaxy collection install --requirements-file ci/requirements.yml
|
||||
|
||||
# Install this collection
|
||||
if [ -z "$PIP_INSTALL" ]; then
|
||||
tox -ebuild
|
||||
ansible-galaxy collection install $(ls build_artifact/openstack-cloud-*) --force
|
||||
ansible-galaxy collection install "$(find build_artifact/ -maxdepth 1 -name 'openstack-cloud-*')" --force
|
||||
TEST_COLLECTIONS_PATHS=${HOME}/.ansible/collections:$ANSIBLE_COLLECTIONS_PATHS
|
||||
else
|
||||
pip freeze | grep ansible-collections-openstack
|
||||
@@ -70,25 +83,16 @@ fi
|
||||
|
||||
# We need to source the current tox environment so that Ansible will
|
||||
# be setup for the correct python environment.
|
||||
source $ENVDIR/bin/activate
|
||||
source "$ENVDIR/bin/activate"
|
||||
|
||||
if [ ${USE_DEV} -eq 1 ]
|
||||
then
|
||||
if [ -d ${ENVDIR}/ansible ]
|
||||
then
|
||||
if [ "$USE_DEV" -eq 1 ]; then
|
||||
if [ -d "$ENVDIR/ansible" ]; then
|
||||
echo "Using existing Ansible source repo"
|
||||
else
|
||||
echo "Installing Ansible source repo at $ENVDIR"
|
||||
git clone --recursive https://github.com/ansible/ansible.git ${ENVDIR}/ansible
|
||||
git clone --recursive https://github.com/ansible/ansible.git "$ENVDIR/ansible"
|
||||
fi
|
||||
source $ENVDIR/ansible/hacking/env-setup
|
||||
fi
|
||||
|
||||
# Run the shade Ansible tests
|
||||
tag_opt=""
|
||||
if [ ! -z ${TAGS} ]
|
||||
then
|
||||
tag_opt="--tags ${TAGS}"
|
||||
source "$ENVDIR/ansible/hacking/env-setup"
|
||||
fi
|
||||
|
||||
# Loop through all ANSIBLE_VAR_ environment variables to allow passing the further
|
||||
@@ -100,44 +104,32 @@ for var in $(env | grep -e '^ANSIBLE_VAR_'); do
|
||||
ANSIBLE_VARS+="${ANSIBLE_VAR_NAME}=${ANSIBLE_VAR_VALUE} " # concat variables
|
||||
done
|
||||
|
||||
# Until we have a module that lets us determine the image we want from
|
||||
# within a playbook, we have to find the image here and pass it in.
|
||||
# We use the openstack client instead of nova client since it can use clouds.yaml.
|
||||
IMAGE=`openstack --os-cloud=${CLOUD} image list -f value -c Name | grep cirros | grep -v -e ramdisk -e kernel`
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo "Failed to find Cirros image"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# In case of Octavia enabled:
|
||||
_octavia_image_path="/tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
|
||||
if systemctl list-units --full -all | grep -Fq "devstack@o-api.service" && \
|
||||
test -f "$_octavia_image_path"
|
||||
then
|
||||
# Upload apmhora image for Octavia to test load balancers
|
||||
OCTAVIA_AMP_IMAGE_FILE=${OCTAVIA_AMP_IMAGE_FILE:-"$_octavia_image_path"}
|
||||
OCTAVIA_AMP_IMAGE_NAME=${OCTAVIA_AMP_IMAGE_NAME:-"test-only-amphora-x64-haproxy-ubuntu-bionic"}
|
||||
OCTAVIA_AMP_IMAGE_SIZE=${OCTAVIA_AMP_IMAGE_SIZE:-3}
|
||||
openstack --os-cloud=${CLOUD} image create \
|
||||
--container-format bare \
|
||||
--disk-format qcow2 \
|
||||
--private \
|
||||
--file $OCTAVIA_AMP_IMAGE_FILE \
|
||||
--project service $OCTAVIA_AMP_IMAGE_NAME
|
||||
openstack --os-cloud=${CLOUD} image set --tag amphora $OCTAVIA_AMP_IMAGE_NAME
|
||||
# End of Octavia preparement
|
||||
else
|
||||
tag_opt="$tag_opt --skip-tags loadbalancer"
|
||||
fi
|
||||
|
||||
# Discover openstackSDK version
|
||||
# Discover openstacksdk version
|
||||
SDK_VER=$(python -c "import openstack; print(openstack.version.__version__)")
|
||||
pushd ci/
|
||||
# run tests
|
||||
|
||||
# Choose integration tests
|
||||
tag_opt=""
|
||||
if [ -n "$TAGS" ]; then
|
||||
tag_opt="--tags $TAGS"
|
||||
fi
|
||||
|
||||
if ! systemctl is-enabled devstack@o-api.service 2>&1; then
|
||||
# Skip loadbalancer tasks if Octavia is not available
|
||||
tag_opt+=" --skip-tags loadbalancer"
|
||||
fi
|
||||
|
||||
# TODO: Replace with more robust test for Magnum availability
|
||||
if [ ! -e /etc/magnum ]; then
|
||||
# Skip coe tasks if Magnum is not available
|
||||
tag_opt+=" --skip-tags coe_cluster,coe_cluster_template"
|
||||
fi
|
||||
|
||||
cd ci/
|
||||
|
||||
# Run tests
|
||||
set -o pipefail
|
||||
# shellcheck disable=SC2086
|
||||
ANSIBLE_COLLECTIONS_PATHS=$TEST_COLLECTIONS_PATHS ansible-playbook \
|
||||
-vvv ./run-collection.yml \
|
||||
-e "sdk_version=${SDK_VER} cloud=${CLOUD} cloud_alt=${CLOUD_ALT} image=${IMAGE} ${ANSIBLE_VARS}" \
|
||||
-e "sdk_version=${SDK_VER} cloud=${CLOUD} cloud_alt=${CLOUD_ALT} ${ANSIBLE_VARS}" \
|
||||
${tag_opt} 2>&1 | sudo tee /opt/stack/logs/test_output.log
|
||||
popd
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
- { role: auth, tags: auth }
|
||||
- { role: catalog_service, tags: catalog_service }
|
||||
- { role: client_config, tags: client_config }
|
||||
- { role: coe_cluster, tags: coe_cluster }
|
||||
- { role: coe_cluster_template, tags: coe_cluster_template }
|
||||
- { role: dns_zone_info, tags: dns_zone_info }
|
||||
- role: object_container
|
||||
tags: object_container
|
||||
@@ -38,6 +40,7 @@
|
||||
- role: keystone_federation_protocol
|
||||
tags: keystone_federation_protocol
|
||||
when: sdk_version is version(0.44, '>=')
|
||||
- { role: logging, tags: logging }
|
||||
- { role: network, tags: network }
|
||||
- role: neutron_rbac
|
||||
tags:
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
# For resource changing module
|
||||
ansible localhost -c local -m template -a "src=module_template.py.j2 dest=my_module.py" -e @module_template_vars.yaml
|
||||
# For resource info collection module
|
||||
ansible localhost -c local -m template -a "src=module_info_template.py.j2 dest=my_module_info.py" -e @module_template_vars.yaml
|
||||
@@ -1,110 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020, {{ author_name }} <{{ author_mail }}>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: {{ module_name }}
|
||||
short_description: {{ module_short_description }}
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- {{ module_long_description }}
|
||||
options:
|
||||
{{ options|to_nice_yaml(indent=2,sort_keys=false)|indent(width=2)|trim }}
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
{{ module_returns_example|to_nice_yaml(indent=2,sort_keys=false) }}
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# What modules does for example
|
||||
- {{ module_name }}:
|
||||
name:
|
||||
- name1
|
||||
- name2
|
||||
timeout: 200
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
|
||||
|
||||
class {{ module_name.split("_")|map("capitalize")|list|join("") }}Module(OpenStackModule):
|
||||
|
||||
argument_spec = dict(
|
||||
{% for k, v in options.items() %}
|
||||
{{ k | indent( width=8, indentfirst=True) }}=dict(type='{{ v.type }}'
|
||||
{%- if 'required' in v %}, required={{ v.required }}{% endif %}
|
||||
{%- if 'elements' in v %}, elements={{ v.elements }}{% endif %}
|
||||
{%- if 'default' in v %}, default={% if v.type == 'str' %}"{{ v.default }}"{% else %}{{ v.default }}{% endif %}{% endif %}
|
||||
{%- if 'aliases' in v %}, aliases={{ v.aliases }}{% endif %}
|
||||
{%- if 'choices' in v %}, choices={{ v.choices }}{% endif %}),
|
||||
{% endfor %}
|
||||
),
|
||||
|
||||
# Optional arguments requirements
|
||||
module_kwargs = dict(
|
||||
required_if=[
|
||||
['action', 'rebuild', ['image']], # if need to rebuild image (only), the 'image' is required
|
||||
["state", "present", ["username", "user_roles"]], # for creating user 'user_roles' is required
|
||||
["state", "absent", ["username"]], # for state 'absent' only username is required
|
||||
],
|
||||
required_by=dict( # for weather and population 'city' is required to set
|
||||
weather=('city'),
|
||||
population=('city'),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
['use_cloud1', 'use_cloud2'] # can't run on both, choose only one to set
|
||||
],
|
||||
required_together=[
|
||||
['remove_image', 'image_name'] # if need to remove image, must to specify which one
|
||||
],
|
||||
required_one_of_args=[["password", "password_hash"]], # one of these args must be set
|
||||
supports_check_mode={{ check_mode_support }}, # good practice is to support check_mode
|
||||
)
|
||||
|
||||
# you main funciton is here
|
||||
def run(self):
|
||||
# do any arguments check if needed
|
||||
data = self.preliminary_checks()
|
||||
# check if we need to prepare various filters for results
|
||||
filters = self.prepare_filters()
|
||||
# run SDK call to get information about requested resource
|
||||
result = self.conn.compute.resource_list(
|
||||
filters=filters,
|
||||
detailed=self.params['detailed'],
|
||||
# any other parameters
|
||||
)
|
||||
# process results if they require a change
|
||||
result = self.normalize_result()
|
||||
self.results.update({'resource_name': result})
|
||||
|
||||
def preliminary_checks(self):
|
||||
# you checks before running like arguments and options checks, etc
|
||||
return data
|
||||
|
||||
def prepare_filters(self):
|
||||
# process filters if they require additional checks
|
||||
return filters
|
||||
|
||||
def normalize_result(self):
|
||||
# process filters if they require additional checks
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
module = {{ module_name.split("_")|map("capitalize")|list|join("") }}Module()
|
||||
module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,149 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020, {{ author_name }} <{{ author_mail }}>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: {{ module_name }}
|
||||
short_description: {{ module_short_description }}
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- {{ module_long_description }}
|
||||
options:
|
||||
{{ options|to_nice_yaml(indent=2,sort_keys=false)|indent(width=2)|trim }}
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
{{ module_returns_example|to_nice_yaml(indent=2,sort_keys=false) }}
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# What modules does for example
|
||||
- {{ module_name }}:
|
||||
action: pause
|
||||
auth:
|
||||
auth_url: https://identity.example.com
|
||||
username: admin
|
||||
password: admin
|
||||
project_name: admin
|
||||
server: vm1
|
||||
timeout: 200
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
|
||||
|
||||
class {{ module_name.split("_")|map("capitalize")|list|join("") }}Module(OpenStackModule):
|
||||
|
||||
argument_spec = dict(
|
||||
{% for k, v in options.items() %}
|
||||
{{ k | indent( width=8, indentfirst=True) }}=dict(type='{{ v.type }}'
|
||||
{%- if 'required' in v %}, required={{ v.required }}{% endif %}
|
||||
{%- if 'elements' in v %}, elements={{ v.elements }}{% endif %}
|
||||
{%- if 'default' in v %}, default={% if v.type == 'str' %}"{{ v.default }}"{% else %}{{ v.default }}{% endif %}{% endif %}
|
||||
{%- if 'aliases' in v %}, aliases={{ v.aliases }}{% endif %}
|
||||
{%- if 'choices' in v %}, choices={{ v.choices }}{% endif %}),
|
||||
{% endfor %}
|
||||
),
|
||||
|
||||
# Optional arguments requirements
|
||||
module_kwargs = dict(
|
||||
required_if=[
|
||||
['action', 'rebuild', ['image']], # if need to rebuild image (only), the 'image' is required
|
||||
["state", "present", ["username", "user_roles"]], # for creating user 'user_roles' is required
|
||||
["state", "absent", ["username"]], # for state 'absent' only username is required
|
||||
],
|
||||
required_by=dict( # for weather and population 'city' is required to set
|
||||
weather=('city'),
|
||||
population=('city'),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
['use_cloud1', 'use_cloud2'] # can't run on both, choose only one to set
|
||||
],
|
||||
required_together=[
|
||||
['remove_image', 'image_name'] # if need to remove image, must to specify which one
|
||||
],
|
||||
required_one_of_args=[["password", "password_hash"]], # one of these args must be set
|
||||
supports_check_mode={{ check_mode_support }}, # good practice is to support check_mode
|
||||
)
|
||||
|
||||
# you main funciton is here
|
||||
def run(self):
|
||||
# do any arguments check if needed
|
||||
data = self.preliminary_checks()
|
||||
# check if we need to run or the resource is in desired state already
|
||||
must_run = self.check_mode_test()
|
||||
# if the resource is good
|
||||
if not must_run:
|
||||
# updated returned results if need
|
||||
self.results.update({"returning_data": some_data})
|
||||
# returning {changed: False, ...} because we didn't change resource
|
||||
self.exit_json(self.results)
|
||||
# do something if must to run the module
|
||||
self.execute()
|
||||
|
||||
def preliminary_checks(self):
|
||||
# you checks before running like arguments and options checks, etc
|
||||
return data
|
||||
|
||||
def check_mode_test(self):
|
||||
# check the idempotency - does module should do anything or
|
||||
# it's already in the desired state?
|
||||
return must_run
|
||||
|
||||
def execute(self):
|
||||
# doing here what should be done, using OpenstackSDK
|
||||
# for example actions for resource:
|
||||
# self.params['action'] = "rebuild"
|
||||
action_name = self.params['action'] + "_resource" # action_name='rebuild_resource'
|
||||
try:
|
||||
# find a method "rebuild_resource" in openstack SDK compute:
|
||||
func_name = getattr(self.conn.compute, action_name)
|
||||
# found self.conn.compute.rebuild_resource
|
||||
except AttributeError:
|
||||
self.fail_json(
|
||||
msg="Method %s wasn't found in OpenstackSDK compute" % action_name)
|
||||
summary = func_name(data) # summary = self.conn.compute.rebuild_resource(data)
|
||||
self.results.update({"returning_data": summary})
|
||||
# that's it, exiting, results will be returned from module automatically
|
||||
|
||||
# another option for states
|
||||
def execute_with_action_map(self):
|
||||
actions_map = {
|
||||
'start': self._start_resource,
|
||||
'stop': self._stop_resource,
|
||||
'restart': self._restart_resource,
|
||||
'absent': self._absent_resource,
|
||||
}
|
||||
summary = actions_map(self.params['action'])() # summary = self.start_resource()
|
||||
self.results.update({"changed": True, "data2return": summary})
|
||||
|
||||
def _start_resource(self, some_other_data):
|
||||
pass
|
||||
|
||||
def _stop_resource(self, some_other_data):
|
||||
pass
|
||||
|
||||
def _restart_resource(self, some_other_data):
|
||||
pass
|
||||
|
||||
def _absent_resource(self, some_other_data):
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
module = {{ module_name.split("_")|map("capitalize")|list|join("") }}Module()
|
||||
module()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,81 +0,0 @@
|
||||
##### PLEASE READ BEFORE #####
|
||||
|
||||
# Module format and documentation
|
||||
# https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_documenting.html#module-format-and-documentation
|
||||
|
||||
|
||||
module_name: server_manage
|
||||
author_name: 'Happy Ansible User'
|
||||
author_mail: dontwriteme@example.com
|
||||
module_short_description: "Doing something very useful"
|
||||
module_long_description: "Here is the place to release your inner writer"
|
||||
check_mode_support: True # good practice to support check_mode:
|
||||
# https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html#check-mode-dry-run
|
||||
|
||||
module_returns_example:
|
||||
image:
|
||||
description: Image inspection results for the image that was pulled, pushed, or built.
|
||||
returned: always # or 'success' in case of success only
|
||||
type: dict
|
||||
sample:
|
||||
Image Name: Sample Image
|
||||
Image ID: e6471d00796a13de8142c15d7ad3a44f
|
||||
Nested:
|
||||
images list:
|
||||
- data 1
|
||||
- image 1234
|
||||
boolean_1: True
|
||||
|
||||
options:
|
||||
optional_string:
|
||||
description:
|
||||
- This variable is set for having string argument, for example 'action'
|
||||
type: str
|
||||
required: true
|
||||
default: "my_lovely_action"
|
||||
choices:
|
||||
- allowed_option1
|
||||
- allowed_option1
|
||||
optional_boolean:
|
||||
description:
|
||||
- This variable is set for having a boolean argument, for example whether
|
||||
to wait for resource creation or not
|
||||
type: bool
|
||||
required: false # may be omitted if false
|
||||
# and no default because not required
|
||||
optional_integer:
|
||||
description:
|
||||
- This variable is set for having a integer argument, for example how many
|
||||
seconds to wait for the resource to come alive
|
||||
required: true
|
||||
default: 60
|
||||
type: int
|
||||
aliases: # sometimes we allow to pass the same option with different name
|
||||
- old_optional_integer_name
|
||||
- different_option_name
|
||||
optional_list:
|
||||
description:
|
||||
- This variable is set for having a list argument, for example files need
|
||||
to create with the resource
|
||||
type: list
|
||||
elements: str # type of elements of the list, can be dict, str, int, list
|
||||
optional_dictionary:
|
||||
description:
|
||||
- This variable is set for having a dictionary argument, for example to
|
||||
set environment variables or to pass more complex data to SDK
|
||||
required: true
|
||||
default: {}
|
||||
type: dict
|
||||
suboptions:
|
||||
suboption_1:
|
||||
description:
|
||||
- suboption_1 description, what it does
|
||||
type: str
|
||||
aliases:
|
||||
- suboption_1_another_name
|
||||
suboption_2:
|
||||
description:
|
||||
- suboption_2 description, what it does
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
115
docs/branching.md
Normal file
115
docs/branching.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Ansible OpenStack Collection and its branches
|
||||
|
||||
Our codebase has been split into two separate release series, `2.x.x` and `1.x.x`:
|
||||
|
||||
* `2.x.x` releases of Ansible OpenStack collection are compatible with [OpenStack SDK][openstacksdk] `1.x.x` and its
|
||||
release candidates `0.99.0` and later *only* (OpenStack Zed and later). Our [`master` branch][a-c-o-branch-master]
|
||||
tracks our `2.x.x` releases.
|
||||
* `1.x.x` releases of Ansible OpenStack collection are compatible with [OpenStack SDK][openstacksdk] `0.x.x` prior to
|
||||
`0.99.0` *only* (OpenStack Yoga and earlier). Our [`stable/1.0.0` branch][a-c-o-branch-stable-1-0-0] tracks our
|
||||
`1.x.x` releases.
|
||||
* `2.x.x` releases of Ansible OpenStack collection are not backward compatible to `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!
|
||||
|
||||
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.0`) 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][openstacksdk-release-notes-zed]. The Ansible OpenStack collection is heavily based on OpenStack SDK.
|
||||
With OpenStack SDK becoming backward incompatible, so does our Ansible OpenStack collection. For example, with
|
||||
openstacksdk `>=0.99.0` most Ansible modules return dictionaries instead `Munch` objects and many of their keys have
|
||||
been renamed. We simply lack the development resources to maintain a backward compatible interface in Ansible OpenStack
|
||||
collection across several SDK releases.
|
||||
|
||||
[a-c-o-branch-master]: https://opendev.org/openstack/ansible-collections-openstack/src/branch/master
|
||||
[a-c-o-branch-stable-1-0-0]: https://opendev.org/openstack/ansible-collections-openstack/src/branch/stable/1.0.0
|
||||
[ansible-tags]: https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html
|
||||
[openstacksdk-cloud-layer-stays]: https://meetings.opendev.org/irclogs/%23openstack-sdks/%23openstack-sdks.2022-04-27.log.html
|
||||
[openstacksdk-release-notes-zed]: https://docs.openstack.org/releasenotes/openstacksdk/zed.html
|
||||
[openstacksdk-to-dict]: https://opendev.org/openstack/openstacksdk/src/branch/master/openstack/resource.py
|
||||
[openstacksdk]: https://opendev.org/openstack/openstacksdk
|
||||
|
||||
## Notable changes between release series 2.x.x and 1.x.x
|
||||
|
||||
When we ported our collection to [openstacksdk][openstacksdk] `>=0.99.0`, a series of changes were applied to our
|
||||
`master` branch. We went through each module in our collection and did the following:
|
||||
|
||||
* Identify function calls which use [openstacksdk][openstacksdk]'s cloud layer, e.g. `self.conn.get_network()`. Change
|
||||
these calls to functions from openstacksdk's resource proxies, e.g. `self.conn.network.find_network()`, if possible.
|
||||
As a guideline use this decision tree:
|
||||
- If a functionality requires a single api call (to the OpenStack API), then use functions from openstacksdk's
|
||||
resource proxies.
|
||||
- If a functionality requires multiple api calls (to the OpenStack API), e.g. when creating and attaching a floating
|
||||
ip to a server, then use functions from openstacksdk's cloud layer.
|
||||
- When unsure which of openstacksdk's layers to use, then first go to resource proxies and then to its cloud layer.
|
||||
Mainly this applies to functions retrieving information, i.e. all calls where we get info about cloud resources
|
||||
should be changed to openstacksdk functions which return proxy resources.
|
||||
**Note**: Using openstacksdk's cloud layer for functionality which is not provided by openstacksdk's proxy layer is
|
||||
acceptable. [openstacksdk's cloud layer is not going away][openstacksdk-cloud-layer-stays]. For example, listing
|
||||
functions in openstacksdk's cloud layer such as `search_users()` often allow to filter results with function parameter
|
||||
`filters`. openstacksdk's proxy layer does not provide an equivalent and thus using `search_users()` is fine.
|
||||
* Functions in openstacksdk's cloud layer often have different return values then pre-0.99.0 releases. When return
|
||||
values have changed in any of the functions which a module uses, update `RETURN` variable. If a module has no `RETURN`
|
||||
variable, define it.
|
||||
* Only return data types such as lists or dictionaries to Ansible. For example, the return statement
|
||||
`self.exit_json(changed=False, floating_ips=floating_ips)` in module [`floating_ip_info`](
|
||||
../plugins/modules/floating_ip_info.py) shall return a list of `dict`'s. Use openstacksdk's `to_dict` function to
|
||||
convert resources to dictionaries. Setting its parameters such as `computed` to `False` will drop computed attributes
|
||||
from the resulting dict. Read [`to_dict`'s docstring][openstacksdk-to-dict] for more parameters. Using `to_dict` might
|
||||
change the return values of your Ansible module. Please document changes to return values in `RETURN`.
|
||||
* Older openstacksdk releases did not provide the `to_dict` function. We decided to allow breaking backward
|
||||
compatibility with release `2.x.x`, so workarounds such as `(o.to_dict() if hasattr(o, 'to_dict') else dict(o))` are
|
||||
not required anymore and shall be avoided.
|
||||
* Manually dropping attributes such as `location` or `link` from openstacksdk resources is no longer necessary.
|
||||
Workarounds such as
|
||||
```Python
|
||||
for raw in self.conn.block_storage.backups(**attrs):
|
||||
dt = raw.to_dict()
|
||||
dt.pop('location')
|
||||
data.append(dt)
|
||||
```
|
||||
are no longer necessary and can be removed.
|
||||
* Add tests to [ci/run-collection.yml](../ci/run-collection.yml) and [ci/roles](../ci/roles). Each module has a
|
||||
dedicated Ansible role with tests in `ci/roles`. Create one if no such directory exist.
|
||||
* With release of openstacksdk 0.99.0 most of our CI tests in [ci/](../ci/) failed. To prove that module patches
|
||||
actually fix issues all CI tests for unrelated broken modules have to be skipped. To run CI tests for patched modules
|
||||
only, temporarily list the [Ansible tags][ansible-tags] of all CI tests which should run in
|
||||
`vars: { tox_extra_args: ... }` of job `ansible-collections-openstack-functional-devstack-ansible` in `.zuul.yaml`
|
||||
([example](https://review.opendev.org/c/openstack/ansible-collections-openstack/+/825291/16/.zuul.yaml)) and send the
|
||||
patch for review. Once all CI tests are passing in Zuul CI, undo changes to [`.zuul.yaml`](../.zuul.yaml), i.e. revert
|
||||
changes to `tox_extra_args` and submit final patch for review.
|
||||
* ~~Cherry-pick or backport patches for `master` branch to `stable/1.0.0` branch. Both branches should divert only if
|
||||
necessary in order to keep maintainence of two separate branches simple. When applying patches to the `stable/1.0.0`
|
||||
branch, it is often necessary to make changes to not break backward compatibility on the `stable/1.0.0` branch. On
|
||||
`master` we use `.to_dict(computed=False)` which we have to change to `.to_dict(computed=True)` on `stable/1.0.0`. For
|
||||
example, this [patch for `master` branch](
|
||||
https://review.opendev.org/c/openstack/ansible-collections-openstack/+/828108) has been [tweaked and cherry-picked to
|
||||
`stable/1.0.0` branch](https://review.opendev.org/c/openstack/ansible-collections-openstack/+/836312).~~
|
||||
Backporting patches from `master` to `stable/1.0.0` branch have been abandoned due to lack of time and resources ⚠️
|
||||
* Version checks in modules are no longer necessary because we require openstacksdk >=0.99.0 globally. For example,
|
||||
drop `min_ver`/`max_ver` constraints on module arguments.
|
||||
* Rename module parameter names to the attribute names that openstacksdk uses, e.g. `shared` becomes `is_shared`. Keep
|
||||
old names as aliases for input backward compatibility.
|
||||
* Some modules have if-else branches for handling cases where a `name` is given. For most modules these can be dropped
|
||||
safely because names can be passed as a query parameter.
|
||||
* Some modules do not use `name` as module parameters for resource names. For example, `port` module had an attribute
|
||||
called `port` instead of `name`. Rename those attributes to `name` to be consistent with other modules and because
|
||||
openstacksdk is doing the same. Add old attribute names as aliases to keep input backward compatibility.
|
||||
* Replacing `self.conn.get_*` with `self.conn.*.find_*` functions provide a `ignore_missing=False` parameter. This
|
||||
allows to drop `self.fail_json()` calls in modules. Less code means less to maintain.
|
||||
* Some modules pass `ignore_missing=True` to `self.conn.*.find_*` functions and then fail if the return value is `None`.
|
||||
Often this code can be simplified by changing `ignore_missing` to `False` and dropping the if-else branches.
|
||||
* When module attribute that have choices, always doubt its values. The module code was probably written long ago and
|
||||
the choices given might be outdated. It might also make sense to drop the `choices` parameter completely when choices
|
||||
are to narrow and might soon be outdated again.
|
||||
* Check comments whether they are still relevant.
|
||||
* Sanity check existing integration tests. For example, return values of module calls should be tested else running a
|
||||
test could be useless in the first place.
|
||||
* Most functions in openstacksdk's cloud layer no longer return `Munch` objects. Instead they return resources which
|
||||
should be converted to dictionaries. Update `RETURN` docs in modules, e.g. change from `type: complex` to
|
||||
`type: dict`.
|
||||
* Move list of expected module results to role defaults, e.g. define a variable `expected_fields`. This enables easier
|
||||
reuse.
|
||||
* Following and applying our [development guide](contributing.md) and [review guide](reviewing.md)
|
||||
189
docs/contributing.md
Normal file
189
docs/contributing.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Development Guide for Ansible OpenStack Collection
|
||||
|
||||
Ansible OpenStack collection is a set of Ansible modules for interacting with the OpenStack API as either an admin or an
|
||||
end user.
|
||||
|
||||
We, and the OpenStack community in general, use OpenDev for its development. Patches are submitted to [OpenDev Gerrit][
|
||||
opendev-gerrit]. Pull requests submitted through GitHub will be ignored. Please read OpenStack's [Developer Workflow][
|
||||
openstack-developer-workflow] for details.
|
||||
|
||||
For hacking on the Ansible OpenStack collection it helps to [prepare a DevStack environment](devstack.md) first.
|
||||
|
||||
## Hosting
|
||||
|
||||
* [Bug tracker][storyboard]
|
||||
* [Mailing list `openstack-discuss@lists.openstack.org`][openstack-discuss].
|
||||
Prefix subjects with `[aoc]` or `[aco]` for faster responses.
|
||||
* [Code Hosting][opendev-a-c-o]
|
||||
* [Code Review][gerrit-a-c-o]
|
||||
|
||||
## Branches
|
||||
|
||||
For rationale behind our `master` and `stable/1.0.0` branches and details on our relation to [openstacksdk][
|
||||
openstacksdk], please read our [branching docs](branching.md).
|
||||
|
||||
## Examples
|
||||
|
||||
* For an example on how to write a `*_info` module, have a look at module
|
||||
[`openstack.cloud.neutron_rbac_policies_info`](../plugins/modules/neutron_rbac_policies_info.py).
|
||||
* For an example on how to write a regular non-`*_info` module, have a look at module
|
||||
[`openstack.cloud.neutron_rbac_policy`](../plugins/modules/neutron_rbac_policy.py) or any other module which
|
||||
contains a `_will_change` method.
|
||||
* Do NOT use modules which define a `_system_state_change` function as examples, because they often do not properly
|
||||
define Ansible's check mode, idempotency and/or updates. Refer to modules which define a `_will_change` function
|
||||
instead.
|
||||
|
||||
## Naming
|
||||
|
||||
* This collection is named `openstack.cloud`. There is no need for further namespace prefixing.
|
||||
* Name any module that a cloud consumer would expect from [openstackclient (OSC)][openstackclient], for example `server`
|
||||
instead of `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 ip address
|
||||
are managed by Nova or Neutron.
|
||||
|
||||
## 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`, that should be returned instead of
|
||||
the `id`.
|
||||
* Modules should return a value of type `dict`, `list` or other primitive data types. For example, `floating_ips` in
|
||||
`self.exit_json(changed=False, floating_ips=floating_ips)` should to be a list of `dict`s. Use `to_dict()` on
|
||||
[openstacksdk][openstacksdk] objects to convert resources to dictionaries. Setting its parameters such as `computed`
|
||||
to `False` will drop computed attributes from the resulting dict. Read [`to_dict`'s docstring][openstacksdk-to-dict]
|
||||
for more parameters.
|
||||
* Module results have to be documented in `RETURN` docstring.
|
||||
* We should document which attribute cannot be updated in `DOCUMENTATION` variable. For example, insert
|
||||
`'This attribute cannot be updated.'` to `DOCUMENTATION` like we did for the `server` module and others.
|
||||
* Sorting module options in `DOCUMENTATION`, attributes in `RETURN`, entries in `argument_spec` and expected fields in
|
||||
integration tests will make reviewing easier and faster.
|
||||
|
||||
## Interoperability
|
||||
|
||||
* It should be assumed that the cloud consumer does not know details about the deployment choices their cloud provider
|
||||
made. A best effort should be made to present one sane interface to the Ansible user regardless of deployer choices.
|
||||
* It should be assumed that a user may have more than one cloud account that they wish to combine as part of a single
|
||||
Ansible-managed infrastructure.
|
||||
* All modules should work appropriately against all existing versions of OpenStack regardless of upstream EOL status.
|
||||
The reason for this is that the Ansible modules are for consumers of cloud APIs who are not in a position to impact
|
||||
what version of OpenStack their cloud provider is running. It is known that there are OpenStack Public Clouds running
|
||||
rather old versions of OpenStack, but from a user point of view the Ansible modules can still support these users
|
||||
without impacting use of more modern versions.
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
* Modules should
|
||||
+ be idempotent (not being idempotent requires a solid reason),
|
||||
+ return whether something has `changed`,
|
||||
+ support `check mode`,
|
||||
+ be based on (be subclasses of) `OpenStackModule` in
|
||||
`ansible_collections.openstack.cloud.plugins.module_utils.openstack`,
|
||||
+ should include `extends_documentation_fragment: openstack` in their `DOCUMENTATION` docstring,
|
||||
+ be registered in `meta/action_groups.yml` for enabling the variables to be set in
|
||||
[group level][ansible-module-defaults].
|
||||
* Complex functionality, cloud interaction or interoperability code should be moved to [openstacksdk][openstacksdk].
|
||||
* OpenStack API interactions should happen via [openstacksdk][openstacksdk] and not via OpenStack component libraries.
|
||||
The OpenStack component libraries do no have end users as a primary audience, they are for intra-server communication.
|
||||
* When a resource exist and should be deleted (absent), then pass the resource to the `delete_*` function, not its name.
|
||||
Passing a name requires openstacksdk to find that resource again, doing a unnecessary api call, because we queried the
|
||||
resource before.
|
||||
* `*_info` modules never raise exceptions when resources cannot be found. When resources cannot be found, then a
|
||||
`*_info` module returns an empty list instead. For example, module `openstack.cloud.neutron_rbac_policies_info` will
|
||||
return an empty list when no project with name given in module parameter `project` can be found.
|
||||
* When a id is given in `*_info` modules, then we do not need nor want extra code to handle that. Instead most
|
||||
[openstacksdk][openstacksdk] resources allow to pass ids as query arguments to OpenStack API. For example,
|
||||
`identity.identity_providers()` can be used for both cases: Where an id is given and where no id is given. No need to
|
||||
call `get_identity_provider()`.
|
||||
* `EXAMPLES` docstring in modules (and Ansible's own modules) consist of a list of tasks. They do not contain YAML
|
||||
directives end marker line (---) and do not define playbooks (e.g. hosts keyword). They shall be simple, e.g. do not
|
||||
do fancy loops, heavy use of variables or use Ansible directives for no apparent reason such as ignore_errors or
|
||||
register.
|
||||
* `self.params.get('...')` can be replaced with `self.params['...']` because parameters from `argument_spec` will always
|
||||
be in `self.params`. If not defined differently, they have a default value of `None`.
|
||||
* Writing code to check that some options cannot be updated and to fail if user still tries to update that value is most
|
||||
often not worth it. It would require much more code to catch all cases where updates are impossible and we would have
|
||||
to implement it consistently across modules. Atm we are fine with documenting which attribute cannot be updated in
|
||||
`DOCUMENTATION` variable. We could simply drop these checks and insert `'This attribute cannot be updated.'` to
|
||||
`DOCUMENTATION` like we did for the server module and others.
|
||||
* [openstacksdk][openstacksdk] functions often accept IDs but no names, e.g. `find_address_scope()` and
|
||||
`create_address_scope()` accept a `project_id` parameter. Most modules in our collection use names for finding
|
||||
resources, so we want to support the same for resources attributes such as `project_id` in `AddressScope`.
|
||||
* Constraints for module parameters and error handling can often be implemented in `argument_spec` or `module_kwargs`
|
||||
`module_kwargs` allows to define dependencies between module options such as [`mutually_exclusive`,
|
||||
`required_together`, `required_if` etc.][ansible-argument-spec-dependencies].
|
||||
* When using [openstacksdk][openstacksdk]'s `find_*` functions (`self.conn.*.find_*`), then pass `ignore_missing=False`
|
||||
instead of checking its return value and failing with `self.fail_json()` if it is `None`.
|
||||
* Use module option names which match attribute names used in [openstacksdk][openstacksdk], e.g. use `is_shared` instead
|
||||
of `shared`. When refactoring modules, keep old option names as aliases to keep backward compatibility. Using
|
||||
openstacksdk names provides two benefits:
|
||||
- The module inputs and outputs do match, are consistent and thus the module is easier to use.
|
||||
- Most code for filters and query arguments can be replaced with loops. [This patch for floating_ip_info has some
|
||||
ideas for how to write loops](https://review.opendev.org/c/openstack/ansible-collections-openstack/+/828613).
|
||||
* Use functions from [openstacksdk][openstacksdk]'s proxy layer instead of its cloud layer, if possible. For example,
|
||||
use `self.conn.network.find_network()`, not `self.conn.get_network()`. As a guideline use this decision tree:
|
||||
- If a functionality requires a single api call (to the OpenStack API), then use functions from openstacksdk's proxy
|
||||
layer.
|
||||
- If a functionality requires several api calls (to the OpenStack API), e.g. when creating and attaching a floating ip
|
||||
to a server, then use functions from openstacksdk's cloud layer.
|
||||
- When unsure which of openstacksdk's layers to use, then first go to proxy layer, then to its cloud layer and if this
|
||||
is not sufficient, then use its resource layer. Mainly, this applies to functions retrieving information, i.e. all
|
||||
calls where we get info about cloud resources should be changed to openstacksdk functions which return proxy
|
||||
resources.
|
||||
- It is perfectly fine to use openstacksdk's cloud layer for functionality which is not provided by openstacksdk's
|
||||
proxy layer. [SDK's cloud layer is not going away][openstacksdk-cloud-layer-stays].
|
||||
For example, `list_*` functions from openstacksdk's cloud layer such as `search_users()` allow to filter retrieved
|
||||
results with function parameter `filters`. openstacksdk's proxy layer does not provide an equivalent and thus the
|
||||
use of `search_users()` is perfectly fine.
|
||||
|
||||
## Testing
|
||||
|
||||
* Modules have to be tested with CI integration tests (if possible).
|
||||
* Each module has a corresponding Ansible role containing integration tests in [`ci/roles`](../ci/roles) directory.
|
||||
* Ensure role names of integration tests in [`ci/roles`](../ci/roles) match the module names.
|
||||
Only exception are `*_info` modules: Their integration tests are located in the same Ansible roles as their
|
||||
non-`*_info` equivalents (to reduce redundant code). For example, tests for both modules `federation_mapping` and
|
||||
`federation_mapping_info` can be found in role `federation_mapping`.
|
||||
* Zuul CI jobs are defined in [`.zuul.yaml`](../.zuul.yaml).
|
||||
* Add assertions on return values from Ansible modules in integration tests. For an example, refer to
|
||||
[`ci/roles/floating_ip/tasks/main.yml`](../ci/roles/floating_ip/tasks/main.yml).
|
||||
We need those checks to validate return values from [openstacksdk][openstacksdk], which might change across releases.
|
||||
Adding those assertions will be done in minutes, while checking the output manually during code reviews takes much
|
||||
more time.
|
||||
* Our Zuul CI jobs will run `ansible-test` for sanity checking.
|
||||
* Use `tox -elinters_latest` to run various linters against your code.
|
||||
|
||||
## Upload
|
||||
|
||||
* Study our [Review Guidelines](reviewing.md) before submitting a patch.
|
||||
* Use Gerrit's work-in-progress feature to mark the status of the patch. A minus workflow (-w) will be reset when a new
|
||||
patchset is uploaded and hence easy to miss.
|
||||
* When you edit a patch, first rebase your patch on top of the current branch. Sometimes we replace code in all modules
|
||||
which might cause merge conflicts for you otherwise. For example, we dropped all options with default values from
|
||||
`argument_spec` such as `required=False`.
|
||||
|
||||
## Release
|
||||
|
||||
Read [Release Guide](releasing.md) on how to publish new releases.
|
||||
|
||||
## Permissions
|
||||
|
||||
* Only [members of group `ansible-collections-openstack-core`][group-a-c-o-core] are allowed to merge patches.
|
||||
* Only [members of group `ansible-collections-openstack-release`][group-a-c-o-release] are allowed to push tags and
|
||||
trigger our release job `ansible-collections-openstack-release` in [galaxy.yml](../galaxy.yml).
|
||||
* Only members of `openstack` namespace in Ansible Galaxy are allowed to apply changes to meta properties of Ansible
|
||||
collection [`openstack.cloud`][ansible-galaxy-openstack-cloud] on Ansible Galaxy.
|
||||
|
||||
[ansible-argument-spec-dependencies]: https://docs.ansible.com/ansible/latest/dev_guide/developing_program_flow_modules.html#argument-spec-dependencies
|
||||
[ansible-galaxy-openstack-cloud]: https://galaxy.ansible.com/openstack/cloud
|
||||
[ansible-module-defaults]: https://docs.ansible.com/ansible/latest/user_guide/playbooks_module_defaults.html
|
||||
[gerrit-a-c-o]: https://review.opendev.org/q/status:open+project:openstack/ansible-collections-openstack
|
||||
[group-a-c-o-core]: https://review.opendev.org/admin/groups/0e01228e912733e8b9a8d957631e41665aa0ffbd,members
|
||||
[group-a-c-o-release]: https://review.opendev.org/admin/groups/8bca2018f3710f94374aee4b3c9771b9ff0a2254,members
|
||||
[opendev-a-c-o]: https://opendev.org/openstack/ansible-collections-openstack
|
||||
[opendev-gerrit]: https://review.opendev.org/
|
||||
[openstack-developer-workflow]: https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
[openstack-discuss]: http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
|
||||
[openstackclient]: https://docs.openstack.org/python-openstackclient/latest/
|
||||
[openstacksdk-cloud-layer-stays]: https://meetings.opendev.org/irclogs/%23openstack-sdks/%23openstack-sdks.2022-04-27.log.html
|
||||
[openstacksdk-to-dict]: https://opendev.org/openstack/openstacksdk/src/branch/master/openstack/resource.py
|
||||
[openstacksdk]: https://opendev.org/openstack/openstacksdk
|
||||
[storyboard]: https://storyboard.openstack.org/#!/project/openstack/ansible-collections-openstack
|
||||
107
docs/devstack.md
Normal file
107
docs/devstack.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Preparing a DevStack environment for Ansible collection development
|
||||
|
||||
For developing on the Ansible OpenStack collection, it helps to install DevStack and two Python [`virtualenv`][
|
||||
virtualenv]s, one with [openstacksdk][openstacksdk] `<0.99.0` and one with [openstacksdk][openstacksdk] `>=1.0.0` (or
|
||||
one of its release candidates `>=0.99.0`). The first is for patches against our `stable/1.0.0` branch of the collection,
|
||||
while the newer openstacksdk is for patches against our `master` branch.
|
||||
|
||||
First, [follow DevStack's guide][devstack] to set up DevStack on a virtual machine. An Ansible inventory and a playbook
|
||||
to set up your own local DevStack as a libvirt domain can be found in Ansible collection [`jm1.cloudy`][jm1-cloudy],
|
||||
look for host `lvrt-lcl-session-srv-200-devstack`.
|
||||
|
||||
**Beware:** DevStack's purpose is to be set up quickly and destroyed after development or testing is done. It cannot
|
||||
be rebooted safely or upgraded easily.
|
||||
|
||||
Some Ansible modules and unit tests in the Ansible OpenStack collection require additional DevStack plugins which
|
||||
are not enabled by default. [Plugins are enabled in DevStack's `local.conf`][devstack-plugins]. Examples:
|
||||
|
||||
- Use the DevStack configuration which the Zuul CI jobs are applying when testing the Ansible OpenStack collection. For
|
||||
example, go to the logs of job [`ansible-collections-openstack-functional-devstack`][devstack-jobs] and use file
|
||||
`controller/logs/local_conf.txt` as your `local.conf` for DevStack.
|
||||
- https://gist.github.com/sshnaidm/43ca23c3f23bd6015d18868ac7405a13
|
||||
- https://paste.opendev.org/show/812460/
|
||||
|
||||
For a list of plugins refer to [DevStack's plugin registry][devstack-plugin-registry].
|
||||
|
||||
Next, prepare two Python [`virtualenv`][virtualenv]s, one with [openstacksdk][openstacksdk] `<0.99.0` and one with
|
||||
[openstacksdk][openstacksdk] `>=1.0.0` (or one of its release candidates `>=0.99.0`):
|
||||
|
||||
```sh
|
||||
# DevStack is presumed to be installed on the development machine
|
||||
# and its configuration file available at ~/devstack/openrc
|
||||
|
||||
git clone https://opendev.org/openstack/ansible-collections-openstack.git
|
||||
mkdir -p ~/.ansible/collections/ansible_collections/openstack/
|
||||
ln -s ansible-collections-openstack ~/.ansible/collections/ansible_collections/openstack/cloud
|
||||
|
||||
# Prepare environment for developing patches against
|
||||
# Ansible OpenStack collection 2.x.x and openstacksdk>=0.99.0
|
||||
cd ansible-collections-openstack/
|
||||
git checkout master
|
||||
virtualenv -p python3 ~/.local/share/virtualenv/ansible-openstacksdk-1
|
||||
source ~/.local/share/virtualenv/ansible-openstacksdk-1/bin/activate
|
||||
pip install -r test-requirements.txt
|
||||
pip install git+https://opendev.org/openstack/openstacksdk
|
||||
pip install ipython
|
||||
source ~/devstack/openrc admin admin
|
||||
ipython
|
||||
|
||||
cd ..
|
||||
|
||||
# Prepare environment for developing patches against
|
||||
# Ansible OpenStack collection 1.x.x and openstacksdk<0.99.0
|
||||
virtualenv -p python3 ~/.local/share/virtualenv/ansible-openstacksdk-0
|
||||
source ~/.local/share/virtualenv/ansible-openstacksdk-0/bin/activate
|
||||
cd ansible-collections-openstack/
|
||||
git checkout stable/1.0.0
|
||||
pip install -r test-requirements.txt
|
||||
pip install 'openstacksdk<0.99.0'
|
||||
pip install ipython
|
||||
source ~/devstack/openrc admin admin
|
||||
ipython
|
||||
```
|
||||
|
||||
The first IPython instance uses openstacksdk >=0.99.0 and is for developing at the 2.x.x series of the Ansible OpenStack
|
||||
collection. The second IPython instance uses openstacksdk <0.99.0 and is suited for the 1.x.x series of the collection.
|
||||
For example, type in each IPython instance:
|
||||
|
||||
```python
|
||||
import openstack
|
||||
conn = openstack.connect()
|
||||
|
||||
# optional
|
||||
openstack.enable_logging(debug=True)
|
||||
|
||||
# and start hacking..
|
||||
list(conn.network.ips())[0].to_dict(computed=False)
|
||||
```
|
||||
|
||||
To run the unit tests of the collection, run this in a Bash shell:
|
||||
|
||||
```sh
|
||||
SDK_VER=$(python -c "import openstack; print(openstack.version.__version__)")
|
||||
ansible-playbook -vvv ci/run-collection.yml -e "sdk_version=${SDK_VER} cloud=devstack-admin cloud_alt=devstack-alt"
|
||||
```
|
||||
|
||||
Use `ansible-playbook`'s `--tags` and `--skip-tags` parameters to skip CI tests. For a list of available tags, refer to
|
||||
[`ci/run-collection.yml`](../ci/run-collection.yml).
|
||||
|
||||
Or run Ansible modules individually:
|
||||
|
||||
```sh
|
||||
ansible localhost -m openstack.cloud.floating_ip -a 'server=ansible_server1 wait=true' -vvv
|
||||
```
|
||||
|
||||
When submitting a patch with `git review`, our Zuul CI jobs will test your changes against different versions of
|
||||
openstacksdk, Ansible and DevStack. Refer to [`.zuul.yaml`](../.zuul.yaml) for a complete view of all CI jobs. To
|
||||
trigger experimental jobs, write a comment in Gerrit which contains `check experimental`.
|
||||
|
||||
Happy hacking!
|
||||
|
||||
[devstack-jobs]: https://zuul.opendev.org/t/openstack/builds?job_name=ansible-collections-openstack-functional-devstack&project=openstack/ansible-collections-openstack
|
||||
[devstack-plugin-registry]: https://docs.openstack.org/devstack/latest/plugin-registry.html
|
||||
[devstack-plugins]: https://docs.openstack.org/devstack/latest/plugins.html
|
||||
[devstack]: https://docs.openstack.org/devstack/latest/
|
||||
[jm1-cloudy]: https://github.com/JM1/ansible-collection-jm1-cloudy
|
||||
[openstacksdk]: https://opendev.org/openstack/openstacksdk/
|
||||
[virtualenv]: https://virtualenv.pypa.io/en/latest/
|
||||
@@ -1,68 +0,0 @@
|
||||
.. _OpenStack_module_development:
|
||||
|
||||
OpenStack Ansible Modules
|
||||
=========================
|
||||
|
||||
These are a set of modules for interacting with the OpenStack API as either an admin
|
||||
or an end user.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
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:
|
||||
``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.
|
||||
|
||||
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
|
||||
----------------
|
||||
|
||||
* It should be assumed that the cloud consumer does not know
|
||||
details about the deployment choices their cloud provider made. A best
|
||||
effort should be made to present one sane interface to the Ansible user
|
||||
regardless of deployer choices.
|
||||
* It should be assumed that a user may have more than one cloud account that
|
||||
they wish to combine as part of a single Ansible-managed infrastructure.
|
||||
* All modules should work appropriately against all existing versions of
|
||||
OpenStack regardless of upstream EOL status. The reason for this is that
|
||||
the Ansible modules are for consumers of cloud APIs who are not in a
|
||||
position to impact what version of OpenStack their cloud provider is
|
||||
running. It is known that there are OpenStack Public Clouds running rather
|
||||
old versions of OpenStack, but from a user point of view the Ansible
|
||||
modules can still support these users without impacting use of more
|
||||
modern versions.
|
||||
|
||||
Libraries
|
||||
---------
|
||||
|
||||
* All modules should use ``OpenStackModule`` from
|
||||
``ansible_collections.openstack.cloud.plugins.module_utils.openstack``
|
||||
as their base class.
|
||||
* All modules should include ``extends_documentation_fragment: openstack``.
|
||||
* 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
|
||||
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
|
||||
variables to be set in `group level
|
||||
<https://docs.ansible.com/ansible/latest/user_guide/playbooks_module_defaults.html>`_.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
* Integration testing is currently done in `OpenStack's CI system
|
||||
<https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/.zuul.yaml>`_
|
||||
125
docs/releasing.md
Normal file
125
docs/releasing.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Release process for Ansible OpenStack collection
|
||||
|
||||
## Publishing to Ansible Galaxy
|
||||
|
||||
1. Create entry in [changelog.yaml](../changelogs/changelog.yaml) with commits since last release.
|
||||
* Modules should be in a separate section `modules`
|
||||
* Bugfixes and minor changes in their sections
|
||||
2. Change version in [galaxy.yml](../galaxy.yml). Apply [Semantic Versioning](https://semver.org/):
|
||||
* Increase major version for breaking changes or modules were removed
|
||||
* Increase minor version when modules were added
|
||||
* Increase patch version for bugfixes
|
||||
3. Run `antsibull-changelog release` command (run `pip install antsibull` before) to generate [CHANGELOG.rst](
|
||||
../CHANGELOG.rst) and verify correctness of generated files.
|
||||
4. Commit changes to `changelog.yaml` and `galaxy.yml`, submit patch and wait until it has been merged
|
||||
5. Tag the release with version as it's described in [OpenStack docs](
|
||||
https://docs.opendev.org/opendev/infra-manual/latest/drivers.html#tagging-a-release):
|
||||
* [Make sure you have a valid GnuPG key pair](
|
||||
https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key)
|
||||
* `git checkout <your_branch>`
|
||||
* `git pull --ff-only`
|
||||
* `git tag -s <version number>` where `<version number>` is your tag
|
||||
* `git push gerrit <version number>`
|
||||
6. When your tag has been pushed in the previous step, our release job `ansible-collections-openstack-release`, defined
|
||||
in [galaxy.yml](../galaxy.yml), will run automatically and publish a new release with your tag to [Ansible Galaxy](
|
||||
https://galaxy.ansible.com/openstack/cloud). When it has finished, its status and logs can be accessed on [Zuul CI's
|
||||
builds page](https://zuul.opendev.org/t/openstack/builds?job_name=ansible-collections-openstack-release).
|
||||
7. When release job `ansible-collections-openstack-release` has failed, you can manually build the collection locally
|
||||
and publish your release to Ansible Galaxy:
|
||||
* `git checkout <version number>` where `<version number>` is your tag
|
||||
* Delete untracked files and directories with `git clean -n; git clean -fd`
|
||||
* Build collection with `ansible-galaxy`, for example:
|
||||
```sh
|
||||
ansible-galaxy collection build --force --output-path /path/to/collection/dir
|
||||
```
|
||||
* On success you will find a `*.tar.gz` file in `/path/to/collection/dir`, e.g. `openstack-cloud-1.5.0.tar.gz`
|
||||
* Go to [your content page on Ansible Galaxy](https://galaxy.ansible.com/my-content/namespaces), open namespace
|
||||
`openstack`, click on `Upload New Version` and upload your release `*.tar.gz`, e.g. `openstack-cloud-1.5.0.tar.gz`.
|
||||
Push collection tarballs to the `openstack.cloud` namespace requires membership in `openstack` namespace on Ansible
|
||||
Galaxy.
|
||||
* Instead of using Ansible Galaxy web interface, you could also upload your release from cli. For example:
|
||||
```sh
|
||||
ansible-galaxy collection publish --token $API_GALAXY_TOKEN -v /path/to/openstack-cloud-1.5.0.tar.gz
|
||||
```
|
||||
where `$API_GALAXY_TOKEN` is your API key from [Ansible Galaxy](https://galaxy.ansible.com/me/preferences).
|
||||
* [Monitor import progress on Ansible Galaxy](https://galaxy.ansible.com/my-imports/) and act accordingly to issues.
|
||||
8. Announce new release to [The Bullhorn](https://github.com/ansible/community/wiki/News#the-bullhorn): Join
|
||||
[Ansible Social room on Matrix](https://matrix.to/#/#social:ansible.com) and mention [newsbot](
|
||||
https://matrix.to/#/@newsbot:ansible.im) to have your news item tagged for review for the next issue!
|
||||
|
||||
## Publishing to Fedora
|
||||
|
||||
**NOTE:** Before publishing an updated RPM for Fedora or RDO, contact Alfredo Moralejo Alonso <amoralej@redhat.com>
|
||||
(amoralej) or Joel Capitao <jcapitao@redhat.com> (jcapitao[m]) in `#rdo` on [OFTC IRC](https://www.oftc.net/) about the
|
||||
latest release process.
|
||||
|
||||
**NOTE:** If your username is in Fedora's `admins` group, you can push your commit directly to Fedora's repository for
|
||||
Ansible OpenStack collection. Otherwise you will have to open pull requests to sent changes.
|
||||
|
||||
1. Get familiar with packaging for Fedora. Useful resources are:
|
||||
* [Fedora's Package Update Guide](https://docs.fedoraproject.org/en-US/package-maintainers/Package_Update_Guide/)
|
||||
* [Fedora package source for Ansible OpenStack collection](
|
||||
https://src.fedoraproject.org/rpms/ansible-collections-openstack)
|
||||
* [Koji page for `ansible-collections-openstack`](https://koji.fedoraproject.org/koji/packageinfo?packageID=33611)
|
||||
* [Bodhi's page `Create New Update`](https://bodhi.fedoraproject.org/updates/new)
|
||||
2. Install all necessary packaging tools, mainly `fedpkg`.
|
||||
3. Create a scratch space with `mkdir fedora-scm`.
|
||||
4. Fork Fedora repository [rpms/ansible-collections-openstack](
|
||||
https://src.fedoraproject.org/rpms/ansible-collections-openstack).
|
||||
5. Clone [rpms/ansible-collections-openstack](https://src.fedoraproject.org/rpms/ansible-collections-openstack) with
|
||||
`fedpkg clone rpms/ansible-collections-openstack`. Or clone your forked repository (something like
|
||||
`https://src.fedoraproject.org/fork/sshnaidm/rpms/ansible-collections-openstack`) with
|
||||
`fedpkg clone forks/sshnaidm/rpms/ansible-collections-openstack` where `sshnaidm` has to be replaced with your
|
||||
username.
|
||||
6. `cd ansible-collections-openstack` and go to branch `rawhide` with `fedpkg switch-branch rawhide`.
|
||||
7. Download new collection sources from Ansible Galaxy using
|
||||
`wget https://galaxy.ansible.com/download/openstack-cloud-<version_tag>.tar.gz` where `<version_tag>` is a your new
|
||||
version, e.g. `1.10.0`. Or run `spectool -g *.spec` *after* having changed the `*.spec` file in the next step.
|
||||
8. Bump version in `*.spec` file as in this [example for `1.9.4`](
|
||||
https://src.fedoraproject.org/rpms/ansible-collection-containers-podman/c/6dc5eb79a3aa082e062768993bed66675ff9d520):
|
||||
```diff
|
||||
+Version: <version-tag>
|
||||
+Release: 1%{?dist}
|
||||
```
|
||||
and add changelog, sort of:
|
||||
```diff
|
||||
+* Tue Jun 08 2021 Sagi Shnaidman <sshnaidm@redhat.com> - <version-tag>-1
|
||||
+- Bump to <version-tag>-1
|
||||
```
|
||||
9. Upload sources you downloaded before with `fedpkg new-sources <version-tag>.tar.gz`.
|
||||
10. Optionally check build with `fedpkg mockbuild`.
|
||||
11. Verify and commit updated `*.spec` file with:
|
||||
```sh
|
||||
fedpkg diff
|
||||
fedpkg lint # run linters against your changes
|
||||
fedpkg commit # with message such as 'Bumped Ansible OpenStack collection to <version-tag>'
|
||||
```
|
||||
12. Push changes for `rawhide` with `fedpkg push`.
|
||||
13. Ask Koji to build your package with `fedpkg build`.
|
||||
14. Optionally check [Koji's page for `ansible-collections-openstack`](
|
||||
https://koji.fedoraproject.org/koji/packageinfo?packageID=33611).
|
||||
15. Repeat release process for older Fedora branches such as Fedora 36 aka `f36`:
|
||||
```sh
|
||||
fedpkg switch-branch f36
|
||||
git merge rawhide
|
||||
fedpkg push
|
||||
fedpkg build
|
||||
fedpkg update # or use Bodhi's page "Create New Update" at https://bodhi.fedoraproject.org/updates/new
|
||||
```
|
||||
|
||||
## Publishing to RDO
|
||||
|
||||
**NOTE:** Before publishing an updated RPM for Fedora or RDO, contact Alfredo Moralejo Alonso <amoralej@redhat.com>
|
||||
(amoralej) or Joel Capitao <jcapitao@redhat.com> (jcapitao[m]) in `#rdo` on [OFTC IRC](https://www.oftc.net/) about the
|
||||
latest release process.
|
||||
|
||||
[All `master` branches on RDO trunk](https://trunk.rdoproject.org) consume code from the `master` branch of the Ansible
|
||||
OpenStack collection. Its RPM is (re)build whenever a new patch has been merged to the collection repository. Afterwards
|
||||
[it is promoted as any other TripleO components in `client` component CI](
|
||||
https://docs.openstack.org/tripleo-docs/latest/ci/stages-overview.html).
|
||||
|
||||
To update stable RDO branches such as [`CentOS 9 Zed`](https://trunk.rdoproject.org/centos9-zed/), patches have to be
|
||||
submitted to CentOS Cloud SIG repositories. In this case, create a patch for stable branches such as `wallaby-rdo`, and
|
||||
`ussuri-rdo` at [ansible-collections-openstack-distgit](
|
||||
https://github.com/rdo-packages/ansible-collections-openstack-distgit). [Example](
|
||||
https://review.rdoproject.org/r/c/openstack/ansible-collections-openstack-distgit/+/34282).
|
||||
66
docs/reviewing.md
Normal file
66
docs/reviewing.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Reviews
|
||||
|
||||
How to do a review? What to look for when reviewing patches?
|
||||
|
||||
* Should functionality be implemented in Ansible modules or in openstacksdk? Ansible modules should only be "wrappers"
|
||||
for functionality in openstacksdk. Big code chunks are a good indicator that functionality should better be moved to
|
||||
openstacksdk.
|
||||
* For each function call(s) and code section which has been refactored, does the new code return the same results?
|
||||
Pay special attention whenever a function from openstacksdk's cloud layer has been replaced because those functions
|
||||
often have different semantics than functions of SDK's proxy layer.
|
||||
* Can API calls (to OpenStack API, not openstacksdk API) be reduced any further to improve performance?
|
||||
* Can calls to OpenStack API be tweaked to return less data?
|
||||
For example, listing calls such as `image.images()` or `network.networks()` provide filters to reduce the number of
|
||||
returned values.
|
||||
* Sanity check `argument_spec` and `module_kwargs`. Some modules try to be clever and add checks to fail early instead
|
||||
of letting `openstacksdk` or OpenStack API handle incompatible arguments.
|
||||
* Are `choices` in module attributes apropriate? Sometimes it makes sense to get rid of the choices because the choices
|
||||
are simply to narrow and might soon be outdated again.
|
||||
* Are `choices` in module attributes still valid? Module code might be written long ago and thus the choices might be
|
||||
horrible outdated.
|
||||
* Does a module use `name` as module options for resource names instead of e.g. `port` in `port` module? Rename those
|
||||
attributes to `name` to be consistent with other modules and with openstacksdk. When refactoring a module, then add
|
||||
the old attribute as an alias to keep backward compatibility.
|
||||
* Does the module have integration tests in `ci/roles`?
|
||||
* Is documentation in `DOCUMENTATION`, `RETURN` and `EXAMPLES` up to date?
|
||||
* Does `RETURN` list all values which are returned by the module?
|
||||
* Are descriptions, keys, names, types etc. in `RETURN` up to date and sorted?
|
||||
- For example, [`type: complex` often can be changed to `type: list` / `elements: dict`](
|
||||
https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_documenting.html).
|
||||
- `returned: always, but can be null` often has to be changed to `returned: always, but can be empty` or shorter
|
||||
`returned: always`.
|
||||
- Are there any values in `RETURN` which are not returned by OpenStack SDK any longer?
|
||||
- Module return value documentation can be found in [OpenStack SDK docs](
|
||||
https://docs.openstack.org/openstacksdk/latest/), e.g. [Identity v3 API](
|
||||
https://docs.openstack.org/openstacksdk/latest/user/proxies/identity_v3.html).
|
||||
For more detailed descriptions on return values refer to [OpenStack API](https://docs.openstack.org/api-ref/).
|
||||
* Do integration tests have assertions of module's return values?
|
||||
* Does `RETURN` documentation and assertions in integration tests match?
|
||||
* Does `RETURN` documentation and `self.exit_json()` statements match?
|
||||
* Do all modules use `to_dict(computed=False)` before returning values?
|
||||
* Because `id` is already part of most resource dictionaries returned from modules, we can safely drop dedicated `id`
|
||||
attributes in `self.exit_json()` calls. We will not loose data and we break backward compatibility anyway.
|
||||
* Is `EXAMPLES` documentation up to date?
|
||||
When module arguments have been changed, examples have to be updated as well.
|
||||
* Do integration tests execute successfully in your local dev environment? \
|
||||
Example:
|
||||
```sh
|
||||
ansible-playbook -vvv ci/run-collection.yml \
|
||||
-e "sdk_version=1.0.0 cloud=devstack-admin cloud_alt=devstack-alt" \
|
||||
--tags floating_ip_info
|
||||
```
|
||||
* Does a patch remove any functionality or break backwards compatibility? The author must give a good explanation for
|
||||
both.
|
||||
- One valid reason is that a functionality has never worked before.
|
||||
- Not a valid reason for dropping functionality or backwards compatibility is that functions from openstacksdk's proxy
|
||||
layer do not support the functionality from openstacksdk's cloud layer. [SDK's cloud layer is not going away](
|
||||
https://meetings.opendev.org/irclogs/%23openstack-sdks/%23openstack-sdks.2022-04-27.log.html) and can be used for
|
||||
functionality which openstacksdk's proxy layer does not support. For example, `list_*` functions from openstacksdk's
|
||||
cloud layer such as `search_users()` allow to filter retrieved results with function parameter `filters`.
|
||||
openstacksdk's proxy layer does not provide an equivalent and thus the use of `search_users()` is perfectly fine.
|
||||
* Try to look at the patch from user perspective:
|
||||
- Will users understand and approve the change(s)?
|
||||
- Will the patch break their code?
|
||||
**Note**: For operators / administrators, a stable and reliable and bug free API is more important than the number
|
||||
of features.
|
||||
- If a change breaks or changes the behavior of their code, will it be easy to spot the difference?
|
||||
@@ -31,6 +31,5 @@ build_ignore:
|
||||
- .env
|
||||
- .vscode
|
||||
- ansible_collections_openstack.egg-info
|
||||
- contrib
|
||||
- changelogs
|
||||
version: 1.9.1
|
||||
version: 1.10.0
|
||||
|
||||
@@ -31,5 +31,4 @@ build_ignore:
|
||||
- .env
|
||||
- .vscode
|
||||
- ansible_collections_openstack.egg-info
|
||||
- contrib
|
||||
- changelogs
|
||||
|
||||
@@ -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.36, < 0.99.0
|
||||
- "python >= 3.6"
|
||||
- "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.
|
||||
@@ -11,8 +12,6 @@ DOCUMENTATION = '''
|
||||
name: openstack
|
||||
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
|
||||
@@ -110,11 +109,12 @@ options:
|
||||
description: Automatically create groups from host variables.
|
||||
type: bool
|
||||
default: true
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.36, < 0.99.0"
|
||||
extends_documentation_fragment:
|
||||
- inventory_cache
|
||||
- constructed
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
@@ -133,6 +133,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")
|
||||
@@ -174,6 +177,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -71,6 +80,39 @@ 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():
|
||||
# DEPRECATED: This argument spec is only used for the deprecated old
|
||||
# OpenStack modules. It turns out that modern OpenStack auth is WAY
|
||||
@@ -131,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)
|
||||
@@ -157,33 +202,16 @@ 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 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(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))
|
||||
|
||||
if max_version and StrictVersion(sdk_version.__version__) > max_version:
|
||||
module.fail_json(
|
||||
msg="To utilize this module, the installed version of "
|
||||
"the openstacksdk library MUST be <={max_version}.".format(
|
||||
max_version=max_version))
|
||||
msg="Incompatible openstacksdk library found: {error}."
|
||||
.format(error=str(e)))
|
||||
|
||||
cloud_config = module.params.pop('cloud', None)
|
||||
try:
|
||||
@@ -278,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.
|
||||
@@ -297,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.
|
||||
"""
|
||||
@@ -315,40 +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
|
||||
# and maximum version requirements
|
||||
if self.module_min_sdk_version and MINIMUM_SDK_VERSION:
|
||||
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
|
||||
StrictVersion(self.module_min_sdk_version))
|
||||
elif MINIMUM_SDK_VERSION:
|
||||
min_version = StrictVersion(MINIMUM_SDK_VERSION)
|
||||
else:
|
||||
min_version = None
|
||||
|
||||
if self.module_max_sdk_version and MAXIMUM_SDK_VERSION:
|
||||
max_version = min(StrictVersion(MAXIMUM_SDK_VERSION),
|
||||
StrictVersion(self.module_max_sdk_version))
|
||||
elif MAXIMUM_SDK_VERSION:
|
||||
max_version = StrictVersion(MAXIMUM_SDK_VERSION)
|
||||
else:
|
||||
max_version = None
|
||||
|
||||
if min_version and 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))
|
||||
|
||||
if max_version and StrictVersion(self.sdk_version) > max_version:
|
||||
self.fail(
|
||||
msg="To utilize this module, the installed version of "
|
||||
"the openstacksdk library MUST be <={max_version}.".format(
|
||||
max_version=max_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
|
||||
|
||||
@@ -44,10 +44,6 @@ options:
|
||||
required: false
|
||||
default: {}
|
||||
type: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -10,9 +10,6 @@ short_description: Retrieve an auth token
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Retrieve an auth token from an OpenStack Cloud
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -36,11 +36,6 @@ options:
|
||||
- A timeout in seconds to tell the role to wait for the node to complete introspection if wait is set to True.
|
||||
default: 1200
|
||||
type: int
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -123,6 +123,7 @@ options:
|
||||
description:
|
||||
- Definition of the physical characteristics of this server, used for scheduling purposes
|
||||
type: dict
|
||||
default: {}
|
||||
suboptions:
|
||||
cpu_arch:
|
||||
description:
|
||||
@@ -165,10 +166,7 @@ options:
|
||||
aliases:
|
||||
- skip_update_of_driver_password
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
- "jsonpatch"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -91,10 +91,6 @@ options:
|
||||
wait for the node activation or deactivation to complete.
|
||||
default: 1800
|
||||
type: int
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -26,10 +26,6 @@ options:
|
||||
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
|
||||
'''
|
||||
|
||||
@@ -71,10 +71,6 @@ options:
|
||||
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
|
||||
'''
|
||||
|
||||
@@ -30,9 +30,6 @@ options:
|
||||
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
|
||||
'''
|
||||
|
||||
@@ -39,10 +39,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -3,288 +3,363 @@
|
||||
# Copyright (c) 2018 Catalyst IT Ltd.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: coe_cluster
|
||||
short_description: Add/Remove COE cluster from OpenStack Cloud
|
||||
short_description: Manage COE cluster in OpenStack Cloud
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Add or Remove COE cluster from the OpenStack Container Infra service.
|
||||
- Add or remove a COE (Container Orchestration Engine) cluster
|
||||
via OpenStack's Magnum aka Container Infrastructure Management API.
|
||||
options:
|
||||
cluster_template_id:
|
||||
description:
|
||||
- The template ID of cluster template.
|
||||
required: true
|
||||
type: str
|
||||
discovery_url:
|
||||
description:
|
||||
- Url used for cluster node discovery
|
||||
type: str
|
||||
docker_volume_size:
|
||||
description:
|
||||
- The size in GB of the docker volume
|
||||
type: int
|
||||
flavor_id:
|
||||
description:
|
||||
- The flavor of the minion node for this ClusterTemplate
|
||||
type: str
|
||||
keypair:
|
||||
description:
|
||||
- Name of the keypair to use.
|
||||
type: str
|
||||
labels:
|
||||
description:
|
||||
- One or more key/value pairs
|
||||
type: raw
|
||||
master_flavor_id:
|
||||
description:
|
||||
- The flavor of the master node for this ClusterTemplate
|
||||
type: str
|
||||
master_count:
|
||||
description:
|
||||
- The number of master nodes for this cluster
|
||||
default: 1
|
||||
type: int
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the cluster template
|
||||
required: true
|
||||
type: str
|
||||
node_count:
|
||||
description:
|
||||
- The number of nodes for this cluster
|
||||
default: 1
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
timeout:
|
||||
description:
|
||||
- Timeout for creating the cluster in minutes. Default to 60 mins
|
||||
if not set
|
||||
default: 60
|
||||
type: int
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: The cluster UUID.
|
||||
returned: On success when I(state) is 'present'
|
||||
cluster_template_id:
|
||||
description:
|
||||
- The template ID of cluster template.
|
||||
- Required if I(state) is C(present).
|
||||
type: str
|
||||
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
|
||||
cluster:
|
||||
description: Dictionary describing the cluster.
|
||||
returned: On success when I(state) is 'present'
|
||||
type: complex
|
||||
contains:
|
||||
api_address:
|
||||
description:
|
||||
- Api address of cluster master node
|
||||
type: str
|
||||
sample: https://172.24.4.30:6443
|
||||
cluster_template_id:
|
||||
description: The cluster_template UUID
|
||||
type: str
|
||||
sample: '7b1418c8-cea8-48fc-995d-52b66af9a9aa'
|
||||
coe_version:
|
||||
description:
|
||||
- Version of the COE software currently running in this cluster
|
||||
type: str
|
||||
sample: v1.11.1
|
||||
container_version:
|
||||
description:
|
||||
- "Version of the container software. Example: docker version."
|
||||
type: str
|
||||
sample: 1.12.6
|
||||
created_at:
|
||||
description:
|
||||
- The time in UTC at which the cluster is created
|
||||
type: str
|
||||
sample: "2018-08-16T10:29:45+00:00"
|
||||
create_timeout:
|
||||
description:
|
||||
- Timeout for creating the cluster in minutes. Default to 60 if
|
||||
not set.
|
||||
type: int
|
||||
sample: 60
|
||||
discovery_url:
|
||||
description:
|
||||
- Url used for cluster node discovery
|
||||
type: str
|
||||
sample: https://discovery.etcd.io/a42ee38e7113f31f4d6324f24367aae5
|
||||
faults:
|
||||
description:
|
||||
- Fault info collected from the Heat resources of this cluster
|
||||
type: dict
|
||||
sample: {'0': 'ResourceInError: resources[0].resources...'}
|
||||
flavor_id:
|
||||
description:
|
||||
- The flavor of the minion node for this cluster
|
||||
type: str
|
||||
sample: c1.c1r1
|
||||
keypair:
|
||||
description:
|
||||
- Name of the keypair to use.
|
||||
type: str
|
||||
sample: mykey
|
||||
labels:
|
||||
description: One or more key/value pairs
|
||||
type: dict
|
||||
sample: {'key1': 'value1', 'key2': 'value2'}
|
||||
master_addresses:
|
||||
description:
|
||||
- IP addresses of cluster master nodes
|
||||
type: list
|
||||
sample: ['172.24.4.5']
|
||||
master_count:
|
||||
description:
|
||||
- The number of master nodes for this cluster.
|
||||
type: int
|
||||
sample: 1
|
||||
master_flavor_id:
|
||||
description:
|
||||
- The flavor of the master node for this cluster
|
||||
type: str
|
||||
sample: c1.c1r1
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the cluster
|
||||
type: str
|
||||
sample: k8scluster
|
||||
node_addresses:
|
||||
description:
|
||||
- IP addresses of cluster slave nodes
|
||||
type: list
|
||||
sample: ['172.24.4.8']
|
||||
node_count:
|
||||
description:
|
||||
- The number of master nodes for this cluster.
|
||||
type: int
|
||||
sample: 1
|
||||
stack_id:
|
||||
description:
|
||||
- Stack id of the Heat stack
|
||||
type: str
|
||||
sample: '07767ec6-85f5-44cb-bd63-242a8e7f0d9d'
|
||||
status:
|
||||
description: Status of the cluster from the heat stack
|
||||
type: str
|
||||
sample: 'CREATE_COMLETE'
|
||||
status_reason:
|
||||
description:
|
||||
- Status reason of the cluster from the heat stack
|
||||
type: str
|
||||
sample: 'Stack CREATE completed successfully'
|
||||
updated_at:
|
||||
description:
|
||||
- The time in UTC at which the cluster is updated
|
||||
type: str
|
||||
sample: '2018-08-16T10:39:25+00:00'
|
||||
id:
|
||||
description:
|
||||
- Unique UUID for this cluster
|
||||
type: str
|
||||
sample: '86246a4d-a16c-4a58-9e96ad7719fe0f9d'
|
||||
discovery_url:
|
||||
description:
|
||||
- URL used for cluster node discovery.
|
||||
type: str
|
||||
docker_volume_size:
|
||||
description:
|
||||
- The size in GB of the docker volume.
|
||||
type: int
|
||||
flavor_id:
|
||||
description:
|
||||
- The flavor of the minion node for this cluster template.
|
||||
type: str
|
||||
floating_ip_enabled:
|
||||
description:
|
||||
- Indicates whether created cluster should have a floating ip.
|
||||
- Whether enable or not using the floating IP of cloud provider. Some
|
||||
cloud providers used floating IP, some used public IP, thus Magnum
|
||||
provide this option for specifying the choice of using floating IP.
|
||||
- If not set, the value of I(floating_ip_enabled) of the cluster template
|
||||
specified with I(cluster_template_id) will be used.
|
||||
- When I(floating_ip_enabled) is set to C(true), then
|
||||
I(external_network_id) in cluster template must be defined.
|
||||
type: bool
|
||||
keypair:
|
||||
description:
|
||||
- Name of the keypair to use.
|
||||
type: str
|
||||
labels:
|
||||
description:
|
||||
- One or more key/value pairs.
|
||||
type: raw
|
||||
master_count:
|
||||
description:
|
||||
- The number of master nodes for this cluster.
|
||||
- Magnum's default value for I(master_count) is 1.
|
||||
type: int
|
||||
master_flavor_id:
|
||||
description:
|
||||
- The flavor of the master node for this cluster template.
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the cluster template.
|
||||
required: true
|
||||
type: str
|
||||
node_count:
|
||||
description:
|
||||
- The number of nodes for this cluster.
|
||||
- Magnum's default value for I(node_count) is 1.
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
notes:
|
||||
- Return values of this module are preliminary and will most likely change
|
||||
when openstacksdk has finished its transition of cloud layer functions to
|
||||
resource proxies.
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create a new Kubernetes cluster
|
||||
- openstack.cloud.coe_cluster:
|
||||
name: k8s
|
||||
RETURN = r'''
|
||||
id:
|
||||
description: The cluster UUID.
|
||||
returned: On success when I(state) is C(present).
|
||||
type: str
|
||||
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
|
||||
cluster:
|
||||
description: Dictionary describing the cluster.
|
||||
returned: On success when I(state) is C(present).
|
||||
type: complex
|
||||
contains:
|
||||
cluster_template_id:
|
||||
description: The cluster_template UUID
|
||||
type: str
|
||||
sample: '7b1418c8-cea8-48fc-995d-52b66af9a9aa'
|
||||
create_timeout:
|
||||
description: Timeout for creating the cluster in minutes.
|
||||
Default to 60 if not set.
|
||||
type: int
|
||||
sample: 60
|
||||
id:
|
||||
description: Unique UUID for this cluster.
|
||||
type: str
|
||||
sample: '86246a4d-a16c-4a58-9e96ad7719fe0f9d'
|
||||
keypair:
|
||||
description: Name of the keypair to use.
|
||||
type: str
|
||||
sample: mykey
|
||||
location:
|
||||
description: The OpenStack location of this resource.
|
||||
type: str
|
||||
master_count:
|
||||
description: The number of master nodes for this cluster.
|
||||
type: int
|
||||
sample: 1
|
||||
name:
|
||||
description: Name that has to be given to the cluster.
|
||||
type: str
|
||||
sample: k8scluster
|
||||
node_count:
|
||||
description: The number of master nodes for this cluster.
|
||||
type: int
|
||||
sample: 1
|
||||
properties:
|
||||
description: Additional properties of the cluster template.
|
||||
type: dict
|
||||
sample: |
|
||||
{
|
||||
'api_address': 'https://172.24.4.30:6443',
|
||||
'coe_version': 'v1.11.1',
|
||||
'container_version': '1.12.6',
|
||||
'created_at': '2018-08-16T10:29:45+00:00',
|
||||
'discovery_url': 'https://discovery.etcd.io/a42...aae5',
|
||||
'faults': {'0': 'ResourceInError: resources[0].resources...'},
|
||||
'flavor_id': 'c1.c1r1',
|
||||
'floating_ip_enabled': true,
|
||||
'labels': {'key1': 'value1', 'key2': 'value2'},
|
||||
'master_addresses': ['172.24.4.5'],
|
||||
'master_flavor_id': 'c1.c1r1',
|
||||
'node_addresses': ['172.24.4.8'],
|
||||
'status_reason': 'Stack CREATE completed successfully',
|
||||
'updated_at': '2018-08-16T10:39:25+00:00',
|
||||
}
|
||||
stack_id:
|
||||
description: Stack id of the Heat stack.
|
||||
type: str
|
||||
sample: '07767ec6-85f5-44cb-bd63-242a8e7f0d9d'
|
||||
status:
|
||||
description: Status of the cluster from the heat stack.
|
||||
type: str
|
||||
sample: 'CREATE_COMLETE'
|
||||
uuid:
|
||||
description: Unique UUID for this cluster.
|
||||
type: str
|
||||
sample: '86246a4d-a16c-4a58-9e96ad7719fe0f9d'
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Create a new Kubernetes cluster
|
||||
openstack.cloud.coe_cluster:
|
||||
cloud: devstack
|
||||
cluster_template_id: k8s-ha
|
||||
keypair: mykey
|
||||
master_count: 3
|
||||
name: k8s
|
||||
node_count: 5
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
|
||||
|
||||
class CoeClusterModule(OpenStackModule):
|
||||
class COEClusterModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
cluster_template_id=dict(required=True),
|
||||
discovery_url=dict(default=None),
|
||||
cluster_template_id=dict(),
|
||||
discovery_url=dict(),
|
||||
docker_volume_size=dict(type='int'),
|
||||
flavor_id=dict(default=None),
|
||||
keypair=dict(default=None, no_log=False),
|
||||
labels=dict(default=None, type='raw'),
|
||||
master_count=dict(type='int', default=1),
|
||||
master_flavor_id=dict(default=None),
|
||||
flavor_id=dict(),
|
||||
floating_ip_enabled=dict(type='bool'),
|
||||
keypair=dict(no_log=False), # := noqa no-log-needed
|
||||
labels=dict(type='raw'),
|
||||
master_count=dict(type='int'),
|
||||
master_flavor_id=dict(),
|
||||
name=dict(required=True),
|
||||
node_count=dict(type='int', default=1),
|
||||
node_count=dict(type='int'),
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
timeout=dict(type='int', default=60),
|
||||
)
|
||||
module_kwargs = dict()
|
||||
|
||||
def _parse_labels(self, labels):
|
||||
if isinstance(labels, str):
|
||||
labels_dict = {}
|
||||
for kv_str in labels.split(","):
|
||||
k, v = kv_str.split("=")
|
||||
labels_dict[k] = v
|
||||
return labels_dict
|
||||
if not labels:
|
||||
return {}
|
||||
return labels
|
||||
module_kwargs = dict(
|
||||
required_if=[
|
||||
('state', 'present', ('cluster_template_id',))
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
params = self.params.copy()
|
||||
|
||||
state = self.params['state']
|
||||
|
||||
cluster = self._find()
|
||||
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(changed=self._will_change(state, cluster))
|
||||
|
||||
if state == 'present' and not cluster:
|
||||
# Create cluster
|
||||
cluster = self._create()
|
||||
self.exit_json(changed=True,
|
||||
# for backward compatibility
|
||||
id=cluster['id'],
|
||||
cluster=cluster)
|
||||
|
||||
elif state == 'present' and cluster:
|
||||
# Update cluster
|
||||
update = self._build_update(cluster)
|
||||
if update:
|
||||
cluster = self._update(cluster, update)
|
||||
|
||||
self.exit_json(changed=bool(update),
|
||||
# for backward compatibility
|
||||
id=cluster['id'],
|
||||
cluster=cluster)
|
||||
|
||||
elif state == 'absent' and cluster:
|
||||
# Delete cluster
|
||||
self._delete(cluster)
|
||||
self.exit_json(changed=True)
|
||||
|
||||
elif state == 'absent' and not cluster:
|
||||
# Do nothing
|
||||
self.exit_json(changed=False)
|
||||
|
||||
def _build_update(self, cluster):
|
||||
update = {}
|
||||
|
||||
# TODO: Implement support for updates.
|
||||
non_updateable_keys = [k for k in ['cluster_template_id',
|
||||
'discovery_url',
|
||||
'docker_volume_size', 'flavor_id',
|
||||
'floating_ip_enabled', 'keypair',
|
||||
'master_count', 'master_flavor_id',
|
||||
'name', 'node_count']
|
||||
if self.params[k] is not None
|
||||
and self.params[k] != cluster[k]]
|
||||
|
||||
labels = self.params['labels']
|
||||
if labels is not None:
|
||||
if isinstance(labels, str):
|
||||
labels = dict([tuple(kv.split(":"))
|
||||
for kv in labels.split(",")])
|
||||
if labels != cluster['labels']:
|
||||
non_updateable_keys.append('labels')
|
||||
|
||||
if non_updateable_keys:
|
||||
self.fail_json(msg='Cannot update parameters {0}'
|
||||
.format(non_updateable_keys))
|
||||
|
||||
attributes = dict((k, self.params[k])
|
||||
for k in []
|
||||
if self.params[k] is not None
|
||||
and self.params[k] != cluster[k])
|
||||
|
||||
if attributes:
|
||||
update['attributes'] = attributes
|
||||
|
||||
return update
|
||||
|
||||
def _create(self):
|
||||
# TODO: Complement *_id parameters with find_* functions to allow
|
||||
# specifying names in addition to IDs.
|
||||
kwargs = dict((k, self.params[k])
|
||||
for k in ['cluster_template_id', 'discovery_url',
|
||||
'docker_volume_size', 'flavor_id',
|
||||
'floating_ip_enabled', 'keypair',
|
||||
'master_count', 'master_flavor_id',
|
||||
'name', 'node_count']
|
||||
if self.params[k] is not None)
|
||||
|
||||
labels = self.params['labels']
|
||||
if labels is not None:
|
||||
if isinstance(labels, str):
|
||||
labels = dict([tuple(kv.split(":"))
|
||||
for kv in labels.split(",")])
|
||||
kwargs['labels'] = labels
|
||||
|
||||
kwargs['create_timeout'] = self.params['timeout']
|
||||
|
||||
cluster = self.conn.create_coe_cluster(**kwargs)
|
||||
|
||||
if not self.params['wait']:
|
||||
# openstacksdk's create_coe_cluster() returns a cluster's uuid only
|
||||
# but we cannot use self.conn.get_coe_cluster(cluster_id) because
|
||||
# it might return None as long as the cluster is being set up.
|
||||
return cluster
|
||||
|
||||
cluster_id = cluster['id']
|
||||
|
||||
if self.params['wait']:
|
||||
for count in self.sdk.utils.iterate_timeout(
|
||||
timeout=self.params['timeout'],
|
||||
message="Timeout waiting for cluster to be present"
|
||||
):
|
||||
# Fetch cluster again
|
||||
cluster = self.conn.get_coe_cluster(cluster_id)
|
||||
|
||||
if cluster is None:
|
||||
continue
|
||||
elif cluster.status.lower() == 'active':
|
||||
break
|
||||
elif cluster.status.lower() in ['error']:
|
||||
self.fail_json(msg="{0} transitioned to failure state {1}"
|
||||
.format(cluster.name, 'error'))
|
||||
|
||||
return cluster
|
||||
|
||||
def _delete(self, cluster):
|
||||
self.conn.delete_coe_cluster(cluster.name)
|
||||
|
||||
if self.params['wait']:
|
||||
for count in self.sdk.utils.iterate_timeout(
|
||||
timeout=self.params['timeout'],
|
||||
message="Timeout waiting for cluster to be absent"
|
||||
):
|
||||
cluster = self.conn.get_coe_cluster(cluster.id)
|
||||
if cluster is None:
|
||||
break
|
||||
elif cluster['status'].lower() == 'deleted':
|
||||
break
|
||||
|
||||
def _find(self):
|
||||
name = self.params['name']
|
||||
filters = {}
|
||||
|
||||
cluster_template_id = self.params['cluster_template_id']
|
||||
if cluster_template_id is not None:
|
||||
filters['cluster_template_id'] = cluster_template_id
|
||||
|
||||
kwargs = dict(
|
||||
discovery_url=self.params['discovery_url'],
|
||||
docker_volume_size=self.params['docker_volume_size'],
|
||||
flavor_id=self.params['flavor_id'],
|
||||
keypair=self.params['keypair'],
|
||||
labels=self._parse_labels(params['labels']),
|
||||
master_count=self.params['master_count'],
|
||||
master_flavor_id=self.params['master_flavor_id'],
|
||||
node_count=self.params['node_count'],
|
||||
create_timeout=self.params['timeout'],
|
||||
)
|
||||
return self.conn.get_coe_cluster(name_or_id=name, filters=filters)
|
||||
|
||||
changed = False
|
||||
cluster = self.conn.get_coe_cluster(
|
||||
name_or_id=name, filters={'cluster_template_id': cluster_template_id})
|
||||
def _update(self, cluster, update):
|
||||
attributes = update.get('attributes')
|
||||
if attributes:
|
||||
# TODO: Implement support for updates.
|
||||
# cluster = self.conn.update_coe_cluster(...)
|
||||
pass
|
||||
|
||||
if state == 'present':
|
||||
if not cluster:
|
||||
cluster = self.conn.create_coe_cluster(
|
||||
name, cluster_template_id=cluster_template_id, **kwargs)
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
return cluster
|
||||
|
||||
# NOTE (brtknr): At present, create_coe_cluster request returns
|
||||
# cluster_id as `uuid` whereas get_coe_cluster request returns the
|
||||
# same field as `id`. This behaviour may change in the future
|
||||
# therefore try `id` first then `uuid`.
|
||||
cluster_id = cluster.get('id', cluster.get('uuid'))
|
||||
cluster['id'] = cluster['uuid'] = cluster_id
|
||||
self.exit_json(changed=changed, cluster=cluster, id=cluster_id)
|
||||
elif state == 'absent':
|
||||
if not cluster:
|
||||
self.exit_json(changed=False)
|
||||
else:
|
||||
self.conn.delete_coe_cluster(name)
|
||||
self.exit_json(changed=True)
|
||||
def _will_change(self, state, cluster):
|
||||
if state == 'present' and not cluster:
|
||||
return True
|
||||
elif state == 'present' and cluster:
|
||||
return bool(self._build_update(cluster))
|
||||
elif state == 'absent' and cluster:
|
||||
return True
|
||||
else:
|
||||
# state == 'absent' and not cluster:
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
module = CoeClusterModule()
|
||||
module = COEClusterModule()
|
||||
module()
|
||||
|
||||
|
||||
|
||||
@@ -3,384 +3,550 @@
|
||||
# Copyright (c) 2018 Catalyst IT Ltd.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: coe_cluster_template
|
||||
short_description: Add/Remove COE cluster template from OpenStack Cloud
|
||||
short_description: Manage COE cluster template in OpenStack Cloud
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Add or Remove COE cluster template from the OpenStack Container Infra
|
||||
service.
|
||||
- Add or remove a COE (Container Orchestration Engine) cluster template
|
||||
via OpenStack's Magnum aka Container Infrastructure Management API.
|
||||
options:
|
||||
coe:
|
||||
description:
|
||||
- The Container Orchestration Engine for this clustertemplate
|
||||
choices: [kubernetes, swarm, mesos]
|
||||
type: str
|
||||
required: true
|
||||
dns_nameserver:
|
||||
description:
|
||||
- The DNS nameserver address
|
||||
default: '8.8.8.8'
|
||||
type: str
|
||||
docker_storage_driver:
|
||||
description:
|
||||
- Docker storage driver
|
||||
choices: [devicemapper, overlay, overlay2]
|
||||
type: str
|
||||
docker_volume_size:
|
||||
description:
|
||||
- The size in GB of the docker volume
|
||||
type: int
|
||||
external_network_id:
|
||||
description:
|
||||
- The external network to attach to the Cluster
|
||||
type: str
|
||||
fixed_network:
|
||||
description:
|
||||
- The fixed network name to attach to the Cluster
|
||||
type: str
|
||||
fixed_subnet:
|
||||
description:
|
||||
- The fixed subnet name to attach to the Cluster
|
||||
type: str
|
||||
flavor_id:
|
||||
description:
|
||||
- The flavor of the minion node for this ClusterTemplate
|
||||
type: str
|
||||
floating_ip_enabled:
|
||||
description:
|
||||
- Indicates whether created clusters should have a floating ip or not
|
||||
type: bool
|
||||
default: true
|
||||
keypair_id:
|
||||
description:
|
||||
- Name or ID of the keypair to use.
|
||||
type: str
|
||||
image_id:
|
||||
description:
|
||||
- Image id the cluster will be based on
|
||||
type: str
|
||||
required: true
|
||||
labels:
|
||||
description:
|
||||
- One or more key/value pairs
|
||||
type: raw
|
||||
http_proxy:
|
||||
description:
|
||||
- Address of a proxy that will receive all HTTP requests and relay them
|
||||
The format is a URL including a port number
|
||||
type: str
|
||||
https_proxy:
|
||||
description:
|
||||
- Address of a proxy that will receive all HTTPS requests and relay
|
||||
them. The format is a URL including a port number
|
||||
type: str
|
||||
master_flavor_id:
|
||||
description:
|
||||
- The flavor of the master node for this ClusterTemplate
|
||||
type: str
|
||||
master_lb_enabled:
|
||||
description:
|
||||
- Indicates whether created clusters should have a load balancer
|
||||
for master nodes or not
|
||||
type: bool
|
||||
default: 'no'
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the cluster template
|
||||
required: true
|
||||
type: str
|
||||
network_driver:
|
||||
description:
|
||||
- The name of the driver used for instantiating container networks
|
||||
choices: [flannel, calico, docker]
|
||||
type: str
|
||||
no_proxy:
|
||||
description:
|
||||
- A comma separated list of IPs for which proxies should not be
|
||||
used in the cluster
|
||||
type: str
|
||||
public:
|
||||
description:
|
||||
- Indicates whether the ClusterTemplate is public or not
|
||||
type: bool
|
||||
default: 'no'
|
||||
registry_enabled:
|
||||
description:
|
||||
- Indicates whether the docker registry is enabled
|
||||
type: bool
|
||||
default: 'no'
|
||||
server_type:
|
||||
description:
|
||||
- Server type for this ClusterTemplate
|
||||
choices: [vm, bm]
|
||||
default: vm
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
tls_disabled:
|
||||
description:
|
||||
- Indicates whether the TLS should be disabled
|
||||
type: bool
|
||||
default: 'no'
|
||||
volume_driver:
|
||||
description:
|
||||
- The name of the driver used for instantiating container volumes
|
||||
choices: [cinder, rexray]
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
id:
|
||||
description: The cluster UUID.
|
||||
returned: On success when I(state) is 'present'
|
||||
coe:
|
||||
description:
|
||||
- The Container Orchestration Engine for this cluster template
|
||||
- Required if I(state) is C(present).
|
||||
choices: [kubernetes, swarm, mesos]
|
||||
type: str
|
||||
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
|
||||
cluster_template:
|
||||
description: Dictionary describing the template.
|
||||
returned: On success when I(state) is 'present'
|
||||
type: complex
|
||||
contains:
|
||||
coe:
|
||||
description: The Container Orchestration Engine for this clustertemplate
|
||||
type: str
|
||||
sample: kubernetes
|
||||
dns_nameserver:
|
||||
description: The DNS nameserver address
|
||||
type: str
|
||||
sample: '8.8.8.8'
|
||||
docker_storage_driver:
|
||||
description: Docker storage driver
|
||||
type: str
|
||||
sample: devicemapper
|
||||
docker_volume_size:
|
||||
description: The size in GB of the docker volume
|
||||
type: int
|
||||
sample: 5
|
||||
external_network_id:
|
||||
description: The external network to attach to the Cluster
|
||||
type: str
|
||||
sample: public
|
||||
fixed_network:
|
||||
description: The fixed network name to attach to the Cluster
|
||||
type: str
|
||||
sample: 07767ec6-85f5-44cb-bd63-242a8e7f0d9d
|
||||
fixed_subnet:
|
||||
description:
|
||||
- The fixed subnet name to attach to the Cluster
|
||||
type: str
|
||||
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0d9d
|
||||
flavor_id:
|
||||
description:
|
||||
- The flavor of the minion node for this ClusterTemplate
|
||||
type: str
|
||||
sample: c1.c1r1
|
||||
floating_ip_enabled:
|
||||
description:
|
||||
- Indicates whether created clusters should have a floating ip or not
|
||||
type: bool
|
||||
sample: true
|
||||
keypair_id:
|
||||
description:
|
||||
- Name or ID of the keypair to use.
|
||||
type: str
|
||||
sample: mykey
|
||||
image_id:
|
||||
description:
|
||||
- Image id the cluster will be based on
|
||||
type: str
|
||||
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0e9d
|
||||
labels:
|
||||
description: One or more key/value pairs
|
||||
type: dict
|
||||
sample: {'key1': 'value1', 'key2': 'value2'}
|
||||
http_proxy:
|
||||
description:
|
||||
- Address of a proxy that will receive all HTTP requests and relay them
|
||||
The format is a URL including a port number
|
||||
type: str
|
||||
sample: http://10.0.0.11:9090
|
||||
https_proxy:
|
||||
description:
|
||||
- Address of a proxy that will receive all HTTPS requests and relay
|
||||
them. The format is a URL including a port number
|
||||
type: str
|
||||
sample: https://10.0.0.10:8443
|
||||
master_flavor_id:
|
||||
description:
|
||||
- The flavor of the master node for this ClusterTemplate
|
||||
type: str
|
||||
sample: c1.c1r1
|
||||
master_lb_enabled:
|
||||
description:
|
||||
- Indicates whether created clusters should have a load balancer
|
||||
for master nodes or not
|
||||
type: bool
|
||||
sample: true
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the cluster template
|
||||
type: str
|
||||
sample: k8scluster
|
||||
network_driver:
|
||||
description:
|
||||
- The name of the driver used for instantiating container networks
|
||||
type: str
|
||||
sample: calico
|
||||
no_proxy:
|
||||
description:
|
||||
- A comma separated list of IPs for which proxies should not be
|
||||
used in the cluster
|
||||
type: str
|
||||
sample: 10.0.0.4,10.0.0.5
|
||||
public:
|
||||
description:
|
||||
- Indicates whether the ClusterTemplate is public or not
|
||||
type: bool
|
||||
sample: false
|
||||
registry_enabled:
|
||||
description:
|
||||
- Indicates whether the docker registry is enabled
|
||||
type: bool
|
||||
sample: false
|
||||
server_type:
|
||||
description:
|
||||
- Server type for this ClusterTemplate
|
||||
type: str
|
||||
sample: vm
|
||||
tls_disabled:
|
||||
description:
|
||||
- Indicates whether the TLS should be disabled
|
||||
type: bool
|
||||
sample: false
|
||||
volume_driver:
|
||||
description:
|
||||
- The name of the driver used for instantiating container volumes
|
||||
type: str
|
||||
sample: cinder
|
||||
dns_nameserver:
|
||||
description:
|
||||
- The DNS nameserver address.
|
||||
- Magnum's default value for I(dns_nameserver) is C(8.8.8.8).
|
||||
type: str
|
||||
docker_storage_driver:
|
||||
description:
|
||||
- Docker storage driver.
|
||||
choices: [devicemapper, overlay, overlay2]
|
||||
type: str
|
||||
docker_volume_size:
|
||||
description:
|
||||
- The size in GB of the docker volume.
|
||||
type: int
|
||||
external_network_id:
|
||||
description:
|
||||
- The external network to attach to the cluster.
|
||||
- When I(floating_ip_enabled) is set to C(true), then
|
||||
I(external_network_id) must be defined.
|
||||
type: str
|
||||
fixed_network:
|
||||
description:
|
||||
- The fixed network name or id to attach to the cluster.
|
||||
type: str
|
||||
fixed_subnet:
|
||||
description:
|
||||
- The fixed subnet name or id to attach to the cluster.
|
||||
type: str
|
||||
flavor_id:
|
||||
description:
|
||||
- The flavor of the minion node for this cluster template.
|
||||
type: str
|
||||
floating_ip_enabled:
|
||||
description:
|
||||
- Indicates whether created clusters should have a floating ip or not.
|
||||
- When I(floating_ip_enabled) is set to C(true), then
|
||||
I(external_network_id) must be defined.
|
||||
type: bool
|
||||
default: true
|
||||
keypair_id:
|
||||
description:
|
||||
- Name or ID of the keypair to use.
|
||||
type: str
|
||||
image_id:
|
||||
description:
|
||||
- Image id the cluster will be based on.
|
||||
- Required if I(state) is C(present).
|
||||
type: str
|
||||
labels:
|
||||
description:
|
||||
- One or more key/value pairs.
|
||||
type: raw
|
||||
http_proxy:
|
||||
description:
|
||||
- Address of a proxy that will receive all HTTP requests and relay them.
|
||||
- The format is a URL including a port number.
|
||||
type: str
|
||||
https_proxy:
|
||||
description:
|
||||
- Address of a proxy that will receive all HTTPS requests and relay them.
|
||||
- The format is a URL including a port number.
|
||||
type: str
|
||||
master_flavor_id:
|
||||
description:
|
||||
- The flavor of the master node for this cluster template.
|
||||
type: str
|
||||
master_lb_enabled:
|
||||
description:
|
||||
- Indicates whether created clusters should have a load balancer
|
||||
for master nodes or not.
|
||||
- Magnum's default value for I(master_lb_enabled) is C(true),
|
||||
ours is C(false).
|
||||
type: bool
|
||||
default: false
|
||||
name:
|
||||
description:
|
||||
- Name that has to be given to the cluster template.
|
||||
required: true
|
||||
type: str
|
||||
network_driver:
|
||||
description:
|
||||
- The name of the driver used for instantiating container networks.
|
||||
choices: [flannel, calico, docker]
|
||||
type: str
|
||||
no_proxy:
|
||||
description:
|
||||
- A comma separated list of IPs for which proxies should not be
|
||||
used in the cluster.
|
||||
type: str
|
||||
public:
|
||||
description:
|
||||
- Indicates whether the cluster template is public or not.
|
||||
- Magnum's default value for I(public) is C(false).
|
||||
type: bool
|
||||
registry_enabled:
|
||||
description:
|
||||
- Indicates whether the docker registry is enabled.
|
||||
- Magnum's default value for I(registry_enabled) is C(false).
|
||||
type: bool
|
||||
server_type:
|
||||
description:
|
||||
- Server type for this cluster template.
|
||||
- Magnum's default value for I(server_type) is C(vm).
|
||||
choices: [vm, bm]
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the resource.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
tls_disabled:
|
||||
description:
|
||||
- Indicates whether the TLS should be disabled.
|
||||
- Magnum's default value for I(tls_disabled) is C(false).
|
||||
type: bool
|
||||
volume_driver:
|
||||
description:
|
||||
- The name of the driver used for instantiating container volumes.
|
||||
choices: [cinder, rexray]
|
||||
type: str
|
||||
notes:
|
||||
- Return values of this module are preliminary and will most likely change
|
||||
when openstacksdk has finished its transition of cloud layer functions to
|
||||
resource proxies.
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create a new Kubernetes cluster template
|
||||
- openstack.cloud.coe_cluster_template:
|
||||
name: k8s
|
||||
RETURN = r'''
|
||||
id:
|
||||
description: The cluster UUID.
|
||||
returned: On success when I(state) is C(present).
|
||||
type: str
|
||||
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
|
||||
cluster_template:
|
||||
description: Dictionary describing the template.
|
||||
returned: On success when I(state) is C(present).
|
||||
type: complex
|
||||
contains:
|
||||
apiserver_port:
|
||||
description: The exposed port of COE API server.
|
||||
type: int
|
||||
cluster_distro:
|
||||
description: Display the attribute os_distro defined as appropriate
|
||||
metadata in image for the bay/cluster driver.
|
||||
type: str
|
||||
coe:
|
||||
description: The Container Orchestration Engine for this cluster
|
||||
template.
|
||||
type: str
|
||||
sample: kubernetes
|
||||
created_at:
|
||||
description: The date and time when the resource was created.
|
||||
type: str
|
||||
dns_nameserver:
|
||||
description: The DNS nameserver address
|
||||
type: str
|
||||
sample: '8.8.8.8'
|
||||
docker_volume_size:
|
||||
description: The size in GB of the docker volume
|
||||
type: int
|
||||
sample: 5
|
||||
external_network_id:
|
||||
description: The external network to attach to the cluster
|
||||
type: str
|
||||
sample: public
|
||||
fixed_network:
|
||||
description: The fixed network name to attach to the cluster
|
||||
type: str
|
||||
sample: 07767ec6-85f5-44cb-bd63-242a8e7f0d9d
|
||||
fixed_subnet:
|
||||
description: The fixed subnet name to attach to the cluster.
|
||||
type: str
|
||||
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0d9d
|
||||
flavor_id:
|
||||
description: The flavor of the minion node for this cluster template.
|
||||
type: str
|
||||
sample: c1.c1r1
|
||||
floating_ip_enabled:
|
||||
description: Indicates whether created clusters should have a
|
||||
floating ip or not.
|
||||
type: bool
|
||||
sample: true
|
||||
http_proxy:
|
||||
description: Address of a proxy that will receive all HTTP requests
|
||||
and relay them. The format is a URL including a port
|
||||
number.
|
||||
type: str
|
||||
sample: http://10.0.0.11:9090
|
||||
https_proxy:
|
||||
description: Address of a proxy that will receive all HTTPS requests
|
||||
and relay them. The format is a URL including a port
|
||||
number.
|
||||
type: str
|
||||
sample: https://10.0.0.10:8443
|
||||
id:
|
||||
description: The UUID of the cluster template.
|
||||
type: str
|
||||
image_id:
|
||||
description: Image id the cluster will be based on.
|
||||
type: str
|
||||
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0e9d
|
||||
insecure_registry:
|
||||
description: "The URL pointing to users's own private insecure docker
|
||||
registry to deploy and run docker containers."
|
||||
type: str
|
||||
is_public:
|
||||
description: Access to a baymodel/cluster template is normally limited to
|
||||
the admin, owner or users within the same tenant as the
|
||||
owners. Setting this flag makes the baymodel/cluster
|
||||
template public and accessible by other users. The default
|
||||
is not public.
|
||||
type: bool
|
||||
is_registry_enabled:
|
||||
description: "Docker images by default are pulled from the public Docker
|
||||
registry, but in some cases, users may want to use a
|
||||
private registry. This option provides an alternative
|
||||
registry based on the Registry V2: Magnum will create a
|
||||
local registry in the bay/cluster backed by swift to host
|
||||
the images. The default is to use the public registry."
|
||||
type: bool
|
||||
is_tls_disabled:
|
||||
description: Transport Layer Security (TLS) is normally enabled to secure
|
||||
the bay/cluster. In some cases, users may want to disable
|
||||
TLS in the bay/cluster, for instance during development or
|
||||
to troubleshoot certain problems. Specifying this parameter
|
||||
will disable TLS so that users can access the COE endpoints
|
||||
without a certificate. The default is TLS enabled.
|
||||
type: bool
|
||||
keypair_id:
|
||||
description: Name or ID of the keypair to use.
|
||||
type: str
|
||||
sample: mykey
|
||||
labels:
|
||||
description: One or more key/value pairs.
|
||||
type: dict
|
||||
sample: {'key1': 'value1', 'key2': 'value2'}
|
||||
location:
|
||||
description: The OpenStack location of this resource.
|
||||
type: str
|
||||
master_flavor_id:
|
||||
description: The flavor of the master node for this cluster template.
|
||||
type: str
|
||||
sample: c1.c1r1
|
||||
master_lb_enabled:
|
||||
description: Indicates whether created clusters should have a load
|
||||
balancer for master nodes or not.
|
||||
type: bool
|
||||
sample: true
|
||||
name:
|
||||
description: Name that has to be given to the cluster template.
|
||||
type: str
|
||||
sample: k8scluster
|
||||
network_driver:
|
||||
description:
|
||||
- The name of the driver used for instantiating container networks
|
||||
type: str
|
||||
sample: calico
|
||||
no_proxy:
|
||||
description: A comma separated list of IPs for which proxies should
|
||||
not be used in the cluster.
|
||||
type: str
|
||||
sample: 10.0.0.4,10.0.0.5
|
||||
properties:
|
||||
description: Additional properties of the cluster template.
|
||||
type: dict
|
||||
sample: |
|
||||
{
|
||||
"docker_storage_driver": null,
|
||||
"hidden": false,
|
||||
"master_lb_enabled": false,
|
||||
"project_id": "8fb245a1bd714d9a82e419f2b7bb69dd",
|
||||
"tags": null,
|
||||
"user_id": "51510ce12e294d5d9c7391bececcd1e8"
|
||||
}
|
||||
public:
|
||||
description: Access to a baymodel/cluster template is normally limited
|
||||
to the admin, owner or users within the same tenant as the
|
||||
owners. Setting this flag makes the baymodel/cluster
|
||||
template public and accessible by other users. The default
|
||||
is not public.
|
||||
type: bool
|
||||
sample: false
|
||||
registry_enabled:
|
||||
description: "Docker images by default are pulled from the public Docker
|
||||
registry, but in some cases, users may want to use a
|
||||
private registry. This option provides an alternative
|
||||
registry based on the Registry V2: Magnum will create a
|
||||
local registry in the bay/cluster backed by swift to host
|
||||
the images. The default is to use the public registry."
|
||||
type: bool
|
||||
sample: false
|
||||
server_type:
|
||||
description: Server type for this cluster template.
|
||||
type: str
|
||||
sample: vm
|
||||
tls_disabled:
|
||||
description: Transport Layer Security (TLS) is normally enabled to secure
|
||||
the bay/cluster. In some cases, users may want to disable
|
||||
TLS in the bay/cluster, for instance during development or
|
||||
to troubleshoot certain problems. Specifying this parameter
|
||||
will disable TLS so that users can access the COE endpoints
|
||||
without a certificate. The default is TLS enabled.
|
||||
type: bool
|
||||
sample: false
|
||||
updated_at:
|
||||
description: The date and time when the resource was updated.
|
||||
type: str
|
||||
uuid:
|
||||
description: The UUID of the cluster template.
|
||||
type: str
|
||||
volume_driver:
|
||||
description: The name of the driver used for instantiating container
|
||||
volumes.
|
||||
type: str
|
||||
sample: cinder
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Create a new Kubernetes cluster template
|
||||
openstack.cloud.coe_cluster_template:
|
||||
cloud: devstack
|
||||
coe: kubernetes
|
||||
keypair_id: mykey
|
||||
image_id: 2a8c9888-9054-4b06-a1ca-2bb61f9adb72
|
||||
keypair_id: mykey
|
||||
name: k8s
|
||||
public: no
|
||||
'''
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
|
||||
|
||||
class CoeClusterTemplateModule(OpenStackModule):
|
||||
class COEClusterTemplateModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
coe=dict(required=True, choices=['kubernetes', 'swarm', 'mesos']),
|
||||
dns_nameserver=dict(default='8.8.8.8'),
|
||||
docker_storage_driver=dict(choices=['devicemapper', 'overlay', 'overlay2']),
|
||||
coe=dict(choices=['kubernetes', 'swarm', 'mesos']),
|
||||
dns_nameserver=dict(),
|
||||
docker_storage_driver=dict(choices=['devicemapper', 'overlay',
|
||||
'overlay2']),
|
||||
docker_volume_size=dict(type='int'),
|
||||
external_network_id=dict(default=None),
|
||||
fixed_network=dict(default=None),
|
||||
fixed_subnet=dict(default=None),
|
||||
flavor_id=dict(default=None),
|
||||
external_network_id=dict(),
|
||||
fixed_network=dict(),
|
||||
fixed_subnet=dict(),
|
||||
flavor_id=dict(),
|
||||
floating_ip_enabled=dict(type='bool', default=True),
|
||||
keypair_id=dict(default=None),
|
||||
image_id=dict(required=True),
|
||||
labels=dict(default=None, type='raw'),
|
||||
http_proxy=dict(default=None),
|
||||
https_proxy=dict(default=None),
|
||||
http_proxy=dict(),
|
||||
https_proxy=dict(),
|
||||
image_id=dict(),
|
||||
keypair_id=dict(),
|
||||
labels=dict(type='raw'),
|
||||
master_flavor_id=dict(),
|
||||
master_lb_enabled=dict(type='bool', default=False),
|
||||
master_flavor_id=dict(default=None),
|
||||
name=dict(required=True),
|
||||
network_driver=dict(choices=['flannel', 'calico', 'docker']),
|
||||
no_proxy=dict(default=None),
|
||||
public=dict(type='bool', default=False),
|
||||
registry_enabled=dict(type='bool', default=False),
|
||||
server_type=dict(default="vm", choices=['vm', 'bm']),
|
||||
no_proxy=dict(),
|
||||
public=dict(type='bool'),
|
||||
registry_enabled=dict(type='bool'),
|
||||
server_type=dict(choices=['vm', 'bm']),
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
tls_disabled=dict(type='bool', default=False),
|
||||
tls_disabled=dict(type='bool'),
|
||||
volume_driver=dict(choices=['cinder', 'rexray']),
|
||||
)
|
||||
module_kwargs = dict()
|
||||
|
||||
def _parse_labels(self, labels):
|
||||
if isinstance(labels, str):
|
||||
labels_dict = {}
|
||||
for kv_str in labels.split(","):
|
||||
k, v = kv_str.split("=")
|
||||
labels_dict[k] = v
|
||||
return labels_dict
|
||||
if not labels:
|
||||
return {}
|
||||
return labels
|
||||
module_kwargs = dict(
|
||||
required_if=[
|
||||
('state', 'present', ('coe', 'image_id')),
|
||||
],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
params = self.params.copy()
|
||||
|
||||
state = self.params['state']
|
||||
name = self.params['name']
|
||||
coe = self.params['coe']
|
||||
image_id = self.params['image_id']
|
||||
|
||||
kwargs = dict(
|
||||
dns_nameserver=self.params['dns_nameserver'],
|
||||
docker_storage_driver=self.params['docker_storage_driver'],
|
||||
docker_volume_size=self.params['docker_volume_size'],
|
||||
external_network_id=self.params['external_network_id'],
|
||||
fixed_network=self.params['fixed_network'],
|
||||
fixed_subnet=self.params['fixed_subnet'],
|
||||
flavor_id=self.params['flavor_id'],
|
||||
floating_ip_enabled=self.params['floating_ip_enabled'],
|
||||
keypair_id=self.params['keypair_id'],
|
||||
labels=self._parse_labels(params['labels']),
|
||||
http_proxy=self.params['http_proxy'],
|
||||
https_proxy=self.params['https_proxy'],
|
||||
master_lb_enabled=self.params['master_lb_enabled'],
|
||||
master_flavor_id=self.params['master_flavor_id'],
|
||||
network_driver=self.params['network_driver'],
|
||||
no_proxy=self.params['no_proxy'],
|
||||
public=self.params['public'],
|
||||
registry_enabled=self.params['registry_enabled'],
|
||||
server_type=self.params['server_type'],
|
||||
tls_disabled=self.params['tls_disabled'],
|
||||
volume_driver=self.params['volume_driver'],
|
||||
)
|
||||
cluster_template = self._find()
|
||||
|
||||
changed = False
|
||||
template = self.conn.get_coe_cluster_template(
|
||||
name_or_id=name, filters={'coe': coe, 'image_id': image_id})
|
||||
if self.ansible.check_mode:
|
||||
self.exit_json(changed=self._will_change(state, cluster_template))
|
||||
|
||||
if state == 'present':
|
||||
if not template:
|
||||
template = self.conn.create_coe_cluster_template(
|
||||
name, coe=coe, image_id=image_id, **kwargs)
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
if state == 'present' and not cluster_template:
|
||||
# Create cluster_template
|
||||
cluster_template = self._create()
|
||||
self.exit_json(
|
||||
changed=True,
|
||||
# for backward compatibility
|
||||
id=cluster_template['id'],
|
||||
cluster_template=cluster_template)
|
||||
|
||||
elif state == 'present' and cluster_template:
|
||||
# Update cluster_template
|
||||
update = self._build_update(cluster_template)
|
||||
if update:
|
||||
cluster_template = self._update(cluster_template, update)
|
||||
|
||||
self.exit_json(
|
||||
changed=changed, cluster_template=template, id=template['uuid'])
|
||||
elif state == 'absent':
|
||||
if not template:
|
||||
self.exit_json(changed=False)
|
||||
else:
|
||||
self.conn.delete_coe_cluster_template(name)
|
||||
self.exit_json(changed=True)
|
||||
changed=bool(update),
|
||||
# for backward compatibility
|
||||
id=cluster_template['id'],
|
||||
cluster_template=cluster_template)
|
||||
|
||||
elif state == 'absent' and cluster_template:
|
||||
# Delete cluster_template
|
||||
self._delete(cluster_template)
|
||||
self.exit_json(changed=True)
|
||||
|
||||
elif state == 'absent' and not cluster_template:
|
||||
# Do nothing
|
||||
self.exit_json(changed=False)
|
||||
|
||||
def _build_update(self, cluster_template):
|
||||
update = {}
|
||||
|
||||
if self.params['floating_ip_enabled'] \
|
||||
and self.params['external_network_id'] is None:
|
||||
raise ValueError('floating_ip_enabled is True'
|
||||
' but external_network_id is missing')
|
||||
|
||||
# TODO: Implement support for updates.
|
||||
non_updateable_keys = [k for k in ['coe', 'dns_nameserver',
|
||||
'docker_storage_driver',
|
||||
'docker_volume_size',
|
||||
'external_network_id',
|
||||
'fixed_network',
|
||||
'fixed_subnet', 'flavor_id',
|
||||
'floating_ip_enabled',
|
||||
'http_proxy', 'https_proxy',
|
||||
'image_id', 'keypair_id',
|
||||
'master_flavor_id',
|
||||
'master_lb_enabled', 'name',
|
||||
'network_driver', 'no_proxy',
|
||||
'public', 'registry_enabled',
|
||||
'server_type', 'tls_disabled',
|
||||
'volume_driver']
|
||||
if self.params[k] is not None
|
||||
and k in cluster_template
|
||||
and self.params[k] != cluster_template[k]]
|
||||
|
||||
labels = self.params['labels']
|
||||
if labels is not None:
|
||||
if isinstance(labels, str):
|
||||
labels = dict([tuple(kv.split(":"))
|
||||
for kv in labels.split(",")])
|
||||
if labels != cluster_template['labels']:
|
||||
non_updateable_keys.append('labels')
|
||||
|
||||
if non_updateable_keys:
|
||||
self.fail_json(msg='Cannot update parameters {0}'
|
||||
.format(non_updateable_keys))
|
||||
|
||||
attributes = dict((k, self.params[k])
|
||||
for k in []
|
||||
if self.params[k] is not None
|
||||
and self.params[k] != cluster_template[k])
|
||||
|
||||
if attributes:
|
||||
update['attributes'] = attributes
|
||||
|
||||
return update
|
||||
|
||||
def _create(self):
|
||||
if self.params['floating_ip_enabled'] \
|
||||
and self.params['external_network_id'] is None:
|
||||
raise ValueError('floating_ip_enabled is True'
|
||||
' but external_network_id is missing')
|
||||
|
||||
# TODO: Complement *_id parameters with find_* functions to allow
|
||||
# specifying names in addition to IDs.
|
||||
kwargs = dict((k, self.params[k])
|
||||
for k in ['coe', 'dns_nameserver',
|
||||
'docker_storage_driver', 'docker_volume_size',
|
||||
'external_network_id', 'fixed_network',
|
||||
'fixed_subnet', 'flavor_id',
|
||||
'floating_ip_enabled', 'http_proxy',
|
||||
'https_proxy', 'image_id', 'keypair_id',
|
||||
'master_flavor_id', 'master_lb_enabled',
|
||||
'name', 'network_driver', 'no_proxy', 'public',
|
||||
'registry_enabled', 'server_type',
|
||||
'tls_disabled', 'volume_driver']
|
||||
if self.params[k] is not None)
|
||||
|
||||
labels = self.params['labels']
|
||||
if labels is not None:
|
||||
if isinstance(labels, str):
|
||||
labels = dict([tuple(kv.split(":"))
|
||||
for kv in labels.split(",")])
|
||||
kwargs['labels'] = labels
|
||||
|
||||
return self.conn.create_cluster_template(**kwargs)
|
||||
|
||||
def _delete(self, cluster_template):
|
||||
self.conn.delete_cluster_template(cluster_template.name)
|
||||
|
||||
def _find(self):
|
||||
name = self.params['name']
|
||||
filters = {}
|
||||
|
||||
image_id = self.params['image_id']
|
||||
if image_id is not None:
|
||||
filters['image_id'] = image_id
|
||||
|
||||
coe = self.params['coe']
|
||||
if coe is not None:
|
||||
filters['coe'] = coe
|
||||
|
||||
return self.conn.get_cluster_template(name_or_id=name,
|
||||
filters=filters,
|
||||
detail=True)
|
||||
|
||||
def _update(self, cluster_template, update):
|
||||
attributes = update.get('attributes')
|
||||
if attributes:
|
||||
# TODO: Implement support for updates.
|
||||
# cluster_template = self.conn.update_cluster_template(...)
|
||||
pass
|
||||
|
||||
return cluster_template
|
||||
|
||||
def _will_change(self, state, cluster_template):
|
||||
if state == 'present' and not cluster_template:
|
||||
return True
|
||||
elif state == 'present' and cluster_template:
|
||||
return bool(self._build_update(cluster_template))
|
||||
elif state == 'absent' and cluster_template:
|
||||
return True
|
||||
else:
|
||||
# state == 'absent' and not cluster_template:
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
module = CoeClusterTemplateModule()
|
||||
module = COEClusterTemplateModule()
|
||||
module()
|
||||
|
||||
|
||||
|
||||
@@ -67,10 +67,6 @@ options:
|
||||
description:
|
||||
- Metadata dictionary
|
||||
type: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -55,10 +55,6 @@ options:
|
||||
- A string used for filtering flavors based on the amount of ephemeral
|
||||
storage. Format is the same as the I(ram) parameter
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -18,10 +18,6 @@ options:
|
||||
description:
|
||||
- Filter by service host. Requires openstacksdk>=0.53.
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -21,8 +21,8 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.36, < 0.99.0"
|
||||
author: OpenStack Ansible SIG
|
||||
'''
|
||||
|
||||
|
||||
@@ -45,10 +45,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -34,11 +34,6 @@ options:
|
||||
description:
|
||||
- TTL (Time To Live) value in seconds.
|
||||
type: int
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -44,10 +44,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.13.0"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -46,8 +46,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.44"
|
||||
- "openstacksdk >= 0.44, < 0.99.0"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -19,8 +19,7 @@ options:
|
||||
type: str
|
||||
aliases: ['id']
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.44"
|
||||
- "openstacksdk >= 0.44, < 0.99.0"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -45,8 +45,7 @@ options:
|
||||
type: list
|
||||
elements: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.44"
|
||||
- "openstacksdk >= 0.44, < 0.99.0"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -18,8 +18,7 @@ options:
|
||||
type: str
|
||||
aliases: ['id']
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.44"
|
||||
- "openstacksdk >= 0.44, < 0.99.0"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -71,10 +71,6 @@ options:
|
||||
IP completely, or only detach it from the server. Default is to detach only.
|
||||
type: bool
|
||||
default: 'no'
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -45,10 +45,6 @@ options:
|
||||
- The status of a floating IP, which can be ``ACTIVE``or ``DOWN``.
|
||||
choices: ['active', 'down']
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -26,10 +26,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -35,10 +35,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -32,10 +32,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -21,10 +21,6 @@ options:
|
||||
- A dictionary of meta data to use for filtering. Elements of
|
||||
this dictionary may be additional dictionaries.
|
||||
type: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -30,10 +30,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -24,10 +24,6 @@ options:
|
||||
- A dictionary of meta data to use for further filtering. Elements of
|
||||
this dictionary may be additional dictionaries.
|
||||
type: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -21,10 +21,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -22,11 +22,6 @@ options:
|
||||
- Name or ID of the role
|
||||
type: str
|
||||
required: false
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -58,10 +58,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -25,10 +25,6 @@ options:
|
||||
- A dictionary of meta data to use for further filtering. Elements of
|
||||
this dictionary may be additional dictionaries.
|
||||
type: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -54,10 +54,12 @@ options:
|
||||
description:
|
||||
- The minimum disk space (in GB) required to boot this image
|
||||
type: int
|
||||
default: 0
|
||||
min_ram:
|
||||
description:
|
||||
- The minimum ram (in MB) required to boot this image
|
||||
type: int
|
||||
default: 0
|
||||
is_public:
|
||||
description:
|
||||
- Whether the image can be accessed publicly. Note that publicizing an image requires admin role by default.
|
||||
@@ -102,10 +104,6 @@ options:
|
||||
- ID of a volume to create an image from.
|
||||
- The volume must be in AVAILABLE state.
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -24,10 +24,6 @@ options:
|
||||
type: dict
|
||||
required: false
|
||||
aliases: ['properties']
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -36,10 +36,6 @@ options:
|
||||
choices: [present, absent, replace]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -28,10 +28,6 @@ options:
|
||||
description:
|
||||
- The last-seen item.
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -35,8 +35,7 @@ options:
|
||||
type: str
|
||||
aliases: ['mapping_name']
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.44"
|
||||
- "openstacksdk >= 0.44, < 0.99.0"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -22,8 +22,7 @@ options:
|
||||
required: true
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.44"
|
||||
- "openstacksdk >= 0.44, < 0.99.0"
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -78,7 +78,6 @@ options:
|
||||
description:
|
||||
- The HTTP URL path of the request sent by the monitor to test the health of a backend member.
|
||||
Must be a string that begins with a forward slash (/). The default URL path is /.
|
||||
requirements: ["openstacksdk"]
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -59,10 +59,6 @@ options:
|
||||
into ACTIVE state.
|
||||
default: 180
|
||||
type: int
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -59,10 +59,6 @@ options:
|
||||
description:
|
||||
- Port used to monitor this member
|
||||
type: int
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -55,10 +55,6 @@ options:
|
||||
into ACTIVE state.
|
||||
default: 180
|
||||
type: int
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -124,6 +124,7 @@ options:
|
||||
accessible from.
|
||||
elements: dict
|
||||
type: list
|
||||
default: []
|
||||
wait:
|
||||
description:
|
||||
- If the module should wait for the load balancer to be created or
|
||||
@@ -135,10 +136,6 @@ options:
|
||||
- The amount of time the module should wait.
|
||||
default: 180
|
||||
type: int
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -76,10 +76,6 @@ options:
|
||||
Network will use Openstack defaults if this option is
|
||||
not provided.
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -24,10 +24,6 @@ options:
|
||||
this dictionary may be additional dictionaries.
|
||||
required: false
|
||||
type: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -61,7 +61,6 @@ options:
|
||||
- Logically AND'ed with other filters
|
||||
choices: ['access_as_shared', 'access_as_external']
|
||||
type: str
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -58,7 +58,6 @@ options:
|
||||
choices: ['present', 'absent']
|
||||
default: present
|
||||
type: str
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -41,10 +41,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -43,10 +43,6 @@ options:
|
||||
default: 'present'
|
||||
choices: ['present', 'absent']
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -131,10 +131,6 @@ options:
|
||||
description:
|
||||
- The dns domain of the port ( only with dns-integration enabled )
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -23,10 +23,6 @@ options:
|
||||
dictionaries. Matching is currently limited to strings within
|
||||
the port dictionary, or strings within nested dictionaries.
|
||||
type: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -45,10 +45,6 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -44,11 +44,6 @@ options:
|
||||
- The resource name (eg. tiny).
|
||||
required: true
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -25,10 +25,6 @@ options:
|
||||
- A dictionary of meta data to use for further filtering. Elements of
|
||||
this dictionary may be additional dictionaries.
|
||||
type: dict
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -51,6 +51,7 @@ options:
|
||||
- Per driver volume storage quotas. Keys should be
|
||||
prefixed with C(gigabytes_) values should be ints.
|
||||
type: dict
|
||||
default: {}
|
||||
injected_file_size:
|
||||
description: Maximum file size in bytes.
|
||||
type: int
|
||||
@@ -116,6 +117,7 @@ options:
|
||||
- Per-driver volume snapshot quotas. Keys should be
|
||||
prefixed with C(snapshots_) values should be ints.
|
||||
type: dict
|
||||
default: {}
|
||||
subnet:
|
||||
description: Number of subnets to allow.
|
||||
type: int
|
||||
@@ -130,15 +132,12 @@ options:
|
||||
- Per-driver volume count quotas. Keys should be
|
||||
prefixed with C(volumes_) values should be ints.
|
||||
type: dict
|
||||
default: {}
|
||||
project:
|
||||
description: Unused, kept for compatability
|
||||
type: int
|
||||
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk >= 0.13.0"
|
||||
- "keystoneauth1 >= 3.4.0"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
@@ -48,10 +48,6 @@ options:
|
||||
- Name or ID of the zone which manages the recordset
|
||||
required: true
|
||||
type: str
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
'''
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user