1 Commits

Author SHA1 Message Date
Jakob Meng
b640e6207c Release 1.7.1 version
Change-Id: I958ba6890a54c59e0ccdb9249d959c745acfb8e9
2022-03-09 20:17:52 +01:00
172 changed files with 2689 additions and 5222 deletions

View File

@@ -1,32 +1,16 @@
# yamllint disable
---
# Keep parent jobs in sync between branches to avoid issues e.g. with job scheduling. Zuul CI will search in master
# branch first when collecting job variants during job freeze which can have unwanted side effects. For example, when
# parent job *-base has been changed in stable/1.0.0 branch, Zuul could still use *-base variants from master branch
# during job freeze on child jobs such as *-ussuri-ansible-2.11 etc.
#
# Do not share job definitions with the job.branches attribute across multiple branches. Do not define jobs which are
# specific to other branches, except for parent jobs which are shared across branches. For example, to not add a job
# which is specific for the stable/1.0.0 branch to the .zuul.yaml in master branch. In particular do not use the
# job.branches directive on a job which will be copied to multiple branches. When you have multiple copies of a job with
# the job.branches attribute, Zuul CI could pick any of the job definitions which might not be the one you expected.
- job:
name: ansible-collections-openstack-functional-devstack-base
name: ansible-collections-openstack-functional-devstack
parent: openstacksdk-functional-devstack
# Do not restrict branches in base jobs because else Zuul would not find a matching
# parent job variant during job freeze when child jobs are on other branches.
branches: ^(stable/1.0.0|master).*$
post-run: ci/playbooks/postlog.yaml
description: |
Run openstack collections functional tests against a devstack
# Do not set job.override-checkout or job.required-projects.override-checkout in base job because
# else Zuul will use this branch when matching variants for parent jobs during job freeze
Run openstack collections functional tests against a master devstack
using master of openstacksdk with latest ansible release
required-projects:
- openstack/ansible-collections-openstack
- openstack/designate
# openstack/devstack is required through parent job openstacksdk-functional-devstack
# openstack/os-client-config is required through parent job openstacksdk-functional-devstack
# openstack/openstacksdk is required through parent job openstacksdk-functional-devstack
irrelevant-files: &ignore_files
- changelogs/.*
- galaxy.*
@@ -36,11 +20,12 @@
- .*\.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_latest
tox_install_siblings: true
tox_envlist: ansible
tox_install_siblings: false
fetch_subunit: false
devstack_plugins:
designate: https://opendev.org/openstack/designate
@@ -53,59 +38,19 @@
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
name: ansible-collections-openstack-functional-devstack-octavia
parent: ansible-collections-openstack-functional-devstack
branches: ^(stable/1.0.0|master).*$
override-checkout: 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
# 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 Octavia 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
with Octavia plugin enabled, using releases of openstacksdk and latest
ansible release. Run it only on Load Balancer changes.
pre-run: ci/playbooks/get_amphora_tarball.yaml
required-projects:
- openstack/octavia
- name: github.com/ansible/ansible
override-checkout: stable-2.12
files:
- ^ci/roles/loadbalancer/.*$
- ^plugins/modules/lb_health_monitor.py
@@ -115,7 +60,7 @@
- ^plugins/modules/loadbalancer.py
vars:
configure_swap_size: 8192
tox_install_siblings: false
tox_envlist: ansible
devstack_plugins:
designate: https://opendev.org/openstack/designate
octavia: https://opendev.org/openstack/octavia
@@ -127,354 +72,415 @@
o-cw: true
o-hk: true
o-hm: true
- job:
name: ansible-collections-openstack-functional-devstack-octavia
parent: ansible-collections-openstack-functional-devstack-octavia-base
branches: stable/1.0.0
description: |
Run openstack collections functional tests against a master devstack
with Octavia plugin enabled, using 0.*.* releases of openstacksdk
and latest ansible release. Run it only on Load Balancer changes.
required-projects:
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
name: openstack/devstack
override-checkout: master
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
name: openstack/openstacksdk
override-checkout: master
vars:
tox_constraints_file: '{{ ansible_user_dir }}/{{ zuul.project.src_dir }}/tests/constraints-openstacksdk-0.x.x.txt'
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-releases
parent: ansible-collections-openstack-functional-devstack-base
branches: stable/1.0.0
parent: ansible-collections-openstack-functional-devstack
override-checkout: master
description: |
Run openstack collections functional tests against a master devstack
using 0.*.* releases of openstacksdk and latest ansible release
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
name: openstack/devstack
override-checkout: master
- # Choose parent devstack job from master branch instead of non-existing stable/1.0.0 branch
name: openstack/openstacksdk
override-checkout: master
using releases of openstacksdk and latest ansible release
vars:
tox_constraints_file: '{{ ansible_user_dir }}/{{ zuul.project.src_dir }}/tests/constraints-openstacksdk-0.x.x.txt'
tox_envlist: ansible
tox_install_siblings: false
# Job with Ansible 2.9 for checking backward compatibility
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.9
parent: ansible-collections-openstack-functional-devstack-base
branches: stable/1.0.0
parent: ansible-collections-openstack-functional-devstack
override-checkout: master
description: |
Run openstack collections functional tests against a master devstack
using 0.*.* releases of openstacksdk and stable 2.9 branch of ansible
using master of openstacksdk and stable 2.9 branch of ansible
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
- # Choose parent devstack job from stable/yoga branch instead of non-existing stable/1.0.0 branch
name: openstack/devstack
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
parent: ansible-collections-openstack-functional-devstack-base
branches: stable/1.0.0
parent: ansible-collections-openstack-functional-devstack
override-checkout: master
description: |
Run openstack collections functional tests against a master devstack
using 0.*.* releases of openstacksdk and stable 2.12 branch of ansible
using master of openstacksdk and stable 2.12 branch of ansible
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- # Choose parent devstack job from stable/yoga branch instead of non-existing stable/1.0.0 branch
name: openstack/devstack
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
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.12
parent: ansible-collections-openstack-functional-devstack-base
branches: stable/1.0.0
parent: ansible-collections-openstack-functional-devstack
override-checkout: master
description: |
Run openstack collections functional tests against a master devstack
using 0.*.* releases of openstacksdk and stable 2.12 branch of ansible
using master of openstacksdk and stable 2.12 branch of ansible
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
- # Choose parent devstack job from stable/yoga branch instead of non-existing stable/1.0.0 branch
name: openstack/devstack
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
- job:
name: ansible-collections-openstack-functional-devstack-ansible-devel
parent: ansible-collections-openstack-functional-devstack
override-checkout: master
description: |
Run openstack collections functional tests against a master devstack
using master of openstacksdk and devel branch of ansible
voting: false
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
vars:
tox_envlist: ansible-2.12
# Stable branches tests
- job:
name: ansible-collections-openstack-functional-devstack-xena-ansible-2.12
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
# will not match that job when it collects job variants.
#
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
# jobs when collecting variants for parent jobs.
parent: ansible-collections-openstack-functional-devstack-base
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a xena devstack
using xena branch of openstacksdk and stable 2.12 branch of ansible
branches: stable/1.0.0
using xena brach of openstacksdk and stable 2.12 branch of ansible
voting: true
override-checkout: stable/xena
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/xena branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/xena branch
name: openstack/devstack
override-checkout: stable/xena
- name: openstack/designate
override-checkout: stable/xena
- name: openstack/openstacksdk
override-checkout: stable/xena
vars:
tox_envlist: ansible_2_12
- job:
name: ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
# will not match that job when it collects job variants.
#
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
# jobs when collecting variants for parent jobs.
parent: ansible-collections-openstack-functional-devstack-base
description: |
Run openstack collections functional tests against a wallaby devstack
using wallaby branch of openstacksdk and stable 2.12 branch of ansible
branches: stable/1.0.0
override-checkout: stable/wallaby
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/wallaby branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/wallaby branch
name: openstack/devstack
override-checkout: stable/wallaby
- name: openstack/designate
override-checkout: stable/wallaby
- name: openstack/openstacksdk
override-checkout: stable/wallaby
vars:
tox_envlist: ansible_2_12
- job:
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
# will not match that job when it collects job variants.
#
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
# jobs when collecting variants for parent jobs.
parent: ansible-collections-openstack-functional-devstack-base
description: |
Run openstack collections functional tests against a victoria devstack
using victoria branch of openstacksdk and stable 2.12 branch of ansible
branches: stable/1.0.0
override-checkout: stable/victoria
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/victoria branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/victoria branch
name: openstack/devstack
override-checkout: stable/victoria
- name: openstack/designate
override-checkout: stable/victoria
- name: openstack/openstacksdk
override-checkout: stable/victoria
vars:
tox_envlist: ansible_2_12
- job:
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
# will not match that job when it collects job variants.
#
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
# jobs when collecting variants for parent jobs.
parent: ansible-collections-openstack-functional-devstack-base
description: |
Run openstack collections functional tests against a ussuri devstack
using ussuri branch of openstacksdk and stable 2.11 branch of ansible
branches: stable/1.0.0
override-checkout: stable/ussuri
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/ussuri branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/ussuri branch
name: openstack/devstack
override-checkout: stable/ussuri
- name: openstack/designate
override-checkout: stable/ussuri
- name: openstack/openstacksdk
override-checkout: stable/ussuri
vars:
tox_envlist: ansible_2_11
- job:
name: ansible-collections-openstack-functional-devstack-train-ansible-2.11
# Do not inherit from any parent job which does not run for branch stable/1.0.0 because Zuul would dismiss this job
# when collecting parent job variants. For example, when job.branches is set to master in a parent job, then Zuul
# will not match that job when it collects job variants.
#
# Do not inherit from any parent job which sets job.required-projects.override-checkout on openstack/devstack
# because Zuul would use that git ref instead of stable branch defined below to checkout projects of parent devstack
# jobs when collecting variants for parent jobs.
parent: ansible-collections-openstack-functional-devstack-base
description: |
Run openstack collections functional tests against a train devstack
using train branch of openstacksdk and stable 2.11 branch of ansible
branches: stable/1.0.0
override-checkout: stable/train
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/train branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/train branch
name: openstack/devstack
override-checkout: stable/train
- name: openstack/designate
override-checkout: train-eol
- name: openstack/openstacksdk
override-checkout: stable/train
vars:
tox_envlist: ansible_2_11
# Linters
- job:
name: openstack-tox-linters-ansible
parent: openstack-tox-linters
description: |
Run openstack collections linter tests using the devel branch of ansible
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
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_latest
tox_envlist: ansible
tox_install_siblings: true
- job:
name: ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a wallaby devstack
using wallaby brach of openstacksdk and stable 2.12 branch of ansible
voting: true
override-checkout: stable/wallaby
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
- name: openstack/openstacksdk
override-checkout: stable/wallaby
vars:
tox_envlist: ansible
tox_install_siblings: true
- job:
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a victoria devstack
using victoria brach of openstacksdk and stable 2.12 branch of ansible
voting: true
override-checkout: stable/victoria
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
- name: openstack/openstacksdk
override-checkout: stable/victoria
vars:
tox_envlist: ansible
tox_install_siblings: true
- job:
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a ussuri devstack
using ussuri brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/ussuri
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-checkout: stable/ussuri
- name: openstack/os-client-config
override-checkout: stable/ussuri
vars:
tox_envlist: ansible
tox_install_siblings: true
- job:
name: ansible-collections-openstack-functional-devstack-train-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a train devstack
using train brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/train
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-checkout: stable/train
- name: openstack/os-client-config
override-checkout: stable/train
vars:
tox_envlist: ansible
tox_install_siblings: true
- job:
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a queens devstack
using master branch of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/queens
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- name: openstack/openstacksdk
# Run queens with highest possible py2 version of SDK
override-checkout: stable/train
vars:
tox_envlist: ansible
tox_install_siblings: true
# Linters
- job:
name: openstack-tox-linters-ansible-devel
parent: openstack-tox-linters-ansible
nodeset: ubuntu-jammy
parent: openstack-tox-linters
nodeset: ubuntu-focal
description: |
Run openstack collections linter tests using the devel branch of ansible
# non-voting because we can't prevent ansible devel from breaking us
voting: false
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
vars:
python_version: '3.10'
bindep_profile: test py310
tox_envlist: linters-2.12
python_version: 3.8
bindep_profile: test py38
- job:
name: openstack-tox-linters-ansible-2.12
parent: openstack-tox-linters-ansible
parent: openstack-tox-linters
nodeset: ubuntu-focal
description: |
Run openstack collections linter tests using the 2.12 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
vars:
ensure_tox_version: '<4'
tox_envlist: linters_2_12
tox_envlist: linters-2.12
python_version: 3.8
bindep_profile: test py38
- job:
name: openstack-tox-linters-ansible-2.9
parent: openstack-tox-linters
nodeset: ubuntu-bionic
description: |
Run openstack collections linter tests using the 2.9 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
vars:
tox_envlist: linters-2.9
# Cross-checks with other projects
- job:
name: bifrost-collections-src
parent: bifrost-integration-tinyipa-ubuntu-focal
override-checkout: stable/yoga
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- openstack/ansible-collections-openstack
- # always use existing branch when collecting parent job variants, refer to git blame for rationale.
name: openstack/bifrost
# Yoga has the latest SDK release of the 0.*.* series atm
override-checkout: stable/yoga
- job:
name: bifrost-keystone-collections-src
parent: bifrost-integration-tinyipa-keystone-ubuntu-focal
override-checkout: stable/yoga
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- openstack/ansible-collections-openstack
- # always use existing branch when collecting parent job variants, refer to git blame for rationale.
name: openstack/bifrost
# Yoga has the latest SDK release of the 0.*.* series atm
override-checkout: stable/yoga
# TripleO jobs
- job:
name: tripleo-ci-centos-8-standalone-osa
parent: tripleo-ci-base-standalone-centos-8
vars:
featureset: '052'
consumer_job: false
build_container_images: true
irrelevant-files: &irr_files
- .*molecule.*
- ^.*\.md$
- ^.*\.rst$
- ^docs/.*$
- ^contrib/.*$
- ^changelogs/.*$
- ^meta/.*$
- ^tests/.*$
- ^tools/.*$
- ^requirements.*$
- ^test-requirements.*$
- ^setup.*$
- tox.ini
# Run only on files used in TripleO
files: &ooo_files
- ^plugins/modules/catalog_service.*$
- ^plugins/modules/endpoint.*$
- ^plugins/modules/identity_domain.*$
- ^plugins/modules/identity_domain_info.*$
- ^plugins/modules/identity_role.*$
- ^plugins/modules/identity_user.*$
- ^plugins/modules/keypair.*$
- ^plugins/modules/project.*$
- ^plugins/modules/role_assignment.*$
- ^plugins/modules/stack.*$
- ^plugins/module_utils/openstack.*$
- job:
name: tripleo-ci-centos-9-standalone-osa
parent: tripleo-ci-centos-8-standalone-osa
nodeset: single-centos-9-node
branches: ^(stable/1.0.0|master).*$
override-checkout: master
vars:
containers_base_image: quay.io/centos/centos:stream9
consumer_job: false
build_container_images: true
branch_override: master
files: *ooo_files
irrelevant-files: *irr_files
- job:
name: tripleo-ci-centos-8-standalone-train-osa
parent: tripleo-ci-centos-8-standalone-osa
override-checkout: stable/train
vars:
branch_override: stable/train
- job:
name: tripleo-ci-centos-8-standalone-wallaby-osa
parent: tripleo-ci-centos-8-standalone-osa
override-checkout: stable/wallaby
vars:
branch_override: stable/wallaby
- job:
name: tripleo-ci-centos-9-standalone-wallaby-osa
parent: tripleo-ci-centos-9-standalone-osa
branches: ^(stable/1.0.0|master).*$
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
- ansible-collections-openstack-functional-devstack-releases:
dependencies:
- openstack-tox-linters-ansible-2.12
- openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack:
dependencies: &deps_unit_lint
- tox-pep8
- openstack-tox-linters-ansible-2.9
- openstack-tox-linters-ansible-2.12
- ansible-collections-openstack-functional-devstack-releases:
dependencies: *deps_unit_lint
- ansible-collections-openstack-functional-devstack-ansible-2.9:
dependencies: *deps_unit_lint
- ansible-collections-openstack-functional-devstack-ansible-2.12:
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
voting: false
- 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-wallaby-osa:
dependencies: *deps_unit_lint
- tripleo-ci-centos-8-standalone-train-osa:
voting: false
dependencies: *deps_unit_lint
- tripleo-ci-centos-9-standalone-osa:
voting: false
dependencies: *deps_unit_lint
- tripleo-ci-centos-9-standalone-wallaby-osa:
voting: false
dependencies: *deps_unit_lint
gate:
jobs:
- tox-pep8
- openstack-tox-linters-ansible-2.12
- openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack
- ansible-collections-openstack-functional-devstack-releases
- ansible-collections-openstack-functional-devstack-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.12
- ansible-collections-openstack-functional-devstack-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-wallaby-osa
periodic:
jobs:
- openstack-tox-linters-ansible-devel
- openstack-tox-linters-ansible-2.12
- openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack
- ansible-collections-openstack-functional-devstack-releases
- ansible-collections-openstack-functional-devstack-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.12
@@ -483,16 +489,21 @@
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11
- bifrost-collections-src
- bifrost-keystone-collections-src
- ansible-collections-openstack-functional-devstack-magnum
- ansible-collections-openstack-functional-devstack-octavia
- tripleo-ci-centos-9-standalone-wallaby-osa
- tripleo-ci-centos-9-standalone-osa
- tripleo-ci-centos-8-standalone-train-osa
- tripleo-ci-centos-8-standalone-wallaby-osa
experimental:
jobs:
- ansible-collections-openstack-functional-devstack-ansible-2.11
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11
tag:
jobs:

View File

@@ -5,132 +5,6 @@ Openstack Cloud Ansilbe modules Release Notes
.. contents:: Topics
v1.10.0
=======
Release Summary
---------------
Enable logging of openstacksdk activities and warn users about incompatible openstacksdk releases when using inventory plugin
Bugfixes
--------
- Add SDK logging option for openstack ansible collections
- Don't use deprecated distutils from python 3.10
- Ensure openstacksdk compatibility in inventory plugin
- Lowered maximum OpenStack SDK version to 0.98.999 in inventory plugin
Known Issues
------------
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection 2.0.0 or later which is currently under development.
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior to 0.99.0 only.
v1.9.1
======
Release Summary
---------------
Bugfix in keypair module
Bugfixes
--------
- Do not remove trailing spaces when reading public key in keypair module
Known Issues
------------
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection 2.0.0 or later which is currently under development.
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior to 0.99.0 only.
v1.9.0
======
Release Summary
---------------
This release will enforce openstacksdk<0.99.0, has a dozen modules refactored and several bugs fixed.
Bugfixes
--------
- Added support for specifying a maximum version of the OpenStack SDK
- Constrain filters in compute_service_info to SDK >= 0.53.0
- Drop username from return values of identity_user_info
- Fix logic in routers_info
- Fixed return value disable{d,s}_reason in compute_service_info module
- Fixed return values in compute_service_info module again
- Follow up to bump of minimum required OpenStack SDK release to SDK 0.36.0 (Train)
- Lowered maximum OpenStack SDK version to 0.98.999
- Move dns zone info to use proxy layer
- Refactored catalog_service module
- Refactored endpoint module
- Refactored host_aggregate module
- Refactored identity_domain_info module
- Refactored identity_group_info module
- Refactored identity_role module
- Refactored identity_role_info module
- Refactored identity_user module
- Refactored identity_user_info module
- Refactored image_info module
- Refactored keypair_info module
- Refactored recordset module
- Refactored role_assignment module
- Set owner in image module
- Support description in sg-rule creation
- Warn users about us breaking backward compatibility
Known Issues
------------
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection 2.0.0 or later which is currently under development.
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior to 0.99.0 only.
v1.8.0
======
Release Summary
---------------
Subnet pool module and bugfixes
Bugfixes
--------
- Add 'all_projects' to server_action module
- Add subnet pool module
- Bumped minimum required OpenStack SDK release to SDK 0.36.0 (Train)
- Changed compute_flavor_info module to use OpenStack SDK's proxy layer
- Dropped deprecated return values in floating_ip_info and assert remaining fields
- Fix ansible-lint issues for the newest version
- Fix assertion after stack deletion
- Handle aggregate host list set to None
- Reenabled check-import.sh which tests imports to Ansible Galaxy
- Remove old, unsupported parameters from documentation in image_info module
- Router - Remove unneeded 'filter' parameter
- Updated return value docs of compute_service_info module
New Modules
-----------
- openstack.cloud.subnet_pool - Create or Delete subnet pools from OpenStack.
v1.7.2
======
Release Summary
---------------
Bugfixes
Bugfixes
--------
- Fix collection guidelines
v1.7.1
======

40
CONTRIBUTING.rst Normal file
View File

@@ -0,0 +1,40 @@
.. _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

152
README.md
View File

@@ -1,75 +1,58 @@
[![OpenDev Zuul Builds - Ansible OpenStack Collection](https://zuul-ci.org/gated.svg)](
http://zuul.opendev.org/t/openstack/builds?project=openstack%2Fansible-collections-openstack)
[![OpenDev Zuul Builds - Ansible Collection OpenStack](https://zuul-ci.org/gated.svg)](http://zuul.opendev.org/t/openstack/builds?project=openstack%2Fansible-collections-openstack#)
# Ansible OpenStack Collection
# Ansible Collection: openstack.cloud
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.
## Branches and Non Backward Compatibility ⚠️
This repo hosts the `openstack.cloud` Ansible Collection.
Our codebase has been split into two separate release series, `2.x.x` and `1.x.x`:
The collection includes the Openstack modules and plugins supported by Openstack community to help the management of Openstack infrastructure.
* `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 ⚠️
## Installation and Usage
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!
### Installing dependencies
[openstacksdk]: https://opendev.org/openstack/openstacksdk
For using the Openstack Cloud collection firstly you need to install `ansible` and `openstacksdk` Python modules on your Ansible controller.
For example with pip:
## Installation
For using this collection, first you have to install both Python packages `ansible` and `openstacksdk` on your Ansible
controller:
```sh
pip install "ansible>=2.9" "openstacksdk>=0.36,<0.99.0"
```bash
pip install ansible openstacksdk
```
[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.
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.
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.
---
Before using this collection, you have to install it with `ansible-galaxy`:
#### NOTE
```sh
ansible-galaxy collection install openstack.cloud
```
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.
You can also include it in a `requirements.yml` file:
---
### 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:
```yaml
collections:
- name: openstack.cloud
```
And then install it with:
### Playbooks
```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:
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:
@@ -87,7 +70,7 @@ Or you can add the full namespace and collection name in the `collections` eleme
```yaml
---
- name: Using the Ansible OpenStack Collection
- name: Using Openstack Cloud collection
hosts: localhost
collections:
- openstack.cloud
@@ -100,64 +83,49 @@ Or you can add the full namespace and collection name in the `collections` eleme
device: /dev/vdb
```
[Ansible module defaults][ansible-module-defaults] are supported as well:
### Usage
```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:
See the collection docs at Ansible site:
- name: List servers
openstack.cloud.server_info:
```
* [openstack.cloud collection docs (version released in Ansible package)](https://docs.ansible.com/ansible/latest/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)
* [openstack.cloud collection docs (devel version)](https://docs.ansible.com/ansible/devel/collections/openstack/cloud/index.html)
## Contributing
Thank you for your interest in our Ansible OpenStack collection ☺️
For information on contributing, please see [CONTRIBUTING](https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/CONTRIBUTING.rst)
There are many ways in which you can participate in the project, for example:
- [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.
- 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
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.
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
## Communication
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) 🍪
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/).
## License
GNU General Public License v3.0 or later
See [LICENCE](COPYING) to see the full text.
See [LICENCE](https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/COPYING) to see the full text.

View File

@@ -70,21 +70,6 @@ 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:
@@ -328,79 +313,3 @@ releases:
- lb_member - Add monitor_[address,port] parameter
release_summary: Bugfixes
release_date: '2022-03-08'
1.7.2:
changes:
bugfixes:
- Fix collection guidelines
release_summary: Bugfixes
release_date: '2022-03-10'
1.8.0:
changes:
bugfixes:
- Add 'all_projects' to server_action module
- Add subnet pool module
- Bumped minimum required OpenStack SDK release to SDK 0.36.0 (Train)
- Changed compute_flavor_info module to use OpenStack SDK's proxy layer
- Dropped deprecated return values in floating_ip_info and assert remaining
fields
- Fix ansible-lint issues for the newest version
- Fix assertion after stack deletion
- Handle aggregate host list set to None
- Reenabled check-import.sh which tests imports to Ansible Galaxy
- Remove old, unsupported parameters from documentation in image_info module
- Router - Remove unneeded 'filter' parameter
- Updated return value docs of compute_service_info module
release_summary: Subnet pool module and bugfixes
modules:
- description: Create or Delete subnet pools from OpenStack.
name: subnet_pool
namespace: ''
release_date: '2022-04-08'
1.9.0:
changes:
bugfixes:
- Added support for specifying a maximum version of the OpenStack SDK
- Constrain filters in compute_service_info to SDK >= 0.53.0
- Drop username from return values of identity_user_info
- Fix logic in routers_info
- Fixed return value disable{d,s}_reason in compute_service_info module
- Fixed return values in compute_service_info module again
- Follow up to bump of minimum required OpenStack SDK release to SDK 0.36.0
(Train)
- Lowered maximum OpenStack SDK version to 0.98.999
- Move dns zone info to use proxy layer
- Refactored catalog_service module
- Refactored endpoint module
- Refactored host_aggregate module
- Refactored identity_domain_info module
- Refactored identity_group_info module
- Refactored identity_role module
- Refactored identity_role_info module
- Refactored identity_user module
- Refactored identity_user_info module
- Refactored image_info module
- Refactored keypair_info module
- Refactored recordset module
- Refactored role_assignment module
- Set owner in image module
- Support description in sg-rule creation
- Warn users about us breaking backward compatibility
known_issues:
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection
2.0.0 or later which is currently under development.
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior
to 0.99.0 only.
release_summary: This release will enforce openstacksdk<0.99.0, has a dozen
modules refactored and several bugs fixed.
release_date: '2022-08-25'
1.9.1:
changes:
bugfixes:
- Do not remove trailing spaces when reading public key in keypair module
known_issues:
- For compatibility with OpenStack SDK >= 0.99.0 use Ansible OpenStack collection
2.0.0 or later which is currently under development.
- Release series 1.x.x of this collection is compatible to OpenStack SDK prior
to 0.99.0 only.
release_summary: Bugfix in keypair module
release_date: '2022-09-08'

View File

@@ -0,0 +1,6 @@
- hosts: all
tasks:
- name: Download amphora tarball
get_url:
url: "https://tarballs.openstack.org/octavia/test-images/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
dest: /tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2

View File

@@ -1,7 +1,7 @@
---
- hosts: all
vars:
collection_path: "{{ ansible_user_dir }}/{{ zuul.project.src_dir }}"
collection_path: "{{ ansible_user_dir }}/{{ zuul.projects['opendev.org/openstack/ansible-collections-openstack'].src_dir }}"
build_collection_path: /tmp/collection_built/
ansible_galaxy_path: "~/.local/bin/ansible-galaxy"
@@ -63,21 +63,12 @@
url = {{ ansible_galaxy_info.url }}
token = {{ ansible_galaxy_info.token }}
- name: Get content of galaxy.yml
slurp:
src: "{{ collection_path }}/galaxy.yml"
register: galaxy_vars
- name: Parse yaml into variable
set_fact:
galaxy_yaml: "{{ galaxy_vars['content'] | b64decode | from_yaml }}"
- name: Publish collection to Ansible Galaxy / Automation Hub
environment:
ANSIBLE_CONFIG: "{{ _ansiblecfg_tmp.path }}"
shell: >-
{{ ansible_galaxy_path }} collection publish -vvv
{{ build_collection_path }}/{{ galaxy_yaml.namespace }}-{{ galaxy_yaml.name }}-{{ version_tag }}.tar.gz
{{ build_collection_path }}/openstack-cloud-{{ version_tag }}.tar.gz
always:
- name: Shred ansible-galaxy credentials

View File

@@ -1,5 +0,0 @@
---
collections:
- ansible.posix
- ansible.utils
- community.general

View File

@@ -1,94 +0,0 @@
---
- name: Delete service test
openstack.cloud.catalog_service:
cloud: "{{ cloud }}"
service_type: test
name: test
state: absent
register: service_delete
- name: Assert changed is set to false
assert:
that:
- not service_delete.changed
- name: Create a service for test
openstack.cloud.catalog_service:
cloud: "{{ cloud }}"
name: "test_service"
state: present
service_type: test_type
description: "Test service"
register: service_test
- name: Verify returned values
assert:
that:
- item in service_test.service
loop:
- description
- id
- enabled
- name
- service_type
- type
- name: Check if the service test was created successfully
openstack.cloud.catalog_service:
cloud: "{{ cloud }}"
service_type: test
name: test
register: service_created
- name: Verify returned values
assert:
that:
- item in service_created.service
loop:
- description
- id
- enabled
- name
- type
- service_type
- name: Update service test
openstack.cloud.catalog_service:
cloud: "{{ cloud }}"
service_type: test
description: "A new description"
is_enabled: False
name: test
register: service_test
- name: Check if description and enabled were updated
assert:
that:
- service_test.service.description == "A new description"
- not (service_test.service.enabled|bool)
- name: Delete service test
openstack.cloud.catalog_service:
cloud: "{{ cloud }}"
service_type: test
name: test
state: absent
register: service_deleted
- name: Verify if service was deleted
assert:
that:
- service_deleted.changed
- name: Delete service test again
openstack.cloud.catalog_service:
cloud: "{{ cloud }}"
service_type: test
name: test
state: absent
register: service_deleted
- name: Assert changed is set to false
assert:
that:
- not service_deleted.changed

View File

@@ -1,18 +0,0 @@
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

View File

@@ -1,181 +0,0 @@
---
- 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

View File

@@ -1,40 +0,0 @@
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

View File

@@ -1,81 +0,0 @@
---
- 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

View File

@@ -1,33 +0,0 @@
---
- name: List flavors
openstack.cloud.compute_flavor_info:
cloud: "{{ cloud }}"
- name: List flavors with filter
openstack.cloud.compute_flavor_info:
cloud: "{{ cloud }}"
name: "m1.tiny"
register: flavor
- name: Check output of list flavors with filter
assert:
that:
- flavor.openstack_flavors | length == 1
- name: Assert fields on SDK 0.*
when: sdk_version is version(1.0, '<')
assert:
that:
- '["name", "description", "disk", "is_public", "ram",
"vcpus", "swap", "ephemeral", "is_disabled", "rxtx_factor", "id",
"extra_specs"] | difference(flavor.openstack_flavors.0.keys())
| length == 0'
- name: Assert fields on SDK 1.*
when: sdk_version is version(1.0, '>=')
assert:
that:
- '["name", "original_name", "description", "disk", "is_public", "ram",
"vcpus", "swap", "ephemeral", "is_disabled", "rxtx_factor", "id",
"extra_specs"] | difference(flavor.openstack_flavors.0.keys())
| length == 0'

View File

@@ -18,6 +18,53 @@
- debug: var=updated_dns_zone
- name: Create a recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ updated_dns_zone.zone.name }}"
name: "{{ recordset_name }}"
recordset_type: "a"
records: "{{ records }}"
register: recordset
- name: Verify recordset info
assert:
that:
- recordset["recordset"].name == recordset_name
- recordset["recordset"].zone_name == dns_zone.zone.name
- recordset["recordset"].records == records
- name: Update a recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ updated_dns_zone.zone.name }}"
name: "{{ recordset_name }}"
recordset_type: "a"
records: "{{ updated_records }}"
description: "new test recordset"
register: updated_recordset
- name: Verify recordset info
assert:
that:
- updated_recordset["recordset"].zone_name == dns_zone.zone.name
- updated_recordset["recordset"].name == recordset_name
- updated_recordset["recordset"].records == updated_records
- name: Delete recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ updated_dns_zone.zone.name }}"
name: "{{ recordset.recordset.name }}"
state: absent
register: deleted_recordset
- name: Verify recordset deletion
assert:
that:
- deleted_recordset is successful
- deleted_recordset is changed
- name: Delete dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"

View File

@@ -34,14 +34,6 @@
- zone is not changed
- zone.zones | length == 1
- name: Assert keys exist
assert:
that:
- '["action", "attributes", "created_at", "description", "email",
"links", "masters", "name", "pool_id", "project_id", "serial",
"status", "ttl", "type", "updated_at", "id"] |
difference(zone.zones.0.keys()) | length == 0'
- name: Drop created dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"

View File

@@ -1,65 +0,0 @@
---
- name: Create a service for compute
openstack.cloud.endpoint:
cloud: "{{ cloud }}"
service: nova
endpoint_interface: internal
url: http://controller:9292
region: RegionOne
state: present
register: endpoint_test
- name: Ensure service was created
assert:
that:
- endpoint_test.endpoint.id is defined
- name: Ensure service have the proper endpoint
assert:
that:
- endpoint_test.endpoint.url == "http://controller:9292"
- name: Create service for compute again
openstack.cloud.endpoint:
cloud: "{{ cloud }}"
service: nova
endpoint_interface: internal
url: http://controller:9292
region: RegionOne
state: present
register: endpoint_again
- name: Ensure changed is false
assert:
that:
- not endpoint_again.changed
- name: Update endpoint url
openstack.cloud.endpoint:
cloud: "{{ cloud }}"
service: nova
endpoint_interface: internal
url: http://controller:9393
region: RegionOne
state: present
register: endpoint_updated
- name: Ensure endpoint was updated
assert:
that:
- endpoint_updated.endpoint.url == "http://controller:9393"
- name: Delete endpoint
openstack.cloud.endpoint:
cloud: "{{ cloud }}"
service: nova
endpoint_interface: internal
url: http://controller:9393
region: RegionOne
state: absent
register: endpoint_deleted
- name: Ensure endpoint was deleted
assert:
that:
- endpoint_deleted.changed

View File

@@ -1,16 +1,5 @@
---
# 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 }}"
@@ -154,7 +143,7 @@
cloud: "{{ cloud }}"
state: present
name: ansible_server1
image: "{{ image_name }}"
image: "{{ image }}"
flavor: m1.tiny
nics:
# one nic only else simple, first floating ip test does not work
@@ -184,7 +173,7 @@
cloud: "{{ cloud }}"
state: present
name: ansible_server2
image: "{{ image_name }}"
image: "{{ image }}"
flavor: m1.tiny
nics:
- port-name: ansible_internal_port2

View File

@@ -9,13 +9,3 @@
that:
- fips is success
- fips is not changed
- name: assert fields
when: fips.floating_ips|length > 0
assert:
that:
# allow new fields to be introduced but prevent fields from being removed
- '["created_at", "description", "dns_domain", "dns_name", "fixed_ip_address", "floating_ip_address",
"floating_network_id", "id", "name", "port_details", "port_id", "project_id", "qos_policy_id",
"revision_number", "router_id", "status", "subnet_id", "tags", "updated_at"]|
difference(fips.floating_ips.0.keys())|length == 0'

View File

@@ -1,10 +0,0 @@
# Parameter deleted has been renamed to is_deleted in openstacksdk 0.52.0,
# hence we cannot test with this list here.
# Ref.: https://github.com/openstack/openstacksdk/commit/b60915aab3ee0348f3e3cc8aa548f94d2a68b7eb
expected_fields:
- availability_zone
- hosts
- id
- location
- metadata
- name

View File

@@ -1,99 +0,0 @@
---
- name: ensure aggregate doesn't exist before tests
openstack.cloud.host_aggregate:
cloud: "{{ cloud }}"
state: absent
name: test_aggregate
register: aggregate
- block:
- name: create aggregate
openstack.cloud.host_aggregate:
cloud: "{{ cloud }}"
state: present
name: test_aggregate
hosts:
- "{{ ansible_hostname }}"
register: aggregate
- name: assert aggregate is changed
assert:
that: aggregate is changed
- name: assert aggregate fields
assert:
that: item in aggregate.aggregate
loop: "{{ expected_fields }}"
- block:
- name: recreate aggregate
openstack.cloud.host_aggregate:
cloud: "{{ cloud }}"
state: present
name: test_aggregate
hosts:
- "{{ ansible_hostname }}"
register: aggregate
- name: assert aggregate is not changed
assert:
that: aggregate is not changed
- name: assert aggregate fields
assert:
that: item in aggregate.aggregate
loop: "{{ expected_fields }}"
- block:
- name: update aggregate
openstack.cloud.host_aggregate:
cloud: "{{ cloud }}"
state: present
name: test_aggregate
metadata:
ssd: "true"
hosts:
- "{{ ansible_hostname }}"
register: aggregate
- name: assert aggregate is changed
assert:
that: aggregate is changed
- name: assert aggregate fields
assert:
that: item in aggregate.aggregate
loop: "{{ expected_fields }}"
- block:
- name: purge hosts
openstack.cloud.host_aggregate:
cloud: "{{ cloud }}"
state: present
name: test_aggregate
hosts: []
purge_hosts: true
register: aggregate
- name: assert hosts were purged
assert:
that:
- aggregate is changed
- aggregate.aggregate.hosts | length == 0
- name: assert aggregate fields
assert:
that: item in aggregate.aggregate
loop: "{{ expected_fields }}"
- block:
- name: delete aggregate
openstack.cloud.host_aggregate:
cloud: "{{ cloud }}"
state: absent
name: test_aggregate
register: aggregate
- name: assert aggregate is changed
assert:
that: aggregate is changed

View File

@@ -1,8 +0,0 @@
domain_name: domain_info_test_domain
unexistent_domain_name: domain_info_unexistent_domain
disabled_domain_name: test_domain_disabled
domain_info_fields:
- description
- id
- enabled
- name

View File

@@ -1,72 +0,0 @@
---
- block:
- name: Ensure domain does not exist
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
state: absent
name: "{{ unexistent_domain_name }}"
- name: Get unexistent domain
openstack.cloud.identity_domain_info:
cloud: "{{ cloud }}"
name: "{{ unexistent_domain_name }}"
register: domain_info
- name: Assert no results returned
assert:
that: not domain_info.openstack_domains
- block:
- name: Ensure domain exists
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
state: present
name: "{{ domain_name }}"
description: "test description"
register: domain
- name: Get domain
openstack.cloud.identity_domain_info:
cloud: "{{ cloud }}"
name: "{{ domain_name }}"
register: domain_info
- name: Assert one result exists
assert:
that: domain_info.openstack_domains | length == 1
- name: Assert fields are present
assert:
that: item in domain_info.openstack_domains[0]
loop: "{{ domain_info_fields }}"
- name: Assert returned value
assert:
that:
- domain_info.openstack_domains[0].description == domain.domain.description
- block:
- name: Get all domains
openstack.cloud.identity_domain_info:
cloud: "{{ cloud }}"
register: domain_info
- block:
- name: Ensure disabled domain exists
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
state: present
name: "{{ disabled_domain_name }}"
enabled: false
description: "test description"
register: domain
- name: Get filtered domains
openstack.cloud.identity_domain_info:
cloud: "{{ cloud }}"
filters:
enabled: true
register: domain_info
- name: Assert at least one result
assert:
that: domain_info.openstack_domains | length >= 1
- name: Assert returned value
assert:
that: item.enabled == true
loop: "{{ domain_info.openstack_domains }}"

View File

@@ -1,74 +0,0 @@
---
- name: List group by domain_id
openstack.cloud.identity_group_info:
cloud: "{{ cloud }}"
domain: default
register: group_domain
- name: Assert groups were returned
assert:
that:
- group_domain.openstack_groups | length > 0
- group_domain.openstack_groups[0].domain_id == 'default'
- group_domain.openstack_groups[0].id is defined
- group_domain.openstack_groups[0].description is defined
- group_domain.openstack_groups[0].name is defined
- name: List group by domain_id and group
openstack.cloud.identity_group_info:
cloud: "{{ cloud }}"
domain: default
name: admins
register: groups_info
- name: Assert groups by domain_id and grouph returned
assert:
that:
- groups_info.openstack_groups | length > 0
- groups_info.openstack_groups[0].domain_id == 'default'
- groups_info.openstack_groups[0].id is defined
- groups_info.openstack_groups[0].description is defined
- groups_info.openstack_groups[0].name is defined
- name: List group by filter
openstack.cloud.identity_group_info:
cloud: "{{ cloud }}"
domain: default
filters:
name: admins
register: groups_filter
- name: Assert group by filter returned
assert:
that:
- groups_filter.openstack_groups | length > 0
- groups_filter.openstack_groups[0].domain_id == 'default'
- groups_filter.openstack_groups[0].id is defined
- groups_filter.openstack_groups[0].description is defined
- groups_filter.openstack_groups[0].name is defined
- name: Verify returned values of group info
assert:
that:
- item in groups_info.openstack_groups[0]
loop:
- description
- domain_id
- id
- name
- name: List group by group name
openstack.cloud.identity_group_info:
cloud: "{{ cloud }}"
name: admins
register: groups_name
- name: Assert group by name returned
assert:
that:
- groups_name.openstack_groups | length > 0
- groups_name.openstack_groups[0].domain_id == 'default'
- groups_name.openstack_groups[0].id is defined
- groups_name.openstack_groups[0].description is defined
- groups_name.openstack_groups[0].name is defined
- groups_name.openstack_groups[0].name == 'admins'

View File

@@ -1,5 +0,0 @@
role_name: ansible_keystone_role
expected_fields:
- domain_id
- id
- name

View File

@@ -1,83 +0,0 @@
---
- name: Cleanup before tests
block:
- openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: absent
name: "{{ role_name }}"
- block:
- name: Delete unexistent role
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: absent
name: "{{ role_name }}"
register: role
- name: Assert role didn't change
assert:
that: role is not changed
- block:
- name: Create keystone role
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: present
name: "{{ role_name }}"
register: role
- name: Try to get role
openstack.cloud.identity_role_info:
cloud: "{{ cloud }}"
name: "{{ role_name }}"
register: roles
- name: Assert role found
assert:
that:
- roles.openstack_roles | length == 1
- name: Assert role changed
assert:
that: role is changed
- name: Assert return fields
assert:
that: item in role['role']
loop: "{{ expected_fields }}"
- name: Assert return value
assert:
that: role['role']['name'] == role_name
- name: Assert retrieved values
assert:
that: roles.openstack_roles[0].name == role_name
- block:
- name: Create existing keystone role
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: present
name: "{{ role_name }}"
register: role
- name: Assert role not changed
assert:
that: role is not changed
- name: Assert return fields
assert:
that: item in role['role']
loop: "{{ expected_fields }}"
- block:
- name: Delete keystone role
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: absent
name: "{{ role_name }}"
register: role
- name: Assert role changed
assert:
that: role is changed
- name: Try to get role
openstack.cloud.identity_role_info:
cloud: "{{ cloud }}"
name: "{{ role_name }}"
register: roles
- name: Assert no role found
assert:
that:
- roles.openstack_roles | length == 0

View File

@@ -1,62 +0,0 @@
- name: Ensure role does not exist before tests
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: absent
name: test_role
- name: Get unexistent role
openstack.cloud.identity_role_info:
cloud: "{{ cloud }}"
name: test_role
register: roleinfo
- debug:
var: roleinfo
- name: Assert that no results were returned
assert:
that: not roleinfo.openstack_roles
- name: Create keystone role
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: present
name: test_role
- name: Create second role
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: present
name: test_role2
- name: Get role by name
openstack.cloud.identity_role_info:
cloud: "{{ cloud }}"
name: test_role
register: roleinfo
- debug:
var: roleinfo
- name: Assert that only one result was returned
assert:
that: roleinfo.openstack_roles | length == 1
- name: Assert that roleinfo has fields
assert:
that: item in roleinfo.openstack_roles[0]
loop:
- domain_id
- id
- name
- name: Post-test cleanup
block:
- name: Clean up roles
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: absent
name: "{{ item }}"
loop:
- test_role
- test_role2

View File

@@ -1,9 +0,0 @@
os_identity_user_fields:
- default_project_id
- description
- domain_id
- email
- enabled
- id
- name
- username

View File

@@ -1,197 +0,0 @@
---
- name: setup
block:
- name: Delete user before running tests
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: "{{ item }}"
loop:
- ansible_user
- ansible_user2
register: user
- block:
- name: Delete unexistent user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: ansible_user
register: user
- name: Ensure user was not changed
assert:
that: user is not changed
- block:
- name: Create a user without a password
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
email: ansible.user@nowhere.net
domain: default
default_project: demo
register: user
- name: Ensure user was changed
assert:
that: user is changed
- name: Ensure user has fields
assert:
that: item in user['user']
loop: "{{ os_identity_user_fields }}"
- name: Fail when update_password is always but no password specified
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
update_password: always
email: ansible.user@nowhere.net
domain: default
default_project: demo
register: user
ignore_errors: yes
- assert:
that: user.msg == "update_password is always but a password value is missing"
- name: Delete user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: ansible_user
- block:
- name: Create user with a password
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
password: secret
email: ansible.user@nowhere.net
update_password: on_create
domain: default
default_project: demo
register: user
- name: Assert user has fields
assert:
that: item in user['user']
loop: "{{ os_identity_user_fields }}"
- block:
- name: Create identical user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
password: secret
email: ansible.user@nowhere.net
update_password: on_create
domain: default
default_project: demo
register: user
- name: Assert user was not changed
assert:
that: user is not changed
- name: Assert user has fields
assert:
that: item in user['user']
loop: "{{ os_identity_user_fields }}"
- block:
- name: Update user with password
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
password: secret2
email: updated.ansible.user@nowhere.net
register: user
- name: Ensure user was changed
assert:
that: user is changed
- name: Ensure user has fields
assert:
that: item in user['user']
loop: "{{ os_identity_user_fields }}"
- name: Update user without password and update_password set to always
block:
- openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
update_password: always
email: updated.ansible.user@nowhere.net
register: user
ignore_errors: yes
- assert:
that: user.msg == "update_password is always but a password value is missing"
- block:
- name: Ensure user with update_password set to on_create
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
update_password: on_create
password: secret3
email: updated.ansible.user@nowhere.net
register: user
- name: Ensure user was not changed
assert:
that: user is not changed
- block:
- name: Ensure user with update_password set to always
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
update_password: always
password: secret3
email: updated.ansible.user@nowhere.net
register: user
- name: Ensure user was changed
assert:
that: user is changed
- block:
- name: Create user without a password
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user2
password: secret
email: ansible.user2@nowhere.net
update_password: on_create
domain: default
default_project: demo
register: user
- name: Assert user has fields
assert:
that: item in user['user']
loop: "{{ os_identity_user_fields }}"
- block:
- name: Delete user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: ansible_user
- name: Ensure user was changed
assert:
that: user is changed

View File

@@ -1,9 +0,0 @@
os_expected_user_info_fields:
- default_project_id
- description
- domain_id
- email
- enabled
- id
- name
- username

View File

@@ -1,72 +0,0 @@
- name: Ensure user does not exist before tests
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: ansible_user
- block:
- name: Get unexistent user
openstack.cloud.identity_user_info:
cloud: "{{ cloud }}"
name: ansible_user
register: userinfo
- name: Ensure nothing was returned
assert:
that: not userinfo.openstack_users
- block:
- name: Create user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
password: secret
email: ansible.user@nowhere.net
domain: default
default_project: demo
register: user
- name: Create second user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user2
password: secret
email: ansible.user2@nowhere.net
domain: default
default_project: demo
register: user
- name: Get first user info
openstack.cloud.identity_user_info:
cloud: "{{ cloud }}"
name: ansible_user
register: userinfo
- name: Assert only one result exists
assert:
that:
- userinfo.openstack_users | length == 1
- name: Assert userinfo has fields
assert:
that:
- item in userinfo.openstack_users[0]
loop: "{{ os_expected_user_info_fields }}"
- block:
- name: Get all users
openstack.cloud.identity_user_info:
cloud: "{{ cloud }}"
register: userinfo
- name: Assert results were returned
assert:
that:
- userinfo.openstack_users | length > 0
- name: Post-test cleanup
block:
- name: Ensure users do not exist
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: "{{ item }}"
loop:
- ansible_user
- ansible_user2

View File

@@ -0,0 +1,4 @@
image_name: ansible_image
image_tags:
- test
- ansible

View File

@@ -10,37 +10,35 @@
openstack.cloud.image:
cloud: "{{ cloud }}"
state: present
name: ansible_image
name: "{{ image_name }}"
filename: "{{ tmp_file.stdout }}"
disk_format: raw
tags:
- test
- ansible
tags: "{{ image_tags }}"
register: image
- name: Get details of created image
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: ansible_image
image: "{{ image_name }}"
register: image_info_result
- name: Verify image info
assert:
that:
- image_info_result.openstack_images[0].name == "ansible_image"
- image_info_result.openstack_images[0].tags | sort == ['test', 'ansible'] | sort
- "image_info_result.openstack_image.name == image_name"
- "image_info_result.openstack_image.tags | sort == image_tags | sort"
- name: Delete raw image (defaults)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: absent
name: ansible_image
name: "{{ image_name }}"
- name: Create raw image (complex)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: present
name: ansible_image
name: "{{ image_name }}"
filename: "{{ tmp_file.stdout }}"
disk_format: raw
is_public: True
@@ -57,98 +55,20 @@
openstack.cloud.image:
cloud: "{{ cloud }}"
state: absent
name: ansible_image
- name: Try to get details of deleted image
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: ansible_image
register: deleted_image_info_result
- name: Verify image is deleted
assert:
that:
- not deleted_image_info_result.openstack_images
- name: Create owner project
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: image_owner_project
description: Project owning test image
domain_id: default
enabled: True
register: owner_project
- name: Create raw image (owner by project name)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: present
name: ansible_image
filename: "{{ tmp_file.stdout }}"
disk_format: raw
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: ansible_image
register: image_info_result
- name: Verify image owner (owner by project name)
assert:
that:
- image_info_result.openstack_images[0].owner == owner_project.project.id
- name: Delete raw image (owner by project name)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: absent
name: ansible_image
- name: Create raw image (owner by project name and domain name)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: present
name: ansible_image
filename: "{{ tmp_file.stdout }}"
disk_format: raw
tags:
- test
- ansible
project: image_owner_project
project_domain: default
register: image
- name: Get details of created image (owner by project name and domain name)
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: ansible_image
register: image_info_result
- name: Verify image owner (owner by project name and domain name)
assert:
that:
- image_info_result.openstack_images[0].owner == owner_project.project.id
- name: Delete raw image (owner by project name and domain name)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: absent
name: ansible_image
- name: Delete owner project
openstack.cloud.project:
cloud: "{{ cloud }}"
state: absent
name: image_owner_project
domain_id: default
name: "{{ image_name }}"
- name: Delete test image file
file:
name: "{{ tmp_file.stdout }}"
state: absent
- name: Try to get details of deleted image
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: "{{ image_name }}"
register: deleted_image_info_result
- name: Verify image is deleted
assert:
that:
- not deleted_image_info_result.openstack_image

View File

@@ -1,23 +0,0 @@
expected_fields:
- checksum
- container_format
- created_at
- direct_url
- disk_format
- file
- id
- locations
- metadata
- min_disk
- min_ram
- name
- os_hidden
- owner
- properties
- schema
- size
- status
- tags
- updated_at
- virtual_size
- visibility

View File

@@ -1,11 +0,0 @@
---
- name: List all images # This will list at least the default cirros image of devstack
openstack.cloud.image_info:
cloud: "{{ cloud }}"
register: image_list_result
- name: Assert fields
assert:
that:
- item in image_list_result.openstack_images.0.keys()
loop: "{{ expected_fields }}"

View File

@@ -1,11 +1 @@
keypair_name: shade_keypair
expected_fields:
- created_at
- fingerprint
- id
- is_deleted
- name
- private_key
- public_key
- type
- user_id

View File

@@ -1,17 +1,13 @@
---
- name: Create keypair (non-existing)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
register: keypair
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
register:
keypair
- name: Get list of all keypairs
openstack.cloud.keypair_info:
cloud: "{{ cloud }}"
register: keypairs_all
- name: Get list of keypairs with filter
- name: Get list of keypairs
openstack.cloud.keypair_info:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
@@ -22,35 +18,17 @@
that:
- keypairs['openstack_keypairs']|length == 1
- name: Assert fields
assert:
that:
- item in keypairs.openstack_keypairs.0.keys()
loop: "{{ expected_fields }}"
# This assert verifies that Ansible is capable serializing data returned by SDK
- name: Ensure public key is returned
- name: Ensure private key is returned
assert:
that:
- keypair.key.public_key is defined and keypair.key.public_key
- name: Create another keypair
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}-2"
state: present
- name: Delete keypair (non-existing)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "non-existing"
state: absent
- name: Delete keypair
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
- name: Get list of keypairs
openstack.cloud.keypair_info:
@@ -63,24 +41,18 @@
that:
- keypairs['openstack_keypairs']|length == 0
- name: Delete another keypair
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}-2"
state: absent
- name: Generate test key file
user:
name: "{{ ansible_env.USER }}"
generate_ssh_key: yes
ssh_key_file: .ssh/shade_id_rsa
name: "{{ ansible_env.USER }}"
generate_ssh_key: yes
ssh_key_file: .ssh/shade_id_rsa
- name: Create keypair (file)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
public_key_file: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
public_key_file: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
- name: Get list of keypairs
openstack.cloud.keypair_info:
@@ -95,9 +67,9 @@
- name: Delete keypair (file)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
- name: Get list of keypairs
openstack.cloud.keypair_info:
@@ -112,10 +84,10 @@
- name: Create keypair (key)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
public_key: "{{ lookup('file', '~/.ssh/shade_id_rsa.pub') }}"
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
public_key: "{{ lookup('file', '~/.ssh/shade_id_rsa.pub') }}"
- name: Get list of keypairs
openstack.cloud.keypair_info:
@@ -130,9 +102,9 @@
- name: Delete keypair (key)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
- name: Get list of keypairs
openstack.cloud.keypair_info:
@@ -147,10 +119,10 @@
- name: Delete test key pub file
file:
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
state: absent
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
state: absent
- name: Delete test key pvt file
file:
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa"
state: absent
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa"
state: absent

View File

@@ -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)

View File

@@ -0,0 +1 @@
role_name: ansible_keystone_role

View File

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

View File

@@ -1,25 +1,5 @@
---
- 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 }}"

View File

@@ -1 +0,0 @@
sdk_log_file_path: "{{ playbook_dir }}/sdk.log"

View File

@@ -1,20 +0,0 @@
---
- 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) }}"

View File

@@ -3,31 +3,13 @@
- name: Get nova compute services info
openstack.cloud.compute_service_info:
cloud: "{{ cloud }}"
binary: "nova-compute"
register: result
failed_when: "result.openstack_compute_services | length <= 0"
- name: Assert fields on OpenStack SDK before 0.53
when: sdk_version is version(0.53, '<')
assert:
that:
- '["availability_zone", "binary", "disables_reason",
"host", "name", "state", "status", "id"] |
difference(result.openstack_compute_services.0.keys()) | length == 0'
- name: Assert fields on OpenStack SDK 0.53 and later
when: sdk_version is version(0.53, '>=')
assert:
that:
- '["availability_zone", "binary", "disabled_reason", "is_forced_down",
"host", "name", "state", "status", "updated_at", "id"] |
difference(result.openstack_compute_services.0.keys()) | length == 0'
- name: Filter compute services. Supported since OpenStack SDK 0.53.0 (Wallaby).
when: sdk_version is version(0.53, '>=')
block:
- name: Get nova compute services info
openstack.cloud.compute_service_info:
cloud: "{{ cloud }}"
binary: "nova-compute"
register: result
failed_when: "result.openstack_compute_services | length <= 0"
- name: Get nova conductor services info
openstack.cloud.compute_service_info:
cloud: "{{ cloud }}"
binary: "nova-conductor"
register: result
failed_when: "result.openstack_compute_services | length <= 0"

View File

@@ -41,4 +41,4 @@
- assert:
that:
- stacks is defined
- (stacks['stacks']|length == 0) or (stacks['stacks'][0]['status'] == 'DELETE_COMPLETE')
- stacks['stacks']|length == 0

View File

@@ -108,6 +108,30 @@
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 }}"

View File

@@ -1,19 +0,0 @@
dns_zone_name: test.dns.zone.
recordset_name: testrecordset.test.dns.zone.
records: ['10.0.0.0']
updated_records: ['10.1.1.1']
recordset_fields:
- action
- created_at
- description
- id
- links
- name
- project_id
- records
- status
- ttl
- type
- zone_id
- zone_name

View File

@@ -1,112 +0,0 @@
- name: Ensure DNS zone not present before tests
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"
name: "{{ dns_zone_name }}"
zone_type: "primary"
email: test@example.net
state: absent
- name: Ensure dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"
name: "{{ dns_zone_name }}"
zone_type: "primary"
email: test@example.net
register: dns_zone
- name: Create a recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ dns_zone.zone.name }}"
name: "{{ recordset_name }}"
recordset_type: "a"
records: "{{ records }}"
register: recordset
- name: Verify recordset info
assert:
that:
- recordset is changed
- recordset["recordset"].name == recordset_name
- recordset["recordset"].zone_name == dns_zone.zone.name
- recordset["recordset"].records == records
- name: Assert recordset fields
assert:
that: item in recordset.recordset
loop: "{{ recordset_fields }}"
- name: Create identical recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ dns_zone.zone.name }}"
name: "{{ recordset_name }}"
recordset_type: "a"
records: "{{ records }}"
register: recordset
- name: Assert recordset not changed
assert:
that:
- recordset is not changed
- name: Assert recordset fields
assert:
that: item in recordset.recordset
loop: "{{ recordset_fields }}"
- name: Update a recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ dns_zone.zone.name }}"
name: "{{ recordset_name }}"
recordset_type: "a"
records: "{{ updated_records }}"
description: "new test recordset"
register: recordset
- name: Verify recordset info
assert:
that:
- recordset is changed
- recordset["recordset"].zone_name == dns_zone.zone.name
- recordset["recordset"].name == recordset_name
- recordset["recordset"].records == updated_records
- name: Assert recordset fields
assert:
that: item in recordset.recordset
loop: "{{ recordset_fields }}"
- name: Delete recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ dns_zone.zone.name }}"
name: "{{ recordset.recordset.name }}"
state: absent
register: deleted_recordset
- name: Verify recordset deletion
assert:
that:
- deleted_recordset is successful
- deleted_recordset is changed
- name: Delete unexistent recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ dns_zone.zone.name }}"
name: "{{ recordset.recordset.name }}"
state: absent
register: deleted_recordset
- name: Verify recordset deletion
assert:
that:
- deleted_recordset is not changed
- name: Delete dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"
name: "{{ dns_zone.zone.name }}"
state: absent

View File

@@ -1,47 +0,0 @@
---
- name: Create project
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
description: dummy description
domain_id: default
enabled: True
register: project
- name: Grant an admin role on the user admin in the project ansible_project
openstack.cloud.role_assignment:
cloud: "{{ cloud }}"
domain: default
project: ansible_project
role: admin
user: admin
- name: Grant an admin role on the user admin in the project ansible_project again
openstack.cloud.role_assignment:
cloud: "{{ cloud }}"
domain: default
project: ansible_project
role: admin
user: admin
register: grant_again
- name: Ensure grant again doesn't change anything
assert:
that:
- not grant_again.changed
- name: Revoke the admin role on the user admin in the project ansible_project
openstack.cloud.role_assignment:
cloud: "{{ cloud }}"
domain: default
project: ansible_project
role: admin
state: absent
user: admin
- name: Delete project
openstack.cloud.project:
cloud: "{{ cloud }}"
state: absent
name: ansible_project

View File

@@ -1,8 +1,5 @@
server_network: private
server_name: ansible_server
server_alt_network: private_alt
server_alt_subnet: subnet_alt
server_alt_name: ansible_server_alt
flavor_name: m1.tiny
flavor: m1.tiny
floating_ip_pool_name: public
boot_volume_size: 5

View File

@@ -1,22 +1,11 @@
---
- 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_name }}"
flavor: "{{ flavor_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
network: "{{ server_network }}"
auto_floating_ip: false
meta: "key1=value1,key2=value2"
@@ -57,8 +46,8 @@
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
image: "{{ image_name }}"
flavor: "{{ flavor_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
auto_floating_ip: false
network: "{{ server_network }}"
meta:
@@ -78,7 +67,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:
@@ -92,8 +81,8 @@
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
image: "{{ image_name }}"
flavor: "{{ flavor_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
network: "{{ server_network }}"
floating_ip_pools:
- "{{ floating_ip_pool_name }}"
@@ -112,7 +101,7 @@
- name: Check info about server image name
assert:
that:
info.openstack_servers[0].image.name == image_name
info.openstack_servers[0].image.name == "{{ image }}"
- name: Delete server (FIP from pool/network)
openstack.cloud.server:
@@ -126,8 +115,8 @@
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
image: "{{ image_name }}"
flavor: "{{ flavor_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
network: "{{ server_network }}"
auto_floating_ip: false
boot_from_volume: true
@@ -135,8 +124,12 @@
terminate_volume: true
wait: true
register: server
tags:
- object
- debug: var=server
tags:
- object
- name: Delete server with volume
openstack.cloud.server:
@@ -144,14 +137,16 @@
state: absent
name: "{{ server_name }}"
wait: true
tags:
- object
- name: Create a minimal server
openstack.cloud.server:
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
image: "{{ image_name }}"
flavor: "{{ flavor_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
network: "{{ server_network }}"
auto_floating_ip: false
wait: true

View File

@@ -3,8 +3,8 @@
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
image: "{{ image_name }}"
flavor: "{{ flavor_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
network: "{{ server_network }}"
auto_floating_ip: false
wait: true
@@ -380,7 +380,7 @@
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
image: "{{ image_name }}"
image: "{{ image }}"
action: rebuild
wait: true
register: server
@@ -401,7 +401,7 @@
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
image: "{{ image_name }}"
image: "{{ image }}"
action: rebuild
wait: true
admin_password: random
@@ -518,95 +518,3 @@
that:
- info24.openstack_servers.0.status == 'ACTIVE'
- server is not changed
- name: Create network for alternate server
openstack.cloud.network:
cloud: "{{ cloud_alt }}"
name: "{{ server_alt_network }}"
state: present
- name: Create subnet for alternate server
openstack.cloud.subnet:
cloud: "{{ cloud_alt }}"
network_name: "{{ server_alt_network }}"
name: "{{ server_alt_subnet }}"
state: present
cidr: 192.168.0.0/24
- name: Create server in alternate project
openstack.cloud.server:
cloud: "{{ cloud_alt }}"
state: present
name: "{{ server_alt_name }}"
image: "{{ image_name }}"
flavor: "{{ flavor_name }}"
network: "{{ server_alt_network }}"
auto_floating_ip: false
wait: true
register: server_alt
- name: Get info about server in alternate project
openstack.cloud.server_info:
cloud: "{{ cloud_alt }}"
server: "{{ server_alt_name }}"
register: info25
- name: Ensure status for server in alternate project is ACTIVE
assert:
that:
- info25.openstack_servers.0.status == 'ACTIVE'
- name: Try to stop server in alternate project
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_alt_name }}"
action: stop
wait: true
ignore_errors: true
register: server_alt
- name: Ensure server was not stopped
assert:
that:
- server_alt is failed
- server_alt.msg == "Could not find server {{ server_alt_name }}"
- name: Stop server in alternate project with all_projects=true
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_alt_name }}"
action: stop
wait: true
all_projects: True
register: server_alt
- name: Get info about server in alternate project
openstack.cloud.server_info:
cloud: "{{ cloud_alt }}"
server: "{{ server_alt_name }}"
register: info26
- name: Ensure status for server is SHUTOFF
assert:
that:
- info26.openstack_servers.0.status == 'SHUTOFF'
- server_alt is changed
- name: Delete server in alternate project
openstack.cloud.server:
cloud: "{{ cloud_alt }}"
state: absent
name: "{{ server_alt_name }}"
wait: true
- name: Delete subnet for alternate server
openstack.cloud.subnet:
cloud: "{{ cloud_alt }}"
name: "{{ server_alt_subnet }}"
state: absent
- name: Delete network for alternate server
openstack.cloud.network:
cloud: "{{ cloud_alt }}"
name: "{{ server_alt_network }}"
state: absent

View File

@@ -1,5 +0,0 @@
subnet_pool_name: "subnet_pool"
address_scope_name: "address_scope"
default_prefix_length: 24
minimum_prefix_length: 10
maximum_prefix_length: 30

View File

@@ -1,73 +0,0 @@
---
- name: Create address_scope
openstack.cloud.address_scope:
cloud: "{{ cloud }}"
name: "{{ address_scope_name }}"
shared: False
ip_version: "4"
register: create_address_scope
- name: Create subnet pool
openstack.cloud.subnet_pool:
cloud: "{{ cloud }}"
name: "{{ subnet_pool_name }}"
shared: False
address_scope: "{{ address_scope_name }}"
prefixes:
- 192.168.0.0/24
register: create_subnet_pool
- name: Verify subnet pool
assert:
that:
- create_subnet_pool is successful
- create_subnet_pool is changed
- create_subnet_pool.subnet_pool.name == subnet_pool_name
- create_subnet_pool.subnet_pool.is_shared == False
- create_subnet_pool.subnet_pool.is_default == False
- create_subnet_pool.subnet_pool.address_scope_id == create_address_scope.address_scope.id
- create_subnet_pool.subnet_pool.prefixes == ['192.168.0.0/24']
- name: Update subnet pool
openstack.cloud.subnet_pool:
cloud: "{{ cloud }}"
name: "{{ subnet_pool_name }}"
address_scope: "{{ address_scope_name }}"
shared: False
default_prefix_length: "{{ default_prefix_length }}"
minimum_prefix_length: "{{ minimum_prefix_length }}"
maximum_prefix_length: "{{ maximum_prefix_length }}"
description: "test"
prefixes:
- 192.168.0.0/24
- 192.168.1.0/24
register: update_subnet_pool
- name: Verify updated subnet pool
assert:
that:
- update_subnet_pool is successful
- update_subnet_pool is changed
- update_subnet_pool.subnet_pool.name == subnet_pool_name
- update_subnet_pool.subnet_pool.is_shared == False
- update_subnet_pool.subnet_pool.is_default == False
- update_subnet_pool.subnet_pool.address_scope_id == create_address_scope.address_scope.id
- update_subnet_pool.subnet_pool.prefixes == ['192.168.0.0/23']
- update_subnet_pool.subnet_pool.description == 'test'
- update_subnet_pool.subnet_pool.default_prefix_length == default_prefix_length
- update_subnet_pool.subnet_pool.minimum_prefix_length == minimum_prefix_length
- update_subnet_pool.subnet_pool.maximum_prefix_length == maximum_prefix_length
- name: Delete created subnet pool
openstack.cloud.subnet_pool:
cloud: "{{ cloud }}"
name: "{{ subnet_pool_name }}"
state: absent
- name: Delete created address scope
openstack.cloud.address_scope:
cloud: "{{ cloud }}"
name: "{{ address_scope_name }}"
state: absent

View File

@@ -0,0 +1,30 @@
---
- name: Create user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
password: secret
email: ansible.user@nowhere.net
domain: default
default_project: demo
register: user
- debug: var=user
- name: Update user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
password: secret
email: updated.ansible.user@nowhere.net
register: updateduser
- debug: var=updateduser
- name: Delete user
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: ansible_user

View File

@@ -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

136
ci/run-ansible-tests-collection.sh Executable file → Normal file
View File

@@ -1,4 +1,3 @@
#!/bin/bash
#############################################################################
# run-ansible-tests.sh
#
@@ -7,22 +6,20 @@
#
# tox -e ansible [TAG ...]
# or
# tox -e ansible -- -c cloudX -u cloudY [TAG ...]
# tox -e ansible -- -c cloudX [TAG ...]
# or to use the development version of Ansible:
# tox -e ansible -- -d -c cloudX -u cloudY [TAG ...]
# tox -e ansible -- -d -c cloudX [TAG ...]
#
# USAGE:
# run-ansible-tests.sh -e ENVDIR [-d] [-c CLOUD] [-u CLOUD_ALT] [TAG ...]
# run-ansible-tests.sh -e ENVDIR [-d] [-c CLOUD] [TAG ...]
#
# PARAMETERS:
# -d Use Ansible source repo development branch.
# -e ENVDIR Directory of the tox environment to use for testing.
# -c CLOUD Name of the cloud to use for testing.
# Defaults to "devstack-admin".
# -u CLOUD_ALT Name of another cloud to use for testing.
# Defaults to "devstack-alt".
# [TAG ...] Optional list of space-separated tags to control which
# modules are tested.
# -d Use Ansible source repo development branch.
# -e ENVDIR Directory of the tox environment to use for testing.
# -c CLOUD Name of the cloud to use for testing.
# Defaults to "devstack-admin".
# [TAG ...] Optional list of space-separated tags to control which
# modules are tested.
#
# EXAMPLES:
# # Run all Ansible tests
@@ -34,47 +31,33 @@
set -ex
CLOUD="devstack-admin"
CLOUD_ALT="devstack-alt"
ENVDIR=
USE_DEV=0
while getopts "c:de:u:" opt
while getopts "c:de:" 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} ;;
e) ENVDIR=${OPTARG} ;;
?) echo "Invalid option: -${OPTARG}"
exit 1;;
esac
done
# Shift arguments read by getopts
shift $((OPTIND-1))
# Remaining arguments are Ansible tags
TAGS=$( echo "$*" | tr ' ' , )
if [ -z "$ENVDIR" ]; then
if [ -z ${ENVDIR} ]
then
echo "Option -e is required"
exit 1
fi
if [ ! -d ci ]; then
echo "Script must be run from collection's root directory"
exit 2
fi
shift $((OPTIND-1))
TAGS=$( echo "$*" | tr ' ' , )
# 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
# Install collections before dealing with Ansible virtual environments
if [[ -z "$PIP_INSTALL" ]]; then
tox -ebuild
ansible-galaxy collection install "$(find build_artifact/ -maxdepth 1 -name 'openstack-cloud-*')" --force
ansible-galaxy collection install $(ls build_artifact/openstack-cloud-*) --force
TEST_COLLECTIONS_PATHS=${HOME}/.ansible/collections:$ANSIBLE_COLLECTIONS_PATHS
else
pip freeze | grep ansible-collections-openstack
@@ -83,16 +66,25 @@ 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"
source $ENVDIR/ansible/hacking/env-setup
fi
# Run the shade Ansible tests
tag_opt=""
if [ ! -z ${TAGS} ]
then
tag_opt="--tags ${TAGS}"
fi
# Loop through all ANSIBLE_VAR_ environment variables to allow passing the further
@@ -104,32 +96,44 @@ for var in $(env | grep -e '^ANSIBLE_VAR_'); do
ANSIBLE_VARS+="${ANSIBLE_VAR_NAME}=${ANSIBLE_VAR_VALUE} " # concat variables
done
# Discover openstacksdk version
# 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
SDK_VER=$(python -c "import openstack; print(openstack.version.__version__)")
# 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
pushd 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} ${ANSIBLE_VARS}" \
-e "sdk_version=${SDK_VER} cloud=${CLOUD} image=${IMAGE} ${ANSIBLE_VARS}" \
${tag_opt} 2>&1 | sudo tee /opt/stack/logs/test_output.log
popd

View File

@@ -6,10 +6,7 @@
roles:
- { role: address_scope, tags: address_scope }
- { role: auth, tags: auth }
- { role: catalog_service, tags: catalog_service }
- { role: client_config, tags: client_config }
- { role: 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
@@ -18,17 +15,8 @@
- role: dns
tags: dns
when: sdk_version is version(0.28, '>=')
- { role: endpoint, tags: endpoint }
- { role: floating_ip_info, tags: floating_ip_info }
- { role: host_aggregate, tags: host_aggregate }
- { role: identity_domain_info, tags: identity_domain_info }
- { role: identity_group_info, tags: identity_group_info }
- { role: identity_user, tags: identity_user }
- { role: identity_user_info, tags: identity_user_info }
- { role: identity_role, tags: identity_role }
- { role: identity_role_info, tags: identity_role_info }
- { role: image, tags: image }
- { role: image_info, tags: image_info }
- { role: keypair, tags: keypair }
- { role: keystone_domain, tags: keystone_domain }
- role: keystone_mapping
@@ -40,28 +28,24 @@
- role: keystone_federation_protocol
tags: keystone_federation_protocol
when: sdk_version is version(0.44, '>=')
- { role: logging, tags: logging }
- { role: keystone_role, tags: keystone_role }
- { role: network, tags: network }
- role: neutron_rbac
tags:
- rbac
- neutron_rbac
- { role: nova_flavor, tags: nova_flavor }
- role: compute_flavor_info
tags: nova_flavor
- role: nova_services
tags: nova_services
when: sdk_version is version(0.44, '>=')
- { role: object, tags: object }
- { role: port, tags: port }
- { role: project, tags: project }
- { role: recordset, tags: recordset }
- { role: role_assignment, tags: role_assignment }
- { role: router, tags: router }
- { role: security_group, tags: security_group }
- { role: server, tags: server }
- { role: subnet, tags: subnet }
- { role: subnet_pool, tags: subnet_pool }
- { role: user, tags: user }
- { role: user_group, tags: user_group }
- { role: user_role, tags: user_role }
- { role: volume, tags: volume }

5
contrib/generate_module.sh Executable file
View File

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

View File

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

View File

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

View File

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

View File

@@ -1,115 +0,0 @@
# 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)

View File

@@ -1,189 +0,0 @@
# 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

View File

@@ -1,107 +0,0 @@
# 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/

View File

@@ -0,0 +1,67 @@
.. _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.
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>`_

View File

@@ -1,125 +0,0 @@
# 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).

View File

@@ -1,66 +0,0 @@
# 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?

View File

@@ -31,5 +31,6 @@ build_ignore:
- .env
- .vscode
- ansible_collections_openstack.egg-info
- contrib
- changelogs
version: 1.10.0
version: 1.7.1

View File

@@ -18,7 +18,7 @@ build_ignore:
- ci
- galaxy.yml.in
- setup.cfg
- test-requirements*
- test-requirements.txt
- tests
- tools
- tox.ini
@@ -29,6 +29,7 @@ build_ignore:
- importer_result.json
- .tox
- .env
- .vscode
- ansible_collections_openstack.egg-info
- changelogs
- contrib
- changelogs/.plugin-cache.yaml
- changelogs/fragments

View File

@@ -99,7 +99,6 @@ action_groups:
- stack
- subnet
- subnets_info
- subnet_pool
- volume
- volume_backup
- volume_backup_info

View File

@@ -89,18 +89,9 @@ 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.12.0
notes:
- The standard OpenStack environment variables, such as C(OS_USERNAME)
may be used instead of providing explicit values.

View File

@@ -1,4 +1,3 @@
# -*- 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.
@@ -12,6 +11,8 @@ 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
@@ -24,7 +25,7 @@ options:
show_all:
description: toggles showing all vms vs only those with a working IP
type: bool
default: false
default: 'no'
inventory_hostname:
description: |
What to register as the inventory hostname.
@@ -46,7 +47,7 @@ options:
'ansible_ssh_host' facts. This might be desired when using jump or
bastion hosts and the name is the FQDN of the host.
type: bool
default: false
default: 'no'
expand_hostvars:
description: |
Run extra commands on each host to fill in additional
@@ -55,7 +56,7 @@ options:
(Note, the default value of this is opposite from the default
old openstack.py inventory script's option expand_hostvars)
type: bool
default: false
default: 'no'
private:
description: |
Use the private interface of each server, if it has one, as
@@ -63,13 +64,12 @@ options:
running ansible inside a server in the cloud and would rather
communicate to your servers over the private network.
type: bool
default: false
default: 'no'
only_clouds:
description: |
List of clouds from clouds.yaml to use, instead of using
the whole list.
type: list
elements: str
default: []
fail_on_errors:
description: |
@@ -80,12 +80,12 @@ options:
default value of this is opposite from the old openstack.py
inventory script's option fail_on_errors)
type: bool
default: false
default: 'no'
all_projects:
description: |
Lists servers from all projects
type: bool
default: false
default: 'no'
clouds_yaml_path:
description: |
Override path to clouds.yaml file. If this value is given it
@@ -94,7 +94,6 @@ options:
/etc/ansible/openstack.yml to the regular locations documented
at https://docs.openstack.org/os-client-config/latest/user/configuration.html#config-files
type: list
elements: str
env:
- name: OS_CLIENT_CONFIG_FILE
compose:
@@ -109,12 +108,11 @@ 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,9 +131,6 @@ 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")
@@ -177,13 +172,6 @@ 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)

View File

@@ -29,16 +29,7 @@
import abc
import copy
from ansible.module_utils.six import raise_from
try:
from ansible.module_utils.compat.version import StrictVersion
except ImportError:
try:
from distutils.version import StrictVersion
except ImportError as exc:
raise_from(ImportError('To use this plugin or module with ansible-core'
' < 2.11, you need to use Python < 3.12 with '
'distutils.version present'), exc)
from distutils.version import StrictVersion
import importlib
import os
@@ -76,41 +67,7 @@ OVERRIDES = {'os_client_config': 'config',
CUSTOM_VAR_PARAMS = ['min_ver', 'max_ver']
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))
MINIMUM_SDK_VERSION = '0.12.0'
def openstack_argument_spec():
@@ -173,9 +130,6 @@ 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)
@@ -198,20 +152,25 @@ def openstack_module_kwargs(**kwargs):
# for compatibility with old versions
def openstack_cloud_from_module(module, min_version=None, max_version=None):
def openstack_cloud_from_module(module, min_version=None):
try:
# Due to the name shadowing we should import other way
sdk = importlib.import_module('openstack')
sdk_version = importlib.import_module('openstack.version')
except ImportError:
module.fail_json(msg='openstacksdk is required for this module')
try:
ensure_compatibility(sdk.version.__version__,
min_version, max_version)
except ImportError as e:
if min_version:
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
StrictVersion(min_version))
else:
min_version = StrictVersion(MINIMUM_SDK_VERSION)
if StrictVersion(sdk_version.__version__) < min_version:
module.fail_json(
msg="Incompatible openstacksdk library found: {error}."
.format(error=str(e)))
msg="To utilize this module, the installed version of "
"the openstacksdk library MUST be >={min_version}.".format(
min_version=min_version))
cloud_config = module.params.pop('cloud', None)
try:
@@ -285,7 +244,6 @@ class OpenStackModule:
argument_spec = {}
module_kwargs = {}
module_min_sdk_version = None
module_max_sdk_version = None
def __init__(self):
"""Initialize Openstack base class.
@@ -306,7 +264,6 @@ 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.
@@ -326,16 +283,6 @@ 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.
"""
@@ -354,18 +301,23 @@ class OpenStackModule:
try:
# Due to the name shadowing we should import other way
sdk = importlib.import_module('openstack')
self.sdk_version = sdk.version.__version__
sdk_version_lib = importlib.import_module('openstack.version')
self.sdk_version = sdk_version_lib.__version__
except ImportError:
self.fail_json(msg='openstacksdk is required for this module')
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 the available SDK version doesn't meet the minimum version
# requirements
if self.module_min_sdk_version:
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
StrictVersion(self.module_min_sdk_version))
else:
min_version = StrictVersion(MINIMUM_SDK_VERSION)
if StrictVersion(self.sdk_version) < min_version:
self.fail(
msg="To utilize this module, the installed version of "
"the openstacksdk library MUST be >={min_version}.".format(
min_version=min_version))
# Fail if there are set unsupported for this version parameters
# New parameters should NOT use 'default' but rely on SDK defaults

View File

@@ -30,7 +30,7 @@ options:
ip_version:
description:
- The IP version of the subnet 4 or 6
default: '4'
default: 4
type: str
choices: ['4', '6']
shared:
@@ -44,6 +44,10 @@ options:
required: false
default: {}
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -10,6 +10,9 @@ 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
'''

View File

@@ -36,6 +36,11 @@ 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
'''

View File

@@ -123,7 +123,6 @@ options:
description:
- Definition of the physical characteristics of this server, used for scheduling purposes
type: dict
default: {}
suboptions:
cpu_arch:
description:
@@ -166,7 +165,10 @@ options:
aliases:
- skip_update_of_driver_password
requirements:
- "python >= 3.6"
- "openstacksdk"
- "jsonpatch"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -91,6 +91,10 @@ 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
'''

View File

@@ -26,6 +26,10 @@ 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
'''

View File

@@ -71,6 +71,10 @@ 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
'''

View File

@@ -30,6 +30,9 @@ 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
'''

View File

@@ -26,19 +26,21 @@ options:
- Is the service enabled
type: bool
default: 'yes'
aliases: ['is_enabled']
type:
service_type:
description:
- The type of service
required: true
type: str
aliases: ['service_type']
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
@@ -49,14 +51,14 @@ EXAMPLES = '''
cloud: mycloud
state: present
name: glance
type: image
service_type: image
description: OpenStack Image Service
# Delete a service
- openstack.cloud.catalog_service:
cloud: mycloud
state: absent
name: glance
type: image
service_type: image
'''
RETURN = '''
@@ -73,10 +75,6 @@ service:
description: Service name.
type: str
sample: "glance"
type:
description: Service type.
type: str
sample: "image"
service_type:
description: Service type.
type: str
@@ -102,9 +100,9 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
class IdentityCatalogServiceModule(OpenStackModule):
argument_spec = dict(
description=dict(default=None),
enabled=dict(default=True, aliases=['is_enabled'], type='bool'),
enabled=dict(default=True, type='bool'),
name=dict(required=True),
type=dict(required=True, aliases=['service_type']),
service_type=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
)
@@ -113,9 +111,11 @@ class IdentityCatalogServiceModule(OpenStackModule):
)
def _needs_update(self, service):
for parameter in ('enabled', 'description', 'type'):
if service[parameter] != self.params[parameter]:
return True
if service.enabled != self.params['enabled']:
return True
if service.description is not None and \
service.description != self.params['description']:
return True
return False
def _system_state_change(self, service):
@@ -135,34 +135,33 @@ class IdentityCatalogServiceModule(OpenStackModule):
enabled = self.params['enabled']
name = self.params['name']
state = self.params['state']
type = self.params['type']
service_type = self.params['service_type']
services = self.conn.search_services(
name_or_id=name, filters=(dict(type=type) if type else None))
name_or_id=name, filters=dict(type=service_type))
service = None
if len(services) > 1:
self.fail_json(
msg='Service name %s and type %s are not unique'
% (name, type))
% (name, service_type))
elif len(services) == 1:
service = services[0]
else:
service = None
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(service))
args = {'name': name, 'enabled': enabled, 'type': type}
if description:
args['description'] = description
if state == 'present':
if service is None:
service = self.conn.create_service(**args)
service = self.conn.create_service(
name=name, description=description, type=service_type, enabled=True)
changed = True
else:
if self._needs_update(service):
service = self.conn.update_service(service,
**args)
service = self.conn.update_service(
service.id, name=name, type=service_type, enabled=enabled,
description=description)
changed = True
else:
changed = False
@@ -172,7 +171,7 @@ class IdentityCatalogServiceModule(OpenStackModule):
if service is None:
changed = False
else:
self.conn.identity.delete_service(service.id)
self.conn.delete_service(service.id)
changed = True
self.exit_json(changed=changed)

View File

@@ -3,363 +3,288 @@
# 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 = r'''
DOCUMENTATION = '''
---
module: coe_cluster
short_description: Manage COE cluster in OpenStack Cloud
short_description: Add/Remove COE cluster from OpenStack Cloud
author: OpenStack Ansible SIG
description:
- Add or remove a COE (Container Orchestration Engine) cluster
via OpenStack's Magnum aka Container Infrastructure Management API.
- Add or Remove COE cluster from the OpenStack Container Infra service.
options:
cluster_template_id:
description:
- The template ID of cluster template.
- Required if I(state) is C(present).
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 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.
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
- openstack.cloud.openstack
'''
RETURN = r'''
RETURN = '''
id:
description: The cluster UUID.
returned: On success when I(state) is C(present).
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
description: The cluster UUID.
returned: On success when I(state) is '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'
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'
'''
EXAMPLES = r'''
- name: Create a new Kubernetes cluster
openstack.cloud.coe_cluster:
cloud: devstack
EXAMPLES = '''
# Create a new Kubernetes cluster
- openstack.cloud.coe_cluster:
name: k8s
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(),
discovery_url=dict(),
cluster_template_id=dict(required=True),
discovery_url=dict(default=None),
docker_volume_size=dict(type='int'),
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(),
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),
name=dict(required=True),
node_count=dict(type='int'),
node_count=dict(type='int', default=1),
state=dict(default='present', choices=['absent', 'present']),
timeout=dict(type='int', default=60),
)
module_kwargs = dict(
required_if=[
('state', 'present', ('cluster_template_id',))
],
supports_check_mode=True,
)
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
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
return self.conn.get_coe_cluster(name_or_id=name, filters=filters)
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'],
)
def _update(self, cluster, update):
attributes = update.get('attributes')
if attributes:
# TODO: Implement support for updates.
# cluster = self.conn.update_coe_cluster(...)
pass
changed = False
cluster = self.conn.get_coe_cluster(
name_or_id=name, filters={'cluster_template_id': cluster_template_id})
return cluster
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
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
# 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 main():
module = COEClusterModule()
module = CoeClusterModule()
module()

View File

@@ -3,550 +3,384 @@
# 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 = r'''
DOCUMENTATION = '''
---
module: coe_cluster_template
short_description: Manage COE cluster template in OpenStack Cloud
short_description: Add/Remove COE cluster template from OpenStack Cloud
author: OpenStack Ansible SIG
description:
- Add or remove a COE (Container Orchestration Engine) cluster template
via OpenStack's Magnum aka Container Infrastructure Management API.
- Add or Remove COE cluster template from the OpenStack Container Infra
service.
options:
coe:
description:
- The Container Orchestration Engine for this cluster template
- Required if I(state) is C(present).
choices: [kubernetes, swarm, mesos]
type: str
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
'''
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:
coe:
description:
- The name of the driver used for instantiating container networks
- The Container Orchestration Engine for this clustertemplate
choices: [kubernetes, swarm, mesos]
type: str
sample: calico
no_proxy:
description: A comma separated list of IPs for which proxies should
not be used in the cluster.
required: true
dns_nameserver:
description:
- The DNS nameserver address
default: '8.8.8.8'
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.
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
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."
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
sample: false
server_type:
description: Server type for this cluster template.
default: 'no'
name:
description:
- Name that has to be given to the cluster template
required: true
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.
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
sample: false
updated_at:
description: The date and time when the resource was updated.
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
uuid:
description: The UUID of the cluster template.
state:
description:
- Indicate desired state of the resource.
choices: [present, absent]
default: present
type: str
volume_driver:
description: The name of the driver used for instantiating container
volumes.
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
sample: cinder
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = r'''
- name: Create a new Kubernetes cluster template
openstack.cloud.coe_cluster_template:
cloud: devstack
coe: kubernetes
image_id: 2a8c9888-9054-4b06-a1ca-2bb61f9adb72
keypair_id: mykey
RETURN = '''
id:
description: The cluster UUID.
returned: On success when I(state) is 'present'
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
'''
EXAMPLES = '''
# Create a new Kubernetes cluster template
- openstack.cloud.coe_cluster_template:
name: k8s
coe: kubernetes
keypair_id: mykey
image_id: 2a8c9888-9054-4b06-a1ca-2bb61f9adb72
public: no
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class COEClusterTemplateModule(OpenStackModule):
class CoeClusterTemplateModule(OpenStackModule):
argument_spec = dict(
coe=dict(choices=['kubernetes', 'swarm', 'mesos']),
dns_nameserver=dict(),
docker_storage_driver=dict(choices=['devicemapper', 'overlay',
'overlay2']),
coe=dict(required=True, choices=['kubernetes', 'swarm', 'mesos']),
dns_nameserver=dict(default='8.8.8.8'),
docker_storage_driver=dict(choices=['devicemapper', 'overlay', 'overlay2']),
docker_volume_size=dict(type='int'),
external_network_id=dict(),
fixed_network=dict(),
fixed_subnet=dict(),
flavor_id=dict(),
external_network_id=dict(default=None),
fixed_network=dict(default=None),
fixed_subnet=dict(default=None),
flavor_id=dict(default=None),
floating_ip_enabled=dict(type='bool', default=True),
http_proxy=dict(),
https_proxy=dict(),
image_id=dict(),
keypair_id=dict(),
labels=dict(type='raw'),
master_flavor_id=dict(),
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),
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(),
public=dict(type='bool'),
registry_enabled=dict(type='bool'),
server_type=dict(choices=['vm', 'bm']),
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']),
state=dict(default='present', choices=['absent', 'present']),
tls_disabled=dict(type='bool'),
tls_disabled=dict(type='bool', default=False),
volume_driver=dict(choices=['cinder', 'rexray']),
)
module_kwargs = dict(
required_if=[
('state', 'present', ('coe', 'image_id')),
],
supports_check_mode=True,
)
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
def run(self):
params = self.params.copy()
state = self.params['state']
cluster_template = self._find()
if self.ansible.check_mode:
self.exit_json(changed=self._will_change(state, cluster_template))
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=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
image_id = self.params['image_id']
return self.conn.get_cluster_template(name_or_id=name,
filters=filters,
detail=True)
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'],
)
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
changed = False
template = self.conn.get_coe_cluster_template(
name_or_id=name, filters={'coe': coe, 'image_id': image_id})
return 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
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
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)
def main():
module = COEClusterTemplateModule()
module = CoeClusterTemplateModule()
module()

View File

@@ -67,6 +67,10 @@ options:
description:
- Metadata dictionary
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -55,6 +55,10 @@ 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
'''
@@ -122,29 +126,6 @@ openstack_flavors:
returned: success
type: str
sample: "tiny"
description:
description: Description of the flavor
returned: success
type: str
sample: "Small flavor"
is_disabled:
description: Wether the flavor is enabled or not
returned: success
type: bool
sample: False
rxtx_factor:
description: Factor to be multiplied by the rxtx_base property of
the network it is attached to in order to have a
different bandwidth cap.
returned: success
type: float
sample: 1.0
extra_specs:
description: Optional parameters to configure different flavors
options.
returned: success
type: dict
sample: "{'hw_rng:allowed': True}"
disk:
description: Size of local disk, in GB.
returned: success
@@ -215,22 +196,16 @@ class ComputeFlavorInfoModule(OpenStackModule):
filters['ephemeral'] = ephemeral
if name:
# extra_specs are exposed in the flavor representation since Rocky, so we do not
# need get_extra_specs=True which is not available in OpenStack SDK 0.36 (Train)
# Ref.: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html
flavor = self.conn.compute.find_flavor(name)
flavors = [flavor] if flavor else []
flavors = self.conn.search_flavors(filters={'name': name})
else:
flavors = list(self.conn.compute.flavors())
flavors = self.conn.list_flavors()
if filters:
flavors = self.conn.range_search(flavors, filters)
if limit is not None:
flavors = flavors[:limit]
# Transform entries to dict
flavors = [flavor.to_dict(computed=True) for flavor in flavors]
self.exit_json(changed=False, openstack_flavors=flavors)

View File

@@ -12,12 +12,16 @@ description:
options:
binary:
description:
- Filter by service binary type. Requires openstacksdk>=0.53.
- Filter by service binary type
type: str
host:
description:
- Filter by service host. Requires openstacksdk>=0.53.
- Filter by service host
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
@@ -55,26 +59,10 @@ openstack_compute_services:
description: The name of the host.
returned: success
type: str
disabled_reason:
description: The reason why the service is disabled
returned: success and OpenStack SDK >= 0.53
type: str
disables_reason:
description: The reason why the service is disabled
returned: success and OpenStack SDK < 0.53
type: str
availability_zone:
zone:
description: The availability zone name.
returned: success
type: str
is_forced_down:
description: If the service has been forced down or nova-compute
returned: success
type: bool
name:
description: Service name
returned: success
type: str
status:
description: The status of the service. One of enabled or disabled.
returned: success
@@ -83,7 +71,7 @@ openstack_compute_services:
description: The state of the service. One of up or down.
returned: success
type: str
update_at:
update:
description: The date and time when the resource was updated
returned: success
type: str
@@ -94,18 +82,23 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
class ComputeServiceInfoModule(OpenStackModule):
argument_spec = dict(
binary=dict(required=False, default=None, min_ver='0.53.0'),
host=dict(required=False, default=None, min_ver='0.53.0'),
binary=dict(required=False, default=None),
host=dict(required=False, default=None),
)
module_kwargs = dict(
supports_check_mode=True
)
def run(self):
filters = self.check_versioned(binary=self.params['binary'], host=self.params['host'])
filters = {k: v for k, v in filters.items() if v is not None}
binary = self.params['binary']
host = self.params['host']
filters = {}
if binary:
filters['binary'] = binary
if host:
filters['host'] = host
services = self.conn.compute.services(**filters)
services = [service.to_dict(computed=True) for service in services]
services = list(services)
self.exit_json(changed=False, openstack_compute_services=services)

View File

@@ -21,8 +21,8 @@ options:
type: list
elements: str
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.36, < 0.99.0"
- "python >= 3.6"
- "openstacksdk"
author: OpenStack Ansible SIG
'''

View File

@@ -45,6 +45,10 @@ options:
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -34,6 +34,11 @@ options:
description:
- TTL (Time To Live) value in seconds.
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
@@ -156,8 +161,12 @@ class DnsZoneInfoModule(OpenStackModule):
if ttl:
kwargs['ttl'] = ttl
data = [zone.to_dict(computed=False) for zone in
self.conn.dns.zones(**kwargs)]
data = []
for raw in self.conn.dns.zones(**kwargs):
dt = raw.to_dict()
dt.pop('location')
data.append(dt)
self.exit_json(zones=data, changed=False)

View File

@@ -44,6 +44,10 @@ options:
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.13.0"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
@@ -77,34 +81,26 @@ endpoint:
description: Endpoint ID.
type: str
sample: 3292f020780b4d5baf27ff7e1d224c44
interface:
description: Endpoint Interface.
type: str
sample: public
enabled:
description: Service status.
type: bool
sample: True
links:
description: Links for the endpoint
type: str
sample: http://controller/identity/v3/endpoints/123
region:
description: Same as C(region_id). Deprecated.
type: str
sample: RegionOne
region_id:
description: Region ID.
description: Region Name.
type: str
sample: RegionOne
service_id:
description: Service ID.
type: str
sample: b91f1318f735494a825a55388ee118f3
interface:
description: Endpoint Interface.
type: str
sample: public
url:
description: Service URL.
type: str
sample: http://controller:9292
enabled:
description: Service status.
type: bool
sample: True
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
@@ -152,11 +148,10 @@ class IdentityEndpointModule(OpenStackModule):
state = self.params['state']
service = self.conn.get_service(service_name_or_id)
if service is None and state == 'absent':
self.exit_json(changed=False)
if service is None and state == 'present':
elif service is None and state == 'present':
self.fail_json(msg='Service %s does not exist' % service_name_or_id)
filters = dict(service_id=service.id, interface=interface)
@@ -164,27 +159,24 @@ class IdentityEndpointModule(OpenStackModule):
filters['region'] = region
endpoints = self.conn.search_endpoints(filters=filters)
endpoint = None
if len(endpoints) > 1:
self.fail_json(msg='Service %s, interface %s and region %s are '
'not unique' %
(service_name_or_id, interface, region))
elif len(endpoints) == 1:
endpoint = endpoints[0]
else:
endpoint = None
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(endpoint))
if state == 'present':
if endpoint is None:
args = {'url': url, 'interface': interface,
'service_name_or_id': service.id, 'enabled': enabled,
'region': region}
endpoints = self.conn.create_endpoint(**args)
# safe because endpoints contains a single item when url is
# given to self.conn.create_endpoint()
endpoint = endpoints[0]
result = self.conn.create_endpoint(
service_name_or_id=service, url=url, interface=interface,
region=region, enabled=enabled)
endpoint = result[0]
changed = True
else:
if self._needs_update(endpoint):
@@ -193,8 +185,7 @@ class IdentityEndpointModule(OpenStackModule):
changed = True
else:
changed = False
self.exit_json(changed=changed,
endpoint=endpoint)
self.exit_json(changed=changed, endpoint=endpoint)
elif state == 'absent':
if endpoint is None:

View File

@@ -46,7 +46,8 @@ options:
type: list
elements: str
requirements:
- "openstacksdk >= 0.44, < 0.99.0"
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -19,7 +19,8 @@ options:
type: str
aliases: ['id']
requirements:
- "openstacksdk >= 0.44, < 0.99.0"
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -45,7 +45,8 @@ options:
type: list
elements: dict
requirements:
- "openstacksdk >= 0.44, < 0.99.0"
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -18,7 +18,8 @@ options:
type: str
aliases: ['id']
requirements:
- "openstacksdk >= 0.44, < 0.99.0"
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -71,6 +71,10 @@ 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
'''

View File

@@ -45,6 +45,10 @@ 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
'''
@@ -161,6 +165,7 @@ class FloatingIPInfoModule(OpenStackModule):
router = self.params['router']
status = self.params['status']
data = []
query = {}
if description:
query['description'] = description
@@ -189,8 +194,15 @@ class FloatingIPInfoModule(OpenStackModule):
if status:
query['status'] = status.upper()
ips = [ip.to_dict(computed=False) for ip in self.conn.network.ips(**query)]
self.exit_json(changed=False, floating_ips=ips)
for raw in self.conn.network.ips(**query):
dt = raw.to_dict()
dt.pop('location')
data.append(dt)
self.exit_json(
changed=False,
floating_ips=data
)
def main():

View File

@@ -26,6 +26,10 @@ options:
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''

View File

@@ -35,6 +35,10 @@ options:
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
@@ -69,36 +73,8 @@ EXAMPLES = '''
name: db_aggregate
'''
RETURN = r'''
aggregate:
description: A host aggregate resource.
type: complex
returned: On success, when I(state) is present
contains:
availability_zone:
description: Availability zone of the aggregate
type: str
returned: always
deleted:
description: Whether or not the resource is deleted
type: bool
returned: always
hosts:
description: Hosts belonging to the aggregate
type: str
returned: always
id:
description: The UUID of the aggregate.
type: str
returned: always
metadata:
description: Metadata attached to the aggregate
type: str
returned: always
name:
description: Name of the aggregate
type: str
returned: always
RETURN = '''
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
@@ -118,20 +94,14 @@ class ComputeHostAggregateModule(OpenStackModule):
supports_check_mode=True
)
def _find_aggregate(self, name_or_id):
aggregates = self.conn.search_aggregates(name_or_id=name_or_id)
if len(aggregates) == 1:
return aggregates[0]
elif len(aggregates) == 0:
return None
raise Exception("Aggregate is not unique, this should be impossible")
def _needs_update(self, aggregate):
new_metadata = self.params['metadata'] or {}
new_metadata = (self.params['metadata'] or {})
if self.params['availability_zone'] is not None:
new_metadata['availability_zone'] = self.params['availability_zone']
if self.params['name'] != aggregate.name:
return True
if self.params['hosts'] is not None:
if self.params['purge_hosts']:
if set(self.params['hosts']) != set(aggregate.hosts):
@@ -140,10 +110,11 @@ class ComputeHostAggregateModule(OpenStackModule):
intersection = set(self.params['hosts']).intersection(set(aggregate.hosts))
if set(self.params['hosts']) != intersection:
return True
for param in ('availability_zone', 'metadata'):
if self.params[param] is not None and \
self.params[param] != aggregate[param]:
if self.params['availability_zone'] is not None:
if self.params['availability_zone'] != aggregate.availability_zone:
return True
if self.params['metadata'] is not None:
if new_metadata != aggregate.metadata:
return True
return False
@@ -164,16 +135,16 @@ class ComputeHostAggregateModule(OpenStackModule):
if hosts is None:
return
hosts_to_add = set(hosts) - set(aggregate['hosts'] or [])
for host in hosts_to_add:
self.conn.add_host_to_aggregate(aggregate.id, host)
hosts_to_add = set(hosts) - set(aggregate.get("hosts", []))
for i in hosts_to_add:
self.conn.add_host_to_aggregate(aggregate.id, i)
if not purge_hosts:
return
hosts_to_remove = set(aggregate["hosts"] or []) - set(hosts)
for host in hosts_to_remove:
self.conn.remove_host_from_aggregate(aggregate.id, host)
hosts_to_remove = set(aggregate.get("hosts", [])) - set(hosts)
for i in hosts_to_remove:
self.conn.remove_host_from_aggregate(aggregate.id, i)
def run(self):
name = self.params['name']
@@ -186,12 +157,18 @@ class ComputeHostAggregateModule(OpenStackModule):
if metadata is not None:
metadata.pop('availability_zone', None)
aggregate = self._find_aggregate(name)
aggregates = self.conn.search_aggregates(name_or_id=name)
if len(aggregates) == 1:
aggregate = aggregates[0]
elif len(aggregates) == 0:
aggregate = None
else:
raise Exception("Should not happen")
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(aggregate))
changed = False
if state == 'present':
if aggregate is None:
aggregate = self.conn.create_aggregate(
@@ -200,27 +177,32 @@ class ComputeHostAggregateModule(OpenStackModule):
if metadata:
self.conn.set_aggregate_metadata(aggregate.id, metadata)
changed = True
elif self._needs_update(aggregate):
if availability_zone is not None:
aggregate = self.conn.update_aggregate(
aggregate.id, name=name,
availability_zone=availability_zone)
if metadata is not None:
metas = metadata
for i in set(aggregate.metadata.keys() - set(metadata.keys())):
if i != 'availability_zone':
metas[i] = None
self.conn.set_aggregate_metadata(aggregate.id, metas)
self._update_hosts(aggregate, hosts, purge_hosts)
changed = True
aggregate = self._find_aggregate(name)
self.exit_json(changed=changed, aggregate=aggregate)
else:
if self._needs_update(aggregate):
if availability_zone is not None:
aggregate = self.conn.update_aggregate(
aggregate.id, name=name,
availability_zone=availability_zone)
if metadata is not None:
metas = metadata
for i in (set(aggregate.metadata.keys()) - set(metadata.keys())):
if i != 'availability_zone':
metas[i] = None
self.conn.set_aggregate_metadata(aggregate.id, metas)
self._update_hosts(aggregate, hosts, purge_hosts)
changed = True
else:
changed = False
self.exit_json(changed=changed)
elif state == 'absent' and aggregate is not None:
self._update_hosts(aggregate, [], True)
self.conn.delete_aggregate(aggregate.id)
changed = True
self.exit_json(changed=changed)
elif state == 'absent':
if aggregate is None:
changed = False
else:
self._update_hosts(aggregate, [], True)
self.conn.delete_aggregate(aggregate.id)
changed = True
self.exit_json(changed=changed)
def main():

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