1 Commits
1.9.0 ... 1.7.1

Author SHA1 Message Date
Jakob Meng
b640e6207c Release 1.7.1 version
Change-Id: I958ba6890a54c59e0ccdb9249d959c745acfb8e9
2022-03-09 20:17:52 +01:00
89 changed files with 956 additions and 3026 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.*
@@ -41,7 +25,7 @@
vars:
zuul_work_dir: src/opendev.org/openstack/ansible-collections-openstack
tox_envlist: ansible
tox_install_siblings: true
tox_install_siblings: false
fetch_subunit: false
devstack_plugins:
designate: https://opendev.org/openstack/designate
@@ -54,17 +38,19 @@
log: true
- 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.
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 devstack with Octavia plugin enabled
Run openstack collections functional tests against a master devstack
with Octavia plugin enabled, using releases of openstacksdk and latest
ansible release. Run it only on Load Balancer changes.
pre-run: ci/playbooks/get_amphora_tarball.yaml
# Do not set job.override-checkout or job.required-projects.override-checkout in base job because
# else Zuul will use this branch when matching variants for parent jobs during job freeze
required-projects:
- openstack/octavia
- name: github.com/ansible/ansible
override-checkout: stable-2.12
files:
- ^ci/roles/loadbalancer/.*$
- ^plugins/modules/lb_health_monitor.py
@@ -74,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
@@ -91,99 +77,68 @@
OCTAVIA_AMP_IMAGE_SIZE: 3
OCTAVIA_AMP_IMAGE_NAME: "test-only-amphora-x64-haproxy-ubuntu-bionic"
- 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'
- 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 master branch instead of non-existing stable/1.0.0 branch
name: openstack/devstack
override-checkout: master
- name: openstack/openstacksdk
# Yoga has the latest SDK release of the 0.*.* series atm
override-checkout: stable/yoga
vars:
tox_envlist: ansible-2.9
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-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 master branch instead of non-existing stable/1.0.0 branch
name: openstack/devstack
override-checkout: master
- name: openstack/openstacksdk
# Yoga has the latest SDK release of the 0.*.* series atm
override-checkout: stable/yoga
vars:
tox_envlist: ansible-2.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 master branch instead of non-existing stable/1.0.0 branch
name: openstack/devstack
override-checkout: master
- name: openstack/openstacksdk
# Yoga has the latest SDK release of the 0.*.* series atm
override-checkout: stable/yoga
vars:
tox_envlist: ansible
- 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
@@ -191,186 +146,136 @@
- 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/openstacksdk
override-checkout: stable/xena
vars:
tox_envlist: ansible-2.12
tox_envlist: ansible
tox_install_siblings: true
- 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
parent: ansible-collections-openstack-functional-devstack-ansible-devel
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
using wallaby brach of openstacksdk and stable 2.12 branch of ansible
voting: true
override-checkout: stable/wallaby
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/wallaby branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/wallaby branch
name: openstack/devstack
override-checkout: stable/wallaby
- name: openstack/openstacksdk
override-checkout: stable/wallaby
vars:
tox_envlist: ansible-2.12
tox_envlist: ansible
tox_install_siblings: true
- 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
parent: ansible-collections-openstack-functional-devstack-ansible-devel
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
using victoria brach of openstacksdk and stable 2.12 branch of ansible
voting: true
override-checkout: stable/victoria
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.12
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/victoria branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/victoria branch
name: openstack/devstack
override-checkout: stable/victoria
- name: openstack/openstacksdk
override-checkout: stable/victoria
vars:
tox_envlist: ansible-2.12
tox_envlist: ansible
tox_install_siblings: true
- 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
parent: ansible-collections-openstack-functional-devstack-ansible-devel
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
using ussuri brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/ussuri
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/ussuri branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/ussuri branch
name: openstack/devstack
override-checkout: stable/ussuri
- name: openstack/openstacksdk
override-checkout: stable/ussuri
- name: openstack/os-client-config
override-checkout: stable/ussuri
vars:
tox_envlist: ansible-2.11
tox_envlist: ansible
tox_install_siblings: true
- 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
parent: ansible-collections-openstack-functional-devstack-ansible-devel
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
using train brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/train
# job.override-checkout will not override job.required-projects.override-checkout in parent jobs
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- # Choose parent devstack job from stable/1.0.0 branch instead of non-existing stable/train branch
name: openstack/ansible-collections-openstack
override-checkout: stable/1.0.0
- # Choose parent devstack job from stable/train branch
name: openstack/devstack
override-checkout: stable/train
- name: openstack/openstacksdk
override-checkout: stable/train
- name: openstack/os-client-config
override-checkout: stable/train
vars:
tox_envlist: ansible-2.11
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
name: openstack-tox-linters-ansible-devel
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:
# override tox_constraints_file from parent job
tox_constraints_file: '{{ ansible_user_dir }}/{{ zuul.project.src_dir }}/tests/constraints-none.txt'
tox_envlist: linters
tox_install_siblings: true
- job:
name: openstack-tox-linters-ansible-devel
parent: openstack-tox-linters-ansible
description: |
Run openstack collections linter tests using the devel branch of ansible
# non-voting because we can't prevent ansible devel from breaking us
voting: false
vars:
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
@@ -379,110 +284,100 @@
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-pre-zed
parent: tripleo-ci-centos-8-standalone-build
# Do not restrict branches in base jobs because else Zuul would not find a matching
# parent job variant during job freeze when child jobs are on other branches.
vars: &tripleo_vars
name: tripleo-ci-centos-8-standalone-osa
parent: tripleo-ci-base-standalone-centos-8
vars:
featureset: '052'
consumer_job: false
# Define branch_override in child jobs, e.g. branch_override: stable/yoga
force_non_periodic: true
required-projects: &tripleo_required_projects
- # Required for TripleO Quickstart to install current patchset of the collection
# Ref.: https://opendev.org/openstack/tripleo-quickstart/src/commit/b48d869e14de40444d69a107a0b718b5f721e912/quickstart.sh#L123
openstack/ansible-collections-openstack
- # always use master branch when collecting parent job variants, refer to git blame for rationale.
name: openstack/tripleo-ci
override-checkout: master
irrelevant-files: &tripleo_irrelevant_files
build_container_images: true
irrelevant-files: &irr_files
- .*molecule.*
- ^.*\.md$
- ^.*\.rst$
- ^changelogs/.*$
- ^contrib/.*$
- ^docs/.*$
- ^contrib/.*$
- ^changelogs/.*$
- ^meta/.*$
- ^requirements.*$
- ^setup.*$
- ^tests/.*$
- ^tools/.*$
- ^requirements.*$
- ^test-requirements.*$
- ^setup.*$
- tox.ini
# Run only on files used in TripleO
files: &tripleo_files
- ^.zuul.yaml$
- ^plugins/module_utils/openstack.*$
files: &ooo_files
- ^plugins/modules/catalog_service.*$
- ^plugins/modules/compute_flavor.*$
- ^plugins/modules/endpoint.*$
- ^plugins/modules/identity_domain.*$
- ^plugins/modules/identity_domain_info.*$
- ^plugins/modules/identity_role.*$
- ^plugins/modules/identity_user.*$
- ^plugins/modules/image.*$
- ^plugins/modules/keypair.*$
- ^plugins/modules/network.*$
- ^plugins/modules/project.*$
- ^plugins/modules/role_assignment.*$
- ^plugins/modules/router.*$
- ^plugins/modules/stack.*$
- ^plugins/modules/subnet.*$
- ^plugins/module_utils/openstack.*$
- job:
name: tripleo-ci-centos-9-standalone-osa-pre-zed
parent: tripleo-ci-centos-9-standalone-build
# Do not restrict branches in base jobs because else Zuul would not find a matching
# parent job variant during job freeze when child jobs are on other branches.
vars: *tripleo_vars
required-projects: *tripleo_required_projects
irrelevant-files: *tripleo_irrelevant_files
files: *tripleo_files
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-osa-train
parent: tripleo-ci-centos-8-standalone-osa-pre-zed
branches: stable/1.0.0
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-osa-wallaby
parent: tripleo-ci-centos-8-standalone-osa-pre-zed
branches: stable/1.0.0
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-osa-wallaby
parent: tripleo-ci-centos-9-standalone-osa-pre-zed
branches: stable/1.0.0
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
@@ -516,10 +411,15 @@
- tox-pep8
- openstack-tox-linters-ansible-devel
- openstack-tox-linters-ansible-2.12
- ansible-collections-openstack-functional-devstack-releases:
- 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:
@@ -532,6 +432,7 @@
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
@@ -544,9 +445,18 @@
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- tripleo-ci-centos-8-standalone-osa-wallaby:
- tripleo-ci-centos-8-standalone-wallaby-osa:
dependencies: *deps_unit_lint
- tripleo-ci-centos-9-standalone-osa-wallaby:
- 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
@@ -554,19 +464,23 @@
jobs:
- tox-pep8
- openstack-tox-linters-ansible-2.12
- openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack
- ansible-collections-openstack-functional-devstack-releases
# - ansible-collections-openstack-functional-devstack-ansible-2.9
# - ansible-collections-openstack-functional-devstack-ansible-2.12
- ansible-collections-openstack-functional-devstack-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.12
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.12
- ansible-collections-openstack-functional-devstack-xena-ansible-2.12
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
# - ansible-collections-openstack-functional-devstack-train-ansible-2.11
- ansible-collections-openstack-functional-devstack-octavia
- tripleo-ci-centos-8-standalone-osa-wallaby
- 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
@@ -575,18 +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-octavia
- tripleo-ci-centos-9-standalone-osa-wallaby
- tripleo-ci-centos-8-standalone-osa-wallaby
- 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:
- tripleo-ci-centos-8-standalone-osa-train
- ansible-collections-openstack-functional-devstack-ansible-2.11
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.12
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11
tag:
jobs:

View File

@@ -5,92 +5,6 @@ Openstack Cloud Ansilbe modules Release Notes
.. contents:: Topics
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
======

View File

@@ -2,44 +2,11 @@
# Ansible Collection: openstack.cloud
This repo hosts the `openstack.cloud` Ansible Collection.
The collection includes the Openstack modules and plugins supported by Openstack community to help the management of Openstack infrastructure.
## Breaking backward compatibility :warning:
Dear contributors and users of the Ansible OpenStack collection!
Our codebase has been split into two separate release series:
* `2.x.x` releases of Ansible OpenStack collection are compatible with OpenStack SDK `1.x.x` and its release candidates
`0.99.x` *only* (OpenStack Zed and later). Our `master` branch tracks our `2.x.x` releases.
* `1.x.x` releases of Ansible OpenStack collection are compatible with OpenStack SDK `0.x.x` prior to `0.99.0` *only*
(OpenStack Yoga and earlier). Our `stable/1.0.0` branch tracks our `1.x.x` releases.
Both branches will be developed in parallel for the time being. Patches from `master` will be backported to
`stable/1.0.0` on a best effort basis but expect new features to be introduced in our `master` branch only.
Contributions are welcome for both branches!
Differences between both branches are mainly renamed and sometimes dropped module return values. We try to keep our
module parameters backward compatible by offering aliases but e.g. the semantics of `filters` parameters in `*_info`
modules have changed due to updates in the OpenStack SDK.
Our decision to break backward compatibility was not taken lightly. OpenStack SDK's first major release (`1.0.0` and its
release candidates `0.99.x`) has streamlined and improved large parts of its codebase. For example, its Connection
interface now consistently uses the Resource interfaces under the hood. This required breaking changes from older SDK
releases though. The Ansible OpenStack collection is heavily based on OpenStack SDK. With OpenStack SDK becoming
backward incompatible, so does our Ansible OpenStack collection. We simply lack the devpower to maintain a backward
compatible interface in Ansible OpenStack collection across several SDK releases.
Our first `2.0.0` release is currently under development and we still have a long way to go. If you use modules of the
Ansible OpenStack collection and want to join us in porting them to the upcoming OpenStack SDK, please contact us!
Ping Jakob Meng <mail@jakobmeng.de> (jm1) or Rafael Castillo <rcastill@redhat.com> (rcastillo) and we will give you a
quick introduction. We are also hanging around on `irc.oftc.net/#openstack-ansible-sig` and `irc.oftc.net/#oooq` 😎
We have extensive documentation on [why, what and how we are adopting and reviewing the new modules](
https://hackmd.io/szgyWa5qSUOWw3JJBXLmOQ?view), [how to set up a working DevStack environment for hacking on the
collection](https://hackmd.io/PI10x-iCTBuO09duvpeWgQ?view) and, most importantly, [a list of modules where we are
coordinating our porting efforts](https://hackmd.io/7NtovjRkRn-tKraBXfz9jw?view).
## Installation and Usage
### Installing dependencies
@@ -48,7 +15,7 @@ For using the Openstack Cloud collection firstly you need to install `ansible` a
For example with pip:
```bash
pip install "ansible>=2.9" "openstacksdk>=0.36,<0.99.0"
pip install ansible openstacksdk
```
OpenStackSDK has to be available to Ansible and to the Python interpreter on the host, where Ansible executes the module (target host).

View File

@@ -313,68 +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'

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,15 +63,12 @@
url = {{ ansible_galaxy_info.url }}
token = {{ ansible_galaxy_info.token }}
- name: Include galaxy.yml as vars file
include_vars: "{{ collection_path }}/galaxy.yml"
- name: Publish collection to Ansible Galaxy / Automation Hub
environment:
ANSIBLE_CONFIG: "{{ _ansiblecfg_tmp.path }}"
shell: >-
{{ ansible_galaxy_path }} collection publish -vvv
{{ build_collection_path }}/{{ namespace }}-{{ name }}-{{ version_tag }}.tar.gz
{{ build_collection_path }}/openstack-cloud-{{ version_tag }}.tar.gz
always:
- name: Shred ansible-galaxy credentials

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,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
- name: Check output of list flavors with filter
assert:
that:
- flavor_name.openstack_flavors | length == 1
- name: Assert fields on SDK 0.*
when: sdk_version is version(1.0, '<')
assert:
that:
- '["name", "description", "disk", "is_public", "ram",
"vcpus", "swap", "ephemeral", "is_disabled", "rxtx_factor", "id",
"extra_specs"] | difference(flavor_name.openstack_flavors.0.keys())
| length == 0'
- name: Assert fields on SDK 1.*
when: sdk_version is version(1.0, '>=')
assert:
that:
- '["name", "original_name", "description", "disk", "is_public", "ram",
"vcpus", "swap", "ephemeral", "is_disabled", "rxtx_factor", "id",
"extra_specs"] | difference(flavor_name.openstack_flavors.0.keys())
| length == 0'

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

@@ -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,69 +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

@@ -25,8 +25,8 @@
- name: Verify image info
assert:
that:
- "image_info_result.openstack_images[0].name == image_name"
- "image_info_result.openstack_images[0].tags | sort == image_tags | sort"
- "image_info_result.openstack_image.name == image_name"
- "image_info_result.openstack_image.tags | sort == image_tags | sort"
- name: Delete raw image (defaults)
openstack.cloud.image:
@@ -57,6 +57,11 @@
state: absent
name: "{{ image_name }}"
- name: Delete test image file
file:
name: "{{ tmp_file.stdout }}"
state: absent
- name: Try to get details of deleted image
openstack.cloud.image_info:
cloud: "{{ cloud }}"
@@ -66,83 +71,4 @@
- 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: "{{ image_name }}"
filename: "{{ tmp_file.stdout }}"
disk_format: raw
tags: "{{ image_tags }}"
project: image_owner_project
register: image
- name: Get details of created image (owner by project name)
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: "{{ image_name }}"
register: image_info_result
- name: Verify image owner (owner by project name)
assert:
that:
- image_info_result.openstack_images[0].owner == owner_project.project.id
- name: Delete raw image (owner by project name)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: absent
name: "{{ image_name }}"
- name: Create raw image (owner by project name and domain name)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: present
name: "{{ image_name }}"
filename: "{{ tmp_file.stdout }}"
disk_format: raw
tags: "{{ image_tags }}"
project: image_owner_project
project_domain: default
register: image
- name: Get details of created image (owner by project name and domain name)
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: "{{ image_name }}"
register: image_info_result
- name: Verify image owner (owner by project name and domain name)
assert:
that:
- image_info_result.openstack_images[0].owner == owner_project.project.id
- name: Delete raw image (owner by project name and domain name)
openstack.cloud.image:
cloud: "{{ cloud }}"
state: absent
name: "{{ image_name }}"
- name: Delete owner project
openstack.cloud.project:
cloud: "{{ cloud }}"
state: absent
name: image_owner_project
domain_id: default
- name: Delete test image file
file:
name: "{{ tmp_file.stdout }}"
state: absent
- 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

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

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

@@ -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: m1.tiny
floating_ip_pool_name: public
boot_volume_size: 5

View File

@@ -124,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:
@@ -133,6 +137,9 @@
state: absent
name: "{{ server_name }}"
wait: true
tags:
- object
- name: Create a minimal server
openstack.cloud.server:
cloud: "{{ cloud }}"

View File

@@ -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 }}"
flavor: "{{ flavor }}"
network: "{{ server_alt_network }}"
auto_floating_ip: false
wait: true
register: server_alt
- name: Get info about server in alternate project
openstack.cloud.server_info:
cloud: "{{ cloud_alt }}"
server: "{{ server_alt_name }}"
register: info25
- name: Ensure status for server in alternate project is ACTIVE
assert:
that:
- info25.openstack_servers.0.status == 'ACTIVE'
- name: Try to stop server in alternate project
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_alt_name }}"
action: stop
wait: true
ignore_errors: true
register: server_alt
- name: Ensure server was not stopped
assert:
that:
- server_alt is failed
- server_alt.msg == "Could not find server {{ server_alt_name }}"
- name: Stop server in alternate project with all_projects=true
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_alt_name }}"
action: stop
wait: true
all_projects: True
register: server_alt
- name: Get info about server in alternate project
openstack.cloud.server_info:
cloud: "{{ cloud_alt }}"
server: "{{ server_alt_name }}"
register: info26
- name: Ensure status for server is SHUTOFF
assert:
that:
- info26.openstack_servers.0.status == 'SHUTOFF'
- server_alt is changed
- name: Delete server in alternate project
openstack.cloud.server:
cloud: "{{ cloud_alt }}"
state: absent
name: "{{ server_alt_name }}"
wait: true
- name: Delete subnet for alternate server
openstack.cloud.subnet:
cloud: "{{ cloud_alt }}"
name: "{{ server_alt_subnet }}"
state: absent
- name: Delete network for alternate server
openstack.cloud.network:
cloud: "{{ cloud_alt }}"
name: "{{ server_alt_network }}"
state: absent

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

@@ -6,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
@@ -33,16 +31,14 @@
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}"
exit 1;;
@@ -138,6 +134,6 @@ pushd ci/
set -o pipefail
ANSIBLE_COLLECTIONS_PATHS=$TEST_COLLECTIONS_PATHS ansible-playbook \
-vvv ./run-collection.yml \
-e "sdk_version=${SDK_VER} cloud=${CLOUD} cloud_alt=${CLOUD_ALT} image=${IMAGE} ${ANSIBLE_VARS}" \
-e "sdk_version=${SDK_VER} cloud=${CLOUD} image=${IMAGE} ${ANSIBLE_VARS}" \
${tag_opt} 2>&1 | sudo tee /opt/stack/logs/test_output.log
popd

View File

@@ -6,7 +6,6 @@
roles:
- { role: address_scope, tags: address_scope }
- { role: auth, tags: auth }
- { role: catalog_service, tags: catalog_service }
- { role: client_config, tags: client_config }
- { role: dns_zone_info, tags: dns_zone_info }
- role: object_container
@@ -16,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
@@ -38,27 +28,24 @@
- role: keystone_federation_protocol
tags: keystone_federation_protocol
when: sdk_version is version(0.44, '>=')
- { 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 }

View File

@@ -13,7 +13,7 @@ Naming
------
* This is a collection named ``openstack.cloud``. There is no need for further namespace prefixing.
* Name any module that a cloud consumer would expect to use after the logical resource it manages:
* Name any module that a cloud consumer would expect to use after the logical resource it manages:
``server`` not ``nova``. This naming convention acknowledges that the end user does not care
which service manages the resource - that is a deployment detail. For example cloud consumers may
not know whether their floating IPs are managed by Nova or Neutron.
@@ -24,7 +24,6 @@ Interface
* If the resource being managed has an id, it should be returned.
* If the resource being managed has an associated object more complex than
an id, it should also be returned.
* Return format shall be a dictionary or list
Interoperability
----------------
@@ -54,7 +53,7 @@ Libraries
* All complex cloud interaction or interoperability code should be housed in
the `openstacksdk <https://opendev.org/openstack/openstacksdk>`_
library.
* All OpenStack API interactions should happen via the openstackSDK and not via
* All OpenStack API interactions should happen via the openstacksdk and not via
OpenStack Client libraries. The OpenStack Client libraries do no have end
users as a primary audience, they are for intra-server communication.
* All modules should be registered in ``meta/action_groups.yml`` for enabling the

View File

@@ -33,4 +33,4 @@ build_ignore:
- ansible_collections_openstack.egg-info
- contrib
- changelogs
version: 1.9.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,7 +29,7 @@ build_ignore:
- importer_result.json
- .tox
- .env
- .vscode
- ansible_collections_openstack.egg-info
- contrib
- changelogs
- 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

@@ -91,7 +91,7 @@ options:
type: str
requirements:
- python >= 3.6
- openstacksdk >= 0.36, < 0.99.0
- 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

@@ -25,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.
@@ -47,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
@@ -56,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
@@ -64,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: |
@@ -81,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
@@ -95,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:

View File

@@ -67,8 +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'
MINIMUM_SDK_VERSION = '0.12.0'
def openstack_argument_spec():
@@ -153,7 +152,7 @@ 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')
@@ -161,30 +160,18 @@ def openstack_cloud_from_module(module, min_version=None, max_version=None):
except ImportError:
module.fail_json(msg='openstacksdk is required for this module')
if min_version and MINIMUM_SDK_VERSION:
if min_version:
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
StrictVersion(min_version))
elif MINIMUM_SDK_VERSION:
else:
min_version = StrictVersion(MINIMUM_SDK_VERSION)
if max_version and MAXIMUM_SDK_VERSION:
max_version = min(StrictVersion(MAXIMUM_SDK_VERSION),
StrictVersion(max_version))
elif MAXIMUM_SDK_VERSION:
max_version = StrictVersion(MAXIMUM_SDK_VERSION)
if min_version and StrictVersion(sdk_version.__version__) < min_version:
if StrictVersion(sdk_version.__version__) < min_version:
module.fail_json(
msg="To utilize this module, the installed version of "
"the openstacksdk library MUST be >={min_version}.".format(
min_version=min_version))
if max_version and StrictVersion(sdk_version.__version__) > max_version:
module.fail_json(
msg="To utilize this module, the installed version of "
"the openstacksdk library MUST be <={max_version}.".format(
max_version=max_version))
cloud_config = module.params.pop('cloud', None)
try:
if isinstance(cloud_config, dict):
@@ -257,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.
@@ -320,36 +306,19 @@ class OpenStackModule:
except ImportError:
self.fail_json(msg='openstacksdk is required for this module')
# Fail if the available SDK version doesn't meet the minimum
# and maximum version requirements
if self.module_min_sdk_version and MINIMUM_SDK_VERSION:
# 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))
elif MINIMUM_SDK_VERSION:
else:
min_version = StrictVersion(MINIMUM_SDK_VERSION)
else:
min_version = None
if self.module_max_sdk_version and MAXIMUM_SDK_VERSION:
max_version = min(StrictVersion(MAXIMUM_SDK_VERSION),
StrictVersion(self.module_max_sdk_version))
elif MAXIMUM_SDK_VERSION:
max_version = StrictVersion(MAXIMUM_SDK_VERSION)
else:
max_version = None
if min_version and StrictVersion(self.sdk_version) < min_version:
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))
if max_version and StrictVersion(self.sdk_version) > max_version:
self.fail(
msg="To utilize this module, the installed version of "
"the openstacksdk library MUST be <={max_version}.".format(
max_version=max_version))
# Fail if there are set unsupported for this version parameters
# New parameters should NOT use 'default' but rely on SDK defaults
for param in self.argument_spec:

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:

View File

@@ -26,13 +26,11 @@ 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.
@@ -53,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 = '''
@@ -77,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
@@ -106,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']),
)
@@ -117,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):
@@ -139,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
@@ -176,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

@@ -126,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
@@ -219,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,11 +12,11 @@ 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"
@@ -59,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
@@ -87,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
@@ -98,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

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

@@ -81,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
@@ -156,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)
@@ -168,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):
@@ -197,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

@@ -165,6 +165,7 @@ class FloatingIPInfoModule(OpenStackModule):
router = self.params['router']
status = self.params['status']
data = []
query = {}
if description:
query['description'] = description
@@ -193,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

@@ -73,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
@@ -122,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):
@@ -144,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
@@ -168,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']
@@ -190,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(
@@ -204,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():

View File

@@ -18,7 +18,7 @@ options:
type: str
filters:
description:
- A dictionary of meta data to use for filtering. Elements of
- A dictionary of meta data to use for further filtering. Elements of
this dictionary may be additional dictionaries.
type: dict
requirements:
@@ -61,8 +61,7 @@ RETURN = '''
openstack_domains:
description: has all the OpenStack information about domains
returned: always, but can be null
type: list
elements: dict
type: complex
contains:
id:
description: Unique UUID.
@@ -90,8 +89,10 @@ class IdentityDomainInfoModule(OpenStackModule):
name=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module_kwargs = dict(
mutually_exclusive=[
['name', 'filters'],
],
supports_check_mode=True
)
@@ -99,14 +100,18 @@ class IdentityDomainInfoModule(OpenStackModule):
def run(self):
name = self.params['name']
filters = self.params['filters'] or {}
filters = self.params['filters']
args = {}
if name:
args['name_or_id'] = name
args['filters'] = filters
# Let's suppose user is passing domain ID
try:
domains = self.conn.get_domain(name)
except Exception:
domains = self.conn.search_domains(filters={'name': name})
else:
domains = self.conn.search_domains(filters)
domains = self.conn.search_domains(**args)
self.exit_json(changed=False, openstack_domains=domains)

View File

@@ -89,7 +89,7 @@ EXAMPLES = '''
RETURN = '''
openstack_groups:
description: Dictionary describing all the matching groups.
returned: always, but can be an empty list
returned: always, but can be null
type: complex
contains:
name:
@@ -126,19 +126,27 @@ class IdentityGroupInfoModule(OpenStackModule):
def run(self):
name = self.params['name']
domain = self.params['domain']
filters = self.params['filters'] or {}
filters = self.params['filters']
args = {}
if domain:
dom = self.conn.identity.find_domain(domain)
if dom:
args['domain_id'] = dom['id']
else:
self.fail_json(msg='Domain name or ID does not exist')
try:
# We assume admin is passing domain id
dom = self.conn.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
dom = self.conn.search_domains(filters={'name': domain})
if dom:
domain = dom[0]['id']
else:
self.fail_json(msg='Domain name or ID does not exist')
groups = self.conn.search_groups(name, filters, **args)
# groups is for backward (and forward) compatibility
self.exit_json(changed=False, groups=groups, openstack_groups=groups)
if not filters:
filters = {}
groups = self.conn.search_groups(name, filters, domain_id=domain)
self.exit_json(changed=False, groups=groups)
def main():

View File

@@ -49,10 +49,6 @@ role:
returned: On success when I(state) is 'present'.
type: complex
contains:
domain_id:
description: Domain to which the role belongs
type: str
sample: default
id:
description: Unique role ID.
type: str
@@ -92,16 +88,20 @@ class IdentityRoleModule(OpenStackModule):
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(state, role))
changed = False
if state == 'present':
if role is None:
role = self.conn.create_role(name=name)
role = self.conn.create_role(name)
changed = True
else:
changed = False
self.exit_json(changed=changed, role=role)
elif state == 'absent' and role is not None:
self.conn.identity.delete_role(role['id'])
changed = True
self.exit_json(changed=changed)
elif state == 'absent':
if role is None:
changed = False
else:
self.conn.delete_role(name)
changed = True
self.exit_json(changed=changed)
def main():

View File

@@ -7,19 +7,19 @@
DOCUMENTATION = '''
---
module: identity_role_info
short_description: Retrieve information about roles
short_description: Retrive information about roles
author: OpenStack Ansible SIG
description:
- Get information about identity roles in Openstack
options:
domain_id:
description:
- Domain ID which owns the role
- List roles in specified domain only
type: str
required: false
name:
description:
- Name or ID of the role
- List role speficied by name
type: str
required: false
@@ -37,19 +37,21 @@ openstack_roles:
returned: always
type: list
elements: dict
contains:
id:
description: Unique ID for the role
returned: success
type: str
name:
description: Unique role name, within the owning domain.
returned: success
type: str
domain_id:
description: References the domain ID which owns the role.
returned: success
type: str
sample:
- domain_id: None
id: 19bf514fdda84f808ccee8463bd85c1a
location:
cloud: mycloud
project:
domain_id: None
domain_name: None
id: None
name: None
region_name: None
zone: None
name: member
properties:
'''
EXAMPLES = '''
@@ -73,24 +75,23 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
class IdentityRoleInfoModule(OpenStackModule):
argument_spec = dict(
domain_id=dict(type='str', required=False),
name=dict(type='str', required=False),
)
module_kwargs = dict(
supports_check_mode=True,
)
def run(self):
params = {
'domain_id': self.params['domain_id'],
'name_or_id': self.params['name'],
}
params = {k: v for k, v in params.items() if v is not None}
roles = self.conn.search_roles(**params)
self.exit_json(changed=False, openstack_roles=roles)
roles = self.conn.list_roles(domain_id=self.params['domain_id'])
# Dictionaries are supported from Train release
roles = [item if isinstance(item, dict) else item.to_dict() for item in roles]
# Filtering by name is supported from Wallaby release
if self.params['name']:
roles = [item for item in roles if self.params['name'] in (item['id'], item['name'])]
self.results.update({'openstack_roles': roles})
def main():

View File

@@ -26,7 +26,6 @@ options:
update_password:
required: false
choices: ['always', 'on_create']
default: on_create
description:
- C(always) will attempt to update password. C(on_create) will only
set the password for newly created users.
@@ -109,46 +108,28 @@ RETURN = '''
user:
description: Dictionary describing the user.
returned: On success when I(state) is 'present'
type: dict
type: complex
contains:
default_project_id:
description: User default project ID. Only present with Keystone >= v3.
returned: success
type: str
sample: "4427115787be45f08f0ec22a03bfc735"
description:
description: The description of this user
returned: success
type: str
sample: "a user"
domain_id:
description: User domain ID. Only present with Keystone >= v3.
returned: success
type: str
sample: "default"
email:
description: User email address
returned: success
type: str
sample: "demo@example.com"
id:
description: User ID
returned: success
type: str
sample: "f59382db809c43139982ca4189404650"
enabled:
description: Indicates whether the user is enabled
type: bool
name:
description: Unique user name, within the owning domain
returned: success
description: User name
type: str
sample: "demouser"
username:
description: Username with Identity API v2 (OpenStack Pike or earlier) else Null
returned: success
type: str
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
@@ -164,37 +145,46 @@ class IdentityUserModule(OpenStackModule):
domain=dict(required=False, default=None),
enabled=dict(default=True, type='bool'),
state=dict(default='present', choices=['absent', 'present']),
update_password=dict(default='on_create', choices=['always', 'on_create']),
update_password=dict(default=None, choices=['always', 'on_create']),
)
module_kwargs = dict()
def _needs_update(self, params_dict, user):
for k in params_dict:
# We don't get password back in the user object, so assume any supplied
# password is a change.
if k == 'password':
return True
if k == 'default_project':
if user['default_project_id'] != params_dict['default_project']:
return True
else:
continue
if user[k] != params_dict[k]:
if k not in ('password', 'update_password') and user[k] != params_dict[k]:
return True
# We don't get password back in the user object, so assume any supplied
# password is a change.
if (
params_dict['password'] is not None
and params_dict['update_password'] == 'always'
):
return True
return False
def _get_domain_id(self, domain):
dom_obj = self.conn.identity.find_domain(domain)
if dom_obj is None:
# Ok, let's hope the user is non-admin and passing a sane id
return domain
return dom_obj.id
try:
# We assume admin is passing domain id
domain_id = self.conn.get_domain(domain)['id']
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
try:
domain_id = self.conn.search_domains(filters={'name': domain})[0]['id']
except Exception:
# Ok, let's hope the user is non-admin and passing a sane id
domain_id = domain
return domain_id
def _get_default_project_id(self, default_project, domain_id):
project = self.conn.identity.find_project(default_project, domain_id=domain_id)
project = self.conn.get_project(default_project, domain_id=domain_id)
if not project:
self.fail_json(msg='Default project %s is not valid' % default_project)
return project['id']
def run(self):
@@ -208,50 +198,84 @@ class IdentityUserModule(OpenStackModule):
update_password = self.params['update_password']
description = self.params['description']
domain_id = None
if domain:
domain_id = self._get_domain_id(domain)
user = self.conn.get_user(name, domain_id=domain_id)
else:
domain_id = None
user = self.conn.get_user(name)
changed = False
if state == 'present':
user_args = {
'name': name,
'email': email,
'domain_id': domain_id,
'description': description,
'enabled': enabled,
}
if update_password in ('always', 'on_create'):
if not password:
msg = "update_password is %s but a password value is missing" % update_password
self.fail_json(msg=msg)
default_project_id = None
if default_project:
default_project_id = self._get_default_project_id(
default_project, domain_id)
user_args['default_project'] = default_project_id
user_args = {k: v for k, v in user_args.items() if v is not None}
changed = False
if user is None:
if password:
user_args['password'] = password
user = self.conn.create_user(**user_args)
if description is not None:
user = self.conn.create_user(
name=name, password=password, email=email,
default_project=default_project_id, domain_id=domain_id,
enabled=enabled, description=description)
else:
user = self.conn.create_user(
name=name, password=password, email=email,
default_project=default_project_id, domain_id=domain_id,
enabled=enabled)
changed = True
else:
if update_password == 'always':
if not password:
self.fail_json(msg="update_password is always but a password value is missing")
user_args['password'] = password
params_dict = {'email': email, 'enabled': enabled,
'password': password,
'update_password': update_password}
if description is not None:
params_dict['description'] = description
if domain_id is not None:
params_dict['domain_id'] = domain_id
if default_project_id is not None:
params_dict['default_project_id'] = default_project_id
if self._needs_update(user_args, user):
user = self.conn.update_user(user['id'], **user_args)
if self._needs_update(params_dict, user):
if update_password == 'always':
if description is not None:
user = self.conn.update_user(
user['id'], password=password, email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled, description=description)
else:
user = self.conn.update_user(
user['id'], password=password, email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled)
else:
if description is not None:
user = self.conn.update_user(
user['id'], email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled, description=description)
else:
user = self.conn.update_user(
user['id'], email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled)
changed = True
else:
changed = False
self.exit_json(changed=changed, user=user)
elif state == 'absent' and user is not None:
self.conn.identity.delete_user(user['id'])
changed = True
self.exit_json(changed=changed)
elif state == 'absent':
if user is None:
changed = False
else:
if domain:
self.conn.delete_user(user['id'], domain_id=domain_id)
else:
self.conn.delete_user(user['id'])
changed = True
self.exit_json(changed=changed)
def main():

View File

@@ -74,40 +74,35 @@ EXAMPLES = '''
RETURN = '''
openstack_users:
description: has all the OpenStack information about users
returned: always
type: list
elements: dict
returned: always, but can be null
type: complex
contains:
id:
description: Unique UUID.
returned: success
type: str
name:
description: Username of the user.
returned: success
type: str
default_project_id:
description: Default project ID of the user
returned: success
type: str
description:
description: The description of this user
returned: success
type: str
domain_id:
description: Domain ID containing the user
returned: success
type: str
email:
description: Email of the user
description: Name given to the user.
returned: success
type: str
enabled:
description: Flag to indicate if the user is enabled
returned: success
type: bool
domain_id:
description: Domain ID containing the user
returned: success
type: str
default_project_id:
description: Default project ID of the user
returned: success
type: str
email:
description: Email of the user
returned: success
type: str
username:
description: Username with Identity API v2 (OpenStack Pike or earlier) else Null
description: Username of the user
returned: success
type: str
'''
@@ -132,15 +127,21 @@ class IdentityUserInfoModule(OpenStackModule):
domain = self.params['domain']
filters = self.params['filters']
args = {}
if domain:
dom_obj = self.conn.identity.find_domain(domain)
if dom_obj is None:
self.fail_json(
msg="Domain name or ID '{0}' does not exist".format(domain))
args['domain_id'] = dom_obj.id
try:
# We assume admin is passing domain id
dom = self.conn.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
dom = self.conn.search_domains(filters={'name': domain})
if dom:
domain = dom[0]['id']
else:
self.fail_json(msg='Domain name or ID does not exist')
users = self.conn.search_users(name, filters, **args)
users = self.conn.search_users(name, filters, domain_id=domain)
self.exit_json(changed=False, openstack_users=users)

View File

@@ -40,15 +40,9 @@ options:
default: bare
choices: ['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']
type: str
project:
owner:
description:
- The name or ID of the project owning the image
type: str
aliases: ['owner']
project_domain:
description:
- The domain the project owning the image belongs to
- May be used to identify a unique project when providing a name to the project argument and multiple projects with such name exist
- The owner of the image
type: str
min_disk:
description:
@@ -67,7 +61,7 @@ options:
description:
- Prevent image from being deleted
type: bool
default: false
default: 'no'
filename:
description:
- The path to the file which has to be uploaded
@@ -175,8 +169,7 @@ class ImageModule(OpenStackModule):
disk_format=dict(default='qcow2',
choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']),
container_format=dict(default='bare', choices=['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']),
project=dict(type='str', aliases=['owner']),
project_domain=dict(type='str'),
owner=dict(type='str'),
min_disk=dict(type='int', default=0),
min_ram=dict(type='int', default=0),
is_public=dict(type='bool', default=False),
@@ -209,16 +202,6 @@ class ImageModule(OpenStackModule):
kwargs = {}
if self.params['id'] is not None:
kwargs['id'] = self.params['id']
if self.params['project']:
project_domain = {'id': None}
if self.params['project_domain']:
project_domain = self.conn.get_domain(name_or_id=self.params['project_domain'])
if not project_domain or project_domain['id'] is None:
self.fail(msg='Project domain %s could not be found' % self.params['project_domain'])
project = self.conn.get_project(name_or_id=self.params['project'], domain_id=project_domain['id'])
if not project:
self.fail(msg='Project %s could not be found' % self.params['project'])
kwargs['owner'] = project['id']
image = self.conn.create_image(
name=self.params['name'],
filename=self.params['filename'],

View File

@@ -4,7 +4,6 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: image_info
short_description: Retrieve information about an image within OpenStack.
author: OpenStack Ansible SIG
@@ -18,12 +17,11 @@ options:
- Name or ID of the image
required: false
type: str
filters:
properties:
description:
- Dict of properties of the images used for query
type: dict
required: false
aliases: ['properties']
requirements:
- "python >= 3.6"
- "openstacksdk"
@@ -45,7 +43,7 @@ EXAMPLES = '''
- name: Show openstack information
debug:
msg: "{{ result.image }}"
msg: "{{ result.openstack_image }}"
# Show all available Openstack images
- name: Retrieve all available Openstack images
@@ -54,22 +52,22 @@ EXAMPLES = '''
- name: Show images
debug:
msg: "{{ result.image }}"
msg: "{{ result.openstack_image }}"
# Show images matching requested properties
- name: Retrieve images having properties with desired values
openstack.cloud.image_facts:
filters:
properties:
some_property: some_value
OtherProp: OtherVal
- name: Show images
debug:
msg: "{{ result.image }}"
msg: "{{ result.openstack_image }}"
'''
RETURN = '''
openstack_images:
openstack_image:
description: has all the openstack information about the image
returned: always, but can be null
type: complex
@@ -90,14 +88,14 @@ openstack_images:
description: Image created at timestamp.
returned: success
type: str
deleted:
description: Image deleted flag.
returned: success
type: bool
container_format:
description: Container format of the image.
returned: success
type: str
direct_url:
description: URL to access the image file kept in external store.
returned: success
type: str
min_ram:
description: Min amount of RAM required for this image.
returned: success
@@ -106,39 +104,19 @@ openstack_images:
description: Disk format of the image.
returned: success
type: str
file:
description: The URL for the virtual machine image file.
returned: success
type: str
os_hidden:
description: Controls whether an image is displayed in the default image-list response
returned: success
type: bool
locations:
description: A list of URLs to access the image file in external store.
returned: success
type: str
metadata:
description: The location metadata.
returned: success
type: str
schema:
description: URL for the schema describing a virtual machine image.
returned: success
type: str
updated_at:
description: Image updated at timestamp.
returned: success
type: str
virtual_size:
description: The virtual size of the image.
properties:
description: Additional properties associated with the image.
returned: success
type: str
type: dict
min_disk:
description: Min amount of disk space required for this image.
returned: success
type: int
is_protected:
protected:
description: Image protected flag.
returned: success
type: bool
@@ -150,8 +128,12 @@ openstack_images:
description: Owner for the image.
returned: success
type: str
visibility:
description: Indicates who has access to the image.
is_public:
description: Is public flag of the image.
returned: success
type: bool
deleted_at:
description: Image deleted at timestamp.
returned: success
type: str
size:
@@ -163,6 +145,7 @@ openstack_images:
returned: success
type: list
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
@@ -172,27 +155,20 @@ class ImageInfoModule(OpenStackModule):
argument_spec = dict(
image=dict(type='str', required=False),
filters=dict(type='dict', required=False, aliases=['properties']),
properties=dict(type='dict', required=False),
)
module_kwargs = dict(
supports_check_mode=True
)
def run(self):
args = {
'name_or_id': self.params['image'],
'filters': self.params['filters'],
}
args = {k: v for k, v in args.items() if v is not None}
images = self.conn.search_images(**args)
# for backward compatibility
if 'name_or_id' in args:
image = images[0] if images else None
if self.params['image']:
image = self.conn.get_image(self.params['image'])
self.exit(changed=False, openstack_image=image)
else:
image = images
self.exit(changed=False, openstack_images=images, image=image)
images = self.conn.search_images(filters=self.params['properties'])
self.exit(changed=False, openstack_image=images)
def main():

View File

@@ -54,8 +54,7 @@ RETURN = '''
openstack_keypairs:
description:
- Lists keypairs that are associated with the account.
type: list
elements: dict
type: complex
returned: always
contains:
created_at:
@@ -121,15 +120,31 @@ class KeyPairInfoModule(OpenStackModule):
)
def run(self):
filters = {k: self.params[k] for k in
['user_id', 'name', 'limit', 'marker']
if self.params[k] is not None}
keypairs = self.conn.search_keypairs(name_or_id=self.params['name'],
filters=filters)
# self.conn.search_keypairs() returned munch.Munch objects before Train
result = [raw if isinstance(raw, dict) else raw.to_dict(computed=False)
for raw in keypairs]
self.exit(changed=False, openstack_keypairs=result)
name = self.params['name']
user_id = self.params['user_id']
limit = self.params['limit']
marker = self.params['marker']
filters = {}
data = []
if user_id:
filters['user_id'] = user_id
if limit:
filters['limit'] = limit
if marker:
filters['marker'] = marker
result = self.conn.search_keypairs(name_or_id=name,
filters=filters)
raws = [raw if isinstance(raw, dict) else raw.to_dict()
for raw in result]
for raw in raws:
raw.pop('location')
data.append(raw)
self.exit(changed=False, openstack_keypairs=data)
def main():

View File

@@ -45,7 +45,7 @@ options:
- The number of successful checks before changing the operating status of the member to ONLINE.
max_retries_down:
type: 'str'
default: '3'
default: 3
description:
- The number of allowed check failures before changing the operating status of the member to ERROR. A valid value is from 1 to 10. The default is 3.
resp_timeout:
@@ -60,7 +60,7 @@ options:
type: bool
expected_codes:
type: 'str'
default: '200'
default: 200
description:
- The list of HTTP status codes expected in response from the member to declare it healthy. Specify one of the following values
A single value, such as 200

View File

@@ -12,41 +12,41 @@ description:
updated. Only the I(records), I(description), and I(ttl) values
can be updated.
options:
description:
zone:
description:
- Description of the recordset
- Zone managing the recordset
required: true
type: str
name:
description:
- Name of the recordset. It must be ended with name of dns zone.
required: true
type: str
recordset_type:
description:
- Recordset type
- Required when I(state=present).
choices: ['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']
type: str
records:
description:
- List of recordset definitions.
- Required when I(state=present).
type: list
elements: str
recordset_type:
description:
description:
- Recordset type
- Required when I(state=present).
choices: ['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
- Description of the recordset
type: str
ttl:
description:
- TTL (Time To Live) value in seconds
type: int
zone:
state:
description:
- Name or ID of the zone which manages the recordset
required: true
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
@@ -90,73 +90,36 @@ RETURN = '''
recordset:
description: Dictionary describing the recordset.
returned: On success when I(state) is 'present'.
type: dict
type: complex
contains:
action:
description: Current action in progress on the resource
type: str
returned: always
created_at:
description: Timestamp when the zone was created
type: str
returned: always
description:
description: Recordset description
type: str
sample: "Test description"
returned: always
id:
description: Unique recordset ID
type: str
sample: "c1c530a3-3619-46f3-b0f6-236927b2618c"
links:
description: Links related to the resource
type: dict
returned: always
name:
description: Recordset name
type: str
sample: "www.example.net."
returned: always
project_id:
description: ID of the proect to which the recordset belongs
zone_id:
description: Zone id
type: str
returned: always
records:
description: Recordset records
type: list
sample: ['10.0.0.1']
returned: always
status:
description:
- Recordset status
- Valid values include `PENDING_CREATE`, `ACTIVE`,`PENDING_DELETE`,
`ERROR`
sample: 9508e177-41d8-434e-962c-6fe6ca880af7
type:
description: Recordset type
type: str
returned: always
sample: "A"
description:
description: Recordset description
type: str
sample: "Test description"
ttl:
description: Zone TTL value
type: int
sample: 3600
returned: always
type:
description:
- Recordset type
- Valid values include `A`, `AAAA`, `MX`, `CNAME`, `TXT`, `NS`,
`SSHFP`, `SPF`, `SRV`, `PTR`
type: str
sample: "A"
returned: always
zone_id:
description: The id of the Zone which this recordset belongs to
type: str
sample: 9508e177-41d8-434e-962c-6fe6ca880af7
returned: always
zone_name:
description: The name of the Zone which this recordset belongs to
type: str
sample: "example.com."
returned: always
records:
description: Recordset records
type: list
sample: ['10.0.0.1']
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
@@ -164,13 +127,13 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
class DnsRecordsetModule(OpenStackModule):
argument_spec = dict(
description=dict(required=False, default=None),
name=dict(required=True),
records=dict(required=False, type='list', elements='str'),
recordset_type=dict(required=False, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
state=dict(default='present', choices=['absent', 'present']),
ttl=dict(required=False, type='int'),
zone=dict(required=True),
name=dict(required=True),
recordset_type=dict(required=False, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
records=dict(required=False, type='list', elements='str'),
description=dict(required=False, default=None),
ttl=dict(required=False, type='int'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = dict(
@@ -182,73 +145,88 @@ class DnsRecordsetModule(OpenStackModule):
module_min_sdk_version = '0.28.0'
def _needs_update(self, params, recordset):
for k in ('description', 'records', 'ttl'):
if k not in params:
continue
if params[k] is not None and params[k] != recordset[k]:
return True
return False
def _system_state_change(self, state, recordset):
def _system_state_change(self, state, records, description, ttl, recordset):
if state == 'present':
if recordset is None:
return True
kwargs = self._build_params()
return self._needs_update(kwargs, recordset)
if records is not None and recordset['records'] != records:
return True
if description is not None and recordset['description'] != description:
return True
if ttl is not None and recordset['ttl'] != ttl:
return True
if state == 'absent' and recordset:
return True
return False
def _build_params(self):
recordset_type = self.params['recordset_type']
records = self.params['records']
description = self.params['description']
ttl = self.params['ttl']
params = {
'description': description,
'records': records,
'type': recordset_type.upper(),
'ttl': ttl,
}
return {k: v for k, v in params.items() if v is not None}
def run(self):
zone = self.params.get('zone')
name = self.params.get('name')
state = self.params.get('state')
ttl = self.params.get('ttl')
recordsets = self.conn.search_recordsets(zone, name_or_id=name)
recordset = None
if recordsets:
recordset = recordsets[0]
try:
recordset_id = recordset['id']
except KeyError as e:
self.fail_json(msg=str(e))
else:
# recordsets is filtered by type and should never be more than 1 return
recordset = None
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(state, recordset))
changed = False
if state == 'present':
kwargs = self._build_params()
recordset_type = self.params.get('recordset_type').upper()
records = self.params.get('records')
description = self.params.get('description')
ttl = self.params.get('ttl')
kwargs = {}
if description:
kwargs['description'] = description
kwargs['records'] = records
if self.ansible.check_mode:
self.exit_json(
changed=self._system_state_change(
state, records, description, ttl, recordset))
if recordset is None:
kwargs['ttl'] = ttl or 300
type = kwargs.pop('type', None)
if type is not None:
kwargs['recordset_type'] = type
recordset = self.conn.create_recordset(zone=zone, name=name,
**kwargs)
changed = True
elif self._needs_update(kwargs, recordset):
type = kwargs.pop('type', None)
recordset = self.conn.update_recordset(zone, recordset['id'],
**kwargs)
if ttl:
kwargs['ttl'] = ttl
else:
kwargs['ttl'] = 300
recordset = self.conn.create_recordset(
zone=zone, name=name, recordset_type=recordset_type,
**kwargs)
changed = True
else:
if ttl:
kwargs['ttl'] = ttl
pre_update_recordset = recordset
changed = self._system_state_change(
state, records, description, ttl, pre_update_recordset)
if changed:
recordset = self.conn.update_recordset(
zone=zone, name_or_id=recordset_id, **kwargs)
self.exit_json(changed=changed, recordset=recordset)
elif state == 'absent' and recordset is not None:
self.conn.delete_recordset(zone, recordset['id'])
changed = True
self.exit_json(changed=changed)
elif state == 'absent':
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(
state, None, None, None, recordset))
if recordset is None:
changed = False
else:
self.conn.delete_recordset(zone, recordset_id)
changed = True
self.exit_json(changed=changed)
def main():

View File

@@ -120,42 +120,51 @@ class IdentityRoleAssignmentModule(OpenStackModule):
state = self.params.get('state')
filters = {}
find_filters = {}
domain_id = None
r = self.conn.identity.find_role(role)
r = self.conn.get_role(role)
if r is None:
self.fail_json(msg="Role %s is not valid" % role)
filters['role'] = r['id']
if domain:
d = self.conn.identity.find_domain(domain)
d = self.conn.get_domain(name_or_id=domain)
if d is None:
self.fail_json(msg="Domain %s is not valid" % domain)
filters['domain'] = d['id']
domain_id = d['id']
find_filters['domain_id'] = domain_id
if user:
u = self.conn.identity.find_user(user, **find_filters)
if domain:
u = self.conn.get_user(user, domain_id=filters['domain'])
else:
u = self.conn.get_user(user)
if u is None:
self.fail_json(msg="User %s is not valid" % user)
filters['user'] = u['id']
if group:
# self.conn.identity.find_group() does not accept
# a domain_id argument in Train's openstacksdk
g = self.conn.get_group(group, **find_filters)
if domain:
g = self.conn.get_group(group, domain_id=filters['domain'])
else:
g = self.conn.get_group(group)
if g is None:
self.fail_json(msg="Group %s is not valid" % group)
filters['group'] = g['id']
if project:
p = self.conn.identity.find_project(project, **find_filters)
if domain:
p = self.conn.get_project(project, domain_id=filters['domain'])
# OpenStack won't allow us to use both a domain and project as
# filter. Once we identified the project (using the domain as
# a filter criteria), we need to remove the domain itself from
# the filters list.
domain_id = filters.pop('domain')
else:
p = self.conn.get_project(project)
if p is None:
self.fail_json(msg="Project %s is not valid" % project)
filters['project'] = p['id']
# Keeping the self.conn.list_role_assignments because it calls directly
# the identity.role_assignments and there are some logics for the
# filters that won't worth rewrite here.
assignment = self.conn.list_role_assignments(filters=filters)
if self.ansible.check_mode:
@@ -163,9 +172,6 @@ class IdentityRoleAssignmentModule(OpenStackModule):
changed = False
# Both grant_role and revoke_role calls directly the proxy layer, and
# has some logic that won't worth to rewrite here so keeping it is a
# good idea
if state == 'present':
if not assignment:
kwargs = self._build_kwargs(user, group, project, domain_id)

View File

@@ -234,7 +234,8 @@ class RouterModule(OpenStackModule):
missing_port_ids,
requested_subnet_ids,
existing_subnet_ids,
router_ifs_cfg):
router_ifs_cfg,
filters=None):
"""Decide if the given router needs an update."""
if router['admin_state_up'] != self.params['admin_state_up']:
return True
@@ -475,7 +476,8 @@ class RouterModule(OpenStackModule):
missing_port_ids,
requested_subnet_ids,
existing_subnet_ids,
router_ifs_cfg)
router_ifs_cfg,
filters)
self.exit_json(changed=changed)
if state == 'present':
@@ -508,7 +510,8 @@ class RouterModule(OpenStackModule):
missing_port_ids,
requested_subnet_ids,
existing_subnet_ids,
router_ifs_cfg):
router_ifs_cfg,
filters):
changed = True
kwargs = self._build_kwargs(router, net)
updated_router = self.conn.update_router(**kwargs)

View File

@@ -49,7 +49,6 @@ options:
description:
- A list of tags to filter the list result by. Resources that match all tags in this list will be returned.
type: list
elements: str
requirements:
- "python >= 3.6"
- "openstacksdk"
@@ -179,7 +178,7 @@ class RouterInfoModule(OpenStackModule):
'ip_address': ip_spec.get('ip_address'),
'subnet_id': ip_spec.get('subnet_id')
}
interfaces_info.append(int_info)
interfaces_info.append(int_info)
router['interfaces_info'] = interfaces_info
self.exit(changed=False, openstack_routers=routers)

View File

@@ -63,11 +63,6 @@ options:
- Unique name or ID of the project.
required: false
type: str
description:
required: false
description:
- Description of the rule.
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
@@ -261,7 +256,6 @@ class SecurityGroupRuleModule(OpenStackModule):
choices=['egress', 'ingress']),
state=dict(default='present',
choices=['absent', 'present']),
description=dict(required=False, default=None),
project=dict(default=None),
)
@@ -355,12 +349,10 @@ class SecurityGroupRuleModule(OpenStackModule):
kwargs = {}
if project_id:
kwargs['project_id'] = project_id
if self.params["description"] is not None:
kwargs["description"] = self.params['description']
rule = self.conn.network.create_security_group_rule(
security_group_id=secgroup['id'],
port_range_min=None if self.params['port_range_min'] == -1 else self.params['port_range_min'],
port_range_max=None if self.params['port_range_max'] == -1 else self.params['port_range_max'],
rule = self.conn.create_security_group_rule(
secgroup['id'],
port_range_min=self.params['port_range_min'],
port_range_max=self.params['port_range_max'],
protocol=self.params['protocol'],
remote_ip_prefix=self.params['remote_ip_prefix'],
remote_group_id=remotegroup['id'],

View File

@@ -46,12 +46,6 @@ options:
description:
- Admin password for server to rebuild
type: str
all_projects:
description:
- Whether to search for server in all projects or just the current
auth scoped project.
type: bool
default: 'no'
requirements:
- "python >= 3.6"
@@ -126,7 +120,6 @@ class ServerActionModule(OpenStackModule):
'rebuild', 'shelve', 'shelve_offload', 'unshelve']),
image=dict(required=False, type='str'),
admin_password=dict(required=False, type='str', no_log=True),
all_projects=dict(required=False, type='bool', default=False),
)
module_kwargs = dict(
required_if=[('action', 'rebuild', ['image'])],
@@ -144,10 +137,7 @@ class ServerActionModule(OpenStackModule):
def _preliminary_checks(self):
# Using Munch object for getting information about a server
os_server = self.conn.get_server(
self.params['server'],
all_projects=self.params['all_projects'],
)
os_server = self.conn.get_server(self.params['server'])
if not os_server:
self.fail_json(msg='Could not find server %s' % self.params['server'])
# check mode
@@ -203,9 +193,8 @@ class ServerActionModule(OpenStackModule):
def _wait(self, os_server):
"""Wait for the server to reach the desired state for the given action."""
# The wait_for_server function needs a Server object instead of the
# Munch object returned by self.conn.get_server
server = self.conn.compute.get_server(os_server['id'])
# Using Server object for wait_for_server function
server = self.conn.compute.find_server(self.params['server'])
states = _action_map[self.params['action']]
try:

View File

@@ -39,7 +39,7 @@ options:
ip_version:
description:
- The IP version of the subnet 4 or 6
default: '4'
default: 4
type: str
choices: ['4', '6']
enable_dhcp:

View File

@@ -1,345 +0,0 @@
#!/usr/bin/python
# coding: utf-8 -*-
#
# Copyright (c) 2021 by Uemit Seren <uemit.seren@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: subnet_pool
short_description: Create or delete subnet pools from OpenStack
author: OpenStack Ansible SIG
description:
- Create or Delete subnet pools from OpenStack.
options:
state:
description:
- Indicate desired state of the resource
choices: ['present', 'absent']
default: present
type: str
name:
description:
- Name to be give to the subnet pool
required: true
type: str
project:
description:
- Unique name or ID of the project.
type: str
prefixes:
description:
- Set subnet pool prefixes (in CIDR notation)
type: list
elements: str
minimum_prefix_length:
description:
- The minimum prefix length that can be allocated from the subnet pool.
required: False
type: int
maximum_prefix_length:
description:
- The maximum prefix length that can be allocated from the subnet pool.
required: False
type: int
default_prefix_length:
description:
- The length of the prefix to allocate when the cidr or prefixlen attributes
are omitted when creating a subnet
type: int
required: False
address_scope:
description:
- Set address scope (ID or name) associated with the subnet pool
type: str
required: False
is_default:
description:
- Whether this subnet pool is by default
type: bool
default: 'no'
description:
description: The subnet pool description
type: str
required: False
default_quota:
description:
- A per-project quota on the prefix space that can be allocated
from the subnet pool for project subnets
required: False
type: int
shared:
description:
- Whether this subnet pool is shared or not.
type: bool
default: 'no'
extra_specs:
description:
- Dictionary with extra key/value pairs passed to the API
required: false
default: {}
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create an subnet pool.
- openstack.cloud.subnet_pool:
cloud: mycloud
state: present
name: my_subnet_pool
prefixes:
- 10.10.10.0/24
# Create a subnet pool for a given project.
- openstack.cloud.subnet_pool:
cloud: mycloud
state: present
name: my_subnet_pool
project: myproj
prefixes:
- 10.10.10.0/24
# Create a shared and default subnet pool in existing address scope
- openstack.cloud.subnet_pool:
cloud: mycloud
state: present
name: my_subnet_pool
address_scope: my_adress_scope
is_default: True
default_quota: 10
maximum_prefix_length: 32
minimum_prefix_length: 8
default_prefix_length: 24
shared: True
prefixes:
- 10.10.10.0/8
# Delete subnet poool.
- openstack.cloud.subnet_pool:
cloud: mycloud
state: absent
name: my_subnet_pool
'''
RETURN = '''
subnet_pool:
description: Dictionary describing the subnet pool.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Subnet Pool ID.
type: str
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
name:
description: Subnet Pool name.
type: str
sample: "my_subnet_pool"
project_id:
description: The ID of the project.
type: str
sample: "861174b82b43463c9edc5202aadc60ef"
ip_version:
description: The IP version of the subnet pool 4 or 6.
type: int
sample: 4
is_shared:
description: Indicates whether this subnet pool is shared across all projects.
type: bool
sample: false
is_default:
description: Indicates whether this is the default subnet pool.
type: bool
sample: false
address_scope_id:
description: The address scope ID.
type: str
sample: "861174b82b43463c9edc5202aadc60ef"
created_at:
description: Timestamp when the subnet pool was created.
type: str
sample: ""
default_prefix_length:
description:
- The length of the prefix to allocate when the cidr or prefixlen
attributes are omitted when creating a subnet
type: int
sample: 32
default_quota:
description:
- The per-project quota on the prefix space that can be allocated
from the subnet pool for project subnets.
type: int
sample: 22
description:
description: The subnet pool description.
type: str
sample: "My test subnet pool."
maximum_prefix_length:
description: The maximum prefix length that can be allocated from the subnet pool.
type: int
sample: 22
minimum_prefix_length:
description: The minimum prefix length that can be allocated from the subnet pool.
type: int
sample: 8
prefixes:
description: A list of subnet prefixes that are assigned to the subnet pool.
type: list
sample: ['10.10.20.0/24', '10.20.10.0/24']
revision_number:
description: Revision number of the subnet pool.
type: int
sample: 5
updated_at:
description: Timestamp when the subnet pool was last updated.
type: str
sample:
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class SubnetPoolModule(OpenStackModule):
argument_spec = dict(
state=dict(default='present', choices=['absent', 'present']),
name=dict(required=True),
shared=dict(default=False, type='bool'),
minimum_prefix_length=dict(default=None, type='int'),
maximum_prefix_length=dict(default=None, type='int'),
default_prefix_length=dict(default=None, type='int'),
description=dict(default=None, type='str'),
default_quota=dict(default=None, type='int'),
prefixes=dict(type='list', elements='str'),
is_default=dict(default=False, type='bool'),
address_scope=dict(default=None),
project=dict(default=None),
extra_specs=dict(type='dict', default=dict())
)
def _needs_update(self, subnet_pool):
"""Check for differences in the updatable values.
NOTE: We don't currently allow name updates.
"""
compare_simple = ['is_default',
'minimum_prefix_length',
'maximum_prefix_length',
'default_prefix_length',
'description',
'default_quota']
compare_list = ['prefixes']
for key in compare_simple:
if self.params[key] is not None and self.params[key] != subnet_pool[key]:
return True
for key in compare_list:
if (
self.params[key] is not None
and set(self.params[key]) != set(subnet_pool[key])
):
return True
return False
def _system_state_change(self, subnet_pool, filters=None):
"""Check if the system state would be changed."""
state = self.params['state']
if state == 'absent' and subnet_pool:
return True
if state == 'present':
if not subnet_pool:
return True
return self._needs_update(subnet_pool, filters)
return False
def _compose_subnet_pool_args(self):
subnet_pool_kwargs = {}
optional_parameters = ['name',
'minimum_prefix_length',
'maximum_prefix_length',
'default_prefix_length',
'description',
'is_default',
'default_quota',
'prefixes']
for optional_param in optional_parameters:
if self.params[optional_param] is not None:
subnet_pool_kwargs[optional_param] = self.params[optional_param]
return subnet_pool_kwargs
def run(self):
state = self.params['state']
name = self.params['name']
project = self.params['project']
address_scope = self.params['address_scope']
extra_specs = self.params['extra_specs']
if project is not None:
proj = self.conn.get_project(project)
if proj is None:
self.fail(msg='Project %s could not be found' % project)
project_id = proj['id']
else:
project_id = self.conn.current_project_id
address_scope_id = None
if address_scope is not None:
address_scope = self.conn.network.find_address_scope(name_or_id=address_scope)
if address_scope is None:
self.fail(msg='AddressScope %s could not be found' % address_scope)
address_scope_id = address_scope['id']
subnet_pool = self.conn.network.find_subnet_pool(name_or_id=name)
if self.ansible.check_mode:
self.exit_json(
changed=self._system_state_change(subnet_pool)
)
if state == 'present':
changed = False
if not subnet_pool:
kwargs = self._compose_subnet_pool_args()
kwargs['address_scope_id'] = address_scope_id
kwargs['project_id'] = project_id
kwargs['is_shared'] = self.params['shared']
dup_args = set(kwargs.keys()) & set(extra_specs.keys())
if dup_args:
raise ValueError('Duplicate key(s) {0} in extra_specs'
.format(list(dup_args)))
kwargs = dict(kwargs, **extra_specs)
subnet_pool = self.conn.network.create_subnet_pool(**kwargs)
changed = True
else:
if self._needs_update(subnet_pool):
kwargs = self._compose_subnet_pool_args()
subnet_pool = self.conn.network.update_subnet_pool(subnet_pool['id'], **kwargs)
changed = True
else:
changed = False
self.exit_json(changed=changed, subnet_pool=subnet_pool, id=subnet_pool['id'])
elif state == 'absent':
if not subnet_pool:
self.exit(changed=False)
else:
self.conn.network.delete_subnet_pool(subnet_pool['id'])
self.exit_json(changed=True)
def main():
module = SubnetPoolModule()
module()
if __name__ == '__main__':
main()

View File

@@ -1 +1 @@
openstacksdk>=0.36,<0.99.0
openstacksdk>=0.13

View File

@@ -1,11 +1,11 @@
ansible-core>=2.11.0,<2.12.0
flake8
galaxy-importer
openstacksdk
ansible-core
pycodestyle
flake8
pylint
rstcheck
ruamel.yaml
tox
voluptuous
yamllint
rstcheck
ruamel.yaml
#galaxy-importer # see https://review.opendev.org/#/c/743054
tox

View File

@@ -1,11 +1,11 @@
ansible-core>=2.12.0,<2.13.0
flake8
galaxy-importer
openstacksdk
ansible-core
pycodestyle
flake8
pylint
rstcheck
ruamel.yaml
tox
voluptuous
yamllint
rstcheck
ruamel.yaml
#galaxy-importer # see https://review.opendev.org/#/c/743054
tox

11
test-requirements-2.9.txt Normal file
View File

@@ -0,0 +1,11 @@
openstacksdk
ansible<2.10
pycodestyle
flake8
pylint
voluptuous
yamllint
rstcheck
ruamel.yaml
#galaxy-importer # see https://review.opendev.org/#/c/743054
tox

1
test-requirements.txt Symbolic link
View File

@@ -0,0 +1 @@
test-requirements-2.11.txt

View File

@@ -1 +0,0 @@
# No constraints are defined by default

View File

@@ -1,2 +0,0 @@
# 0.99.x are release candidates for the first major release of OpenStackSDK 1.x.x
openstacksdk<0.99.0

View File

@@ -1,2 +0,0 @@
# 0.99.x are release candidates for the first major release of OpenStackSDK 1.x.x
openstacksdk>=0.99.0

View File

@@ -1,13 +0,0 @@
ansible>=2.9.0,<2.10.0
flake8
# galaxy-importer 0.3.2 moved from ansible 2.9 to ansible-core 2.11
# Ref.: https://github.com/ansible/galaxy-importer/commit/98933547831922c45243f39d85eefe150b55fc36
galaxy-importer==0.3.1
openstacksdk
pycodestyle
pylint
rstcheck
ruamel.yaml
tox
voluptuous
yamllint

View File

@@ -1,15 +0,0 @@
# ansible.builtin.user module in ansible-core 2.13.0 and 2.13.1 is affected by #78017
# Ref.: https://github.com/ansible/ansible/issues/78017
#
# TODO: Remove ansible-core constraint once issue #78017 has been fixed.
ansible-core!=2.13.0,!=2.13.1
flake8
galaxy-importer
openstacksdk
pycodestyle
pylint
rstcheck
ruamel.yaml
tox
voluptuous
yamllint

View File

@@ -104,7 +104,7 @@ class TestNetworkArgs(object):
nics: net-id=1234
'''
args = os_server._network_args(self.module, self.cloud)
assert args[0]['net-id'] == '1234'
assert(args[0]['net-id'] == '1234')
def test_nics_string_net_id_list(self):
'''
@@ -112,8 +112,8 @@ class TestNetworkArgs(object):
nics: net-id=1234,net-id=4321
'''
args = os_server._network_args(self.module, self.cloud)
assert args[0]['net-id'] == '1234'
assert args[1]['net-id'] == '4321'
assert(args[0]['net-id'] == '1234')
assert(args[1]['net-id'] == '4321')
def test_nics_string_port_id(self):
'''
@@ -121,7 +121,7 @@ class TestNetworkArgs(object):
nics: port-id=1234
'''
args = os_server._network_args(self.module, self.cloud)
assert args[0]['port-id'] == '1234'
assert(args[0]['port-id'] == '1234')
def test_nics_string_net_name(self):
'''
@@ -129,7 +129,7 @@ class TestNetworkArgs(object):
nics: net-name=network1
'''
args = os_server._network_args(self.module, self.cloud)
assert args[0]['net-id'] == '5678'
assert(args[0]['net-id'] == '5678')
def test_nics_string_port_name(self):
'''
@@ -137,7 +137,7 @@ class TestNetworkArgs(object):
nics: port-name=port1
'''
args = os_server._network_args(self.module, self.cloud)
assert args[0]['port-id'] == '1234'
assert(args[0]['port-id'] == '1234')
def test_nics_structured_net_id(self):
'''
@@ -146,7 +146,7 @@ class TestNetworkArgs(object):
- net-id: '1234'
'''
args = os_server._network_args(self.module, self.cloud)
assert args[0]['net-id'] == '1234'
assert(args[0]['net-id'] == '1234')
def test_nics_structured_mixed(self):
'''
@@ -157,10 +157,10 @@ class TestNetworkArgs(object):
- 'net-name=network1,port-id=4321'
'''
args = os_server._network_args(self.module, self.cloud)
assert args[0]['net-id'] == '1234'
assert args[1]['port-id'] == '1234'
assert args[2]['net-id'] == '5678'
assert args[3]['port-id'] == '4321'
assert(args[0]['net-id'] == '1234')
assert(args[1]['port-id'] == '1234')
assert(args[2]['net-id'] == '5678')
assert(args[3]['port-id'] == '4321')
class TestCreateServer(object):
@@ -190,10 +190,10 @@ class TestCreateServer(object):
with pytest.raises(AnsibleExit):
os_server._create_server(self.module, self.cloud)
assert self.cloud.create_server.call_count == 1
assert self.cloud.create_server.call_args[1]['image'] == self.cloud.get_image_id('cirros')
assert self.cloud.create_server.call_args[1]['flavor'] == self.cloud.get_flavor('m1.tiny')['id']
assert self.cloud.create_server.call_args[1]['nics'][0]['net-id'] == self.cloud.get_network('network1')['id']
assert(self.cloud.create_server.call_count == 1)
assert(self.cloud.create_server.call_args[1]['image'] == self.cloud.get_image_id('cirros'))
assert(self.cloud.create_server.call_args[1]['flavor'] == self.cloud.get_flavor('m1.tiny')['id'])
assert(self.cloud.create_server.call_args[1]['nics'][0]['net-id'] == self.cloud.get_network('network1')['id'])
def test_create_server_bad_flavor(self):
'''
@@ -206,7 +206,8 @@ class TestCreateServer(object):
with pytest.raises(AnsibleFail):
os_server._create_server(self.module, self.cloud)
assert 'missing_flavor' in self.module.fail_json.call_args[1]['msg']
assert('missing_flavor' in
self.module.fail_json.call_args[1]['msg'])
def test_create_server_bad_nic(self):
'''
@@ -219,4 +220,5 @@ class TestCreateServer(object):
with pytest.raises(AnsibleFail):
os_server._create_server(self.module, self.cloud)
assert 'missing_network' in self.module.fail_json.call_args[1]['msg']
assert('missing_network' in
self.module.fail_json.call_args[1]['msg'])

View File

@@ -15,10 +15,11 @@
set -e
if python -c 'import sys; sys.exit(0 if sys.version_info[0:2] < (3, 6) else 1)'; then
echo "Skipped Ansible Galaxy content importer check because it requires Python 3.6 or later" 2>&1
exit
fi
TOXDIR=${1:-.}
######### Disbaled in https://review.opendev.org/#/c/743054
TOXDIR="${1:-.}"
python -m galaxy_importer.main "$TOXDIR/build_artifact/"*
# galaxy_importer.main does not return non-zero error code on error
#output=$(python -m galaxy_importer.main $TOXDIR/build_artifact/*)
#if echo $output | grep ERROR: ; then
# exit 1
#fi

39
tox.ini
View File

@@ -1,6 +1,6 @@
[tox]
minversion = 3.18.0
envlist = linters
envlist = pep8
skipsdist = True
ignore_basepython_conflict = True
@@ -21,8 +21,7 @@ setenv =
OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:true}
pip: PIP_INSTALL={env:PIP_INSTALL:true}
deps =
-c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
-r{toxinidir}/tests/requirements.txt
-r{toxinidir}/test-requirements.txt
pip: {toxinidir}
commands = stestr run {posargs}
@@ -34,10 +33,9 @@ commands =
[testenv:build]
deps =
ansible-core
galaxy-importer
pbr
ruamel.yaml
ansible-core
commands =
python {toxinidir}/tools/build.py
@@ -59,22 +57,24 @@ commands =
passenv = {[testenv:linters]passenv}
commands = {[testenv:linters]commands}
deps =
-c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
-r{toxinidir}/tests/requirements-ansible-2.9.txt
-r{toxinidir}/test-requirements-2.9.txt
[testenv:linters-2.11]
passenv = {[testenv:linters]passenv}
commands = {[testenv:linters]commands}
deps =
-c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
-r{toxinidir}/tests/requirements-ansible-2.11.txt
-r{toxinidir}/test-requirements-2.11.txt
[testenv:linters-2.12]
passenv = {[testenv:linters]passenv}
commands = {[testenv:linters]commands}
deps =
-c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
-r{toxinidir}/tests/requirements-ansible-2.12.txt
-r{toxinidir}/test-requirements-2.12.txt
[testenv:venv]
deps =
-r{toxinidir}/test-requirements.txt
commands = {posargs}
[flake8]
# W503 Is supposed to be off by default but in the latest pycodestyle isn't.
@@ -98,24 +98,29 @@ deps =
commands =
/bin/bash {toxinidir}/ci/run-ansible-tests-collection.sh -e {envdir} {posargs}
# PIP job runs with Ansible-2.9
[testenv:ansible-pip]
deps =
-r{toxinidir}/test-requirements-2.9.txt
{toxinidir}
passenv = {[testenv:ansible]passenv}
commands = {[testenv:ansible]commands}
[testenv:ansible-2.9]
deps =
-c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
-r{toxinidir}/tests/requirements-ansible-2.9.txt
-r{toxinidir}/test-requirements-2.9.txt
passenv = {[testenv:ansible]passenv}
commands = {[testenv:ansible]commands}
[testenv:ansible-2.11]
deps =
-c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
-r{toxinidir}/tests/requirements-ansible-2.11.txt
-r{toxinidir}/test-requirements-2.11.txt
passenv = {[testenv:ansible]passenv}
commands = {[testenv:ansible]commands}
[testenv:ansible-2.12]
deps =
-c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
-r{toxinidir}/tests/requirements-ansible-2.12.txt
-r{toxinidir}/test-requirements-2.12.txt
passenv = {[testenv:ansible]passenv}
commands = {[testenv:ansible]commands}