├── tests
├── unit
│ ├── __init__.py
│ ├── mock
│ │ ├── __init__.py
│ │ ├── path.py
│ │ ├── vault_helper.py
│ │ └── procenv.py
│ ├── modules
│ │ ├── __init__.py
│ │ ├── cloud
│ │ │ ├── __init__.py
│ │ │ └── openstack
│ │ │ │ └── __init__.py
│ │ ├── conftest.py
│ │ └── utils.py
│ ├── plugins
│ │ ├── __init__.py
│ │ └── inventory
│ │ │ ├── __init__.py
│ │ │ └── test_openstack.py
│ └── requirements.txt
├── .gitignore
├── sanity
│ ├── ignore-2.11.txt
│ ├── ignore-2.12.txt
│ ├── ignore-2.13.txt
│ └── ignore-2.9.txt
├── constraints-none.txt
├── constraints-openstacksdk-0.x.x.txt
├── constraints-openstacksdk-1.x.x.txt
├── requirements-ansible-2.11.txt
├── requirements-ansible-2.12.txt
├── requirements-ansible-2.16.txt
├── requirements-ansible-2.18.txt
├── requirements-ansible-2.9.txt
└── requirements.txt
├── plugins
├── modules
│ ├── __init__.py
│ ├── auth.py
│ ├── federation_idp_info.py
│ ├── federation_mapping_info.py
│ ├── config.py
│ ├── catalog_service_info.py
│ ├── group_assignment.py
│ ├── identity_role_info.py
│ ├── volume_service_info.py
│ ├── identity_domain_info.py
│ ├── trait.py
│ ├── keystone_federation_protocol_info.py
│ ├── compute_service_info.py
│ ├── identity_group_info.py
│ └── project_info.py
├── doc_fragments
│ └── __init__.py
├── inventory
│ └── __init__.py
└── module_utils
│ ├── __init__.py
│ └── ironic.py
├── requirements.txt
├── ci
├── roles
│ ├── auth
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── trait
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── resource
│ │ └── defaults
│ │ │ └── main.yml
│ ├── resources
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── logging
│ │ ├── defaults
│ │ │ └── main.yaml
│ │ └── tasks
│ │ │ └── main.yaml
│ ├── inventory
│ │ ├── files
│ │ │ └── ansible.cfg
│ │ └── templates
│ │ │ └── openstack.yaml.j2
│ ├── keystone_federation_protocol
│ │ └── defaults
│ │ │ └── main.yml
│ ├── identity_group
│ │ └── defaults
│ │ │ └── main.yml
│ ├── identity_role
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── identity_domain
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── catalog_service
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── baremetal_deploy_template
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── endpoint
│ │ ├── defaults
│ │ │ └── main.yaml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── address_scope
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── application_credential
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── project
│ │ └── defaults
│ │ │ └── main.yml
│ ├── volume_service
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── server_group
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── share_type
│ │ └── defaults
│ │ │ └── main.yml
│ ├── compute_service
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── host_aggregate
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── neutron_rbac_policy
│ │ └── defaults
│ │ │ └── main.yml
│ ├── keypair
│ │ └── defaults
│ │ │ └── main.yml
│ ├── identity_user
│ │ └── defaults
│ │ │ └── main.yml
│ ├── stack
│ │ ├── files
│ │ │ └── hello-world.yaml
│ │ ├── defaults
│ │ │ └── main.yaml
│ │ └── tasks
│ │ │ └── main.yaml
│ ├── security_group
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── volume_snapshot
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── compute_flavor
│ │ └── defaults
│ │ │ └── main.yml
│ ├── baremetal_port
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── dns_zone
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── config
│ │ └── tasks
│ │ │ └── main.yml
│ ├── volume_type
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ ├── volume_encryption.yml
│ │ │ └── main.yml
│ ├── recordset
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── security_group_rule
│ │ └── defaults
│ │ │ └── main.yml
│ ├── floating_ip
│ │ └── defaults
│ │ │ └── main.yml
│ ├── volume_backup
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── keystone_idp
│ │ └── defaults
│ │ │ └── main.yml
│ ├── loadbalancer
│ │ └── defaults
│ │ │ └── main.yml
│ ├── object_container
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── baremetal_inspect
│ │ ├── tasks
│ │ │ └── main.yml
│ │ └── defaults
│ │ │ └── main.yml
│ ├── coe_cluster
│ │ └── defaults
│ │ │ └── main.yml
│ ├── trunk
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── subnet_pool
│ │ └── defaults
│ │ │ └── main.yml
│ ├── subnet
│ │ └── defaults
│ │ │ └── main.yml
│ ├── federation_mapping
│ │ └── defaults
│ │ │ └── main.yml
│ ├── server_volume
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── object
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── coe_cluster_template
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── volume_manage
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── volume
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ ├── main.yml
│ │ │ └── volume_info.yml
│ ├── object_containers_info
│ │ └── defaults
│ │ │ └── main.yml
│ ├── quota
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── network
│ │ └── defaults
│ │ │ └── main.yml
│ ├── port
│ │ └── defaults
│ │ │ └── main.yml
│ ├── router
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ ├── shared_ext_network.yml
│ │ │ └── shared_network.yml
│ ├── baremetal_node
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── image
│ │ └── defaults
│ │ │ └── main.yml
│ ├── server
│ │ └── defaults
│ │ │ └── main.yml
│ ├── group_assignment
│ │ └── tasks
│ │ │ └── main.yml
│ ├── compute_flavor_access
│ │ └── tasks
│ │ │ └── main.yml
│ ├── volume_type_access
│ │ └── tasks
│ │ │ └── main.yml
│ └── server_metadata
│ │ └── tasks
│ │ └── main.yml
├── playbooks
│ ├── devstack
│ │ └── post.yaml
│ └── postlog.yaml
├── requirements.yml
├── run-collection.yml
└── publish
│ └── publish_collection.yml
├── .gitignore
├── .gitreview
├── setup.py
├── bindep.txt
├── changelogs
└── config.yaml
├── galaxy.yml.in
├── galaxy.yml
├── tools
├── check-import.sh
├── build.py
└── run-ansible-sanity.sh
├── setup.cfg
├── meta
└── runtime.yml
└── tox.ini
/tests/unit/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/plugins/modules/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 | output/
2 |
--------------------------------------------------------------------------------
/tests/sanity/ignore-2.11.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/sanity/ignore-2.12.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/sanity/ignore-2.13.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/sanity/ignore-2.9.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/mock/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/plugins/doc_fragments/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/plugins/inventory/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/plugins/module_utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/modules/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/plugins/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/modules/cloud/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | openstacksdk>=1.0.0
2 |
--------------------------------------------------------------------------------
/tests/unit/plugins/inventory/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/modules/cloud/openstack/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ci/roles/auth/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - auth_token
3 |
--------------------------------------------------------------------------------
/ci/roles/trait/defaults/main.yml:
--------------------------------------------------------------------------------
1 | trait_name: CUSTOM_ANSIBLE_TRAIT
2 |
--------------------------------------------------------------------------------
/tests/constraints-none.txt:
--------------------------------------------------------------------------------
1 | # No constraints are defined by default
2 |
--------------------------------------------------------------------------------
/ci/roles/resource/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | expected_fields:
3 | - resource
4 |
--------------------------------------------------------------------------------
/ci/roles/resources/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | expected_fields:
3 | - resources
4 |
--------------------------------------------------------------------------------
/tests/unit/requirements.txt:
--------------------------------------------------------------------------------
1 | # requirements for openstack collection
2 | munch
3 |
--------------------------------------------------------------------------------
/ci/playbooks/devstack/post.yaml:
--------------------------------------------------------------------------------
1 | - hosts: all
2 | roles:
3 | - fetch-tox-output
4 |
--------------------------------------------------------------------------------
/ci/roles/logging/defaults/main.yaml:
--------------------------------------------------------------------------------
1 | sdk_log_file_path: "{{ playbook_dir }}/sdk.log"
2 |
--------------------------------------------------------------------------------
/ci/roles/inventory/files/ansible.cfg:
--------------------------------------------------------------------------------
1 | [inventory]
2 | enable_plugins=openstack.cloud.openstack
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .tox
2 | build_artifact
3 | ansible_collections
4 | FILES.json
5 | MANIFEST.json
6 | importer_result.json
7 |
--------------------------------------------------------------------------------
/.gitreview:
--------------------------------------------------------------------------------
1 | [gerrit]
2 | host=review.opendev.org
3 | port=29418
4 | project=openstack/ansible-collections-openstack.git
5 |
--------------------------------------------------------------------------------
/ci/roles/keystone_federation_protocol/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - id
3 | - mapping_id
4 | - name
5 |
--------------------------------------------------------------------------------
/ci/roles/identity_group/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - description
3 | - domain_id
4 | - id
5 | - name
6 |
--------------------------------------------------------------------------------
/ci/roles/identity_role/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - description
3 | - domain_id
4 | - id
5 | - links
6 | - name
7 |
--------------------------------------------------------------------------------
/ci/roles/identity_domain/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - description
3 | - id
4 | - is_enabled
5 | - name
6 | - links
7 |
--------------------------------------------------------------------------------
/ci/roles/catalog_service/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - description
3 | - id
4 | - is_enabled
5 | - links
6 | - name
7 | - type
8 |
--------------------------------------------------------------------------------
/ci/roles/baremetal_deploy_template/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - created_at
3 | - extra
4 | - id
5 | - name
6 | - steps
7 | - updated_at
8 |
--------------------------------------------------------------------------------
/tests/constraints-openstacksdk-0.x.x.txt:
--------------------------------------------------------------------------------
1 | # 0.99.0 and later are release candidates for the first major release of OpenStackSDK 1.x.x
2 | openstacksdk<0.99.0
3 |
--------------------------------------------------------------------------------
/tests/constraints-openstacksdk-1.x.x.txt:
--------------------------------------------------------------------------------
1 | # 0.99.0 and later are release candidates for the first major release of OpenStackSDK 1.x.x
2 | openstacksdk>=1.0.0
3 |
--------------------------------------------------------------------------------
/ci/roles/endpoint/defaults/main.yaml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - id
3 | - interface
4 | - is_enabled
5 | - links
6 | - name
7 | - region_id
8 | - service_id
9 | - url
10 |
--------------------------------------------------------------------------------
/ci/roles/address_scope/defaults/main.yml:
--------------------------------------------------------------------------------
1 | address_scope_name: "address_scope"
2 | expected_fields:
3 | - id
4 | - ip_version
5 | - is_shared
6 | - name
7 | - project_id
8 | - tenant_id
9 |
--------------------------------------------------------------------------------
/ci/roles/application_credential/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - description
3 | - expires_at
4 | - id
5 | - name
6 | - project_id
7 | - roles
8 | - secret
9 | - unrestricted
10 |
--------------------------------------------------------------------------------
/ci/roles/project/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - description
3 | - domain_id
4 | - id
5 | - is_domain
6 | - is_enabled
7 | - name
8 | - options
9 | - parent_id
10 | - tags
11 |
--------------------------------------------------------------------------------
/ci/roles/volume_service/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - availability_zone
3 | - binary
4 | - disabled_reason
5 | - host
6 | - name
7 | - state
8 | - status
9 | - updated_at
10 |
--------------------------------------------------------------------------------
/ci/roles/server_group/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - id
3 | - name
4 | - policy
5 | - policies
6 | - member_ids
7 | - metadata
8 | - project_id
9 | - rules
10 | - user_id
11 |
12 |
--------------------------------------------------------------------------------
/tests/requirements-ansible-2.11.txt:
--------------------------------------------------------------------------------
1 | ansible-core>=2.11.0,<2.12.0
2 | flake8
3 | galaxy-importer
4 | openstacksdk
5 | pycodestyle
6 | pylint
7 | rstcheck
8 | ruamel.yaml
9 | tox
10 | voluptuous
11 | yamllint
12 |
--------------------------------------------------------------------------------
/tests/requirements-ansible-2.12.txt:
--------------------------------------------------------------------------------
1 | ansible-core>=2.12.0,<2.13.0
2 | flake8
3 | galaxy-importer
4 | openstacksdk
5 | pycodestyle
6 | pylint
7 | rstcheck
8 | ruamel.yaml
9 | tox
10 | voluptuous
11 | yamllint
12 |
--------------------------------------------------------------------------------
/tests/unit/mock/path.py:
--------------------------------------------------------------------------------
1 | from unittest.mock import MagicMock
2 |
3 | from ansible.utils.path import unfrackpath
4 |
5 |
6 | mock_unfrackpath_noop = MagicMock(spec_set=unfrackpath, side_effect=lambda x, *args, **kwargs: x)
7 |
--------------------------------------------------------------------------------
/ci/roles/share_type/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | share_backend_name: GENERIC_BACKEND
3 | share_type_name: test_share_type
4 | share_type_description: Test share type for CI
5 | share_type_alt_description: Changed test share type
6 |
--------------------------------------------------------------------------------
/tests/requirements-ansible-2.16.txt:
--------------------------------------------------------------------------------
1 | ansible-core>=2.16.0,<2.17.0
2 | flake8
3 | galaxy-importer
4 | openstacksdk
5 | pycodestyle
6 | pylint
7 | rstcheck
8 | ruamel.yaml
9 | tox
10 | voluptuous
11 | yamllint
12 | setuptools
13 |
--------------------------------------------------------------------------------
/tests/requirements-ansible-2.18.txt:
--------------------------------------------------------------------------------
1 | ansible-core>=2.18.0,<2.19.0
2 | flake8
3 | galaxy-importer
4 | openstacksdk
5 | pycodestyle
6 | pylint
7 | rstcheck
8 | ruamel.yaml
9 | tox
10 | voluptuous
11 | yamllint
12 | setuptools
13 |
--------------------------------------------------------------------------------
/ci/playbooks/postlog.yaml:
--------------------------------------------------------------------------------
1 | - hosts: all
2 | tasks:
3 | - zuul_return:
4 | data:
5 | zuul:
6 | artifacts:
7 | - name: Test log
8 | url: controller/logs/test_output_log.txt
9 |
--------------------------------------------------------------------------------
/ci/roles/compute_service/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - availability_zone
3 | - binary
4 | - disabled_reason
5 | - host
6 | - id
7 | - is_forced_down
8 | - name
9 | - state
10 | - status
11 | - updated_at
12 |
--------------------------------------------------------------------------------
/ci/roles/host_aggregate/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - availability_zone
3 | - created_at
4 | - deleted_at
5 | - hosts
6 | - id
7 | - is_deleted
8 | - metadata
9 | - name
10 | - updated_at
11 | - uuid
12 |
--------------------------------------------------------------------------------
/ci/roles/neutron_rbac_policy/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - action
3 | - id
4 | - name
5 | - object_id
6 | - object_type
7 | - project_id
8 | - target_project_id
9 | - tenant_id
10 | all_project_symbol: '*'
11 |
--------------------------------------------------------------------------------
/ci/roles/keypair/defaults/main.yml:
--------------------------------------------------------------------------------
1 | keypair_name: shade_keypair
2 | expected_fields:
3 | - created_at
4 | - fingerprint
5 | - id
6 | - is_deleted
7 | - name
8 | - private_key
9 | - public_key
10 | - type
11 | - user_id
12 |
--------------------------------------------------------------------------------
/ci/roles/identity_user/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - default_project_id
3 | - description
4 | - domain_id
5 | - email
6 | - id
7 | - is_enabled
8 | - links
9 | - name
10 | - password
11 | - password_expires_at
12 |
--------------------------------------------------------------------------------
/ci/roles/stack/files/hello-world.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Minimal HOT template defining a single compute server.
3 | #
4 | heat_template_version: 2013-05-23
5 |
6 | description: >
7 | Minimal HOT template for stack
8 |
9 | parameters:
10 | resources:
11 | outputs:
12 |
--------------------------------------------------------------------------------
/ci/roles/security_group/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - created_at
3 | - description
4 | - name
5 | - project_id
6 | - security_group_rules
7 | - stateful
8 | - tenant_id
9 | - updated_at
10 | - revision_number
11 | - id
12 | - tags
13 |
--------------------------------------------------------------------------------
/ci/roles/volume_snapshot/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - created_at
3 | - description
4 | - id
5 | - is_forced
6 | - metadata
7 | - name
8 | - progress
9 | - project_id
10 | - size
11 | - status
12 | - updated_at
13 | - volume_id
14 |
--------------------------------------------------------------------------------
/ci/roles/compute_flavor/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - description
3 | - disk
4 | - ephemeral
5 | - extra_specs
6 | - id
7 | - is_disabled
8 | - is_public
9 | - name
10 | - original_name
11 | - ram
12 | - rxtx_factor
13 | - swap
14 | - vcpus
15 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # Copyright Red Hat, Inc. All Rights Reserved.
2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3 |
4 | import setuptools
5 |
6 | setuptools.setup(
7 | setup_requires=['pbr', 'setuptools'],
8 | pbr=True,
9 | py_modules=[])
10 |
--------------------------------------------------------------------------------
/ci/roles/baremetal_port/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - address
3 | - created_at
4 | - extra
5 | - id
6 | - internal_info
7 | - is_pxe_enabled
8 | - links
9 | - local_link_connection
10 | - name
11 | - node_id
12 | - physical_network
13 | - port_group_id
14 | - updated_at
15 |
--------------------------------------------------------------------------------
/ci/roles/dns_zone/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - action
3 | - attributes
4 | - created_at
5 | - description
6 | - email
7 | - id
8 | - links
9 | - masters
10 | - name
11 | - pool_id
12 | - project_id
13 | - serial
14 | - status
15 | - ttl
16 | - type
17 | - updated_at
18 |
--------------------------------------------------------------------------------
/ci/roles/inventory/templates/openstack.yaml.j2:
--------------------------------------------------------------------------------
1 | plugin: openstack.cloud.openstack
2 |
3 | all_projects: true
4 | compose:
5 | ci_compose_id: openstack.id
6 | ci_compose_project_id: openstack.project_id
7 | expand_hostvars: true
8 | fail_on_errors: true
9 | only_clouds:
10 | - "{{ cloud }}"
11 | strict: true
12 |
--------------------------------------------------------------------------------
/ci/requirements.yml:
--------------------------------------------------------------------------------
1 | ---
2 | collections:
3 | - ansible.posix
4 | - ansible.utils
5 | - name: community.general
6 | version: 4.8.8
7 | # 5.0.0 dropped compatibility with ansible 2.9 and ansible-base 2.10
8 | # Ref.: https://github.com/ansible-collections/community.general/commit/1a9b3214fdf1eaccba5cc9ee210cbc5b5070fe4b
9 |
--------------------------------------------------------------------------------
/ci/roles/config/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: List all cloud profiles
3 | openstack.cloud.config:
4 | register: config
5 | # WARNING: This will output sensitive authentication information!!!!
6 |
7 | - name: Assert config module
8 | assert:
9 | that:
10 | - cloud in (config.clouds | map(attribute='name') | list)
11 |
--------------------------------------------------------------------------------
/ci/roles/volume_type/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | volume_backend_name: LVM_iSCSI
3 | volume_type_name: test_type
4 | volume_type_description: Test volume type
5 |
6 | enc_provider_name: nova.volume.encryptors.luks.LuksEncryptor
7 | enc_cipher: aes-xts-plain64
8 | enc_control_location: front-end
9 | enc_control_alt_location: back-end
10 | enc_key_size: 256
11 |
--------------------------------------------------------------------------------
/ci/roles/auth/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Authenticate to the cloud
3 | openstack.cloud.auth:
4 | cloud={{ cloud }}
5 | register: auth
6 |
7 | - name: Assert return values of auth module
8 | assert:
9 | that:
10 | # allow new fields to be introduced but prevent fields from being removed
11 | - expected_fields|difference(auth.keys())|length == 0
12 |
--------------------------------------------------------------------------------
/tests/requirements-ansible-2.9.txt:
--------------------------------------------------------------------------------
1 | ansible>=2.9.0,<2.10.0
2 | flake8
3 | # galaxy-importer 0.3.2 moved from ansible 2.9 to ansible-core 2.11
4 | # Ref.: https://github.com/ansible/galaxy-importer/commit/98933547831922c45243f39d85eefe150b55fc36
5 | galaxy-importer==0.3.1
6 | openstacksdk
7 | pycodestyle
8 | pylint
9 | rstcheck
10 | ruamel.yaml
11 | tox
12 | voluptuous
13 | yamllint
14 |
--------------------------------------------------------------------------------
/bindep.txt:
--------------------------------------------------------------------------------
1 | # This is a cross-platform list tracking distribution packages needed by tests;
2 | # see https://docs.openstack.org/infra/bindep/ for additional information.
3 |
4 | gcc [compile platform:centos-8 platform:rhel-8]
5 | python38-cryptography [platform:centos-8 platform:rhel-8]
6 | python38-devel [compile platform:centos-8 platform:rhel-8]
7 | python38-requests [platform:centos-8 platform:rhel-8]
8 |
--------------------------------------------------------------------------------
/tests/requirements.txt:
--------------------------------------------------------------------------------
1 | # ansible.builtin.user module in ansible-core 2.13.0 and 2.13.1 is affected by #78017
2 | # Ref.: https://github.com/ansible/ansible/issues/78017
3 | #
4 | # TODO: Remove ansible-core constraint once issue #78017 has been fixed.
5 | ansible-core!=2.13.0,!=2.13.1
6 | flake8
7 | galaxy-importer
8 | openstacksdk
9 | pycodestyle
10 | pylint
11 | rstcheck
12 | ruamel.yaml
13 | tox
14 | voluptuous
15 | yamllint
16 |
--------------------------------------------------------------------------------
/ci/roles/recordset/defaults/main.yml:
--------------------------------------------------------------------------------
1 | dns_zone_name: test.dns.zone.
2 | recordset_name: testrecordset.test.dns.zone.
3 | records: ['10.0.0.0', '10.0.0.2']
4 | updated_records: ['10.1.1.1', '10.0.0.2']
5 |
6 | recordset_fields:
7 | - action
8 | - created_at
9 | - description
10 | - id
11 | - links
12 | - name
13 | - project_id
14 | - records
15 | - status
16 | - ttl
17 | - type
18 | - zone_id
19 | - zone_name
20 |
--------------------------------------------------------------------------------
/ci/roles/security_group_rule/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - created_at
3 | - description
4 | - direction
5 | - ether_type
6 | - id
7 | - name
8 | - port_range_max
9 | - port_range_min
10 | - project_id
11 | - protocol
12 | - remote_address_group_id
13 | - remote_group_id
14 | - remote_ip_prefix
15 | - revision_number
16 | - security_group_id
17 | - tags
18 | - tenant_id
19 | - updated_at
20 |
--------------------------------------------------------------------------------
/ci/roles/floating_ip/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | expected_fields:
3 | - created_at
4 | - description
5 | - dns_domain
6 | - dns_name
7 | - fixed_ip_address
8 | - floating_ip_address
9 | - floating_network_id
10 | - id
11 | - name
12 | - port_details
13 | - port_id
14 | - project_id
15 | - qos_policy_id
16 | - revision_number
17 | - router_id
18 | - status
19 | - subnet_id
20 | - tags
21 | - updated_at
22 |
--------------------------------------------------------------------------------
/ci/roles/volume_backup/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - availability_zone
3 | - container
4 | - created_at
5 | - data_timestamp
6 | - description
7 | - fail_reason
8 | - force
9 | - has_dependent_backups
10 | - id
11 | - is_incremental
12 | - links
13 | - metadata
14 | - name
15 | - object_count
16 | - project_id
17 | - size
18 | - snapshot_id
19 | - status
20 | - updated_at
21 | - user_id
22 | - volume_id
23 |
--------------------------------------------------------------------------------
/ci/roles/keystone_idp/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - description
3 | - domain_id
4 | - id
5 | - is_enabled
6 | - name
7 | - remote_ids
8 | remote_ids_1:
9 | - 'https://auth.example.com/auth/realms/ExampleRealm'
10 | - 'https://auth.stage.example.com/auth/realms/ExampleRealm'
11 | remote_ids_2:
12 | - 'https://auth.example.com/auth/realms/ExampleRealm'
13 | remote_ids_3:
14 | - 'https://auth.stage.example.com/auth/realms/ExampleRealm'
15 |
--------------------------------------------------------------------------------
/ci/roles/loadbalancer/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - additional_vips
3 | - availability_zone
4 | - created_at
5 | - description
6 | - flavor_id
7 | - id
8 | - is_admin_state_up
9 | - listeners
10 | - name
11 | - operating_status
12 | - pools
13 | - project_id
14 | - provider
15 | - provisioning_status
16 | - tags
17 | - updated_at
18 | - vip_address
19 | - vip_network_id
20 | - vip_port_id
21 | - vip_qos_policy_id
22 | - vip_subnet_id
23 |
--------------------------------------------------------------------------------
/ci/roles/object_container/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - bytes
3 | - bytes_used
4 | - content_type
5 | - count
6 | - history_location
7 | - id
8 | - if_none_match
9 | - is_content_type_detected
10 | - is_newest
11 | - meta_temp_url_key
12 | - meta_temp_url_key_2
13 | - metadata
14 | - name
15 | - object_count
16 | - read_ACL
17 | - storage_policy
18 | - sync_key
19 | - sync_to
20 | - timestamp
21 | - versions_location
22 | - write_ACL
23 |
--------------------------------------------------------------------------------
/ci/roles/logging/tasks/main.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Trigger flavor listing to create logs
3 | openstack.cloud.compute_flavor_info:
4 | cloud: "{{ cloud }}"
5 | sdk_log_path: "{{ sdk_log_file_path }}"
6 | sdk_log_level: "DEBUG"
7 |
8 | - name: Read openstacksdk's log file
9 | ansible.builtin.slurp:
10 | src: "{{ sdk_log_file_path }}"
11 | register: log
12 |
13 | - name: Print contents of openstacksdk's log
14 | ansible.builtin.debug:
15 | msg: "{{ log['content'] | b64decode }}"
16 |
--------------------------------------------------------------------------------
/ci/roles/baremetal_inspect/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # TODO: Actually run this role in CI. Atm we do not have DevStack's ironic plugin enabled.
3 | - name: Introspect node
4 | openstack.cloud.baremetal_inspect:
5 | cloud: "{{ cloud }}"
6 | name: node-1
7 | register: inspect
8 |
9 | - debug: var=inspect
10 |
11 | - name: assert return values of baremetal_inspect module
12 | assert:
13 | that:
14 | # allow new fields to be introduced but prevent fields from being removed
15 | - expected_fields|difference(inspect.node.keys())|length == 0
16 |
--------------------------------------------------------------------------------
/ci/roles/coe_cluster/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - api_address
3 | - cluster_template_id
4 | - coe_version
5 | - create_timeout
6 | - created_at
7 | - discovery_url
8 | - fixed_network
9 | - fixed_subnet
10 | - flavor_id
11 | - id
12 | - is_floating_ip_enabled
13 | - is_master_lb_enabled
14 | - keypair
15 | - labels
16 | - master_addresses
17 | - master_count
18 | - master_flavor_id
19 | - name
20 | - node_addresses
21 | - node_count
22 | - stack_id
23 | - status
24 | - status_reason
25 | - updated_at
26 | - uuid
27 |
--------------------------------------------------------------------------------
/ci/roles/trunk/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - created_at
3 | - description
4 | - id
5 | - is_admin_state_up
6 | - name
7 | - port_id
8 | - project_id
9 | - revision_number
10 | - status
11 | - sub_ports
12 | - tags
13 | - tenant_id
14 | - updated_at
15 | trunk_name: ansible_trunk
16 | parent_network_name: ansible_parent_port_network
17 | parent_subnet_name: ansible_parent_port_subnet
18 | parent_port_name: ansible_parent_port
19 | subport_network_name: ansible_subport_network
20 | subport_subnet_name: ansible_subport_subnet
21 | subport_name: ansible_subport
22 |
--------------------------------------------------------------------------------
/ci/roles/subnet_pool/defaults/main.yml:
--------------------------------------------------------------------------------
1 | address_scope_name: "ansible_address_scope"
2 | default_prefix_length: 24
3 | expected_fields:
4 | - address_scope_id
5 | - created_at
6 | - default_prefix_length
7 | - default_quota
8 | - description
9 | - id
10 | - ip_version
11 | - is_default
12 | - is_shared
13 | - maximum_prefix_length
14 | - minimum_prefix_length
15 | - name
16 | - prefixes
17 | - project_id
18 | - revision_number
19 | - tags
20 | - tenant_id
21 | - updated_at
22 | maximum_prefix_length: 30
23 | minimum_prefix_length: 10
24 | subnet_pool_name: "ansible_subnet_pool"
25 |
--------------------------------------------------------------------------------
/ci/roles/subnet/defaults/main.yml:
--------------------------------------------------------------------------------
1 | enable_subnet_dhcp: false
2 | expected_fields:
3 | - allocation_pools
4 | - cidr
5 | - created_at
6 | - description
7 | - dns_nameservers
8 | - dns_publish_fixed_ip
9 | - gateway_ip
10 | - host_routes
11 | - id
12 | - ip_version
13 | - ipv6_address_mode
14 | - ipv6_ra_mode
15 | - is_dhcp_enabled
16 | - name
17 | - network_id
18 | - prefix_length
19 | - project_id
20 | - revision_number
21 | - segment_id
22 | - service_types
23 | - subnet_pool_id
24 | - tags
25 | - updated_at
26 | - use_default_subnet_pool
27 | subnet_name: shade_subnet
28 |
--------------------------------------------------------------------------------
/ci/roles/federation_mapping/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - id
3 | - name
4 | - rules
5 | mapping_name: 'ansible-test-mapping'
6 | mapping_name_2: 'ansible-test-mapping-2'
7 | mapping_rules_1:
8 | - local:
9 | - group:
10 | domain:
11 | name: example_domain
12 | name: example-group
13 | remote:
14 | - type: HTTP_OIDC_GROUPS
15 | any_one_of:
16 | - group1
17 | - group2
18 | mapping_rules_2:
19 | - local:
20 | - group:
21 | domain:
22 | name: example_domain
23 | name: example_group
24 | remote:
25 | - type: HTTP_OIDC_GROUPS
26 | any_one_of:
27 | - group1
28 |
--------------------------------------------------------------------------------
/ci/roles/trait/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create trait
3 | openstack.cloud.trait:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | id: "{{ trait_name }}"
7 | until: result is success
8 | retries: 5
9 | delay: 20
10 | register: result
11 |
12 | - name: Assert trait
13 | assert:
14 | that:
15 | - "'name' in result.trait"
16 | - "result.trait.id == trait_name"
17 |
18 | - name: Remove trait
19 | openstack.cloud.trait:
20 | cloud: "{{ cloud }}"
21 | state: absent
22 | id: "{{ trait_name }}"
23 | register: result1
24 |
25 | - name: Assert trait removed
26 | assert:
27 | that:
28 | - "'trait' not in result1"
29 |
--------------------------------------------------------------------------------
/ci/roles/stack/defaults/main.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | stack_name: "test-stack"
3 | expected_fields:
4 | - added
5 | - capabilities
6 | - created_at
7 | - deleted
8 | - deleted_at
9 | - description
10 | - environment
11 | - environment_files
12 | - files
13 | - files_container
14 | - id
15 | - is_rollback_disabled
16 | - links
17 | - name
18 | - notification_topics
19 | - outputs
20 | - owner_id
21 | - parameters
22 | - parent_id
23 | - replaced
24 | - status
25 | - status_reason
26 | - tags
27 | - template
28 | - template_description
29 | - template_url
30 | - timeout_mins
31 | - unchanged
32 | - updated
33 | - updated_at
34 | - user_project_id
35 |
--------------------------------------------------------------------------------
/ci/roles/server_volume/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - attachments
3 | - availability_zone
4 | - consistency_group_id
5 | - created_at
6 | - description
7 | - extended_replication_status
8 | - group_id
9 | - host
10 | - id
11 | - image_id
12 | - is_bootable
13 | - is_encrypted
14 | - metadata
15 | - migration_id
16 | - migration_status
17 | - name
18 | - project_id
19 | - replication_driver_data
20 | - replication_status
21 | - scheduler_hints
22 | - size
23 | - snapshot_id
24 | - source_volume_id
25 | - status
26 | - updated_at
27 | - user_id
28 | - volume_image_metadata
29 | - volume_type
30 | flavor: m1.tiny
31 | server_name: ansible_server
32 | server_network: private
33 |
--------------------------------------------------------------------------------
/ci/roles/object/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - accept_ranges
3 | - access_control_allow_origin
4 | - content_disposition
5 | - content_encoding
6 | - content_length
7 | - content_type
8 | - copy_from
9 | - delete_after
10 | - delete_at
11 | - etag
12 | - expires_at
13 | - id
14 | - if_match
15 | - if_modified_since
16 | - if_none_match
17 | - if_unmodified_since
18 | - is_content_type_detected
19 | - is_newest
20 | - is_static_large_object
21 | - last_modified_at
22 | - manifest
23 | - metadata
24 | - multipart_manifest
25 | - name
26 | - object_manifest
27 | - range
28 | - signature
29 | - symlink_target
30 | - symlink_target_account
31 | - timestamp
32 | - transfer_encoding
33 |
--------------------------------------------------------------------------------
/ci/roles/coe_cluster_template/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - apiserver_port
3 | - cluster_distro
4 | - coe
5 | - created_at
6 | - dns_nameserver
7 | - docker_storage_driver
8 | - docker_volume_size
9 | - external_network_id
10 | - fixed_network
11 | - fixed_subnet
12 | - flavor_id
13 | - http_proxy
14 | - https_proxy
15 | - id
16 | - image_id
17 | - insecure_registry
18 | - is_floating_ip_enabled
19 | - is_hidden
20 | - is_master_lb_enabled
21 | - is_public
22 | - is_registry_enabled
23 | - is_tls_disabled
24 | - keypair_id
25 | - labels
26 | - master_flavor_id
27 | - name
28 | - network_driver
29 | - no_proxy
30 | - server_type
31 | - updated_at
32 | - uuid
33 | - volume_driver
34 |
--------------------------------------------------------------------------------
/ci/roles/volume_manage/defaults/main.yml:
--------------------------------------------------------------------------------
1 | test_volume: ansible_test_volume
2 | managed_volume: managed_test_volume
3 | expected_fields:
4 | - attachments
5 | - availability_zone
6 | - consistency_group_id
7 | - created_at
8 | - updated_at
9 | - description
10 | - extended_replication_status
11 | - group_id
12 | - host
13 | - image_id
14 | - is_bootable
15 | - is_encrypted
16 | - is_multiattach
17 | - migration_id
18 | - migration_status
19 | - project_id
20 | - replication_driver_data
21 | - replication_status
22 | - scheduler_hints
23 | - size
24 | - snapshot_id
25 | - source_volume_id
26 | - status
27 | - user_id
28 | - volume_image_metadata
29 | - volume_type
30 | - id
31 | - name
32 | - metadata
33 |
--------------------------------------------------------------------------------
/ci/roles/volume/defaults/main.yml:
--------------------------------------------------------------------------------
1 | test_volume_image: ansible_test_volume_image
2 | test_volume_shared_image: ansible_test_volume_shared_image
3 | expected_fields:
4 | - attachments
5 | - availability_zone
6 | - consistency_group_id
7 | - created_at
8 | - updated_at
9 | - description
10 | - extended_replication_status
11 | - group_id
12 | - host
13 | - image_id
14 | - is_bootable
15 | - is_encrypted
16 | - is_multiattach
17 | - migration_id
18 | - migration_status
19 | - project_id
20 | - replication_driver_data
21 | - replication_status
22 | - scheduler_hints
23 | - size
24 | - snapshot_id
25 | - source_volume_id
26 | - status
27 | - user_id
28 | - volume_image_metadata
29 | - volume_type
30 | - id
31 | - name
32 | - metadata
33 |
--------------------------------------------------------------------------------
/ci/roles/object_containers_info/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | test_container_unprefixed_name: ansible-test-container
4 | test_container_prefixed_prefix: ansible-prefixed-test-container
5 | test_container_prefixed_num: 2
6 |
7 | test_object_data: "Hello, world!"
8 |
9 | expected_fields_single:
10 | - bytes
11 | - bytes_used
12 | - content_type
13 | - count
14 | - history_location
15 | - id
16 | - if_none_match
17 | - is_content_type_detected
18 | - is_newest
19 | - meta_temp_url_key
20 | - meta_temp_url_key_2
21 | - name
22 | - object_count
23 | - read_ACL
24 | - storage_policy
25 | - sync_key
26 | - sync_to
27 | - timestamp
28 | - versions_location
29 | - write_ACL
30 |
31 | expected_fields_multiple:
32 | - bytes
33 | - bytes_used
34 | - count
35 | - id
36 | - name
37 | - object_count
38 |
--------------------------------------------------------------------------------
/ci/roles/quota/defaults/main.yml:
--------------------------------------------------------------------------------
1 | test_project: ansible_project
2 | test_network_quota:
3 | floating_ips: 5
4 | networks: 50
5 | ports: 300
6 | rbac_policies: 5
7 | routers: 5
8 | security_group_rules: 5
9 | security_groups: 5
10 | subnet_pools: 5
11 | subnets: 5
12 | test_volume_quota:
13 | backup_gigabytes: 500
14 | backups: 5
15 | gigabytes: 500
16 | groups: 1
17 | per_volume_gigabytes: 10
18 | snapshots: 5
19 | volumes: 5
20 | test_compute_quota:
21 | cores: 5
22 | injected_file_content_bytes: 5
23 | injected_file_path_bytes: 5
24 | injected_files: 5
25 | instances: 5
26 | key_pairs: 5
27 | metadata_items: 5
28 | ram: 5
29 | server_group_members: 5
30 | server_groups: 5
31 | test_load_balancer_quota:
32 | load_balancers: 5
33 | health_monitors: 5
34 | listeners: 5
35 | pools: 5
36 | members: 5
37 |
--------------------------------------------------------------------------------
/ci/roles/volume_service/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Fetch volume services
3 | openstack.cloud.volume_service_info:
4 | cloud: "{{ cloud }}"
5 | register: volume_services
6 |
7 | - name: Assert return values of volume_service_info module
8 | assert:
9 | that:
10 | - volume_services.volume_services | length > 0
11 | # allow new fields to be introduced but prevent fields from being removed
12 | - expected_fields|difference(volume_services.volume_services[0].keys())|length == 0
13 |
14 | - name: Fetch volume services with filters
15 | openstack.cloud.volume_service_info:
16 | cloud: "{{ cloud }}"
17 | binary: "cinder-volume"
18 | register: volume_services
19 |
20 | - name: Assert return values of volume_service_info module
21 | assert:
22 | that:
23 | - volume_services.volume_services | length > 0
24 |
--------------------------------------------------------------------------------
/ci/roles/compute_service/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Fetch compute services
3 | openstack.cloud.compute_service_info:
4 | cloud: "{{ cloud }}"
5 | register: compute_services
6 |
7 | - name: Assert return values of compute_service_info module
8 | assert:
9 | that:
10 | - compute_services.compute_services | length > 0
11 | # allow new fields to be introduced but prevent fields from being removed
12 | - expected_fields|difference(compute_services.compute_services[0].keys())|length == 0
13 |
14 | - name: Fetch compute services with filters
15 | openstack.cloud.compute_service_info:
16 | cloud: "{{ cloud }}"
17 | binary: "nova-compute"
18 | register: compute_services
19 |
20 | - name: Assert return values of compute_service_info module
21 | assert:
22 | that:
23 | - compute_services.compute_services | length > 0
24 |
--------------------------------------------------------------------------------
/ci/roles/network/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - availability_zone_hints
3 | - availability_zones
4 | - created_at
5 | - description
6 | - dns_domain
7 | - id
8 | - ipv4_address_scope_id
9 | - ipv6_address_scope_id
10 | - is_admin_state_up
11 | - is_default
12 | - is_port_security_enabled
13 | - is_router_external
14 | - is_shared
15 | - is_vlan_transparent
16 | - mtu
17 | - name
18 | - project_id
19 | - provider_network_type
20 | - provider_physical_network
21 | - provider_segmentation_id
22 | - qos_policy_id
23 | - revision_number
24 | - segments
25 | - status
26 | - subnet_ids
27 | - tags
28 | - updated_at
29 | dns_domain: example.opendev.org
30 | mtu: 1250
31 | network_name: shade_network
32 | network_name_newparams: newparams_network
33 | network_name_updates: update_network
34 | network_shared: false
35 | port_security_enabled: false
36 |
--------------------------------------------------------------------------------
/changelogs/config.yaml:
--------------------------------------------------------------------------------
1 | changelog_filename_template: ../CHANGELOG.rst
2 | changelog_filename_version_depth: 0
3 | changes_file: changelog.yaml
4 | changes_format: combined
5 | ignore_other_fragment_extensions: true
6 | keep_fragments: false
7 | mention_ancestor: true
8 | new_plugins_after_name: removed_features
9 | notes_dir: fragments
10 | prelude_name: release_summary
11 | prelude_title: Release Summary
12 | sections:
13 | - - major_changes
14 | - Major Changes
15 | - - minor_changes
16 | - Minor Changes
17 | - - breaking_changes
18 | - Breaking Changes / Porting Guide
19 | - - deprecated_features
20 | - Deprecated Features
21 | - - removed_features
22 | - Removed Features (previously deprecated)
23 | - - security_fixes
24 | - Security Fixes
25 | - - bugfixes
26 | - Bugfixes
27 | - - known_issues
28 | - Known Issues
29 | title: Ansible OpenStack Collection
30 | trivial_section_name: trivial
31 | use_fqcn: true
32 |
--------------------------------------------------------------------------------
/galaxy.yml.in:
--------------------------------------------------------------------------------
1 | namespace: openstack
2 | name: cloud
3 | readme: README.md
4 | authors: Openstack
5 | description: Openstack Ansible modules
6 | license: GPL-3.0-or-later
7 | tags:
8 | - cloud
9 | - openstack
10 | dependencies: {}
11 | repository: https://opendev.org/openstack/ansible-collections-openstack
12 | documentation: https://docs.ansible.com/ansible/latest/collections/openstack/cloud/index.html
13 | homepage: https://opendev.org/openstack/ansible-collections-openstack
14 | issues: https://bugs.launchpad.net/ansible-collections-openstack
15 | build_ignore:
16 | - "*.tar.gz"
17 | - build_artifact
18 | - ci
19 | - galaxy.yml.in
20 | - setup.cfg
21 | - test-requirements*
22 | - tests
23 | - tools
24 | - tox.ini
25 | - .gitignore
26 | - .gitreview
27 | - .zuul.yaml
28 | - .pytest_cache
29 | - importer_result.json
30 | - .tox
31 | - .env
32 | - .vscode
33 | - ansible_collections_openstack.egg-info
34 | - changelogs
35 |
--------------------------------------------------------------------------------
/galaxy.yml:
--------------------------------------------------------------------------------
1 | namespace: openstack
2 | name: cloud
3 | readme: README.md
4 | authors: Openstack
5 | description: Openstack Ansible modules
6 | license: GPL-3.0-or-later
7 | tags:
8 | - cloud
9 | - openstack
10 | dependencies: {}
11 | repository: https://opendev.org/openstack/ansible-collections-openstack
12 | documentation: https://docs.ansible.com/ansible/latest/collections/openstack/cloud/index.html
13 | homepage: https://opendev.org/openstack/ansible-collections-openstack
14 | issues: https://bugs.launchpad.net/ansible-collections-openstack
15 | build_ignore:
16 | - "*.tar.gz"
17 | - build_artifact
18 | - ci
19 | - galaxy.yml.in
20 | - setup.cfg
21 | - test-requirements*
22 | - tests
23 | - tools
24 | - tox.ini
25 | - .gitignore
26 | - .gitreview
27 | - .zuul.yaml
28 | - .pytest_cache
29 | - importer_result.json
30 | - .tox
31 | - .env
32 | - .vscode
33 | - ansible_collections_openstack.egg-info
34 | - changelogs
35 | version: 2.5.0
36 |
--------------------------------------------------------------------------------
/tools/check-import.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Copyright 2020 Red Hat, Inc.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | # not use this file except in compliance with the License. You may obtain
6 | # a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | # License for the specific language governing permissions and limitations
14 | # under the License.
15 |
16 | set -e
17 |
18 | if python -c 'import sys; sys.exit(0 if sys.version_info[0:2] < (3, 6) else 1)'; then
19 | echo "Skipped Ansible Galaxy content importer check because it requires Python 3.6 or later" 2>&1
20 | exit
21 | fi
22 |
23 | TOXDIR="${1:-.}"
24 | python -m galaxy_importer.main "$TOXDIR/build_artifact/"*
25 |
--------------------------------------------------------------------------------
/ci/roles/port/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - allowed_address_pairs
3 | - binding_host_id
4 | - binding_profile
5 | - binding_vif_details
6 | - binding_vif_type
7 | - binding_vnic_type
8 | - created_at
9 | - data_plane_status
10 | - description
11 | - device_id
12 | - device_owner
13 | - device_profile
14 | - dns_assignment
15 | - dns_domain
16 | - dns_name
17 | - extra_dhcp_opts
18 | - fixed_ips
19 | - id
20 | - ip_allocation
21 | - is_admin_state_up
22 | - is_port_security_enabled
23 | - mac_address
24 | - name
25 | - network_id
26 | - numa_affinity_policy
27 | - project_id
28 | - propagate_uplink_status
29 | - qos_network_policy_id
30 | - qos_policy_id
31 | - resource_request
32 | - revision_number
33 | - security_group_ids
34 | - status
35 | - tags
36 | - tenant_id
37 | - trunk_details
38 | - updated_at
39 | network_name: ansible_port_network
40 | no_security_groups: True
41 | port_name: ansible_port
42 | subnet_name: ansible_port_subnet
43 |
--------------------------------------------------------------------------------
/ci/roles/router/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - availability_zone_hints
3 | - availability_zones
4 | - created_at
5 | - description
6 | - external_gateway_info
7 | - flavor_id
8 | - id
9 | - is_admin_state_up
10 | - is_distributed
11 | - is_ha
12 | - name
13 | - project_id
14 | - revision_number
15 | - routes
16 | - status
17 | - tags
18 | - tenant_id
19 | - updated_at
20 | network_name: ansible_net
21 | external_network_name: ansible_external_net
22 | router_name: ansible_router
23 | test_subnets:
24 | - cloud: "{{ cloud }}"
25 | state: present
26 | network_name: "{{ network_name }}"
27 | name: shade_subnet1
28 | cidr: 10.7.7.0/24
29 | - cloud: "{{ cloud }}"
30 | state: present
31 | network_name: "{{ network_name }}"
32 | name: shade_subnet2
33 | cidr: 10.8.8.0/24
34 | - cloud: "{{ cloud }}"
35 | state: present
36 | network_name: "{{ network_name }}"
37 | name: shade_subnet3
38 | cidr: 10.9.9.0/24
39 | - cloud: "{{ cloud }}"
40 | state: present
41 | network_name: "{{ network_name }}"
42 | name: shade_subnet4
43 | cidr: 10.10.10.0/24
44 |
--------------------------------------------------------------------------------
/plugins/modules/auth.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: auth
10 | short_description: Retrieve auth token from OpenStack cloud
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Retrieve auth token from OpenStack cloud
14 | extends_documentation_fragment:
15 | - openstack.cloud.openstack
16 | '''
17 |
18 | EXAMPLES = r'''
19 | - name: Authenticate to cloud and return auth token
20 | openstack.cloud.auth:
21 | cloud: rax-dfw
22 | '''
23 |
24 | RETURN = r'''
25 | auth_token:
26 | description: Openstack API Auth Token
27 | returned: success
28 | type: str
29 | '''
30 |
31 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
32 |
33 |
34 | class AuthModule(OpenStackModule):
35 | def run(self):
36 | self.exit_json(changed=False,
37 | auth_token=self.conn.auth_token)
38 |
39 |
40 | def main():
41 | module = AuthModule()
42 | module()
43 |
44 |
45 | if __name__ == '__main__':
46 | main()
47 |
--------------------------------------------------------------------------------
/ci/roles/baremetal_node/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - allocation_id
3 | - bios_interface
4 | - boot_interface
5 | - boot_mode
6 | - chassis_id
7 | - clean_step
8 | - conductor
9 | - conductor_group
10 | - console_interface
11 | - created_at
12 | - deploy_interface
13 | - deploy_step
14 | - driver
15 | - driver_info
16 | - driver_internal_info
17 | - extra
18 | - fault
19 | - id
20 | - inspect_interface
21 | - instance_id
22 | - instance_info
23 | - is_automated_clean_enabled
24 | - is_console_enabled
25 | - is_maintenance
26 | - is_protected
27 | - is_retired
28 | - is_secure_boot
29 | - last_error
30 | - links
31 | - maintenance_reason
32 | - management_interface
33 | - name
34 | - network_interface
35 | - owner
36 | - port_groups
37 | - ports
38 | - power_interface
39 | - power_state
40 | - properties
41 | - protected_reason
42 | - provision_state
43 | - raid_config
44 | - raid_interface
45 | - rescue_interface
46 | - reservation
47 | - resource_class
48 | - retired_reason
49 | - states
50 | - storage_interface
51 | - target_power_state
52 | - target_provision_state
53 | - target_raid_config
54 | - traits
55 | - updated_at
56 | - vendor_interface
57 |
--------------------------------------------------------------------------------
/ci/roles/baremetal_inspect/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - allocation_id
3 | - bios_interface
4 | - boot_interface
5 | - boot_mode
6 | - chassis_id
7 | - clean_step
8 | - conductor
9 | - conductor_group
10 | - console_interface
11 | - created_at
12 | - deploy_interface
13 | - deploy_step
14 | - driver
15 | - driver_info
16 | - driver_internal_info
17 | - extra
18 | - fault
19 | - id
20 | - inspect_interface
21 | - instance_id
22 | - instance_info
23 | - is_automated_clean_enabled
24 | - is_console_enabled
25 | - is_maintenance
26 | - is_protected
27 | - is_retired
28 | - is_secure_boot
29 | - last_error
30 | - links
31 | - maintenance_reason
32 | - management_interface
33 | - name
34 | - network_interface
35 | - owner
36 | - port_groups
37 | - ports
38 | - power_interface
39 | - power_state
40 | - properties
41 | - protected_reason
42 | - provision_state
43 | - raid_config
44 | - raid_interface
45 | - rescue_interface
46 | - reservation
47 | - resource_class
48 | - retired_reason
49 | - states
50 | - storage_interface
51 | - target_power_state
52 | - target_provision_state
53 | - target_raid_config
54 | - traits
55 | - updated_at
56 | - vendor_interface
57 |
--------------------------------------------------------------------------------
/tests/unit/modules/conftest.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2017 Ansible Project
2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3 |
4 | import json
5 |
6 | import pytest
7 |
8 | from ansible.module_utils.six import string_types
9 | from ansible.module_utils._text import to_bytes
10 | from ansible.module_utils.common._collections_compat import MutableMapping
11 |
12 |
13 | @pytest.fixture
14 | def patch_ansible_module(request, mocker):
15 | if isinstance(request.param, string_types):
16 | args = request.param
17 | elif isinstance(request.param, MutableMapping):
18 | if 'ANSIBLE_MODULE_ARGS' not in request.param:
19 | request.param = {'ANSIBLE_MODULE_ARGS': request.param}
20 | if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']:
21 | request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp'
22 | if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']:
23 | request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False
24 | args = json.dumps(request.param)
25 | else:
26 | raise Exception('Malformed data to the patch_ansible_module pytest fixture')
27 |
28 | mocker.patch('ansible.module_utils.basic._ANSIBLE_ARGS', to_bytes(args))
29 |
--------------------------------------------------------------------------------
/tests/unit/modules/utils.py:
--------------------------------------------------------------------------------
1 | import json
2 | import unittest
3 | from unittest.mock import patch
4 |
5 | from ansible.module_utils import basic
6 | from ansible.module_utils._text import to_bytes
7 |
8 |
9 | def set_module_args(args):
10 | if '_ansible_remote_tmp' not in args:
11 | args['_ansible_remote_tmp'] = '/tmp'
12 | if '_ansible_keep_remote_files' not in args:
13 | args['_ansible_keep_remote_files'] = False
14 |
15 | args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
16 | basic._ANSIBLE_ARGS = to_bytes(args)
17 |
18 |
19 | class AnsibleExitJson(Exception):
20 | pass
21 |
22 |
23 | class AnsibleFailJson(Exception):
24 | pass
25 |
26 |
27 | def exit_json(*args, **kwargs):
28 | if 'changed' not in kwargs:
29 | kwargs['changed'] = False
30 | raise AnsibleExitJson(kwargs)
31 |
32 |
33 | def fail_json(*args, **kwargs):
34 | kwargs['failed'] = True
35 | raise AnsibleFailJson(kwargs)
36 |
37 |
38 | class ModuleTestCase(unittest.TestCase):
39 |
40 | def setUp(self):
41 | self.mock_module = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
42 | self.mock_module.start()
43 | self.mock_sleep = patch('time.sleep')
44 | self.mock_sleep.start()
45 | set_module_args({})
46 | self.addCleanup(self.mock_module.stop)
47 | self.addCleanup(self.mock_sleep.stop)
48 |
--------------------------------------------------------------------------------
/ci/roles/image/defaults/main.yml:
--------------------------------------------------------------------------------
1 | expected_fields:
2 | - architecture
3 | - checksum
4 | - container_format
5 | - created_at
6 | - direct_url
7 | - disk_format
8 | - file
9 | - has_auto_disk_config
10 | - hash_algo
11 | - hash_value
12 | - hw_cpu_cores
13 | - hw_cpu_policy
14 | - hw_cpu_sockets
15 | - hw_cpu_thread_policy
16 | - hw_cpu_threads
17 | - hw_disk_bus
18 | - hw_machine_type
19 | - hw_qemu_guest_agent
20 | - hw_rng_model
21 | - hw_scsi_model
22 | - hw_serial_port_count
23 | - hw_video_model
24 | - hw_video_ram
25 | - hw_vif_model
26 | - hw_watchdog_action
27 | - hypervisor_type
28 | - id
29 | - instance_type_rxtx_factor
30 | - instance_uuid
31 | - is_hidden
32 | - is_hw_boot_menu_enabled
33 | - is_hw_vif_multiqueue_enabled
34 | - is_protected
35 | - kernel_id
36 | - locations
37 | - metadata
38 | - min_disk
39 | - min_ram
40 | - name
41 | - needs_config_drive
42 | - needs_secure_boot
43 | - os_admin_user
44 | - os_command_line
45 | - os_distro
46 | - os_require_quiesce
47 | - os_shutdown_timeout
48 | - os_type
49 | - os_version
50 | - owner
51 | - owner_id
52 | - properties
53 | - ramdisk_id
54 | - schema
55 | - size
56 | - status
57 | - store
58 | - tags
59 | - updated_at
60 | - url
61 | - virtual_size
62 | - visibility
63 | - vm_mode
64 | - vmware_adaptertype
65 | - vmware_ostype
66 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = ansible-collections-openstack.cloud
3 | summary = Ansible collections for Openstack cloud
4 | description_file =
5 | README.md
6 |
7 | author = OpenStack
8 | author_email = openstack-discuss@lists.openstack.org
9 | home_page = https://opendev.org/openstack/ansible-collections-openstack/
10 | classifier =
11 | License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
12 | Development Status :: 5 - Production/Stable
13 | Intended Audience :: Developers
14 | Intended Audience :: System Administrators
15 | Intended Audience :: Information Technology
16 | Topic :: System :: Systems Administration
17 | Topic :: Utilities
18 |
19 | [global]
20 | setup_hooks =
21 | pbr.hooks.setup_hook
22 |
23 | [files]
24 | data_files =
25 | share/ansible/collections/ansible_collections/openstack/cloud/ = README.md
26 | share/ansible/collections/ansible_collections/openstack/cloud/roles/ = roles/*
27 | share/ansible/collections/ansible_collections/openstack/cloud/plugins/ = plugins/*
28 | share/ansible/collections/ansible_collections/openstack/cloud/playbooks/ = playbooks/*
29 | share/ansible/collections/ansible_collections/openstack/cloud/docs/ = docs/*
30 | share/ansible/collections/ansible_collections/openstack/cloud/meta/ = meta/*
31 |
32 | [wheel]
33 | universal = 1
34 |
35 | [pbr]
36 | skip_authors = True
37 | skip_changelog = True
38 |
--------------------------------------------------------------------------------
/tools/build.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright 2019 Red Hat, Inc.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | # not use this file except in compliance with the License. You may obtain
6 | # a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | # License for the specific language governing permissions and limitations
14 | # under the License.
15 |
16 | import pbr.version
17 |
18 | from ruamel.yaml import YAML
19 |
20 | import os
21 | import shutil
22 |
23 |
24 | def generate_version_info():
25 | version_info = pbr.version.VersionInfo('openstack-cloud')
26 | semantic_version = version_info.semantic_version()
27 | release_string = semantic_version._long_version('-')
28 |
29 | yaml = YAML()
30 | yaml.explicit_start = True
31 | yaml.indent(sequence=4, offset=2)
32 |
33 | config = yaml.load(open('galaxy.yml.in'))
34 | config['version'] = release_string
35 |
36 | with open('galaxy.yml', 'w') as fp:
37 | yaml.dump(config, fp)
38 |
39 |
40 | def main():
41 | generate_version_info()
42 | shutil.rmtree('build_artifact', ignore_errors=True)
43 | if os.path.exists('MANIFEST.json'):
44 | os.unlink('MANIFEST.json')
45 |
46 |
47 | if __name__ == '__main__':
48 | main()
49 |
--------------------------------------------------------------------------------
/ci/roles/address_scope/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create address_scope
3 | openstack.cloud.address_scope:
4 | cloud: "{{ cloud }}"
5 | name: "{{ address_scope_name }}"
6 | shared: False
7 | ip_version: "4"
8 | register: create_address_scope
9 |
10 | - name: Verify returned values
11 | assert:
12 | that:
13 | - item in create_address_scope.address_scope
14 | loop: "{{ expected_fields }}"
15 |
16 | - name: Verify address scope
17 | assert:
18 | that:
19 | - create_address_scope is successful
20 | - create_address_scope is changed
21 | - create_address_scope.address_scope.name == address_scope_name
22 | - create_address_scope.address_scope.is_shared == False
23 | - create_address_scope.address_scope.ip_version == 4
24 |
25 | - name: Update address scope
26 | openstack.cloud.address_scope:
27 | cloud: "{{ cloud }}"
28 | name: "{{ address_scope_name }}"
29 | shared: True
30 | ip_version: "4"
31 | register: update_address_scope
32 |
33 | - name: Verify updated IPv4 address scope
34 | assert:
35 | that:
36 | - update_address_scope is successful
37 | - update_address_scope is changed
38 | - update_address_scope.address_scope.name == address_scope_name
39 | - update_address_scope.address_scope.is_shared == True
40 | - update_address_scope.address_scope.ip_version == 4
41 |
42 | - name: Delete created address scope
43 | openstack.cloud.address_scope:
44 | cloud: "{{ cloud }}"
45 | name: "{{ address_scope_name }}"
46 | state: absent
47 |
--------------------------------------------------------------------------------
/ci/roles/server/defaults/main.yml:
--------------------------------------------------------------------------------
1 | boot_volume_size: 5
2 | expected_fields:
3 | - access_ipv4
4 | - access_ipv6
5 | - addresses
6 | - admin_password
7 | - attached_volumes
8 | - availability_zone
9 | - block_device_mapping
10 | - compute_host
11 | - config_drive
12 | - created_at
13 | - description
14 | - disk_config
15 | - flavor
16 | - flavor_id
17 | - has_config_drive
18 | - host_id
19 | - host_status
20 | - hostname
21 | - hypervisor_hostname
22 | - id
23 | - image
24 | - image_id
25 | - instance_name
26 | - is_locked
27 | - kernel_id
28 | - key_name
29 | - launch_index
30 | - launched_at
31 | - links
32 | - max_count
33 | - metadata
34 | - min_count
35 | - name
36 | - networks
37 | - power_state
38 | - progress
39 | - project_id
40 | - ramdisk_id
41 | - reservation_id
42 | - root_device_name
43 | - scheduler_hints
44 | - security_groups
45 | - server_groups
46 | - status
47 | - tags
48 | - task_state
49 | - terminated_at
50 | - trusted_image_certificates
51 | - updated_at
52 | - user_data
53 | - user_id
54 | - vm_state
55 | - volumes
56 | flavor_name: m1.tiny
57 | floating_ip_pool_name: public
58 | server_alt_name: ansible_server_alt
59 | server_alt_network: ansible_server_network_alt
60 | server_alt_security_group: ansible_server_security_group_alt
61 | server_alt_subnet: ansible_server_subnet_alt
62 | server_name: ansible_server
63 | server_network: ansible_server_network
64 | server_port: ansible_server_port
65 | server_security_group: ansible_server_security_group
66 | server_subnet: ansible_server_subnet
67 |
--------------------------------------------------------------------------------
/tests/unit/mock/vault_helper.py:
--------------------------------------------------------------------------------
1 | # Ansible is free software: you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation, either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # Ansible is distributed in the hope that it will be useful,
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 | # GNU General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with Ansible. If not, see .
13 |
14 | # Make coding more python3-ish
15 |
16 | from ansible.module_utils._text import to_bytes
17 |
18 | from ansible.parsing.vault import VaultSecret
19 |
20 |
21 | class TextVaultSecret(VaultSecret):
22 | '''A secret piece of text. ie, a password. Tracks text encoding.
23 |
24 | The text encoding of the text may not be the default text encoding so
25 | we keep track of the encoding so we encode it to the same bytes.'''
26 |
27 | def __init__(self, text, encoding=None, errors=None, _bytes=None):
28 | super(TextVaultSecret, self).__init__()
29 | self.text = text
30 | self.encoding = encoding or 'utf-8'
31 | self._bytes = _bytes
32 | self.errors = errors or 'strict'
33 |
34 | @property
35 | def bytes(self):
36 | '''The text encoded with encoding, unless we specifically set _bytes.'''
37 | return self._bytes or to_bytes(self.text, encoding=self.encoding, errors=self.errors)
38 |
--------------------------------------------------------------------------------
/ci/roles/volume_manage/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create volume
3 | openstack.cloud.volume:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | size: 1
7 | name: "{{ test_volume }}"
8 | description: Test volume
9 | register: vol
10 |
11 | - assert:
12 | that: item in vol.volume
13 | loop: "{{ expected_fields }}"
14 |
15 | - name: Unmanage volume
16 | openstack.cloud.volume_manage:
17 | cloud: "{{ cloud }}"
18 | state: absent
19 | name: "{{ vol.volume.id }}"
20 |
21 | - name: Unmanage volume again
22 | openstack.cloud.volume_manage:
23 | cloud: "{{ cloud }}"
24 | state: absent
25 | name: "{{ vol.volume.id }}"
26 | register: unmanage_idempotency
27 |
28 | - assert:
29 | that:
30 | - unmanage_idempotency is not changed
31 |
32 | - name: Manage volume
33 | openstack.cloud.volume_manage:
34 | cloud: "{{ cloud }}"
35 | state: present
36 | source_name: volume-{{ vol.volume.id }}
37 | host: "{{ vol.volume.host }}"
38 | name: "{{ managed_volume }}"
39 | register: new_vol
40 |
41 | - assert:
42 | that:
43 | - new_vol.volume.name == managed_volume
44 |
45 | - name: Manage volume again
46 | openstack.cloud.volume_manage:
47 | cloud: "{{ cloud }}"
48 | state: present
49 | source_name: volume-{{ vol.volume.id }}
50 | host: "{{ vol.volume.host }}"
51 | name: "{{ managed_volume }}"
52 | register: vol_idempotency
53 |
54 | - assert:
55 | that:
56 | - vol_idempotency is not changed
57 |
58 | - pause:
59 | seconds: 10
60 |
61 | - name: Delete volume
62 | openstack.cloud.volume:
63 | cloud: "{{ cloud }}"
64 | state: absent
65 | name: "{{ managed_volume }}"
66 |
--------------------------------------------------------------------------------
/ci/roles/resources/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - module_defaults:
3 | group/openstack.cloud.openstack:
4 | cloud: "{{ cloud }}"
5 | # Listing modules individually is required for
6 | # backward compatibility with Ansible 2.9 only
7 | openstack.cloud.resources:
8 | cloud: "{{ cloud }}"
9 | block:
10 | - name: List images
11 | openstack.cloud.resources:
12 | service: image
13 | type: image
14 | register: images
15 |
16 | - name: Identify CirrOS image id
17 | set_fact:
18 | image_id: "{{ images.resources|community.general.json_query(query)|first }}"
19 | vars:
20 | query: "[?starts_with(name, 'cirros')].id"
21 |
22 | - name: Assert return values of resources module
23 | assert:
24 | that:
25 | - images is not changed
26 | # allow new fields to be introduced but prevent fields from being removed
27 | - expected_fields|difference(images.keys())|length == 0
28 |
29 | - name: List compute flavors
30 | openstack.cloud.resources:
31 | service: compute
32 | type: flavor
33 | register: flavors
34 |
35 | - name: Identify m1.tiny flavor id
36 | set_fact:
37 | flavor_id: "{{ flavors.resources|community.general.json_query(query)|first }}"
38 | vars:
39 | query: "[?name == 'm1.tiny'].id"
40 |
41 | - name: List public network
42 | openstack.cloud.resources:
43 | service: network
44 | type: network
45 | parameters:
46 | name: public
47 | register: networks
48 |
49 | - name: Assert public network
50 | assert:
51 | that:
52 | - networks.resources|length == 1
53 | - networks.resources.0.name == 'public'
54 |
--------------------------------------------------------------------------------
/ci/roles/group_assignment/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create user
3 | openstack.cloud.identity_user:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | name: ansible_user
7 | password: secret
8 | email: ansible.user@nowhere.net
9 | domain: default
10 | default_project: demo
11 |
12 | - name: Assign user to nonadmins group
13 | openstack.cloud.group_assignment:
14 | cloud: "{{ cloud }}"
15 | state: present
16 | user: ansible_user
17 | group: nonadmins
18 | register: group_assignment
19 |
20 | - name: Assert group assignment
21 | assert:
22 | that:
23 | - group_assignment is changed
24 |
25 | - name: Assign user to nonadmins group again
26 | openstack.cloud.group_assignment:
27 | cloud: "{{ cloud }}"
28 | state: present
29 | user: ansible_user
30 | group: nonadmins
31 | register: group_assignment
32 |
33 | - name: Assert group assignment
34 | assert:
35 | that:
36 | - group_assignment is not changed
37 |
38 | - name: Remove user from nonadmins group
39 | openstack.cloud.group_assignment:
40 | cloud: "{{ cloud }}"
41 | state: absent
42 | user: ansible_user
43 | group: nonadmins
44 | register: group_assignment
45 |
46 | - name: Assert group assignment
47 | assert:
48 | that:
49 | - group_assignment is changed
50 |
51 | - name: Remove user from nonadmins group again
52 | openstack.cloud.group_assignment:
53 | cloud: "{{ cloud }}"
54 | state: absent
55 | user: ansible_user
56 | group: nonadmins
57 | register: group_assignment
58 |
59 | - name: Assert group assignment
60 | assert:
61 | that:
62 | - group_assignment is not changed
63 |
64 | - name: Delete user
65 | openstack.cloud.identity_user:
66 | cloud: "{{ cloud }}"
67 | state: absent
68 | name: ansible_user
69 |
--------------------------------------------------------------------------------
/ci/roles/server_group/tasks/main.yml:
--------------------------------------------------------------------------------
1 | - name: Create server group
2 | openstack.cloud.server_group:
3 | cloud: "{{ cloud }}"
4 | name: ansible_group
5 | policy: affinity
6 | register: server_group
7 |
8 | - name: Assert changed
9 | assert:
10 | that: server_group is changed
11 |
12 | - name: Assert return values
13 | assert:
14 | that: item in server_group.server_group
15 | loop: "{{ expected_fields }}"
16 |
17 | - name: Create server group again
18 | openstack.cloud.server_group:
19 | cloud: "{{ cloud }}"
20 | name: ansible_group
21 | policy: affinity
22 | register: server_group
23 |
24 | - name: Assert not changed
25 | assert:
26 | that: server_group is not changed
27 |
28 | - name: Delete server group
29 | openstack.cloud.server_group:
30 | cloud: "{{ cloud }}"
31 | name: ansible_group
32 | state: absent
33 | register: server_group
34 |
35 | - name: Assert changed
36 | assert:
37 | that: server_group is changed
38 |
39 | - name: Delete server group again
40 | openstack.cloud.server_group:
41 | cloud: "{{ cloud }}"
42 | name: ansible_group
43 | state: absent
44 | register: server_group
45 |
46 | - name: Assert not changed
47 | assert:
48 | that: server_group is not changed
49 |
50 | - name: Create server group with rules
51 | openstack.cloud.server_group:
52 | cloud: "{{ cloud }}"
53 | name: ansible_group
54 | policy: anti-affinity
55 | rules:
56 | max_server_per_host: 2
57 | register: server_group
58 |
59 | - name: Assert changed
60 | assert:
61 | that: server_group is changed
62 |
63 | - name: Assert return values
64 | assert:
65 | that: item in server_group.server_group
66 | loop: "{{ expected_fields }}"
67 |
68 | - name: Delete server group
69 | openstack.cloud.server_group:
70 | cloud: "{{ cloud }}"
71 | name: ansible_group
72 | state: absent
73 |
--------------------------------------------------------------------------------
/ci/roles/baremetal_deploy_template/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # TODO: Actually run this role in CI. Atm we do not have DevStack's ironic plugin enabled.
3 | - name: Create baremetal deploy template
4 | openstack.cloud.baremetal_deploy_template:
5 | cloud: "{{ cloud }}"
6 | state: present
7 | name: CUSTOM_ANSIBLE_DEPLOY_TEMPLATE
8 | steps:
9 | - interface: bios
10 | step: apply_configuration
11 | args:
12 | settings:
13 | - name: some-setting
14 | value: some-value
15 | priority: 110
16 | register: template
17 |
18 | - debug: var=template
19 |
20 | - name: Assert return values of baremetal_deploy_template module
21 | assert:
22 | that:
23 | # allow new fields to be introduced but prevent fields from being removed
24 | - expected_fields|difference(template.template.keys())|length == 0
25 |
26 | - name: Update baremetal deploy template
27 | openstack.cloud.baremetal_deploy_template:
28 | cloud: "{{ cloud }}"
29 | state: present
30 | id: "{{ template.template.id }}"
31 | extra:
32 | foo: bar
33 | register: updated_template
34 |
35 | - name: Assert return values of updated baremetal deploy template
36 | assert:
37 | that:
38 | - updated_template is changed
39 | - updated_template.template.id == template.template.id
40 |
41 | - name: Update baremetal deploy template again
42 | openstack.cloud.baremetal_deploy_template:
43 | cloud: "{{ cloud }}"
44 | state: present
45 | id: "{{ template.template.id }}"
46 | register: updated_template
47 |
48 | - name: Assert return values of updated baremetal deploy template
49 | assert:
50 | that:
51 | - updated_template is not changed
52 | - updated_template.template.id == template.template.id
53 |
54 | - name: Delete Bare Metal deploy template
55 | openstack.cloud.baremetal_deploy_template:
56 | cloud: "{{ cloud }}"
57 | state: absent
58 | id: "{{ template.template.id }}"
59 |
--------------------------------------------------------------------------------
/ci/roles/endpoint/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create a service endpoint for compute
3 | openstack.cloud.endpoint:
4 | cloud: "{{ cloud }}"
5 | service: nova
6 | endpoint_interface: internal
7 | url: http://controller:9292
8 | region: RegionOne
9 | state: present
10 | register: endpoint_test
11 |
12 | - debug: var=endpoint_test
13 |
14 | - name: Assert return values of endpoint module
15 | assert:
16 | that:
17 | # allow new fields to be introduced but prevent fields from being removed
18 | - expected_fields|difference(endpoint_test.endpoint.keys())|length == 0
19 |
20 | - name: Ensure service have the proper endpoint
21 | assert:
22 | that:
23 | - endpoint_test.endpoint.url == "http://controller:9292"
24 |
25 | - name: Create service endpoint for compute again
26 | openstack.cloud.endpoint:
27 | cloud: "{{ cloud }}"
28 | service: nova
29 | endpoint_interface: internal
30 | url: http://controller:9292
31 | region: RegionOne
32 | state: present
33 | register: endpoint_again
34 |
35 | - name: Ensure changed is false
36 | assert:
37 | that:
38 | - not endpoint_again.changed
39 |
40 | - name: Update service endpoint url
41 | openstack.cloud.endpoint:
42 | cloud: "{{ cloud }}"
43 | service: nova
44 | endpoint_interface: internal
45 | url: http://controller:9393
46 | region: RegionOne
47 | state: present
48 | register: endpoint_updated
49 |
50 | - name: Ensure service endpoint was updated
51 | assert:
52 | that:
53 | - endpoint_updated.endpoint.url == "http://controller:9393"
54 |
55 | - name: Delete service endpoint
56 | openstack.cloud.endpoint:
57 | cloud: "{{ cloud }}"
58 | service: nova
59 | endpoint_interface: internal
60 | url: http://controller:9393
61 | region: RegionOne
62 | state: absent
63 | register: endpoint_deleted
64 |
65 | - name: Ensure service endpoint was deleted
66 | assert:
67 | that:
68 | - endpoint_deleted.changed
69 |
--------------------------------------------------------------------------------
/ci/roles/dns_zone/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create dns zone
3 | openstack.cloud.dns_zone:
4 | cloud: "{{ cloud }}"
5 | name: ansible.test.zone.
6 | type: primary
7 | email: test@example.net
8 | register: dns_zone
9 |
10 | - name: Assert return values of dns_zone module
11 | assert:
12 | that:
13 | - dns_zone.zone.name == "ansible.test.zone."
14 | - dns_zone.zone.type|lower == "primary"
15 | - dns_zone.zone.email == "test@example.net"
16 | # allow new fields to be introduced but prevent fields from being removed
17 | - expected_fields|difference(dns_zone.zone.keys())|length == 0
18 |
19 | - name: Update dns zone
20 | openstack.cloud.dns_zone:
21 | cloud: "{{ cloud }}"
22 | name: ansible.test.zone.
23 | description: "Another description"
24 | register: dns_zone
25 |
26 | - name: Assert return values of dns_zone module
27 | assert:
28 | that:
29 | - dns_zone.zone.description == "Another description"
30 |
31 | - name: Fetch all dns zones
32 | openstack.cloud.dns_zone_info:
33 | cloud: "{{ cloud }}"
34 | register: dns_zones
35 |
36 | - name: Assert return values of dns_zone_info module
37 | assert:
38 | that:
39 | - dns_zones is not changed
40 | - dns_zones | length > 0
41 | # allow new fields to be introduced but prevent fields from being removed
42 | - expected_fields|difference(dns_zones.zones[0].keys())|length == 0
43 |
44 | - name: Fetch a dns zone by name
45 | openstack.cloud.dns_zone_info:
46 | cloud: "{{ cloud }}"
47 | name: ansible.test.zone.
48 | register: dns_zones
49 |
50 | - name: Assert return values of dns_zone_info module
51 | assert:
52 | that:
53 | - dns_zones is not changed
54 | - dns_zones.zones | length == 1
55 | - dns_zones.zones[0].id == dns_zone.zone.id
56 |
57 | - name: Delete dns zone
58 | openstack.cloud.dns_zone:
59 | cloud: "{{ cloud }}"
60 | name: ansible.test.zone.
61 | state: absent
62 | register: dns_zone
63 |
64 | - name: Verify dns zone
65 | assert:
66 | that:
67 | - dns_zone is changed
68 |
--------------------------------------------------------------------------------
/ci/roles/application_credential/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - name: Create application credentials
4 | openstack.cloud.application_credential:
5 | cloud: "{{ cloud }}"
6 | state: present
7 | name: ansible_creds
8 | description: dummy description
9 | register: appcred
10 |
11 | - name: Assert return values of application_credential module
12 | assert:
13 | that:
14 | - appcred is changed
15 | # allow new fields to be introduced but prevent fields from being removed
16 | - expected_fields|difference(appcred.application_credential.keys())|length == 0
17 |
18 | - name: Create the application credential again
19 | openstack.cloud.application_credential:
20 | cloud: "{{ cloud }}"
21 | state: present
22 | name: ansible_creds
23 | description: dummy description
24 | register: appcred
25 |
26 | - name: Assert return values of ansible_credential module
27 | assert:
28 | that:
29 | # credentials are immutable so creating twice will cause delete and create
30 | - appcred is changed
31 | # allow new fields to be introduced but prevent fields from being removed
32 | - expected_fields|difference(appcred.application_credential.keys())|length == 0
33 |
34 | - name: Update the application credential again
35 | openstack.cloud.application_credential:
36 | cloud: "{{ cloud }}"
37 | state: present
38 | name: ansible_creds
39 | description: new description
40 | register: appcred
41 |
42 | - name: Assert application credential changed
43 | assert:
44 | that:
45 | - appcred is changed
46 | - appcred.application_credential.description == 'new description'
47 |
48 | - name: Get list of all keypairs using application credential
49 | openstack.cloud.keypair_info:
50 | cloud: "{{ appcred.cloud }}"
51 |
52 | - name: Delete application credential
53 | openstack.cloud.application_credential:
54 | cloud: "{{ cloud }}"
55 | state: absent
56 | name: ansible_creds
57 | register: appcred
58 |
59 | - name: Assert application credential changed
60 | assert:
61 | that: appcred is changed
62 |
--------------------------------------------------------------------------------
/tools/run-ansible-sanity.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Copyright 2020 Red Hat, Inc.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | # not use this file except in compliance with the License. You may obtain
6 | # a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | # License for the specific language governing permissions and limitations
14 | # under the License.
15 |
16 | TOXDIR=${1:-.}
17 | ANSIBLE_COLLECTIONS_PATH=$(mktemp -d)
18 | echo "Executing ansible-test sanity checks in ${ANSIBLE_COLLECTIONS_PATH}"
19 |
20 | trap "rm -rf ${ANSIBLE_COLLECTIONS_PATH}" err exit
21 |
22 | PY_VER=$(python3 -c "from platform import python_version;print(python_version())" | cut -f 1,2 -d".")
23 | echo "Running test with Python version ${PY_VER}"
24 |
25 | rm -rf "${ANSIBLE_COLLECTIONS_PATH}"
26 | mkdir -p ${ANSIBLE_COLLECTIONS_PATH}/ansible_collections/openstack/cloud
27 | cp -a ${TOXDIR}/{plugins,meta,tests,docs,galaxy.yml} ${ANSIBLE_COLLECTIONS_PATH}/ansible_collections/openstack/cloud
28 | cd ${ANSIBLE_COLLECTIONS_PATH}/ansible_collections/openstack/cloud/
29 | echo "Running ansible-test with version:"
30 | ansible --version
31 | # Ansible-core 2.17 dropped support for the metaclass-boilerplate and future-import-boilerplate tests.
32 | # TODO(mgoddard): Drop this workaround when ansible-core 2.16 is EOL.
33 | ANSIBLE_VER=$(python3 -m pip show ansible-core | awk '$1 == "Version:" { print $2 }')
34 | ANSIBLE_MAJOR_VER=$(echo "$ANSIBLE_VER" | sed 's/^\([0-9]\)\..*/\1/g')
35 | SKIP_TESTS=""
36 | if [[ $ANSIBLE_MAJOR_VER -eq 2 ]]; then
37 | ANSIBLE_MINOR_VER=$(echo "$ANSIBLE_VER" | sed 's/^2\.\([^\.]*\)\..*/\1/g')
38 | if [[ $ANSIBLE_MINOR_VER -le 16 ]]; then
39 | SKIP_TESTS="--skip-test metaclass-boilerplate --skip-test future-import-boilerplate"
40 | fi
41 | fi
42 | ansible-test sanity -v \
43 | --venv \
44 | --python ${PY_VER} \
45 | $SKIP_TESTS \
46 | plugins/ docs/ meta/
47 |
--------------------------------------------------------------------------------
/ci/roles/object/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create container
3 | openstack.cloud.object_container:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | name: ansible_container
7 |
8 | - name: Create object from data
9 | openstack.cloud.object:
10 | cloud: "{{ cloud }}"
11 | state: present
12 | name: ansible_object
13 | data: "this is a test"
14 | container: ansible_container
15 | register: object
16 |
17 | - name: Assert return values of object module
18 | assert:
19 | that:
20 | - object.object.id == "ansible_object"
21 | # allow new fields to be introduced but prevent fields from being removed
22 | - expected_fields|difference(object.object.keys())|length == 0
23 |
24 | - name: Delete object
25 | openstack.cloud.object:
26 | cloud: "{{ cloud }}"
27 | state: absent
28 | name: ansible_object
29 | container: ansible_container
30 |
31 | - name: Create object from file
32 | block:
33 | - name: Create temporary data file
34 | ansible.builtin.tempfile:
35 | register: tmp_file
36 |
37 | - name: Populate data file
38 | ansible.builtin.copy:
39 | content: "this is a test"
40 | dest: "{{ tmp_file.path }}"
41 |
42 | - name: Create object from data file
43 | openstack.cloud.object:
44 | cloud: "{{ cloud }}"
45 | state: present
46 | name: ansible_object
47 | filename: "{{ tmp_file.path }}"
48 | container: ansible_container
49 | register: object
50 |
51 | always:
52 | - name: Remove temporary data file
53 | ansible.builtin.file:
54 | path: "{{ tmp_file.path }}"
55 | state: absent
56 | when: tmp_file is defined and 'path' in tmp_file
57 |
58 | - name: Assert return values of object module
59 | assert:
60 | that:
61 | - object.object.id == "ansible_object"
62 | # allow new fields to be introduced but prevent fields from being removed
63 | - expected_fields|difference(object.object.keys())|length == 0
64 |
65 | - name: Delete object
66 | openstack.cloud.object:
67 | cloud: "{{ cloud }}"
68 | state: absent
69 | name: ansible_object
70 | container: ansible_container
71 |
72 | - name: Delete container
73 | openstack.cloud.object_container:
74 | cloud: "{{ cloud }}"
75 | state: absent
76 | name: ansible_container
77 |
--------------------------------------------------------------------------------
/ci/roles/baremetal_node/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # TODO: Actually run this role in CI. Atm we do not have DevStack's ironic plugin enabled.
3 | - name: Create baremetal node
4 | openstack.cloud.baremetal_node:
5 | cloud: "{{ cloud }}"
6 | driver_info:
7 | ipmi_address: "1.2.3.4"
8 | ipmi_username: "admin"
9 | ipmi_password: "secret"
10 | name: ansible_baremetal_node
11 | nics:
12 | - mac: "aa:bb:cc:aa:bb:cc"
13 | state: present
14 | register: node
15 |
16 | - debug: var=node
17 |
18 | - name: assert return values of baremetal_node module
19 | assert:
20 | that:
21 | # allow new fields to be introduced but prevent fields from being removed
22 | - expected_fields|difference(node.node.keys())|length == 0
23 |
24 | - name: Fetch baremetal nodes
25 | openstack.cloud.baremetal_node_info:
26 | cloud: "{{ cloud }}"
27 | register: nodes
28 |
29 | - name: assert module results of baremetal_node_info module
30 | assert:
31 | that:
32 | - nodes.nodes|list|length > 0
33 |
34 | - name: assert return values of baremetal_node_info module
35 | assert:
36 | that:
37 | # allow new fields to be introduced but prevent fields from being removed
38 | - expected_fields|difference(nodes.nodes.0.keys())|length == 0
39 |
40 | - name: Fetch baremetal node by name
41 | openstack.cloud.baremetal_node_info:
42 | cloud: "{{ cloud }}"
43 | name: ansible_baremetal_node
44 | register: nodes
45 |
46 | - name: assert module results of baremetal_node_info module
47 | assert:
48 | that:
49 | - nodes.nodes|list|length == 1
50 | - nodes.nodes.0.id == node.node.id
51 | - nodes.nodes.0.name == "ansible_baremetal_node"
52 |
53 | - name: Delete baremetal node
54 | openstack.cloud.baremetal_node:
55 | cloud: "{{ cloud }}"
56 | driver_info:
57 | ipmi_address: "1.2.3.4"
58 | ipmi_username: "admin"
59 | ipmi_password: "secret"
60 | name: ansible_baremetal_node
61 | nics:
62 | - mac: "aa:bb:cc:aa:bb:cc"
63 | state: absent
64 |
65 | - name: Fetch baremetal node by name
66 | openstack.cloud.baremetal_node_info:
67 | cloud: "{{ cloud }}"
68 | name: ansible_baremetal_node
69 | register: nodes
70 |
71 | - name: Assert that baremetal node has been deleted
72 | assert:
73 | that:
74 | - nodes.nodes|list|length == 0
75 |
--------------------------------------------------------------------------------
/ci/roles/identity_role/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create identity role
3 | openstack.cloud.identity_role:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | name: ansible_role
7 | description: "ansible role"
8 | register: role
9 |
10 | - name: Assert return values of identity_role module
11 | assert:
12 | that:
13 | - role.role.name == 'ansible_role'
14 | - role.role.description == "ansible role"
15 | # allow new fields to be introduced but prevent fields from being removed
16 | - expected_fields|difference(role.role.keys())|length == 0
17 |
18 | - name: Try to get role
19 | openstack.cloud.identity_role_info:
20 | cloud: "{{ cloud }}"
21 | name: ansible_role
22 | register: roles
23 |
24 | - name: Assert role found
25 | assert:
26 | that:
27 | - roles.roles | length == 1
28 | - roles.roles.0.name == 'ansible_role'
29 |
30 | - name: Fetch all roles
31 | openstack.cloud.identity_role_info:
32 | cloud: "{{ cloud }}"
33 | register: roles
34 |
35 | - name: Assert return values of identity_role_info module
36 | assert:
37 | that:
38 | - roles.roles | length > 0
39 | # allow new fields to be introduced but prevent fields from being removed
40 | - expected_fields|difference(roles.roles.0.keys())|length == 0
41 |
42 | - name: Create identity role again
43 | openstack.cloud.identity_role:
44 | cloud: "{{ cloud }}"
45 | state: present
46 | name: ansible_role
47 | description: "ansible role"
48 | register: role
49 |
50 | - name: Assert role did not change
51 | assert:
52 | that:
53 | - role is not changed
54 |
55 | - name: Delete identity role
56 | openstack.cloud.identity_role:
57 | cloud: "{{ cloud }}"
58 | state: absent
59 | name: ansible_role
60 | register: role
61 |
62 | - name: Assert role changed
63 | assert:
64 | that:
65 | - role is changed
66 |
67 | - name: Try to get role
68 | openstack.cloud.identity_role_info:
69 | cloud: "{{ cloud }}"
70 | name: ansible_role
71 | register: roles
72 |
73 | - name: Assert no role found
74 | assert:
75 | that:
76 | - roles.roles | length == 0
77 |
78 | - name: Delete role again
79 | openstack.cloud.identity_role:
80 | cloud: "{{ cloud }}"
81 | state: absent
82 | name: ansible_role
83 | register: role
84 |
85 | - name: Assert role did not change
86 | assert:
87 | that:
88 | - role is not changed
89 |
--------------------------------------------------------------------------------
/meta/runtime.yml:
--------------------------------------------------------------------------------
1 | requires_ansible: ">=2.8"
2 | action_groups:
3 | openstack:
4 | - address_scope
5 | - application_credential
6 | - auth
7 | - baremetal_deploy_template
8 | - baremetal_inspect
9 | - baremetal_node
10 | - baremetal_node_action
11 | - baremetal_node_info
12 | - baremetal_port
13 | - baremetal_port_info
14 | - catalog_service
15 | - catalog_service_info
16 | - coe_cluster
17 | - coe_cluster_template
18 | - compute_flavor
19 | - compute_flavor_access
20 | - compute_flavor_info
21 | - compute_service_info
22 | - config
23 | - dns_zone
24 | - dns_zone_info
25 | - endpoint
26 | - federation_idp
27 | - federation_idp_info
28 | - federation_mapping
29 | - federation_mapping_info
30 | - floating_ip
31 | - floating_ip_info
32 | - group_assignment
33 | - host_aggregate
34 | - identity_domain
35 | - identity_domain_info
36 | - identity_group
37 | - identity_group_info
38 | - identity_role
39 | - identity_role_info
40 | - identity_user
41 | - identity_user_info
42 | - image
43 | - image_info
44 | - keypair
45 | - keypair_info
46 | - keystone_federation_protocol
47 | - keystone_federation_protocol_info
48 | - lb_health_monitor
49 | - lb_listener
50 | - lb_member
51 | - lb_pool
52 | - loadbalancer
53 | - network
54 | - networks_info
55 | - neutron_rbac_policies_info
56 | - neutron_rbac_policy
57 | - object
58 | - object_container
59 | - object_containers_info
60 | - port
61 | - port_info
62 | - project
63 | - project_info
64 | - quota
65 | - recordset
66 | - resource
67 | - resources
68 | - role_assignment
69 | - router
70 | - routers_info
71 | - security_group
72 | - security_group_info
73 | - security_group_rule
74 | - security_group_rule_info
75 | - server
76 | - server_action
77 | - server_group
78 | - server_info
79 | - server_metadata
80 | - server_volume
81 | - share_type
82 | - share_type_info
83 | - stack
84 | - stack_info
85 | - subnet
86 | - subnet_pool
87 | - subnets_info
88 | - trunk
89 | - volume
90 | - volume_manage
91 | - volume_backup
92 | - volume_backup_info
93 | - volume_info
94 | - volume_service_info
95 | - volume_snapshot
96 | - volume_snapshot_info
97 | - volume_type_access
98 |
--------------------------------------------------------------------------------
/ci/roles/compute_flavor_access/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create flavor
3 | openstack.cloud.compute_flavor:
4 | cloud: devstack-admin
5 | state: present
6 | name: ansible_flavor
7 | is_public: False
8 | ram: 1024
9 | vcpus: 1
10 | disk: 10
11 | ephemeral: 10
12 | swap: 1
13 | register: flavor
14 |
15 | - name: Fetch demo project
16 | openstack.cloud.project_info:
17 | cloud: devstack-admin
18 | name: demo
19 | register: projects
20 |
21 | - name: Verify demo project
22 | assert:
23 | that:
24 | - projects.projects|length == 1
25 | - projects.projects.0.name == "demo"
26 |
27 | - name: Grant access to flavor
28 | openstack.cloud.compute_flavor_access:
29 | cloud: devstack-admin
30 | name: ansible_flavor
31 | project: demo
32 | state: present
33 | register: access
34 |
35 | - name: Verify access
36 | assert:
37 | that:
38 | - access is changed
39 | - access.flavor.id == flavor.flavor.id
40 |
41 | # TODO: Replace with appropriate Ansible module once available
42 | - name: Get compute flavor
43 | command: openstack --os-cloud=devstack-admin flavor show ansible_flavor -f json
44 | register: flavor_show
45 |
46 | - name: Verify volume type access
47 | assert:
48 | that:
49 | - (flavor_show.stdout | from_json).name == 'ansible_flavor'
50 | - projects.projects.0.id in (flavor_show.stdout | from_json).access_project_ids
51 |
52 | - name: Grant access to flavor again
53 | openstack.cloud.compute_flavor_access:
54 | cloud: devstack-admin
55 | name: ansible_flavor
56 | project: demo
57 | state: present
58 | register: access
59 |
60 | - name: Verify access did not change
61 | assert:
62 | that:
63 | - access is not changed
64 |
65 | - name: Revoke access to flavor
66 | openstack.cloud.compute_flavor_access:
67 | cloud: devstack-admin
68 | name: ansible_flavor
69 | project: demo
70 | state: absent
71 | register: access
72 |
73 | - name: Verify revoked access
74 | assert:
75 | that:
76 | - access is changed
77 | - access.flavor.id == flavor.flavor.id
78 |
79 | - name: Revoke access to flavor again
80 | openstack.cloud.compute_flavor_access:
81 | cloud: devstack-admin
82 | name: ansible_flavor
83 | project: demo
84 | state: absent
85 | register: access
86 |
87 | - name: Verify access did not change
88 | assert:
89 | that:
90 | - access is not changed
91 |
92 | - name: Delete flavor
93 | openstack.cloud.compute_flavor:
94 | cloud: devstack-admin
95 | state: absent
96 | name: ansible_flavor
97 |
--------------------------------------------------------------------------------
/ci/roles/volume_type/tasks/volume_encryption.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Test, Volume type has no encryption
3 | openstack.cloud.volume_type_info:
4 | cloud: "{{ cloud }}"
5 | name: "{{ volume_type_name }}"
6 | register: the_result
7 | - name: Check volume type has no encryption
8 | ansible.builtin.assert:
9 | that:
10 | - the_result.encryption.id == None
11 | success_msg: >-
12 | Success: Volume type has no encryption at the moment
13 |
14 | - name: Test, create volume type encryption
15 | openstack.cloud.volume_type_encryption:
16 | cloud: "{{ cloud }}"
17 | volume_type: "{{ volume_type_name }}"
18 | state: present
19 | encryption_provider: "{{ enc_provider_name }}"
20 | encryption_cipher: "{{ enc_cipher }}"
21 | encryption_control_location: "{{ enc_control_location }}"
22 | encryption_key_size: "{{ enc_key_size }}"
23 | register: the_result
24 | - name: Check volume type encryption
25 | ansible.builtin.assert:
26 | that:
27 | - the_result.encryption.cipher == enc_cipher
28 | - the_result.encryption.control_location == enc_control_location
29 | - the_result.encryption.key_size == enc_key_size
30 | - the_result.encryption.provider == enc_provider_name
31 | success_msg: >-
32 | Success: {{ the_result.encryption.encryption_id }}
33 |
34 | - name: Test, update volume type encryption
35 | openstack.cloud.volume_type_encryption:
36 | cloud: "{{ cloud }}"
37 | volume_type: "{{ volume_type_name }}"
38 | state: present
39 | encryption_provider: "{{ enc_provider_name }}"
40 | encryption_cipher: "{{ enc_cipher }}"
41 | encryption_control_location: "{{ enc_control_alt_location }}"
42 | encryption_key_size: "{{ enc_key_size }}"
43 | register: the_result
44 | - name: Check volume type encryption change
45 | ansible.builtin.assert:
46 | that:
47 | - the_result.encryption.control_location == enc_control_alt_location
48 | success_msg: >-
49 | New location: {{ the_result.encryption.control_location }}
50 |
51 | - name: Test, delete volume type encryption
52 | openstack.cloud.volume_type_encryption:
53 | cloud: "{{ cloud }}"
54 | volume_type: "{{ volume_type_name }}"
55 | state: absent
56 | register: the_result
57 | - name: Get volume type details
58 | openstack.cloud.volume_type_info:
59 | cloud: "{{ cloud }}"
60 | name: "{{ volume_type_name }}"
61 | register: the_result
62 | - name: Check volume type has no encryption
63 | ansible.builtin.assert:
64 | that:
65 | - the_result.encryption.id == None
66 | success_msg: >-
67 | Success: Volume type has no encryption
68 |
--------------------------------------------------------------------------------
/ci/roles/coe_cluster_template/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create keypair
3 | openstack.cloud.keypair:
4 | cloud: "{{ cloud }}"
5 | name: ansible_keypair
6 | state: present
7 | register: keypair
8 |
9 | - name: List all images
10 | openstack.cloud.image_info:
11 | cloud: "{{ cloud }}"
12 | register: images
13 |
14 | - name: Identify Fedora CoreOS image id
15 | set_fact:
16 | image_id: "{{ images.images|community.general.json_query(query)|first }}"
17 | vars:
18 | query: "[?starts_with(name, 'fedora-coreos')].id"
19 |
20 | - name: Create Kubernetes cluster template
21 | openstack.cloud.coe_cluster_template:
22 | cloud: "{{ cloud }}"
23 | coe: kubernetes
24 | is_floating_ip_enabled: false
25 | image_id: '{{ image_id }}'
26 | keypair_id: '{{ keypair.keypair.id }}'
27 | name: k8s
28 | state: present
29 | labels:
30 | docker_volume_size: 10
31 | cloud_provider_tag: v1.23.1
32 | register: coe_cluster_template
33 |
34 | - name: Assert return values of coe_cluster_template module
35 | assert:
36 | that:
37 | # allow new fields to be introduced but prevent fields from being removed
38 | - expected_fields|difference(coe_cluster_template.cluster_template.keys())|length == 0
39 |
40 | - name: Create Kubernetes cluster template again
41 | openstack.cloud.coe_cluster_template:
42 | cloud: "{{ cloud }}"
43 | coe: kubernetes
44 | is_floating_ip_enabled: false
45 | image_id: '{{ image_id }}'
46 | keypair_id: '{{ keypair.keypair.id }}'
47 | name: k8s
48 | state: present
49 | labels:
50 | docker_volume_size: 10
51 | cloud_provider_tag: v1.23.1
52 | register: coe_cluster_template
53 |
54 | - name: Assert return values of coe_cluster_template module
55 | assert:
56 | that:
57 | - coe_cluster_template is not changed
58 |
59 | - name: Delete Kubernetes cluster template
60 | openstack.cloud.coe_cluster_template:
61 | cloud: "{{ cloud }}"
62 | name: k8s
63 | state: absent
64 | register: coe_cluster_template
65 |
66 | - name: Assert return values of coe_cluster_template module
67 | assert:
68 | that:
69 | - coe_cluster_template is changed
70 |
71 | - name: Delete Kubernetes cluster template again
72 | openstack.cloud.coe_cluster_template:
73 | cloud: "{{ cloud }}"
74 | name: k8s
75 | state: absent
76 | register: coe_cluster_template
77 |
78 | - name: Assert return values of coe_cluster_template module
79 | assert:
80 | that:
81 | - coe_cluster_template is not changed
82 |
83 | - name: Delete keypair
84 | openstack.cloud.keypair:
85 | cloud: "{{ cloud }}"
86 | name: ansible_keypair
87 | state: absent
88 |
--------------------------------------------------------------------------------
/ci/roles/stack/tasks/main.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create minimal stack
3 | openstack.cloud.stack:
4 | cloud: "{{ cloud }}"
5 | template: "roles/stack/files/hello-world.yaml"
6 | name: "{{ stack_name }}"
7 | tags: "tag1,tag2"
8 | register: stack
9 |
10 | - name: Assert fields returned by create stack
11 | assert:
12 | that: item in stack.stack
13 | loop: "{{ expected_fields }}"
14 |
15 | - name: List stacks
16 | openstack.cloud.stack_info:
17 | cloud: "{{ cloud }}"
18 | register: stacks
19 |
20 | - name: Assert stack_info module return values
21 | assert:
22 | that:
23 | - stacks.stacks|length > 0
24 |
25 | - name: Assert fields returned by stack info
26 | assert:
27 | that: item in stacks.stacks[0]
28 | loop: "{{ expected_fields }}"
29 |
30 | - name: Get single stack
31 | openstack.cloud.stack_info:
32 | cloud: "{{ cloud }}"
33 | name: "{{ stack_name }}"
34 | register: stacks
35 |
36 | - name: Assert single stack
37 | assert:
38 | that:
39 | - stacks.stacks|length == 1
40 | - stacks.stacks.0.name == stack_name
41 | - stacks.stacks.0.id == stack.stack.id
42 | # Older openstacksdk releases use datatype list instead of str for tags
43 | # Ref.: https://review.opendev.org/c/openstack/openstacksdk/+/860534
44 | - stacks.stacks.0.tags|string in ["tag1,tag2", "['tag1', 'tag2']"]
45 |
46 | - name: Update stack
47 | openstack.cloud.stack:
48 | cloud: "{{ cloud }}"
49 | template: "roles/stack/files/hello-world.yaml"
50 | name: "{{ stack_name }}"
51 | tags: "tag1,tag2,tag3"
52 | register: stack_updated
53 |
54 | - name: Assert updated stack
55 | assert:
56 | that:
57 | - stack_updated.stack.id == stack.stack.id
58 | - stack_updated is changed
59 |
60 | - name: Get updated stack
61 | openstack.cloud.stack_info:
62 | cloud: "{{ cloud }}"
63 | name: "{{ stack_name }}"
64 | register: stacks
65 |
66 | - name: Assert updated stack
67 | assert:
68 | that:
69 | - stacks.stacks|length == 1
70 | - stacks.stacks.0.id == stack.stack.id
71 | # Older openstacksdk releases use datatype list instead of str for tags
72 | # Ref.: https://review.opendev.org/c/openstack/openstacksdk/+/860534
73 | - stacks.stacks.0.tags|string in ["tag1,tag2,tag3", "['tag1', 'tag2', 'tag3']"]
74 |
75 | - name: Delete stack
76 | openstack.cloud.stack:
77 | cloud: "{{ cloud }}"
78 | name: "{{ stack_name }}"
79 | state: absent
80 |
81 | - name: Get single stack
82 | openstack.cloud.stack_info:
83 | cloud: "{{ cloud }}"
84 | name: "{{ stack_name }}"
85 | register: stacks
86 |
87 | - assert:
88 | that:
89 | - (stacks.stacks|length == 0) or (stacks.stacks.0.status == 'DELETE_COMPLETE')
90 |
--------------------------------------------------------------------------------
/ci/roles/host_aggregate/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: ensure aggregate doesn't exist before tests
3 | openstack.cloud.host_aggregate:
4 | cloud: "{{ cloud }}"
5 | state: absent
6 | name: test_aggregate
7 | register: aggregate
8 |
9 | - block:
10 | - name: create aggregate
11 | openstack.cloud.host_aggregate:
12 | cloud: "{{ cloud }}"
13 | state: present
14 | name: test_aggregate
15 | hosts:
16 | - "{{ ansible_hostname }}"
17 | register: aggregate
18 |
19 | - name: assert aggregate is changed
20 | assert:
21 | that: aggregate is changed
22 |
23 | - name: assert aggregate fields
24 | assert:
25 | that: item in aggregate.aggregate
26 | loop: "{{ expected_fields }}"
27 |
28 | - block:
29 | - name: recreate aggregate
30 | openstack.cloud.host_aggregate:
31 | cloud: "{{ cloud }}"
32 | state: present
33 | name: test_aggregate
34 | hosts:
35 | - "{{ ansible_hostname }}"
36 | register: aggregate
37 |
38 | - name: assert aggregate is not changed
39 | assert:
40 | that: aggregate is not changed
41 |
42 | - name: assert aggregate fields
43 | assert:
44 | that: item in aggregate.aggregate
45 | loop: "{{ expected_fields }}"
46 |
47 | - block:
48 | - name: update aggregate
49 | openstack.cloud.host_aggregate:
50 | cloud: "{{ cloud }}"
51 | state: present
52 | name: test_aggregate
53 | metadata:
54 | ssd: "true"
55 | hosts:
56 | - "{{ ansible_hostname }}"
57 | register: aggregate
58 |
59 | - name: assert aggregate is changed
60 | assert:
61 | that: aggregate is changed
62 |
63 | - name: assert aggregate fields
64 | assert:
65 | that: item in aggregate.aggregate
66 | loop: "{{ expected_fields }}"
67 |
68 | - block:
69 | - name: purge hosts
70 | openstack.cloud.host_aggregate:
71 | cloud: "{{ cloud }}"
72 | state: present
73 | name: test_aggregate
74 | hosts: []
75 | purge_hosts: true
76 | register: aggregate
77 |
78 | - name: assert hosts were purged
79 | assert:
80 | that:
81 | - aggregate is changed
82 | - aggregate.aggregate.hosts | length == 0
83 |
84 | - name: assert aggregate fields
85 | assert:
86 | that: item in aggregate.aggregate
87 | loop: "{{ expected_fields }}"
88 |
89 | - block:
90 | - name: delete aggregate
91 | openstack.cloud.host_aggregate:
92 | cloud: "{{ cloud }}"
93 | state: absent
94 | name: test_aggregate
95 | register: aggregate
96 |
97 | - name: assert aggregate is changed
98 | assert:
99 | that: aggregate is changed
100 |
--------------------------------------------------------------------------------
/tests/unit/plugins/inventory/test_openstack.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2018 Lars Kellogg-Stedman
4 | #
5 | # This file is part of Ansible
6 | #
7 | # Ansible is free software: you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # Ansible is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with Ansible. If not, see .
19 |
20 | # Make coding more python3-ish
21 |
22 | import pytest
23 |
24 | from ansible_collections.openstack.cloud.plugins.inventory.openstack import InventoryModule
25 | from ansible.inventory.data import InventoryData
26 | from ansible.template import Templar
27 |
28 |
29 | config_data = {
30 | 'plugin': 'openstack',
31 | 'compose': {
32 | 'composed_var': '"testvar-" + testvar',
33 | },
34 | 'groups': {
35 | 'testgroup': '"host" in inventory_hostname',
36 | },
37 | 'keyed_groups':
38 | [{
39 | 'prefix': 'keyed',
40 | 'key': 'testvar',
41 | }]
42 | }
43 |
44 | hostvars = {
45 | 'host0': {
46 | 'inventory_hostname': 'host0',
47 | 'testvar': '0',
48 | },
49 | 'host1': {
50 | 'inventory_hostname': 'host1',
51 | 'testvar': '1',
52 | },
53 | }
54 |
55 |
56 | @pytest.fixture(scope="module")
57 | def inventory():
58 | inventory = InventoryModule()
59 | inventory._config_data = config_data
60 | inventory.inventory = InventoryData()
61 | inventory.templar = Templar(loader=None)
62 |
63 | for host in hostvars:
64 | inventory.inventory.add_host(host)
65 |
66 | return inventory
67 |
68 |
69 | def test_simple_groups(inventory):
70 | inventory._set_variables(hostvars, {})
71 | groups = inventory.inventory.get_groups_dict()
72 | assert 'testgroup' in groups
73 | assert len(groups['testgroup']) == len(hostvars)
74 |
75 |
76 | def test_keyed_groups(inventory):
77 | inventory._set_variables(hostvars, {})
78 | assert 'keyed_0' in inventory.inventory.groups
79 | assert 'keyed_1' in inventory.inventory.groups
80 |
81 |
82 | def test_composed_vars(inventory):
83 | inventory._set_variables(hostvars, {})
84 |
85 | for host in hostvars:
86 | assert host in inventory.inventory.hosts
87 | host = inventory.inventory.get_host(host)
88 | assert host.vars['composed_var'] == 'testvar-{testvar}'.format(**hostvars[host.name])
89 |
--------------------------------------------------------------------------------
/plugins/modules/federation_idp_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright: Ansible Project
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | module: federation_idp_info
9 | short_description: Fetch OpenStack federation identity providers
10 | author: OpenStack Ansible SIG
11 | description:
12 | - Fetch OpenStack federation identity providers.
13 | options:
14 | id:
15 | description:
16 | - The ID (and name) of the identity provider to fetch.
17 | type: str
18 | aliases: ['name']
19 | extends_documentation_fragment:
20 | - openstack.cloud.openstack
21 | '''
22 |
23 | EXAMPLES = r'''
24 | - name: Fetch a specific identity provider
25 | openstack.cloud.federation_idp_info:
26 | cloud: example_cloud
27 | name: example_provider
28 |
29 | - name: Fetch all providers
30 | openstack.cloud.federation_idp_info:
31 | cloud: example_cloud
32 | '''
33 |
34 | RETURN = r'''
35 | identity_providers:
36 | description: Dictionary describing the identity providers
37 | returned: always
38 | type: list
39 | elements: dict
40 | contains:
41 | description:
42 | description: Identity provider description
43 | type: str
44 | sample: "demodescription"
45 | domain_id:
46 | description: Domain to which the identity provider belongs
47 | type: str
48 | sample: "default"
49 | id:
50 | description: Identity provider ID
51 | type: str
52 | sample: "test-idp"
53 | is_enabled:
54 | description: Indicates whether the identity provider is enabled
55 | type: bool
56 | name:
57 | description: Name of the identity provider, equals its ID.
58 | type: str
59 | sample: "test-idp"
60 | remote_ids:
61 | description: Remote IDs associated with the identity provider
62 | type: list
63 | '''
64 |
65 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
66 |
67 |
68 | class IdentityFederationIdpInfoModule(OpenStackModule):
69 | argument_spec = dict(
70 | id=dict(aliases=['name']),
71 | )
72 | module_kwargs = dict(
73 | supports_check_mode=True
74 | )
75 |
76 | def run(self):
77 | kwargs = dict((k, self.params[k])
78 | for k in ['id']
79 | if self.params[k] is not None)
80 | identity_providers = self.conn.identity.identity_providers(**kwargs)
81 | self.exit_json(
82 | changed=False,
83 | identity_providers=[i.to_dict(computed=False)
84 | for i in identity_providers])
85 |
86 |
87 | def main():
88 | module = IdentityFederationIdpInfoModule()
89 | module()
90 |
91 |
92 | if __name__ == '__main__':
93 | main()
94 |
--------------------------------------------------------------------------------
/ci/roles/volume_snapshot/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Get existing snapshots
3 | openstack.cloud.volume_snapshot_info:
4 | cloud: "{{ cloud }}"
5 | register: info
6 |
7 | - name: Assert volume_snapshot_info
8 | assert:
9 | that:
10 | - info.volume_snapshots|length == 0
11 |
12 | - name: Get non-existing snapshot
13 | openstack.cloud.volume_snapshot_info:
14 | cloud: "{{ cloud }}"
15 | name: non-existing-snapshot
16 | register: info
17 |
18 | - name: Assert volume_snapshot_info
19 | assert:
20 | that:
21 | - info.volume_snapshots|length == 0
22 |
23 | - name: Create volume
24 | openstack.cloud.volume:
25 | cloud: "{{ cloud }}"
26 | state: present
27 | size: 1
28 | name: ansible_volume
29 | description: Test volume
30 | register: volume
31 |
32 | - name: Create volume snapshot
33 | openstack.cloud.volume_snapshot:
34 | cloud: "{{ cloud }}"
35 | state: present
36 | name: ansible_volume_snapshot
37 | volume: ansible_volume
38 | register: snapshot
39 |
40 | - name: Assert volume_snapshot
41 | assert:
42 | that:
43 | - snapshot.volume_snapshot.name == "ansible_volume_snapshot"
44 |
45 | - name: Assert return values of volume_snapshot module
46 | assert:
47 | that:
48 | # allow new fields to be introduced but prevent fields from being removed
49 | - expected_fields|difference(snapshot.volume_snapshot.keys())|length == 0
50 |
51 | - name: Get snapshot info
52 | openstack.cloud.volume_snapshot_info:
53 | cloud: "{{ cloud }}"
54 | name: ansible_volume_snapshot
55 | register: info
56 |
57 | - name: Assert volume_snapshot_info
58 | assert:
59 | that:
60 | - info.volume_snapshots|length == 1
61 | - info.volume_snapshots[0].id == snapshot.volume_snapshot.id
62 | - info.volume_snapshots[0].volume_id == volume.volume.id
63 |
64 | - name: Assert return values of volume_info module
65 | assert:
66 | that:
67 | # allow new fields to be introduced but prevent fields from being removed
68 | - expected_fields|difference(info.volume_snapshots[0].keys())|length == 0
69 |
70 | - name: Create volume from snapshot
71 | openstack.cloud.volume:
72 | cloud: "{{ cloud }}"
73 | state: present
74 | size: 1
75 | snapshot: ansible_volume_snapshot
76 | name: ansible_volume2
77 | description: Test volume
78 |
79 | - name: Delete volume snapshot
80 | openstack.cloud.volume_snapshot:
81 | cloud: "{{ cloud }}"
82 | name: ansible_volume_snapshot
83 | volume: ansible_volume
84 | state: absent
85 |
86 | - name: Delete volume
87 | openstack.cloud.volume:
88 | cloud: "{{ cloud }}"
89 | state: absent
90 | name: ansible_volume2
91 |
92 | - name: Delete volume
93 | openstack.cloud.volume:
94 | cloud: "{{ cloud }}"
95 | state: absent
96 | name: ansible_volume
97 |
--------------------------------------------------------------------------------
/ci/roles/volume_backup/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Get existing backups
3 | openstack.cloud.volume_backup_info:
4 | cloud: "{{ cloud }}"
5 | register: info
6 |
7 | - name: Assert volume_backup_info
8 | assert:
9 | that:
10 | - info.volume_backups|length == 0
11 |
12 | - name: Get non-existing backup
13 | openstack.cloud.volume_backup_info:
14 | cloud: "{{ cloud }}"
15 | name: non-existing-backup
16 | register: info
17 |
18 | - name: Assert volume_backup_info
19 | assert:
20 | that:
21 | - info.volume_backups|length == 0
22 |
23 | - name: Create volume
24 | openstack.cloud.volume:
25 | cloud: "{{ cloud }}"
26 | state: present
27 | size: 1
28 | name: ansible_volume
29 | register: volume
30 |
31 | - name: Create volume backup
32 | openstack.cloud.volume_backup:
33 | cloud: "{{ cloud }}"
34 | state: present
35 | name: ansible_volume_backup
36 | volume: ansible_volume
37 | # TODO: Uncomment code when https://storyboard.openstack.org/#!/story/2010395 has been solved.
38 | #metadata:
39 | # key1: value1
40 | # key2: value2
41 | register: backup
42 |
43 | - name: Assert volume_backup
44 | assert:
45 | that:
46 | - backup.volume_backup.name == "ansible_volume_backup"
47 | - backup.volume_backup.volume_id == volume.volume.id
48 | # TODO: Uncomment code when https://storyboard.openstack.org/#!/story/2010395 has been solved.
49 | #- backup.volume_backup.metadata.keys()|sort == ['key1', 'key2']
50 | #- backup.volume_backup.metadata['key1'] == 'value1'
51 | #- backup.volume_backup.metadata['key2'] == 'value2'
52 |
53 | - name: Assert return values of volume_backup module
54 | assert:
55 | that:
56 | # allow new fields to be introduced but prevent fields from being removed
57 | - expected_fields|difference(backup.volume_backup.keys())|length == 0
58 |
59 | - name: Get backup info
60 | openstack.cloud.volume_backup_info:
61 | cloud: "{{ cloud }}"
62 | name: ansible_volume_backup
63 | register: info
64 |
65 | - name: Assert volume_backup_info
66 | assert:
67 | that:
68 | - info.volume_backups|length == 1
69 | - info.volume_backups[0].id == backup.backup.id
70 | - info.volume_backups[0].volume_id == volume.volume.id
71 |
72 | - name: Assert return values of volume_info module
73 | assert:
74 | that:
75 | # allow new fields to be introduced but prevent fields from being removed
76 | - expected_fields|difference(info.volume_backups[0].keys())|length == 0
77 |
78 | - name: Delete volume backup
79 | openstack.cloud.volume_backup:
80 | cloud: "{{ cloud }}"
81 | name: ansible_volume_backup
82 | wait: false
83 | state: absent
84 |
85 | - name: Delete volume
86 | openstack.cloud.volume:
87 | cloud: "{{ cloud }}"
88 | state: absent
89 | name: ansible_volume
90 |
--------------------------------------------------------------------------------
/plugins/modules/federation_mapping_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright: Ansible Project
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: federation_mapping_info
10 | short_description: Fetch Keystone federation mappings
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Fetch Keystone federation mappings.
14 | options:
15 | name:
16 | description:
17 | - ID or name of the federation mapping.
18 | type: str
19 | aliases: ['id']
20 | notes:
21 | - Name equals the ID of a federation mapping.
22 | extends_documentation_fragment:
23 | - openstack.cloud.openstack
24 | '''
25 |
26 | EXAMPLES = r'''
27 | - name: Fetch all federation mappings
28 | openstack.cloud.federation_mapping_info:
29 | cloud: example_cloud
30 |
31 | - name: Fetch federation mapping by name
32 | openstack.cloud.federation_mapping_info:
33 | cloud: example_cloud
34 | name: example_mapping
35 | '''
36 |
37 | RETURN = r'''
38 | mappings:
39 | description: List of federation mapping dictionaries.
40 | returned: always
41 | type: list
42 | elements: dict
43 | contains:
44 | id:
45 | description: The id of the mapping
46 | type: str
47 | sample: "ansible-test-mapping"
48 | name:
49 | description: Name of the mapping. Equal to C(id).
50 | type: str
51 | sample: "ansible-test-mapping"
52 | rules:
53 | description: List of rules for the mapping
54 | type: list
55 | '''
56 |
57 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
58 |
59 |
60 | class IdentityFederationMappingInfoModule(OpenStackModule):
61 | argument_spec = dict(
62 | name=dict(aliases=['id']),
63 | )
64 |
65 | module_kwargs = dict(
66 | supports_check_mode=True
67 | )
68 |
69 | def run(self):
70 | # name is id for federation mappings
71 | id = self.params['name']
72 |
73 | if id:
74 | # handle id parameter separately because self.conn.identity.\
75 | # mappings() does not allow to filter by id
76 | # Ref.: https://review.opendev.org/c/openstack/
77 | # openstacksdk/+/858522
78 | mapping = self.conn.identity.find_mapping(name_or_id=id,
79 | ignore_missing=True)
80 | mappings = [mapping] if mapping else []
81 | else:
82 | mappings = self.conn.identity.mappings()
83 |
84 | self.exit_json(changed=False,
85 | mappings=[mapping.to_dict(computed=False)
86 | for mapping in mappings])
87 |
88 |
89 | def main():
90 | module = IdentityFederationMappingInfoModule()
91 | module()
92 |
93 |
94 | if __name__ == '__main__':
95 | main()
96 |
--------------------------------------------------------------------------------
/plugins/modules/config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: config
10 | short_description: Get OpenStack Client config
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Get OpenStack cloud credentials and configuration,
14 | e.g. from clouds.yaml and environment variables.
15 | options:
16 | clouds:
17 | description:
18 | - List of clouds to limit the return list to.
19 | - When I(clouds) is not defined, then data
20 | is returned for all configured clouds.
21 | default: []
22 | type: list
23 | elements: str
24 | requirements:
25 | - "python >= 3.6"
26 | - "openstacksdk >= 1.0.0"
27 | '''
28 |
29 | RETURN = r'''
30 | clouds:
31 | description: List of OpenStack cloud configurations.
32 | returned: always
33 | type: list
34 | elements: dict
35 | contains:
36 | name:
37 | description: Name of the cloud.
38 | type: str
39 | config:
40 | description: A dict of configuration values for the CloudRegion and
41 | its services. The key for a ${config_option} for a
42 | specific ${service} should be ${service}_${config_option}.
43 | type: dict
44 | '''
45 |
46 | EXAMPLES = r'''
47 | - name: Read configuration of all defined clouds
48 | openstack.cloud.config:
49 | register: config
50 |
51 | - name: Print clouds which do not support security groups
52 | loop: "{{ config.clouds }}"
53 | when: item.config.secgroup_source|default(None) != None
54 | debug:
55 | var: item
56 |
57 | - name: Read configuration of a two specific clouds
58 | openstack.cloud.config:
59 | clouds:
60 | - devstack
61 | - mordred
62 | '''
63 |
64 | from ansible.module_utils.basic import AnsibleModule
65 |
66 | try:
67 | import openstack.config
68 | from openstack import exceptions
69 | HAS_OPENSTACKSDK = True
70 | except ImportError:
71 | HAS_OPENSTACKSDK = False
72 |
73 |
74 | def main():
75 | module = AnsibleModule(
76 | argument_spec=dict(
77 | clouds=dict(type='list', default=[], elements='str'),
78 | )
79 | )
80 |
81 | if not HAS_OPENSTACKSDK:
82 | module.fail_json(msg='openstacksdk is required for this module')
83 |
84 | try:
85 | clouds = [dict(name=cloud.name, config=cloud.config)
86 | for cloud in openstack.config.OpenStackConfig().get_all()
87 | if not module.params['clouds']
88 | or cloud.name in module.params['clouds']]
89 |
90 | module.exit_json(changed=False, clouds=clouds)
91 |
92 | except exceptions.SDKException as e:
93 | module.fail_json(msg=str(e))
94 |
95 |
96 | if __name__ == "__main__":
97 | main()
98 |
--------------------------------------------------------------------------------
/ci/roles/volume/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create volume
3 | openstack.cloud.volume:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | size: 1
7 | name: ansible_volume
8 | description: Test volume
9 | register: vol
10 |
11 | - assert:
12 | that: item in vol.volume
13 | loop: "{{ expected_fields }}"
14 |
15 | - name: Create volume from existing volume
16 | openstack.cloud.volume:
17 | cloud: "{{ cloud }}"
18 | state: present
19 | size: 1
20 | volume: "{{ vol.volume.id }}"
21 | name: ansible_volume1
22 | description: Test volume
23 |
24 | - name: Delete volume
25 | openstack.cloud.volume:
26 | cloud: "{{ cloud }}"
27 | state: absent
28 | name: ansible_volume1
29 |
30 | - name: Delete volume
31 | openstack.cloud.volume:
32 | cloud: "{{ cloud }}"
33 | state: absent
34 | name: ansible_volume
35 |
36 | - name: Test images
37 | block:
38 | - name: Ensure clean environment
39 | ansible.builtin.set_fact:
40 | tmp_file: !!null
41 |
42 | - name: Create a test image file
43 | ansible.builtin.tempfile:
44 | register: tmp_file
45 |
46 | - name: Fill test image file to 1MB
47 | community.general.filesize:
48 | path: '{{ tmp_file.path }}'
49 | size: 1M
50 |
51 | - name: Create test image
52 | openstack.cloud.image:
53 | cloud: "{{ cloud }}"
54 | state: present
55 | name: "{{ test_volume_image }}"
56 | filename: "{{ tmp_file.path }}"
57 | disk_format: raw
58 |
59 | - name: Create volume from image
60 | openstack.cloud.volume:
61 | cloud: "{{ cloud }}"
62 | state: present
63 | size: 1
64 | image: "{{ test_volume_image }}"
65 | name: ansible_volume2
66 | description: Test volume
67 |
68 | - name: Delete volume from image
69 | openstack.cloud.volume:
70 | cloud: "{{ cloud }}"
71 | name: ansible_volume2
72 | state: absent
73 |
74 | - name: Create test shared image
75 | openstack.cloud.image:
76 | cloud: "{{ cloud }}"
77 | state: present
78 | name: ansible_image
79 | filename: "{{ tmp_file.path }}"
80 | is_public: true
81 | disk_format: raw
82 |
83 | - name: Delete test shared image
84 | openstack.cloud.image:
85 | cloud: "{{ cloud }}"
86 | state: absent
87 | name: ansible_image
88 | filename: "{{ tmp_file.path }}"
89 | is_public: true
90 | disk_format: raw
91 |
92 | always:
93 | - name: Remove temporary image file
94 | ansible.builtin.file:
95 | path: "{{ tmp_file.path }}"
96 | state: absent
97 | when: tmp_file is defined and 'path' in tmp_file
98 |
99 | - include_tasks: volume_info.yml
100 |
--------------------------------------------------------------------------------
/ci/roles/server_volume/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: List all images
3 | openstack.cloud.image_info:
4 | cloud: "{{ cloud }}"
5 | register: images
6 |
7 | - name: Identify CirrOS image name
8 | set_fact:
9 | image_name: "{{ images.images|community.general.json_query(query)|first }}"
10 | vars:
11 | query: "[?starts_with(name, 'cirros')].name"
12 |
13 | - name: Create server
14 | openstack.cloud.server:
15 | cloud: "{{ cloud }}"
16 | state: present
17 | name: "{{ server_name }}"
18 | image: "{{ image_name }}"
19 | flavor: "{{ flavor_name }}"
20 | network: "{{ server_network }}"
21 | auto_ip: false
22 | wait: true
23 | register: server
24 |
25 | - name: Create volume
26 | openstack.cloud.volume:
27 | cloud: "{{ cloud }}"
28 | state: present
29 | size: 1
30 | name: ansible_volume
31 | wait: true
32 | register: volume
33 |
34 | - name: Attach volume to server
35 | openstack.cloud.server_volume:
36 | cloud: "{{ cloud }}"
37 | server: "{{ server.server.id }}"
38 | volume: "{{ volume.volume.id }}"
39 | wait: true
40 | register: server_volume
41 |
42 | - name: Assert changed
43 | assert:
44 | that: server_volume is changed
45 |
46 | - name: Assert return values of server_volume module
47 | assert:
48 | that:
49 | # allow new fields to be introduced but prevent fields from being removed
50 | - expected_fields|difference(server_volume.volume.keys())|length == 0
51 |
52 | - name: Attach volume to server again
53 | openstack.cloud.server_volume:
54 | cloud: "{{ cloud }}"
55 | server: "{{ server.server.id }}"
56 | volume: "{{ volume.volume.id }}"
57 | wait: true
58 | register: server_volume
59 |
60 | - name: Assert not changed
61 | assert:
62 | that: server_volume is not changed
63 |
64 | - name: Detach volume to server
65 | openstack.cloud.server_volume:
66 | cloud: "{{ cloud }}"
67 | state: absent
68 | server: "{{ server.server.id }}"
69 | volume: "{{ volume.volume.id }}"
70 | wait: true
71 | register: server_volume
72 |
73 | - name: Assert changed
74 | assert:
75 | that: server_volume is changed
76 |
77 | - name: Detach volume to server again
78 | openstack.cloud.server_volume:
79 | cloud: "{{ cloud }}"
80 | state: absent
81 | server: "{{ server.server.id }}"
82 | volume: "{{ volume.volume.id }}"
83 | wait: true
84 | register: server_volume
85 |
86 | - name: Assert not changed
87 | assert:
88 | that: server_volume is not changed
89 |
90 | - name: Delete volume
91 | openstack.cloud.volume:
92 | cloud: "{{ cloud }}"
93 | state: absent
94 | name: ansible_volume
95 | wait: true
96 |
97 | - name: Delete server
98 | openstack.cloud.server:
99 | cloud: "{{ cloud }}"
100 | state: absent
101 | name: "{{ server_name }}"
102 | wait: true
103 |
--------------------------------------------------------------------------------
/plugins/modules/catalog_service_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2022 by Red Hat, Inc.
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 |
8 | DOCUMENTATION = r'''
9 | module: catalog_service_info
10 | short_description: Retrieve information about services from OpenStack
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Retrieve information about services from OpenStack.
14 | options:
15 | name:
16 | description:
17 | - Name or ID of the service.
18 | type: str
19 | extends_documentation_fragment:
20 | - openstack.cloud.openstack
21 | '''
22 |
23 | EXAMPLES = r'''
24 | - name: Fetch all services
25 | openstack.cloud.catalog_service_info:
26 | cloud: devstack
27 |
28 | - name: Fetch a single service
29 | openstack.cloud.catalog_service_info:
30 | cloud: devstack
31 | name: heat
32 | '''
33 |
34 | RETURN = r'''
35 | services:
36 | description: List of dictionaries the services.
37 | returned: always
38 | type: list
39 | elements: dict
40 | contains:
41 | id:
42 | description: Service ID.
43 | type: str
44 | sample: "3292f020780b4d5baf27ff7e1d224c44"
45 | name:
46 | description: Service name.
47 | type: str
48 | sample: "glance"
49 | type:
50 | description: Service type.
51 | type: str
52 | sample: "image"
53 | description:
54 | description: Service description.
55 | type: str
56 | sample: "OpenStack Image Service"
57 | is_enabled:
58 | description: Service status.
59 | type: bool
60 | sample: True
61 | links:
62 | description: Link of the service
63 | type: str
64 | sample: http://10.0.0.1/identity/v3/services/0ae87
65 | '''
66 |
67 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
68 | OpenStackModule
69 | )
70 |
71 |
72 | class CatalogServiceInfoModule(OpenStackModule):
73 | argument_spec = dict(
74 | name=dict(),
75 | )
76 |
77 | module_kwargs = dict(
78 | supports_check_mode=True,
79 | )
80 |
81 | def run(self):
82 | name_or_id = self.params['name']
83 |
84 | if name_or_id:
85 | service = self.conn.identity.find_service(name_or_id)
86 | services = [service] if service else []
87 | else:
88 | services = self.conn.identity.services()
89 |
90 | self.exit_json(changed=False,
91 | services=[s.to_dict(computed=False) for s in services])
92 |
93 |
94 | def main():
95 | module = CatalogServiceInfoModule()
96 | module()
97 |
98 |
99 | if __name__ == "__main__":
100 | main()
101 |
--------------------------------------------------------------------------------
/tests/unit/mock/procenv.py:
--------------------------------------------------------------------------------
1 | # (c) 2016, Matt Davis
2 | # (c) 2016, Toshio Kuratomi
3 | #
4 | # This file is part of Ansible
5 | #
6 | # Ansible is free software: you can redistribute it and/or modify
7 | # it under the terms of the GNU General Public License as published by
8 | # the Free Software Foundation, either version 3 of the License, or
9 | # (at your option) any later version.
10 | #
11 | # Ansible is distributed in the hope that it will be useful,
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | # GNU General Public License for more details.
15 | #
16 | # You should have received a copy of the GNU General Public License
17 | # along with Ansible. If not, see .
18 |
19 | # Make coding more python3-ish
20 |
21 | import sys
22 | import json
23 | import unittest
24 |
25 | from contextlib import contextmanager
26 | from io import BytesIO, StringIO
27 | from ansible.module_utils.six import PY3
28 | from ansible.module_utils._text import to_bytes
29 |
30 |
31 | @contextmanager
32 | def swap_stdin_and_argv(stdin_data='', argv_data=tuple()):
33 | """
34 | context manager that temporarily masks the test runner's values for stdin and argv
35 | """
36 | real_stdin = sys.stdin
37 | real_argv = sys.argv
38 |
39 | if PY3:
40 | fake_stream = StringIO(stdin_data)
41 | fake_stream.buffer = BytesIO(to_bytes(stdin_data))
42 | else:
43 | fake_stream = BytesIO(to_bytes(stdin_data))
44 |
45 | try:
46 | sys.stdin = fake_stream
47 | sys.argv = argv_data
48 |
49 | yield
50 | finally:
51 | sys.stdin = real_stdin
52 | sys.argv = real_argv
53 |
54 |
55 | @contextmanager
56 | def swap_stdout():
57 | """
58 | context manager that temporarily replaces stdout for tests that need to verify output
59 | """
60 | old_stdout = sys.stdout
61 |
62 | if PY3:
63 | fake_stream = StringIO()
64 | else:
65 | fake_stream = BytesIO()
66 |
67 | try:
68 | sys.stdout = fake_stream
69 |
70 | yield fake_stream
71 | finally:
72 | sys.stdout = old_stdout
73 |
74 |
75 | class ModuleTestCase(unittest.TestCase):
76 | def setUp(self, module_args=None):
77 | if module_args is None:
78 | module_args = {'_ansible_remote_tmp': '/tmp', '_ansible_keep_remote_files': False}
79 |
80 | args = json.dumps(dict(ANSIBLE_MODULE_ARGS=module_args))
81 |
82 | # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually
83 | self.stdin_swap = swap_stdin_and_argv(stdin_data=args)
84 | self.stdin_swap.__enter__()
85 |
86 | def tearDown(self):
87 | # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually
88 | self.stdin_swap.__exit__(None, None, None)
89 |
--------------------------------------------------------------------------------
/plugins/modules/group_assignment.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: group_assignment
10 | short_description: Assign OpenStack identity users to groups
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Add and remove OpenStack identity (Keystone) users to/from groups.
14 | options:
15 | group:
16 | description:
17 | - Name or ID for the group.
18 | required: true
19 | type: str
20 | state:
21 | description:
22 | - Should the user be present or absent in the group.
23 | choices: [present, absent]
24 | default: present
25 | type: str
26 | user:
27 | description:
28 | - Name or ID for the user.
29 | required: true
30 | type: str
31 | extends_documentation_fragment:
32 | - openstack.cloud.openstack
33 | '''
34 |
35 | EXAMPLES = r'''
36 | - name: Add demo_user user to demo_group group
37 | openstack.cloud.group_assignment:
38 | cloud: mycloud
39 | user: demo_user
40 | group: demo_group
41 | '''
42 |
43 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
44 |
45 |
46 | class IdentityGroupAssignment(OpenStackModule):
47 | argument_spec = dict(
48 | group=dict(required=True),
49 | state=dict(default='present', choices=['absent', 'present']),
50 | user=dict(required=True),
51 | )
52 |
53 | module_kwargs = dict(
54 | supports_check_mode=True
55 | )
56 |
57 | def run(self):
58 | user_name_or_id = self.params['user']
59 | user = self.conn.identity.find_user(user_name_or_id,
60 | ignore_missing=False)
61 |
62 | group_name_or_id = self.params['group']
63 | group = self.conn.identity.find_group(group_name_or_id,
64 | ignore_missing=False)
65 |
66 | is_user_in_group = \
67 | self.conn.identity.check_user_in_group(user, group)
68 |
69 | state = self.params['state']
70 | if self.ansible.check_mode:
71 | self.exit_json(
72 | changed=(
73 | (state == 'present' and not is_user_in_group)
74 | or (state == 'absent' and is_user_in_group)))
75 |
76 | if state == 'present' and not is_user_in_group:
77 | self.conn.identity.add_user_to_group(user, group)
78 | self.exit_json(changed=True)
79 | elif state == 'absent' and is_user_in_group:
80 | self.conn.identity.remove_user_from_group(user, group)
81 | self.exit_json(changed=True)
82 | else:
83 | self.exit_json(changed=False)
84 |
85 |
86 | def main():
87 | module = IdentityGroupAssignment()
88 | module()
89 |
90 |
91 | if __name__ == '__main__':
92 | main()
93 |
--------------------------------------------------------------------------------
/plugins/modules/identity_role_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2020, Sagi Shnaidman
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: identity_role_info
10 | short_description: Fetch OpenStack identity (Keystone) roles
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Fetch OpenStack identity (Keystone) roles.
14 | options:
15 | domain_id:
16 | description:
17 | - Domain ID which owns the role.
18 | type: str
19 | required: false
20 | name:
21 | description:
22 | - Name or ID of the role.
23 | type: str
24 | required: false
25 | extends_documentation_fragment:
26 | - openstack.cloud.openstack
27 | '''
28 |
29 | RETURN = r'''
30 | roles:
31 | description: List of dictionaries describing matching identity roles.
32 | returned: always
33 | type: list
34 | elements: dict
35 | contains:
36 | description:
37 | description: User-facing description of the role.
38 | type: str
39 | domain_id:
40 | description: References the domain ID which owns the role.
41 | type: str
42 | id:
43 | description: Unique ID for the role
44 | type: str
45 | links:
46 | description: The links for the service resources
47 | type: dict
48 | name:
49 | description: Unique role name, within the owning domain.
50 | type: str
51 | '''
52 |
53 | EXAMPLES = r'''
54 | - name: Retrieve info about all roles
55 | openstack.cloud.identity_role_info:
56 | cloud: mycloud
57 |
58 | - name: Retrieve info about all roles in specific domain
59 | openstack.cloud.identity_role_info:
60 | cloud: mycloud
61 | domain_id: some_domain_id
62 |
63 | - name: Retrieve info about role 'admin'
64 | openstack.cloud.identity_role_info:
65 | cloud: mycloud
66 | name: admin
67 | '''
68 |
69 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
70 |
71 |
72 | class IdentityRoleInfoModule(OpenStackModule):
73 | argument_spec = dict(
74 | domain_id=dict(),
75 | name=dict(),
76 | )
77 |
78 | module_kwargs = dict(
79 | supports_check_mode=True,
80 | )
81 |
82 | def run(self):
83 | kwargs = dict((k, self.params[k])
84 | for k in ['domain_id']
85 | if self.params[k] is not None)
86 |
87 | name_or_id = self.params['name']
88 | if name_or_id is not None:
89 | kwargs['name_or_id'] = name_or_id
90 |
91 | self.exit_json(changed=False,
92 | roles=[r.to_dict(computed=False)
93 | for r in self.conn.search_roles(**kwargs)])
94 |
95 |
96 | def main():
97 | module = IdentityRoleInfoModule()
98 | module()
99 |
100 |
101 | if __name__ == '__main__':
102 | main()
103 |
--------------------------------------------------------------------------------
/ci/roles/volume_type/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create volume type
3 | openstack.cloud.volume_type:
4 | name: "{{ volume_type_name }}"
5 | cloud: "{{ cloud }}"
6 | state: present
7 | extra_specs:
8 | volume_backend_name: "{{ volume_backend_name }}"
9 | description: "{{ volume_type_description }}"
10 | is_public: true
11 | register: the_result
12 | - name: Check created volume type
13 | vars:
14 | the_volume: "{{ the_result.volume_type }}"
15 | ansible.builtin.assert:
16 | that:
17 | - "'id' in the_result.volume_type"
18 | - the_volume.description == volume_type_description
19 | - the_volume.is_public == True
20 | - the_volume.name == volume_type_name
21 | - the_volume.extra_specs['volume_backend_name'] == volume_backend_name
22 | success_msg: >-
23 | Created volume: {{ the_result.volume_type.id }},
24 | Name: {{ the_result.volume_type.name }},
25 | Description: {{ the_result.volume_type.description }}
26 |
27 | - name: Test, check idempotency
28 | openstack.cloud.volume_type:
29 | name: "{{ volume_type_name }}"
30 | cloud: "{{ cloud }}"
31 | state: present
32 | extra_specs:
33 | volume_backend_name: "{{ volume_backend_name }}"
34 | description: "{{ volume_type_description }}"
35 | is_public: true
36 | register: the_result
37 | - name: Check result.changed is false
38 | ansible.builtin.assert:
39 | that:
40 | - the_result.changed == false
41 | success_msg: "Request with the same details lead to no changes"
42 |
43 | - name: Add extra spec
44 | openstack.cloud.volume_type:
45 | cloud: "{{ cloud }}"
46 | name: "{{ volume_type_name }}"
47 | state: present
48 | extra_specs:
49 | volume_backend_name: "{{ volume_backend_name }}"
50 | some_spec: fake_spec
51 | description: "{{ volume_type_description }}"
52 | is_public: true
53 | register: the_result
54 | - name: Check volume type extra spec
55 | ansible.builtin.assert:
56 | that:
57 | - "'some_spec' in the_result.volume_type.extra_specs"
58 | - the_result.volume_type.extra_specs["some_spec"] == "fake_spec"
59 | success_msg: >-
60 | New extra specs: {{ the_result.volume_type.extra_specs }}
61 |
62 | # is_public update attempt using openstacksdk result in unexpected attribute
63 | # error... TODO: Find solution
64 | #
65 | # - name: Make volume type private
66 | # openstack.cloud.volume_type:
67 | # cloud: "{{ cloud }}"
68 | # name: "{{ volume_type_alt_name }}"
69 | # state: present
70 | # extra_specs:
71 | # volume_backend_name: "{{ volume_backend_name }}"
72 | # # some_other_spec: test
73 | # description: Changed 3rd time test volume type
74 | # is_public: true
75 | # register: the_result
76 |
77 | - name: Volume encryption tests
78 | ansible.builtin.include_tasks: volume_encryption.yml
79 |
80 | - name: Delete volume type
81 | openstack.cloud.volume_type:
82 | cloud: "{{ cloud }}"
83 | name: "{{ volume_type_name }}"
84 | state: absent
85 | register: the_result
86 |
--------------------------------------------------------------------------------
/ci/roles/catalog_service/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Delete service test
3 | openstack.cloud.catalog_service:
4 | cloud: "{{ cloud }}"
5 | service_type: test
6 | name: test
7 | state: absent
8 | register: service_delete
9 |
10 | - name: Assert changed is set to false
11 | assert:
12 | that:
13 | - not service_delete.changed
14 |
15 | - name: Create a service for test
16 | openstack.cloud.catalog_service:
17 | cloud: "{{ cloud }}"
18 | name: "test_service"
19 | state: present
20 | service_type: test_type
21 | description: "Test service"
22 | register: service_test
23 |
24 | - name: Verify returned values
25 | assert:
26 | that: item in service_test.service
27 | loop: "{{ expected_fields }}"
28 |
29 | - name: Check if the service test was created successfully
30 | openstack.cloud.catalog_service:
31 | cloud: "{{ cloud }}"
32 | service_type: test
33 | name: test
34 | register: service_created
35 |
36 | - name: Verify returned values
37 | assert:
38 | that: item in service_created.service
39 | loop: "{{ expected_fields }}"
40 |
41 | - name: Update service test
42 | openstack.cloud.catalog_service:
43 | cloud: "{{ cloud }}"
44 | service_type: test
45 | description: "A new description"
46 | is_enabled: False
47 | name: test
48 | register: service_test
49 |
50 | - name: Check if description and is_enabled were updated
51 | assert:
52 | that:
53 | - service_test.service.description == "A new description"
54 | - not (service_test.service.is_enabled|bool)
55 |
56 | - name: Get all services
57 | openstack.cloud.catalog_service_info:
58 | cloud: "{{ cloud }}"
59 | register: services
60 |
61 | - name: Assert return values of catalog_service_info module
62 | assert:
63 | that:
64 | # allow new fields to be introduced but prevent fields from being removed
65 | - expected_fields|difference(services.services[0].keys())|length == 0
66 |
67 | - name: Get service by name
68 | openstack.cloud.catalog_service_info:
69 | cloud: "{{ cloud }}"
70 | name: test
71 | register: services
72 |
73 | - name: Assert services returned by catalog_service_info module
74 | assert:
75 | that:
76 | - services.services|length == 1
77 | - services.services[0].id == service_test.service.id
78 |
79 | - name: Delete service test
80 | openstack.cloud.catalog_service:
81 | cloud: "{{ cloud }}"
82 | service_type: test
83 | name: test
84 | state: absent
85 | register: service_deleted
86 |
87 | - name: Verify if service was deleted
88 | assert:
89 | that:
90 | - service_deleted.changed
91 |
92 | - name: Delete service test again
93 | openstack.cloud.catalog_service:
94 | cloud: "{{ cloud }}"
95 | service_type: test
96 | name: test
97 | state: absent
98 | register: service_deleted
99 |
100 | - name: Assert changed is set to false
101 | assert:
102 | that:
103 | - not service_deleted.changed
104 |
--------------------------------------------------------------------------------
/plugins/modules/volume_service_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2023 Bitswalk, inc.
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: volume_service_info
10 | short_description: Fetch OpenStack Volume (Cinder) services
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Fetch OpenStack Volume (Cinder) services.
14 | options:
15 | binary:
16 | description:
17 | - Filter the service list result by binary name of the service.
18 | type: str
19 | host:
20 | description:
21 | - Filter the service list result by the host name.
22 | type: str
23 | extends_documentation_fragment:
24 | - openstack.cloud.openstack
25 | '''
26 |
27 | EXAMPLES = r'''
28 | - name: Fetch all OpenStack Volume (Cinder) services
29 | openstack.cloud.volume_service_info:
30 | cloud: awesomecloud
31 |
32 | - name: Fetch a subset of OpenStack Volume (Cinder) services
33 | openstack.cloud.volume_service_info:
34 | cloud: awesomecloud
35 | binary: "cinder-volume"
36 | host: "localhost"
37 | '''
38 |
39 | RETURN = r'''
40 | volume_services:
41 | description: List of dictionaries describing Volume (Cinder) services.
42 | returned: always
43 | type: list
44 | elements: dict
45 | contains:
46 | availability_zone:
47 | description: The availability zone name.
48 | type: str
49 | binary:
50 | description: The binary name of the service.
51 | type: str
52 | disabled_reason:
53 | description: The reason why the service is disabled
54 | type: str
55 | host:
56 | description: The name of the host.
57 | type: str
58 | name:
59 | description: Service name
60 | type: str
61 | state:
62 | description: The state of the service. One of up or down.
63 | type: str
64 | status:
65 | description: The status of the service. One of enabled or disabled.
66 | type: str
67 | update_at:
68 | description: The date and time when the resource was updated
69 | type: str
70 | '''
71 |
72 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
73 |
74 |
75 | class VolumeServiceInfoModule(OpenStackModule):
76 |
77 | argument_spec = dict(
78 | binary=dict(),
79 | host=dict(),
80 | )
81 |
82 | module_kwargs = dict(
83 | supports_check_mode=True
84 | )
85 |
86 | def run(self):
87 | kwargs = {k: self.params[k]
88 | for k in ['binary', 'host']
89 | if self.params[k] is not None}
90 | volume_services = self.conn.block_storage.services(**kwargs)
91 |
92 | self.exit_json(changed=False,
93 | volume_services=[s.to_dict(computed=False)
94 | for s in volume_services])
95 |
96 |
97 | def main():
98 | module = VolumeServiceInfoModule()
99 | module()
100 |
101 |
102 | if __name__ == '__main__':
103 | main()
104 |
--------------------------------------------------------------------------------
/ci/roles/volume_type_access/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # TODO: Replace with appropriate Ansible module once available
3 | - name: Get volume types
4 | command: openstack --os-cloud=devstack-admin volume type list -f json
5 | register: volume_types
6 |
7 | # TODO: Replace with appropriate Ansible module once available
8 | - name: Create volume type
9 | command: openstack --os-cloud=devstack-admin volume type create ansible_volume_type --private
10 | when: "'ansible_volume_type' not in (volume_types.stdout | from_json) | map(attribute='Name') | list"
11 |
12 | # TODO: Replace with appropriate Ansible module once available
13 | - name: Get volume types
14 | command: openstack --os-cloud=devstack-admin volume type show ansible_volume_type -f json
15 | register: volume_type
16 |
17 | - name: Fetch demo project
18 | openstack.cloud.project_info:
19 | cloud: devstack-admin
20 | name: demo
21 | register: projects
22 |
23 | - name: Verify demo project
24 | assert:
25 | that:
26 | - projects.projects|length == 1
27 | - projects.projects.0.name == "demo"
28 |
29 | - name: Grant access to volume type
30 | openstack.cloud.volume_type_access:
31 | cloud: devstack-admin
32 | name: ansible_volume_type
33 | project: demo
34 | state: present
35 | register: access
36 |
37 | - name: Verify access
38 | assert:
39 | that:
40 | - access is changed
41 | - access.volume_type.id == (volume_type.stdout | from_json).id
42 |
43 | # TODO: Replace with appropriate Ansible module once available
44 | - name: Get volume types
45 | command: openstack --os-cloud=devstack-admin volume type show ansible_volume_type -f json
46 | register: volume_type
47 |
48 | - name: Verify volume type access
49 | assert:
50 | that:
51 | - (volume_type.stdout | from_json).name == 'ansible_volume_type'
52 | - projects.projects.0.id in (volume_type.stdout | from_json).access_project_ids
53 |
54 | - name: Grant access to volume type again
55 | openstack.cloud.volume_type_access:
56 | cloud: devstack-admin
57 | name: ansible_volume_type
58 | project: demo
59 | state: present
60 | register: access
61 |
62 | - name: Verify access did not change
63 | assert:
64 | that:
65 | - access is not changed
66 |
67 | - name: Revoke access to volume type
68 | openstack.cloud.volume_type_access:
69 | cloud: devstack-admin
70 | name: ansible_volume_type
71 | project: demo
72 | state: absent
73 | register: access
74 |
75 | - name: Verify revoked access
76 | assert:
77 | that:
78 | - access is changed
79 | - access.volume_type.id == (volume_type.stdout | from_json).id
80 |
81 | - name: Revoke access to volume type again
82 | openstack.cloud.volume_type_access:
83 | cloud: devstack-admin
84 | name: ansible_volume_type
85 | project: demo
86 | state: absent
87 | register: access
88 |
89 | - name: Verify access did not change
90 | assert:
91 | that:
92 | - access is not changed
93 |
94 | # TODO: Replace with appropriate Ansible module once available
95 | - name: Delete volume type
96 | command: openstack --os-cloud=devstack-admin volume type delete ansible_volume_type
97 |
--------------------------------------------------------------------------------
/plugins/modules/identity_domain_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2016 Hewlett-Packard Enterprise Corporation
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: identity_domain_info
10 | short_description: Fetch identity (Keystone) domains from OpenStack cloud
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Fetch identity (Keystone) domains from OpenStack cloud
14 | options:
15 | filters:
16 | description:
17 | - A dictionary of meta data to use for filtering.
18 | - Elements of this dictionary may be additional dictionaries.
19 | type: dict
20 | name:
21 | description:
22 | - Name or ID of the domain
23 | type: str
24 | extends_documentation_fragment:
25 | - openstack.cloud.openstack
26 | '''
27 |
28 | EXAMPLES = r'''
29 | - name: Gather information about previously created domain
30 | openstack.cloud.identity_domain_info:
31 | cloud: awesomecloud
32 |
33 | - name: Gather information about a previously created domain by name
34 | openstack.cloud.identity_domain_info:
35 | cloud: awesomecloud
36 | name: demodomain
37 |
38 | - name: Gather information about a previously created domain with filter
39 | openstack.cloud.identity_domain_info:
40 | cloud: awesomecloud
41 | name: demodomain
42 | filters:
43 | is_enabled: false
44 | '''
45 |
46 | RETURN = r'''
47 | domains:
48 | description: List of dictionaries describing OpenStack domains
49 | returned: always
50 | type: list
51 | elements: dict
52 | contains:
53 | description:
54 | description: Description of the domain.
55 | type: str
56 | id:
57 | description: Unique UUID.
58 | type: str
59 | is_enabled:
60 | description: Flag to indicate if the domain is enabled.
61 | type: bool
62 | links:
63 | description: The links related to the domain resource
64 | type: list
65 | name:
66 | description: Name given to the domain.
67 | type: str
68 | '''
69 |
70 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
71 |
72 |
73 | class IdentityDomainInfoModule(OpenStackModule):
74 | argument_spec = dict(
75 | filters=dict(type='dict'),
76 | name=dict(),
77 | )
78 |
79 | module_kwargs = dict(
80 | supports_check_mode=True
81 | )
82 |
83 | def run(self):
84 | kwargs = {}
85 | name = self.params['name']
86 | if name is not None:
87 | kwargs['name_or_id'] = name
88 |
89 | filters = self.params['filters']
90 | if filters is not None:
91 | kwargs['filters'] = filters
92 |
93 | self.exit_json(changed=False,
94 | domains=[d.to_dict(computed=False)
95 | for d in self.conn.search_domains(**kwargs)])
96 |
97 |
98 | def main():
99 | module = IdentityDomainInfoModule()
100 | module()
101 |
102 |
103 | if __name__ == '__main__':
104 | main()
105 |
--------------------------------------------------------------------------------
/plugins/modules/trait.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2025, ScaleUp Technologies GmbH & Co. KG
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = '''
8 | ---
9 | module: trait
10 | short_description: Add/Delete a trait from OpenStack
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Add or Delete a trait from OpenStack
14 | options:
15 | id:
16 | description:
17 | - ID/Name of this trait
18 | required: true
19 | type: str
20 | state:
21 | description:
22 | - Should the resource be present or absent.
23 | choices: [present, absent]
24 | default: present
25 | type: str
26 | extends_documentation_fragment:
27 | - openstack.cloud.openstack
28 | '''
29 |
30 | EXAMPLES = '''
31 | # Creates a trait with the ID CUSTOM_WINDOWS_SPLA
32 | - openstack.cloud.trait:
33 | cloud: openstack
34 | state: present
35 | id: CUSTOM_WINDOWS_SPLA
36 | '''
37 |
38 | RETURN = '''
39 | trait:
40 | description: Dictionary describing the trait.
41 | returned: On success when I(state) is 'present'
42 | type: dict
43 | contains:
44 | id:
45 | description: ID of the trait.
46 | returned: success
47 | type: str
48 | '''
49 |
50 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
51 | OpenStackModule)
52 |
53 |
54 | class TraitModule(OpenStackModule):
55 |
56 | argument_spec = dict(
57 | id=dict(required=True),
58 | state=dict(default='present',
59 | choices=['absent', 'present']),
60 | )
61 |
62 | module_kwargs = dict(
63 | supports_check_mode=True,
64 | )
65 |
66 | def _system_state_change(self, trait):
67 | state = self.params['state']
68 | if state == 'present' and not trait:
69 | return True
70 | if state == 'absent' and trait:
71 | return True
72 | return False
73 |
74 | def run(self):
75 |
76 | state = self.params['state']
77 | id = self.params['id']
78 |
79 | try:
80 | trait = self.conn.placement.get_trait(id)
81 | except self.sdk.exceptions.NotFoundException:
82 | trait = None
83 |
84 | if self.ansible.check_mode:
85 | self.exit_json(changed=self._system_state_change(trait), trait=trait)
86 |
87 | changed = False
88 | if state == 'present':
89 | if not trait:
90 | trait = self.conn.placement.create_trait(id)
91 | changed = True
92 |
93 | self.exit_json(
94 | changed=changed, trait=trait.to_dict(computed=False))
95 |
96 | elif state == 'absent':
97 | if trait:
98 | self.conn.placement.delete_trait(id, ignore_missing=False)
99 | self.exit_json(changed=True)
100 |
101 | self.exit_json(changed=False)
102 |
103 |
104 | def main():
105 | module = TraitModule()
106 | module()
107 |
108 |
109 | if __name__ == '__main__':
110 | main()
111 |
--------------------------------------------------------------------------------
/ci/roles/server_metadata/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: List all images
3 | openstack.cloud.image_info:
4 | cloud: "{{ cloud }}"
5 | register: images
6 |
7 | - name: Identify CirrOS image name
8 | set_fact:
9 | image_name: "{{ images.images|community.general.json_query(query)|first }}"
10 | vars:
11 | query: "[?starts_with(name, 'cirros')].name"
12 |
13 | - name: Create test server
14 | openstack.cloud.server:
15 | cloud: "{{ cloud }}"
16 | state: present
17 | name: ansible_server
18 | image: "{{ image_name }}"
19 | flavor: m1.tiny
20 | network: private
21 | auto_ip: false
22 | wait: true
23 | register: server
24 |
25 | - name: Set server metadata by id
26 | openstack.cloud.server_metadata:
27 | cloud: "{{ cloud }}"
28 | name: "{{ server.server.id }}"
29 | meta:
30 | test_key: test_value
31 | second_key: second_value
32 | register: server_metadata
33 |
34 | - name: Assert updated metadata
35 | assert:
36 | that:
37 | - server_metadata is changed
38 | - "server_metadata.server.metadata == {'test_key': 'test_value', 'second_key':
39 | 'second_value'}"
40 |
41 | - name: Set server metadata by name
42 | openstack.cloud.server_metadata:
43 | cloud: "{{ cloud }}"
44 | name: "{{ server.server.name }}"
45 | meta:
46 | test_key: test_value_2
47 | register: server_metadata
48 |
49 | - name: Assert updated metadata
50 | assert:
51 | that:
52 | - server_metadata is changed
53 | - "server_metadata.server.metadata == {'test_key': 'test_value_2', 'second_key':
54 | 'second_value'}"
55 |
56 | - name: Update metadata again
57 | openstack.cloud.server_metadata:
58 | cloud: "{{ cloud }}"
59 | name: "{{ server.server.id }}"
60 | meta:
61 | test_key: test_value_2
62 | register: server_metadata
63 |
64 | - name: Assert not changed
65 | assert:
66 | that:
67 | - server_metadata is not changed
68 | - "server_metadata.server.metadata == {'test_key': 'test_value_2', 'second_key':
69 | 'second_value'}"
70 |
71 | - name: Delete server metadata
72 | openstack.cloud.server_metadata:
73 | cloud: "{{ cloud }}"
74 | name: "{{ server.server.id }}"
75 | state: absent
76 | meta:
77 | test_key:
78 | register: server_metadata
79 |
80 | - name: Assert updated metadata
81 | assert:
82 | that:
83 | - server_metadata is changed
84 | - "server_metadata.server.metadata == {'second_key': 'second_value'}"
85 |
86 | - name: Delete server metadata again
87 | openstack.cloud.server_metadata:
88 | cloud: "{{ cloud }}"
89 | name: "{{ server.server.id }}"
90 | state: absent
91 | meta:
92 | test_key:
93 | register: server_metadata
94 |
95 | - name: Assert not changed
96 | assert:
97 | that:
98 | - server_metadata is not changed
99 | - "server_metadata.server.metadata == {'second_key': 'second_value'}"
100 |
101 | - name: Delete test server
102 | openstack.cloud.server:
103 | cloud: "{{ cloud }}"
104 | name: "{{ server.server.id }}"
105 | state: absent
106 | wait: true
107 | register: server
108 |
--------------------------------------------------------------------------------
/ci/roles/router/tasks/shared_ext_network.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Test the case where we have a shared external network in one project used as
3 | # the gateway on a router in a second project.
4 | # See https://bugs.launchpad.net/ansible-collections-openstack/+bug/2049658
5 |
6 | - name: Create the first project
7 | openstack.cloud.project:
8 | cloud: "{{ cloud }}"
9 | state: present
10 | name: "shared_ext_net_test_1"
11 | description: "Project that contains the external network to be shared"
12 | domain: default
13 | is_enabled: True
14 | register: project_1
15 |
16 | - name: Create the external network to be shared
17 | openstack.cloud.network:
18 | cloud: "{{ cloud }}"
19 | state: present
20 | name: "{{ external_network_name }}"
21 | project: "shared_ext_net_test_1"
22 | external: true
23 | shared: true
24 | register: shared_ext_network
25 |
26 | - name: Create subnet on external network
27 | openstack.cloud.subnet:
28 | cloud: "{{ cloud }}"
29 | state: present
30 | network_name: "{{ shared_ext_network.id }}"
31 | name: "shared_ext_subnet"
32 | project: "shared_ext_net_test_1"
33 | cidr: "10.6.6.0/24"
34 | register: shared_subnet
35 |
36 | - name: Create the second project
37 | openstack.cloud.project:
38 | cloud: "{{ cloud }}"
39 | state: present
40 | name: "shared_ext_net_test_2"
41 | description: "Project that contains the subnet to be shared"
42 | domain: default
43 | is_enabled: True
44 | register: project_2
45 |
46 | - name: Create router with gateway on shared external network
47 | openstack.cloud.router:
48 | cloud: "{{ cloud }}"
49 | state: present
50 | name: "shared_ext_net_test2_router"
51 | project: "shared_ext_net_test_2"
52 | network: "{{ external_network_name }}"
53 | register: router
54 |
55 | - name: Gather routers info
56 | openstack.cloud.routers_info:
57 | cloud: "{{ cloud }}"
58 | name: "shared_ext_net_test2_router"
59 | register: routers
60 |
61 | - name: Verify routers info
62 | assert:
63 | that:
64 | - routers.routers.0.id == router.router.id
65 | - routers.routers.0.external_gateway_info.external_fixed_ips|length == 1
66 |
67 | - name: Delete router
68 | openstack.cloud.router:
69 | cloud: "{{ cloud }}"
70 | state: absent
71 | name: "shared_ext_net_test2_router"
72 | project: "shared_ext_net_test_2"
73 |
74 | - name: Delete subnet
75 | openstack.cloud.subnet:
76 | cloud: "{{ cloud }}"
77 | state: absent
78 | network_name: "{{ shared_ext_network.id }}"
79 | name: "shared_ext_subnet"
80 | project: "shared_ext_net_test_1"
81 |
82 | - name: Delete network
83 | openstack.cloud.network:
84 | cloud: "{{ cloud }}"
85 | state: absent
86 | name: "{{ external_network_name }}"
87 | project: "shared_ext_net_test_1"
88 |
89 | - name: Delete project 2
90 | openstack.cloud.project:
91 | cloud: "{{ cloud }}"
92 | state: absent
93 | name: "shared_ext_net_test_2"
94 |
95 | - name: Delete project 1
96 | openstack.cloud.project:
97 | cloud: "{{ cloud }}"
98 | state: absent
99 | name: "shared_ext_net_test_1"
100 |
--------------------------------------------------------------------------------
/ci/run-collection.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: localhost
3 | connection: local
4 | gather_facts: true
5 |
6 | roles:
7 | - { role: address_scope, tags: address_scope }
8 | - { role: application_credential, tags: application_credential }
9 | - { role: auth, tags: auth }
10 | - { role: catalog_service, tags: catalog_service }
11 | - { role: coe_cluster, tags: coe_cluster }
12 | - { role: coe_cluster_template, tags: coe_cluster_template }
13 | - { role: compute_flavor, tags: compute_flavor }
14 | - { role: compute_flavor_access, tags: compute_flavor_access }
15 | - { role: compute_service, tags: compute_service }
16 | - { role: config, tags: config }
17 | - { role: dns_zone, tags: dns_zone }
18 | - { role: endpoint, tags: endpoint }
19 | - { role: federation_mapping, tags: federation_mapping }
20 | - { role: floating_ip, tags: floating_ip }
21 | - { role: group_assignment, tags: group_assignment }
22 | - { role: host_aggregate, tags: host_aggregate }
23 | - { role: identity_domain, tags: identity_domain }
24 | - { role: identity_group, tags: identity_group }
25 | - { role: identity_role, tags: identity_role }
26 | - { role: identity_user, tags: identity_user }
27 | - { role: image, tags: image }
28 | - { role: inventory, tags: inventory }
29 | - { role: keypair, tags: keypair }
30 | - { role: keystone_federation_protocol, tags: keystone_federation_protocol }
31 | - { role: keystone_idp, tags: keystone_idp }
32 | - { role: loadbalancer, tags: loadbalancer }
33 | - { role: logging, tags: logging }
34 | - { role: network, tags: network }
35 | - { role: neutron_rbac_policy, tags: neutron_rbac_policy }
36 | - { role: object, tags: object }
37 | - { role: object_container, tags: object_container }
38 | - { role: object_containers_info, tags: object_containers_info }
39 | - { role: port, tags: port }
40 | - { role: trait, tags: trait }
41 | - { role: trunk, tags: trunk }
42 | - { role: project, tags: project }
43 | - { role: quota, tags: quota }
44 | - { role: recordset, tags: recordset }
45 | - { role: resource, tags: resource }
46 | - { role: resources, tags: resources }
47 | - { role: role_assignment, tags: role_assignment }
48 | - { role: router, tags: router }
49 | - { role: security_group, tags: security_group }
50 | - { role: security_group_rule, tags: security_group_rule }
51 | - { role: server, tags: server }
52 | - { role: server_action, tags: server_action }
53 | - { role: server_group, tags: server_group }
54 | - { role: server_metadata, tags: server_metadata }
55 | - { role: server_volume, tags: server_volume }
56 | - { role: share_type, tags: share_type }
57 | - { role: stack, tags: stack }
58 | - { role: subnet, tags: subnet }
59 | - { role: subnet_pool, tags: subnet_pool }
60 | - { role: volume, tags: volume }
61 | - { role: volume_type, tags: volume_type }
62 | - { role: volume_backup, tags: volume_backup }
63 | - { role: volume_manage, tags: volume_manage }
64 | - { role: volume_service, tags: volume_service }
65 | - { role: volume_snapshot, tags: volume_snapshot }
66 | - { role: volume_type_access, tags: volume_type_access }
67 |
--------------------------------------------------------------------------------
/ci/publish/publish_collection.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars:
4 | collection_path: "{{ ansible_user_dir }}/{{ zuul.project.src_dir }}"
5 | build_collection_path: /tmp/collection_built/
6 | ansible_virtualenv_path: /tmp/ansible_venv
7 | ansible_galaxy_path: "{{ ansible_virtualenv_path }}/bin/ansible-galaxy"
8 |
9 | tasks:
10 |
11 | - name: Include role for pip
12 | include_role:
13 | name: ensure-pip
14 |
15 | - name: Install Ansible in virtualenv
16 | pip:
17 | name: ansible-core<2.19
18 | virtualenv: "{{ ansible_virtualenv_path }}"
19 | virtualenv_command: "{{ ensure_pip_virtualenv_command }}"
20 |
21 | - name: Detect ansible version
22 | command: "{{ ansible_virtualenv_path }}/bin/ansible --version"
23 | register: ansible_version
24 |
25 | - name: Discover tag version
26 | set_fact:
27 | version_tag: "{{ zuul.tag|default('no_version', true) }}"
28 |
29 | - name: Fail if no tag version found
30 | fail:
31 | msg: "No tag was found in Zuul vars!"
32 | when: version_tag == 'no_version'
33 |
34 | - name: Create a directory for collection
35 | file:
36 | state: "{{ item }}"
37 | path: "{{ build_collection_path }}"
38 | loop:
39 | - absent
40 | - directory
41 |
42 | - name: Set galaxy.yml for right version from tag
43 | lineinfile:
44 | path: '{{ collection_path }}/galaxy.yml'
45 | regexp: '^version:.*'
46 | line: 'version: {{ version_tag }}'
47 |
48 | - name: Build collection
49 | command: "{{ ansible_galaxy_path }} collection build --output-path {{ build_collection_path }} --force"
50 | args:
51 | chdir: "{{ collection_path }}"
52 |
53 | - name: Publish content to Ansible Galaxy
54 | block:
55 | - name: Create ansible.cfg configuration file tempfile
56 | tempfile:
57 | state: file
58 | suffix: .cfg
59 | register: _ansiblecfg_tmp
60 |
61 | - name: Create ansible.cfg configuration file
62 | copy:
63 | dest: "{{ _ansiblecfg_tmp.path }}"
64 | mode: 0600
65 | content: |
66 | [galaxy]
67 | server_list = release_galaxy
68 |
69 | [galaxy_server.release_galaxy]
70 | url = {{ ansible_galaxy_info.url }}
71 | token = {{ ansible_galaxy_info.token }}
72 |
73 | - name: Get content of galaxy.yml
74 | slurp:
75 | src: "{{ collection_path }}/galaxy.yml"
76 | register: galaxy_vars
77 |
78 | - name: Parse yaml into variable
79 | set_fact:
80 | galaxy_yaml: "{{ galaxy_vars['content'] | b64decode | from_yaml }}"
81 |
82 | - name: Publish collection to Ansible Galaxy / Automation Hub
83 | environment:
84 | ANSIBLE_CONFIG: "{{ _ansiblecfg_tmp.path }}"
85 | shell: >-
86 | {{ ansible_galaxy_path }} collection publish -vvv
87 | {{ build_collection_path }}/{{ galaxy_yaml.namespace }}-{{ galaxy_yaml.name }}-{{ version_tag }}.tar.gz
88 |
89 | always:
90 | - name: Shred ansible-galaxy credentials
91 | command: "shred {{ _ansiblecfg_tmp.path }}"
92 |
--------------------------------------------------------------------------------
/ci/roles/volume/tasks/volume_info.yml:
--------------------------------------------------------------------------------
1 | - name: Create volume
2 | openstack.cloud.volume:
3 | cloud: "{{ cloud }}"
4 | state: present
5 | size: 1
6 | name: ansible_test
7 | description: testci
8 | register: vol
9 |
10 | - name: Get info about volumes
11 | openstack.cloud.volume_info:
12 | cloud: "{{ cloud }}"
13 | details: true
14 | all_projects: true
15 | register: info
16 |
17 | - name: Check info
18 | assert:
19 | that:
20 | - info.volumes | selectattr("description", "equalto", "testci") | list | length == 1
21 | - info.volumes.0.name == 'ansible_test'
22 | - info.volumes.0.status == 'available'
23 |
24 | - name: Assert return values of volume_info module
25 | assert:
26 | that:
27 | # allow new fields to be introduced but prevent fields from being removed
28 | - expected_fields|difference(info.volumes[0].keys())|length == 0
29 |
30 | - name: Get not detailed info about volumes
31 | openstack.cloud.volume_info:
32 | cloud: "{{ cloud }}"
33 | details: false
34 | all_projects: true
35 | register: info1
36 |
37 | - name: Check info
38 | assert:
39 | that:
40 | - info1.volumes | selectattr("id", "equalto", info.volumes.0.id) | list | length == 1
41 | - info1.volumes.0.name == 'ansible_test'
42 | - info1.volumes.0.status == None
43 |
44 | - name: Get info about volumes with name
45 | openstack.cloud.volume_info:
46 | cloud: "{{ cloud }}"
47 | details: false
48 | name: ansible_test
49 | all_projects: true
50 | register: info2
51 |
52 | - name: Check info
53 | assert:
54 | that:
55 | - info2.volumes | length == 1
56 | - info2.volumes.0.name == 'ansible_test'
57 |
58 | - name: Get info about volumes with non-existent name
59 | openstack.cloud.volume_info:
60 | cloud: "{{ cloud }}"
61 | details: false
62 | name: nothing_here
63 | all_projects: true
64 | register: info3
65 |
66 | - name: Check info
67 | assert:
68 | that:
69 | - info3.volumes | length == 0
70 |
71 | - name: Get info about volumes
72 | openstack.cloud.volume_info:
73 | cloud: "{{ cloud }}"
74 | details: false
75 | name: ansible_test
76 | all_projects: true
77 | register: info4
78 |
79 | - name: Check info
80 | assert:
81 | that:
82 | - info4.volumes | length == 1
83 | - info4.volumes.0.name == 'ansible_test'
84 |
85 | - name: Get info about volumes not from all projects
86 | openstack.cloud.volume_info:
87 | cloud: "{{ cloud }}"
88 | details: false
89 | name: ansible_test
90 | register: info4a
91 |
92 | - name: Check info
93 | assert:
94 | that:
95 | - info4a.volumes | length == 1
96 | - info4a.volumes.0.name == 'ansible_test'
97 |
98 | - name: Delete volume
99 | openstack.cloud.volume:
100 | cloud: "{{ cloud }}"
101 | state: absent
102 | name: ansible_test
103 |
104 | - name: Get info when no volumes
105 | openstack.cloud.volume_info:
106 | cloud: "{{ cloud }}"
107 | all_projects: true
108 | register: info5
109 |
110 | - name: Check info
111 | assert:
112 | that:
113 | - info5.volumes | selectattr("name", "equalto", "ansible_test") | list | length == 0
114 |
--------------------------------------------------------------------------------
/plugins/modules/keystone_federation_protocol_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright: Ansible Project
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: keystone_federation_protocol_info
10 | short_description: Fetch Keystone federation protocols
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Fetch Keystone federation protocols.
14 | options:
15 | name:
16 | description:
17 | - ID or name of the federation protocol.
18 | type: str
19 | aliases: ['id']
20 | idp:
21 | description:
22 | - ID or name of the identity provider this protocol is associated with.
23 | aliases: ['idp_id', 'idp_name']
24 | required: true
25 | type: str
26 | notes:
27 | - Name equals the ID of a federation protocol.
28 | - Name equals the ID of an identity provider.
29 | extends_documentation_fragment:
30 | - openstack.cloud.openstack
31 | '''
32 |
33 | EXAMPLES = r'''
34 | - name: Fetch all federation protocols attached to an identity provider
35 | openstack.cloud.keystone_federation_protocol_info:
36 | cloud: example_cloud
37 | idp: example_idp
38 |
39 | - name: Fetch federation protocol by name
40 | openstack.cloud.keystone_federation_protocol_info:
41 | cloud: example_cloud
42 | idp: example_idp
43 | name: example_protocol
44 | '''
45 |
46 | RETURN = r'''
47 | protocols:
48 | description: List of federation protocol dictionaries.
49 | returned: always
50 | type: list
51 | elements: dict
52 | contains:
53 | id:
54 | description: ID of the federation protocol.
55 | returned: success
56 | type: str
57 | mapping_id:
58 | description: The definition of the federation protocol.
59 | returned: success
60 | type: str
61 | name:
62 | description: Name of the protocol. Equal to C(id).
63 | returned: success
64 | type: str
65 | '''
66 |
67 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
68 |
69 |
70 | class IdentityFederationProtocolInfoModule(OpenStackModule):
71 | argument_spec = dict(
72 | name=dict(aliases=['id']),
73 | idp=dict(required=True, aliases=['idp_id', 'idp_name']),
74 | )
75 |
76 | module_kwargs = dict(
77 | supports_check_mode=True
78 | )
79 |
80 | def run(self):
81 | # name is id for federation protocols
82 | id = self.params['name']
83 |
84 | # name is id for identity providers
85 | idp_id = self.params['idp']
86 |
87 | if id:
88 | protocol = self.conn.identity.find_federation_protocol(idp_id, id)
89 | protocols = [protocol] if protocol else []
90 | else:
91 | protocols = self.conn.identity.federation_protocols(idp_id)
92 |
93 | self.exit_json(changed=False,
94 | protocols=[p.to_dict(computed=False)
95 | for p in protocols])
96 |
97 |
98 | def main():
99 | module = IdentityFederationProtocolInfoModule()
100 | module()
101 |
102 |
103 | if __name__ == '__main__':
104 | main()
105 |
--------------------------------------------------------------------------------
/plugins/modules/compute_service_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2016 Hewlett-Packard Enterprise Corporation
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: compute_service_info
10 | short_description: Fetch OpenStack Compute (Nova) services
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Fetch OpenStack Compute (Nova) services.
14 | options:
15 | binary:
16 | description:
17 | - Filter the service list result by binary name of the service.
18 | type: str
19 | host:
20 | description:
21 | - Filter the service list result by the host name.
22 | type: str
23 | extends_documentation_fragment:
24 | - openstack.cloud.openstack
25 | '''
26 |
27 | EXAMPLES = r'''
28 | - name: Fetch all OpenStack Compute (Nova) services
29 | openstack.cloud.compute_service_info:
30 | cloud: awesomecloud
31 |
32 | - name: Fetch a subset of OpenStack Compute (Nova) services
33 | openstack.cloud.compute_service_info:
34 | cloud: awesomecloud
35 | binary: "nova-compute"
36 | host: "localhost"
37 | '''
38 |
39 | RETURN = r'''
40 | compute_services:
41 | description: List of dictionaries describing Compute (Nova) services.
42 | returned: always
43 | type: list
44 | elements: dict
45 | contains:
46 | availability_zone:
47 | description: The availability zone name.
48 | type: str
49 | binary:
50 | description: The binary name of the service.
51 | type: str
52 | disabled_reason:
53 | description: The reason why the service is disabled
54 | type: str
55 | id:
56 | description: Unique UUID.
57 | type: str
58 | is_forced_down:
59 | description: If the service has been forced down or nova-compute
60 | type: bool
61 | host:
62 | description: The name of the host.
63 | type: str
64 | name:
65 | description: Service name
66 | type: str
67 | state:
68 | description: The state of the service. One of up or down.
69 | type: str
70 | status:
71 | description: The status of the service. One of enabled or disabled.
72 | type: str
73 | update_at:
74 | description: The date and time when the resource was updated
75 | type: str
76 | '''
77 |
78 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
79 |
80 |
81 | class ComputeServiceInfoModule(OpenStackModule):
82 | argument_spec = dict(
83 | binary=dict(),
84 | host=dict(),
85 | )
86 |
87 | module_kwargs = dict(
88 | supports_check_mode=True
89 | )
90 |
91 | def run(self):
92 | kwargs = {k: self.params[k]
93 | for k in ['binary', 'host']
94 | if self.params[k] is not None}
95 | compute_services = self.conn.compute.services(**kwargs)
96 |
97 | self.exit_json(changed=False,
98 | compute_services=[s.to_dict(computed=False)
99 | for s in compute_services])
100 |
101 |
102 | def main():
103 | module = ComputeServiceInfoModule()
104 | module()
105 |
106 |
107 | if __name__ == '__main__':
108 | main()
109 |
--------------------------------------------------------------------------------
/plugins/module_utils/ironic.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | # This code is part of Ansible, but is an independent component.
5 | # This particular file snippet, and this file snippet only, is BSD licensed.
6 | # Modules you write using this snippet, which is embedded dynamically by Ansible
7 | # still belong to the author of the module, and may assign their own license
8 | # to the complete work.
9 | #
10 | # Redistribution and use in source and binary forms, with or without modification,
11 | # are permitted provided that the following conditions are met:
12 | #
13 | # * Redistributions of source code must retain the above copyright
14 | # notice, this list of conditions and the following disclaimer.
15 | # * Redistributions in binary form must reproduce the above copyright notice,
16 | # this list of conditions and the following disclaimer in the documentation
17 | # and/or other materials provided with the distribution.
18 | #
19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27 | # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 |
30 | from ansible.module_utils.basic import AnsibleModule
31 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
32 |
33 |
34 | def ironic_argument_spec(**kwargs):
35 | spec = dict(
36 | auth_type=dict(),
37 | ironic_url=dict(),
38 | )
39 | spec.update(kwargs)
40 | return openstack_full_argument_spec(**spec)
41 |
42 |
43 | # TODO(dtantsur): inherit the collection's base module
44 | class IronicModule(AnsibleModule):
45 |
46 | def __init__(self, *args, **kwargs):
47 | super().__init__(*args, **kwargs)
48 | self._update_ironic_auth()
49 |
50 | def _update_ironic_auth(self):
51 | """Validate and update authentication parameters for ironic."""
52 | if (
53 | self.params['auth_type'] in [None, 'None', 'none']
54 | and self.params['ironic_url'] is None
55 | and not self.params['cloud']
56 | and not (self.params['auth']
57 | and self.params['auth'].get('endpoint'))
58 | ):
59 | self.fail_json(msg=("Authentication appears to be disabled, "
60 | "Please define either ironic_url, or cloud, "
61 | "or auth.endpoint"))
62 |
63 | if (
64 | self.params['ironic_url']
65 | and self.params['auth_type'] in [None, 'None', 'none']
66 | and not (self.params['auth']
67 | and self.params['auth'].get('endpoint'))
68 | ):
69 | self.params['auth'] = dict(
70 | endpoint=self.params['ironic_url']
71 | )
72 |
--------------------------------------------------------------------------------
/ci/roles/security_group/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create security group
3 | openstack.cloud.security_group:
4 | cloud: "{{ cloud }}"
5 | name: ansible_security_group
6 | state: present
7 | description: 'Created from Ansible playbook'
8 | register: security_group
9 |
10 | - name: Assert return values of security_group module
11 | assert:
12 | that:
13 | - security_group.security_group.name == 'ansible_security_group'
14 | - security_group.security_group.description == 'Created from Ansible playbook'
15 | # allow new fields to be introduced but prevent fields from being removed
16 | - expected_fields|difference(security_group.security_group.keys())|length == 0
17 |
18 | - name: List all security groups
19 | openstack.cloud.security_group_info:
20 | cloud: "{{ cloud }}"
21 | register: security_groups
22 |
23 | - name: Assert return values of security_group_info module
24 | assert:
25 | that:
26 | - security_groups.security_groups | length > 0
27 | # allow new fields to be introduced but prevent fields from being removed
28 | - expected_fields|difference(security_groups.security_groups[0].keys())|length == 0
29 |
30 | - name: Find security group by name
31 | openstack.cloud.security_group_info:
32 | cloud: "{{ cloud }}"
33 | name: ansible_security_group
34 | register: security_groups
35 |
36 | - name: Check filter security group by name
37 | assert:
38 | that:
39 | - security_groups.security_groups | length == 1
40 | - security_groups.security_groups.0.id == security_group.security_group.id
41 |
42 | - name: Filter security group by description
43 | openstack.cloud.security_group_info:
44 | cloud: "{{ cloud }}"
45 | description: 'Created from Ansible playbook'
46 | register: security_groups
47 |
48 | - name: Check filter security group by description
49 | assert:
50 | that:
51 | - security_groups.security_groups | length == 1
52 | - security_groups.security_groups.0.id == security_group.security_group.id
53 |
54 | - name: Filter security group by not_tags
55 | openstack.cloud.security_group_info:
56 | cloud: "{{ cloud }}"
57 | name: ansible_security_group
58 | not_tags:
59 | - ansibletag1
60 | - ansibletag2
61 | register: security_groups
62 |
63 | - name: Check filter security group by not_tags
64 | assert:
65 | that:
66 | - security_groups.security_groups | length == 1
67 | - security_groups.security_groups.0.id == security_group.security_group.id
68 |
69 | - name: Delete security group
70 | openstack.cloud.security_group:
71 | cloud: "{{ cloud }}"
72 | name: ansible_security_group
73 | state: absent
74 |
75 | - name: Create stateless security group
76 | openstack.cloud.security_group:
77 | cloud: "{{ cloud }}"
78 | name: ansible_security_group_stateless
79 | stateful: false
80 | state: present
81 | description: 'Created from Ansible playbook'
82 | register: security_group_stateless
83 |
84 | - name: Assert return values of security_group module
85 | assert:
86 | that:
87 | - security_group_stateless.security_group.name == 'ansible_security_group_stateless'
88 | - security_group_stateless.security_group.stateful == False
89 |
90 | - name: Delete stateless security group
91 | openstack.cloud.security_group:
92 | cloud: "{{ cloud }}"
93 | name: ansible_security_group_stateless
94 | state: absent
95 |
96 | - include_tasks: rules.yml
97 |
--------------------------------------------------------------------------------
/plugins/modules/identity_group_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2019, Phillipe Smith
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: identity_group_info
10 | short_description: Fetch OpenStack identity (Keystone) groups
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Fetch OpenStack identity (Keystone) groups.
14 | options:
15 | domain:
16 | description:
17 | - Name or ID of the domain containing the group.
18 | type: str
19 | filters:
20 | description:
21 | - A dictionary of meta data to use for further filtering. Elements of
22 | this dictionary may be additional dictionaries.
23 | type: dict
24 | name:
25 | description:
26 | - Name or ID of the group.
27 | type: str
28 | extends_documentation_fragment:
29 | - openstack.cloud.openstack
30 | '''
31 |
32 | EXAMPLES = r'''
33 | - name: Gather previously created groups
34 | openstack.cloud.identity_group_info:
35 | cloud: awesomecloud
36 |
37 | - name: Gather previously created groups by name
38 | openstack.cloud.identity_group_info:
39 | cloud: awesomecloud
40 | name: demogroup
41 |
42 | - name: Gather previously created groups in a specific domain
43 | openstack.cloud.identity_group_info:
44 | cloud: awesomecloud
45 | domain: admindomain
46 |
47 | - name: Gather and filter previously created groups
48 | openstack.cloud.identity_group_info:
49 | cloud: awesomecloud
50 | name: demogroup
51 | domain: admindomain
52 | filters:
53 | is_enabled: False
54 | '''
55 |
56 | RETURN = r'''
57 | groups:
58 | description: Dictionary describing all matching identity groups.
59 | returned: always
60 | type: list
61 | elements: dict
62 | contains:
63 | name:
64 | description: Name given to the group.
65 | type: str
66 | description:
67 | description: Description of the group.
68 | type: str
69 | id:
70 | description: Unique UUID.
71 | type: str
72 | domain_id:
73 | description: Domain ID containing the group (keystone v3 clouds only)
74 | type: bool
75 | '''
76 |
77 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
78 |
79 |
80 | class IdentityGroupInfoModule(OpenStackModule):
81 | argument_spec = dict(
82 | domain=dict(),
83 | filters=dict(type='dict'),
84 | name=dict(),
85 | )
86 | module_kwargs = dict(
87 | supports_check_mode=True
88 | )
89 |
90 | def run(self):
91 | name = self.params['name']
92 | filters = self.params['filters'] or {}
93 |
94 | kwargs = {}
95 | domain_name_or_id = self.params['domain']
96 | if domain_name_or_id:
97 | domain = self.conn.identity.find_domain(domain_name_or_id)
98 | if domain is None:
99 | self.exit_json(changed=False, groups=[])
100 | kwargs['domain_id'] = domain['id']
101 |
102 | groups = self.conn.search_groups(name, filters, **kwargs)
103 | self.exit_json(changed=False,
104 | groups=[g.to_dict(computed=False) for g in groups])
105 |
106 |
107 | def main():
108 | module = IdentityGroupInfoModule()
109 | module()
110 |
111 |
112 | if __name__ == '__main__':
113 | main()
114 |
--------------------------------------------------------------------------------
/ci/roles/baremetal_port/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # TODO: Actually run this role in CI. Atm we do not have DevStack's ironic plugin enabled.
3 | - name: Create baremetal node
4 | openstack.cloud.baremetal_node:
5 | cloud: "{{ cloud }}"
6 | driver_info:
7 | ipmi_address: "1.2.3.4"
8 | ipmi_username: "admin"
9 | ipmi_password: "secret"
10 | name: ansible_baremetal_node
11 | nics:
12 | - mac: "aa:bb:cc:aa:bb:cc"
13 | state: present
14 | register: node
15 |
16 | - name: Create baremetal port
17 | openstack.cloud.baremetal_port:
18 | cloud: "{{ cloud }}"
19 | state: present
20 | node: ansible_baremetal_node
21 | address: fa:16:3e:aa:aa:aa
22 | is_pxe_enabled: False
23 | register: port
24 |
25 | - debug: var=port
26 |
27 | - name: Assert return values of baremetal_port module
28 | assert:
29 | that:
30 | - not port.port.is_pxe_enabled
31 | # allow new fields to be introduced but prevent fields from being removed
32 | - expected_fields|difference(port.port.keys())|length == 0
33 |
34 | - name: Fetch baremetal ports
35 | openstack.cloud.baremetal_port_info:
36 | cloud: "{{ cloud }}"
37 | register: ports
38 |
39 | - name: Assert module results of baremetal_port_info module
40 | assert:
41 | that:
42 | - ports.ports|list|length > 0
43 |
44 | - name: assert return values of baremetal_port_info module
45 | assert:
46 | that:
47 | # allow new fields to be introduced but prevent fields from being removed
48 | - expected_fields|difference(ports.ports.0.keys())|length == 0
49 |
50 | - name: Fetch baremetal port by id
51 | openstack.cloud.baremetal_port_info:
52 | cloud: "{{ cloud }}"
53 | id: "{{ port.port.id }}"
54 | register: ports
55 |
56 | - name: assert module results of baremetal_port_info module
57 | assert:
58 | that:
59 | - ports.ports|list|length == 1
60 | - ports.ports.0.id == port.port.id
61 |
62 | - name: Update baremetal port
63 | openstack.cloud.baremetal_port:
64 | cloud: "{{ cloud }}"
65 | state: present
66 | id: "{{ port.port.id }}"
67 | is_pxe_enabled: True
68 | register: updated_port
69 |
70 | - name: Assert return values of updated baremetal port
71 | assert:
72 | that:
73 | - update_port is changed
74 | - update_port.port.id == port.port.id
75 | - update_port.port.address == port.port.address
76 | - update_port.port.is_pxe_enabled
77 |
78 | - name: Update baremetal port again
79 | openstack.cloud.baremetal_port:
80 | cloud: "{{ cloud }}"
81 | state: present
82 | id: "{{ port.port.id }}"
83 | is_pxe_enabled: True
84 | register: updated_port
85 |
86 | - name: Assert return values of updated baremetal port
87 | assert:
88 | that:
89 | - update_port is not changed
90 | - update_port.port.id == port.port.id
91 |
92 | - name: Delete Bare Metal port
93 | openstack.cloud.baremetal_port:
94 | cloud: "{{ cloud }}"
95 | state: absent
96 | id: "{{ port.port.id }}"
97 |
98 | - name: Fetch baremetal ports
99 | openstack.cloud.baremetal_port_info:
100 | cloud: "{{ cloud }}"
101 | register: ports
102 |
103 | - name: Assert no baremetal port is left
104 | assert:
105 | that:
106 | - ports.ports|list|length == 0
107 |
108 | - name: Delete baremetal node
109 | openstack.cloud.baremetal_node:
110 | cloud: "{{ cloud }}"
111 | name: ansible_baremetal_node
112 | state: absent
113 |
--------------------------------------------------------------------------------
/ci/roles/object_container/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create an empty container with public access
3 | openstack.cloud.object_container:
4 | cloud: "{{ cloud }}"
5 | name: ansible_container
6 | read_ACL: ".r:*,.rlistings"
7 | register: container
8 |
9 | - name: Assert return values of container module
10 | assert:
11 | that:
12 | - container is changed
13 | - container.container.name == "ansible_container"
14 | - container.container.read_ACL == ".r:*,.rlistings"
15 | # allow new fields to be introduced but prevent fields from being removed
16 | - expected_fields|difference(container.container.keys())|length == 0
17 |
18 | - name: Set container metadata aka container properties
19 | openstack.cloud.object_container:
20 | cloud: "{{ cloud }}"
21 | name: ansible_container
22 | metadata:
23 | 'Cache-Control': 'no-cache'
24 | 'foo': 'bar'
25 | register: container
26 |
27 | - name: Verify container metadata was set
28 | assert:
29 | that:
30 | - container is changed
31 | - ('cache-control' in container.container.metadata.keys()|map('lower'))
32 | - container.container.metadata['foo'] == 'bar'
33 |
34 | - name: Update container metadata
35 | openstack.cloud.object_container:
36 | cloud: "{{ cloud }}"
37 | name: ansible_container
38 | metadata:
39 | 'foo': 'baz'
40 | register: container
41 |
42 | - name: Verify container metadata was updated
43 | assert:
44 | that:
45 | - container is changed
46 | - ('cache-control' in container.container.metadata.keys()|map('lower'))
47 | - container.container.metadata['foo'] == 'baz'
48 |
49 | - name: Update a container
50 | openstack.cloud.object_container:
51 | cloud: "{{ cloud }}"
52 | name: ansible_container
53 | delete_metadata_keys:
54 | - 'Cache-Control'
55 | read_ACL: ""
56 | register: container
57 |
58 | - name: Verify updated container
59 | assert:
60 | that:
61 | - container is changed
62 | - ('cache-control' not in container.container.metadata.keys()|map('lower'))
63 | - "container.container.metadata == {'foo': 'baz'}"
64 | - container.container.read_ACL is none or container.container.read_ACL == ""
65 |
66 | - name: Delete container
67 | openstack.cloud.object_container:
68 | cloud: "{{ cloud }}"
69 | name: ansible_container
70 | state: absent
71 | register: container
72 |
73 | - name: Verify container was deleted
74 | assert:
75 | that:
76 | - container is changed
77 |
78 | - name: Delete container again
79 | openstack.cloud.object_container:
80 | cloud: "{{ cloud }}"
81 | name: ansible_container
82 | state: absent
83 | register: container
84 |
85 | - name: Verify container was not deleted again
86 | assert:
87 | that:
88 | - container is not changed
89 |
90 | - name: Create another container for recursive deletion
91 | openstack.cloud.object_container:
92 | cloud: "{{ cloud }}"
93 | name: ansible_container2
94 |
95 | - name: Load an object into container
96 | openstack.cloud.object:
97 | cloud: "{{ cloud }}"
98 | state: present
99 | name: ansible_object
100 | data: "this is another test"
101 | container: ansible_container2
102 |
103 | - name: Delete container recursively
104 | openstack.cloud.object_container:
105 | cloud: "{{ cloud }}"
106 | state: absent
107 | name: ansible_container2
108 | delete_with_all_objects: true
109 |
--------------------------------------------------------------------------------
/ci/roles/identity_domain/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create keystone domain
3 | openstack.cloud.identity_domain:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | name: ansible_domain
7 | description: "test description"
8 | register: domain
9 |
10 | - name: Assert return values of identity_domain module
11 | assert:
12 | that:
13 | - domain.domain.name == 'ansible_domain'
14 | - domain.domain.description == "test description"
15 | # allow new fields to be introduced but prevent fields from being removed
16 | - expected_fields|difference(domain.domain.keys())|length == 0
17 |
18 | - name: Update keystone domain
19 | openstack.cloud.identity_domain:
20 | cloud: "{{ cloud }}"
21 | name: ansible_domain
22 | description: "updated description"
23 | register: domain
24 |
25 | - name: Assert updated domain
26 | assert:
27 | that:
28 | - domain.domain.description == "updated description"
29 |
30 | - name: Fetch domains
31 | openstack.cloud.identity_domain_info:
32 | cloud: "{{ cloud }}"
33 | register: domains
34 |
35 | - name: Assert return values of identity_domain_info module
36 | assert:
37 | that:
38 | - domains.domains | length > 0
39 | # allow new fields to be introduced but prevent fields from being removed
40 | - expected_fields|difference(domains.domains.0.keys())|length == 0
41 |
42 | - name: Fetch domain by name
43 | openstack.cloud.identity_domain_info:
44 | cloud: "{{ cloud }}"
45 | name: ansible_domain
46 | register: domains
47 |
48 | - name: Assert named domain
49 | assert:
50 | that:
51 | - domains.domains | length == 1
52 |
53 | - name: Create disabled domain
54 | openstack.cloud.identity_domain:
55 | cloud: "{{ cloud }}"
56 | state: present
57 | name: ansible_domain_disabled
58 | is_enabled: false
59 | description: "test description"
60 | register: domain
61 |
62 | - name: Fetch all domains
63 | openstack.cloud.identity_domain_info:
64 | cloud: "{{ cloud }}"
65 | register: domains
66 |
67 | - name: Assert both ansible domains exist
68 | assert:
69 | that:
70 | - domains.domains | length >= 2
71 |
72 | - name: Fetch disabled domains
73 | openstack.cloud.identity_domain_info:
74 | cloud: "{{ cloud }}"
75 | filters:
76 | is_enabled: false
77 | register: domains
78 |
79 | - name: Assert at least one disabled domain exists
80 | assert:
81 | that:
82 | - domains.domains | length >= 1
83 |
84 | - name: Fetch enabled domains
85 | openstack.cloud.identity_domain_info:
86 | cloud: "{{ cloud }}"
87 | filters:
88 | is_enabled: true
89 | register: domains
90 |
91 | - name: Assert returned value
92 | assert:
93 | that:
94 | - item.is_enabled
95 | loop: "{{ domains.domains }}"
96 |
97 | - name: Delete disabled domain
98 | openstack.cloud.identity_domain:
99 | cloud: "{{ cloud }}"
100 | state: absent
101 | name: ansible_domain_disabled
102 |
103 | - name: Assert domain is disabled
104 | assert:
105 | that:
106 | - not domain.domain.is_enabled
107 |
108 | - name: Delete domain
109 | openstack.cloud.identity_domain:
110 | cloud: "{{ cloud }}"
111 | state: absent
112 | name: ansible_domain
113 |
114 | - name: Get non-existing domain
115 | openstack.cloud.identity_domain_info:
116 | cloud: "{{ cloud }}"
117 | name: ansible_domain
118 | register: domains
119 |
120 | - name: Assert no results returned
121 | assert:
122 | that:
123 | - domains.domains | length == 0
124 |
--------------------------------------------------------------------------------
/ci/roles/trunk/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create parent network
3 | openstack.cloud.network:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | name: "{{ parent_network_name }}"
7 | external: true
8 | register: parent_network
9 |
10 | - name: Create parent subnet
11 | openstack.cloud.subnet:
12 | cloud: "{{ cloud }}"
13 | state: present
14 | name: "{{ parent_subnet_name }}"
15 | network_name: "{{ parent_network_name }}"
16 | cidr: 10.5.5.0/24
17 | register: parent_subnet
18 |
19 | - name: Create parent port
20 | openstack.cloud.port:
21 | cloud: "{{ cloud }}"
22 | state: present
23 | name: "{{ parent_port_name }}"
24 | network: "{{ parent_network_name }}"
25 | fixed_ips:
26 | - ip_address: 10.5.5.69
27 | register: parent_port
28 |
29 | - name: Create subport network
30 | openstack.cloud.network:
31 | cloud: "{{ cloud }}"
32 | state: present
33 | name: "{{ subport_network_name }}"
34 | external: true
35 | register: subport_network
36 |
37 | - name: Create subport subnet
38 | openstack.cloud.subnet:
39 | cloud: "{{ cloud }}"
40 | state: present
41 | name: "{{ subport_subnet_name }}"
42 | network_name: "{{ subport_network_name }}"
43 | cidr: 10.5.6.0/24
44 | register: subport_subnet
45 |
46 | - name: Create subport
47 | openstack.cloud.port:
48 | cloud: "{{ cloud }}"
49 | state: present
50 | name: "{{ subport_name }}"
51 | network: "{{ subport_network_name }}"
52 | fixed_ips:
53 | - ip_address: 10.5.6.55
54 | register: subport
55 |
56 | - name: Create trunk
57 | openstack.cloud.trunk:
58 | cloud: "{{ cloud }}"
59 | state: present
60 | name: "{{ trunk_name }}"
61 | port: "{{ parent_port_name }}"
62 | register: trunk
63 |
64 | - debug: var=trunk
65 |
66 | - name: assert return values of trunk module
67 | assert:
68 | that:
69 | # allow new fields to be introduced but prevent fields from being removed
70 | - expected_fields|difference(trunk.trunk.keys())|length == 0
71 |
72 | - name: Add subport to trunk
73 | openstack.cloud.trunk:
74 | cloud: "{{ cloud }}"
75 | state: present
76 | name: "{{ trunk_name }}"
77 | port: "{{ parent_port_name }}"
78 | sub_ports:
79 | - port: "{{ subport_name }}"
80 | segmentation_type: vlan
81 | segmentation_id: 123
82 |
83 | - name: Update subport from trunk
84 | openstack.cloud.trunk:
85 | cloud: "{{ cloud }}"
86 | state: present
87 | name: "{{ trunk_name }}"
88 | port: "{{ parent_port_name }}"
89 | sub_ports: []
90 |
91 | - name: Delete trunk
92 | openstack.cloud.trunk:
93 | cloud: "{{ cloud }}"
94 | state: absent
95 | name: "{{ trunk_name }}"
96 |
97 | - name: Delete subport
98 | openstack.cloud.port:
99 | cloud: "{{ cloud }}"
100 | state: absent
101 | name: "{{ subport_name }}"
102 |
103 | - name: Delete subport subnet
104 | openstack.cloud.subnet:
105 | cloud: "{{ cloud }}"
106 | state: absent
107 | name: "{{ subport_subnet_name }}"
108 |
109 | - name: Delete subport network
110 | openstack.cloud.network:
111 | cloud: "{{ cloud }}"
112 | state: absent
113 | name: "{{ subport_network_name }}"
114 |
115 | - name: Delete parent port
116 | openstack.cloud.port:
117 | cloud: "{{ cloud }}"
118 | state: absent
119 | name: "{{ parent_port_name }}"
120 |
121 | - name: Delete parent subnet
122 | openstack.cloud.subnet:
123 | cloud: "{{ cloud }}"
124 | state: absent
125 | name: "{{ parent_subnet_name }}"
126 |
127 | - name: Delete parent network
128 | openstack.cloud.network:
129 | cloud: "{{ cloud }}"
130 | state: absent
131 | name: "{{ parent_network_name }}"
132 |
--------------------------------------------------------------------------------
/ci/roles/recordset/tasks/main.yml:
--------------------------------------------------------------------------------
1 | - name: Ensure DNS zone not present before tests
2 | openstack.cloud.dns_zone:
3 | cloud: "{{ cloud }}"
4 | name: "{{ dns_zone_name }}"
5 | zone_type: "primary"
6 | email: test@example.net
7 | state: absent
8 |
9 | - name: Ensure dns zone
10 | openstack.cloud.dns_zone:
11 | cloud: "{{ cloud }}"
12 | name: "{{ dns_zone_name }}"
13 | zone_type: "primary"
14 | email: test@example.net
15 | register: dns_zone
16 |
17 | - name: Ensure recordset not present
18 | openstack.cloud.recordset:
19 | cloud: "{{ cloud }}"
20 | zone: "{{ dns_zone.zone.name }}"
21 | name: "{{ recordset_name }}"
22 | recordset_type: "a"
23 | records: "{{ records }}"
24 | state: absent
25 |
26 | - name: Create a recordset
27 | openstack.cloud.recordset:
28 | cloud: "{{ cloud }}"
29 | zone: "{{ dns_zone.zone.name }}"
30 | name: "{{ recordset_name }}"
31 | recordset_type: "a"
32 | records: "{{ records }}"
33 | register: recordset
34 | until: '"PENDING" not in recordset["recordset"].status'
35 | retries: 10
36 | delay: 5
37 |
38 | - name: Verify recordset info
39 | assert:
40 | that:
41 | - recordset["recordset"].name == recordset_name
42 | - recordset["recordset"].zone_name == dns_zone.zone.name
43 | - recordset["recordset"].records | list | sort == records | list | sort
44 |
45 | - name: Assert recordset fields
46 | assert:
47 | that: item in recordset.recordset
48 | loop: "{{ recordset_fields }}"
49 |
50 | - name: Create identical recordset
51 | openstack.cloud.recordset:
52 | cloud: "{{ cloud }}"
53 | zone: "{{ dns_zone.zone.name }}"
54 | name: "{{ recordset_name }}"
55 | recordset_type: "a"
56 | records: "{{ records }}"
57 | register: recordset
58 |
59 | - name: Assert recordset not changed
60 | assert:
61 | that:
62 | - recordset is not changed
63 |
64 | - name: Assert recordset fields
65 | assert:
66 | that: item in recordset.recordset
67 | loop: "{{ recordset_fields }}"
68 |
69 | - name: Update a recordset
70 | openstack.cloud.recordset:
71 | cloud: "{{ cloud }}"
72 | zone: "{{ dns_zone.zone.name }}"
73 | name: "{{ recordset_name }}"
74 | recordset_type: "a"
75 | records: "{{ updated_records }}"
76 | description: "new test recordset"
77 | register: recordset
78 |
79 | - name: Verify recordset info
80 | assert:
81 | that:
82 | - recordset is changed
83 | - recordset["recordset"].zone_name == dns_zone.zone.name
84 | - recordset["recordset"].name == recordset_name
85 | - recordset["recordset"].records | list | sort == updated_records | list | sort
86 |
87 | - name: Assert recordset fields
88 | assert:
89 | that: item in recordset.recordset
90 | loop: "{{ recordset_fields }}"
91 |
92 | - name: Delete recordset
93 | openstack.cloud.recordset:
94 | cloud: "{{ cloud }}"
95 | zone: "{{ dns_zone.zone.name }}"
96 | name: "{{ recordset.recordset.name }}"
97 | state: absent
98 | register: deleted_recordset
99 |
100 | - name: Verify recordset deletion
101 | assert:
102 | that:
103 | - deleted_recordset is successful
104 | - deleted_recordset is changed
105 |
106 | - name: Delete unexistent recordset
107 | openstack.cloud.recordset:
108 | cloud: "{{ cloud }}"
109 | zone: "{{ dns_zone.zone.name }}"
110 | name: "{{ recordset.recordset.name }}"
111 | state: absent
112 | register: deleted_recordset
113 |
114 | - name: Verify recordset deletion
115 | assert:
116 | that:
117 | - deleted_recordset is not changed
118 |
119 | - name: Delete dns zone
120 | openstack.cloud.dns_zone:
121 | cloud: "{{ cloud }}"
122 | name: "{{ dns_zone.zone.name }}"
123 | state: absent
124 |
--------------------------------------------------------------------------------
/ci/roles/router/tasks/shared_network.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create the first project
3 | openstack.cloud.project:
4 | cloud: "{{ cloud }}"
5 | state: present
6 | name: "shared_net_test_1"
7 | description: "Project that contains the subnet to be shared"
8 | domain: default
9 | is_enabled: True
10 | register: project_1
11 |
12 | - name: Create the network to be shared
13 | openstack.cloud.network:
14 | cloud: "{{ cloud }}"
15 | state: present
16 | name: "my_shared_network"
17 | project: "shared_net_test_1"
18 | external: False
19 | provider_network_type: vxlan
20 | register: shared_network
21 |
22 | - name: Create ipv4 subnet
23 | openstack.cloud.subnet:
24 | cloud: "{{ cloud }}"
25 | state: present
26 | network_name: "{{ shared_network.id }}"
27 | name: "my_shared_subnet"
28 | project: "shared_net_test_1"
29 | ip_version: "4"
30 | cidr: "10.0.0.0/24"
31 | gateway_ip: "10.0.0.1"
32 | register: shared_subnet
33 |
34 | - name: Create the second project
35 | openstack.cloud.project:
36 | cloud: "{{ cloud }}"
37 | state: present
38 | name: "shared_net_test_2"
39 | description: "Project that contains the subnet to be shared"
40 | domain: default
41 | is_enabled: True
42 | register: project_2
43 |
44 | - name: Share the network with the second project
45 | openstack.cloud.neutron_rbac_policy:
46 | cloud: "{{ cloud }}"
47 | action: 'access_as_shared'
48 | object_id: "{{ shared_network.id }}"
49 | object_type: 'network'
50 | target_project_id: "{{ project_2.project.id }}"
51 | project_id: "{{ project_1.project.id }}"
52 | register: rbac_rule
53 |
54 | - name: Create router with interface in shared network
55 | openstack.cloud.router:
56 | cloud: "{{ cloud }}"
57 | state: present
58 | name: "shared_net_test2_router"
59 | project: "shared_net_test_2"
60 | interfaces:
61 | - net: "{{ shared_network.id }}"
62 | portip: "10.0.0.42"
63 | subnet: "{{ shared_subnet.id }}"
64 | register: router
65 |
66 | - name: Gather routers info
67 | openstack.cloud.routers_info:
68 | cloud: "{{ cloud }}"
69 | name: "shared_net_test2_router"
70 | register: routers
71 |
72 | - name: List ports of first router
73 | openstack.cloud.port_info:
74 | cloud: "{{ cloud }}"
75 | filters:
76 | device_id: "{{ routers.routers.0.id }}"
77 | register: ports
78 |
79 | - name: Verify routers info
80 | assert:
81 | that:
82 | - routers.routers.0.id == router.router.id
83 | - ports.ports
84 | |rejectattr('device_owner', 'equalto', 'network:router_gateway')
85 | |sum(attribute='fixed_ips', start=[])
86 | |map(attribute='ip_address')
87 | |sort|list == ["10.0.0.42"]
88 |
89 | - name: delete router
90 | openstack.cloud.router:
91 | cloud: "{{ cloud }}"
92 | state: absent
93 | name: "shared_net_test2_router"
94 | project: "shared_net_test_2"
95 |
96 | - name: delete rbac rule
97 | openstack.cloud.neutron_rbac_policy:
98 | cloud: "{{ cloud }}"
99 | policy_id: "{{ rbac_rule.policy.id }}"
100 | state: absent
101 |
102 | - name: delete subnet
103 | openstack.cloud.subnet:
104 | cloud: "{{ cloud }}"
105 | state: absent
106 | network_name: "{{ shared_network.id }}"
107 | name: "my_shared_subnet"
108 | project: "shared_net_test_1"
109 |
110 | - name: delete network
111 | openstack.cloud.network:
112 | cloud: "{{ cloud }}"
113 | state: absent
114 | name: "my_shared_network"
115 | project: "shared_net_test_1"
116 |
117 | - name: delete project 2
118 | openstack.cloud.project:
119 | cloud: "{{ cloud }}"
120 | state: absent
121 | name: "shared_net_test_2"
122 |
123 | - name: delete project 1
124 | openstack.cloud.project:
125 | cloud: "{{ cloud }}"
126 | state: absent
127 | name: "shared_net_test_1"
128 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | minversion = 3.18.0
3 | envlist = linters_latest,ansible_latest
4 | skipsdist = True
5 | ignore_basepython_conflict = True
6 |
7 | [testenv]
8 | skip_install = True
9 | install_command = python3 -m pip install {opts} {packages}
10 | basepython = python3
11 | passenv =
12 | OS_*
13 | setenv =
14 | VIRTUAL_ENV={envdir}
15 | LANG=en_US.UTF-8
16 | LANGUAGE=en_US:en
17 | LC_ALL=en_US.utf-8
18 | OS_LOG_CAPTURE={env:OS_LOG_CAPTURE:true}
19 | OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:true}
20 | OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:true}
21 | commands = stestr run {posargs}
22 | stestr slowest
23 |
24 | [testenv:pep8]
25 | # for Zuul CI job tox-pep8
26 | commands =
27 | flake8
28 | deps =
29 | -c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
30 | -r{toxinidir}/tests/requirements.txt
31 |
32 | [testenv:build]
33 | allowlist_externals = bash
34 | deps =
35 | ansible-core
36 | galaxy-importer
37 | pbr
38 | ruamel.yaml
39 | setuptools
40 | commands =
41 | python {toxinidir}/tools/build.py
42 | ansible --version
43 | ansible-galaxy collection build --force {toxinidir} --output-path {toxinidir}/build_artifact
44 | bash {toxinidir}/tools/check-import.sh {toxinidir}
45 |
46 | [testenv:linters_{2_9,2_11,2_12,2_16,2_18,latest}]
47 | allowlist_externals = bash
48 | commands =
49 | {[testenv:build]commands}
50 | flake8
51 | ansible --version
52 | bash {toxinidir}/tools/run-ansible-sanity.sh {toxinidir}
53 | deps =
54 | -c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
55 | {[testenv:build]deps}
56 | linters_latest: -r{toxinidir}/tests/requirements.txt
57 | linters_2_9: -r{toxinidir}/tests/requirements-ansible-2.9.txt
58 | linters_2_11: -r{toxinidir}/tests/requirements-ansible-2.11.txt
59 | linters_2_12: -r{toxinidir}/tests/requirements-ansible-2.12.txt
60 | linters_2_16: -r{toxinidir}/tests/requirements-ansible-2.16.txt
61 | linters_2_18: -r{toxinidir}/tests/requirements-ansible-2.18.txt
62 | passenv = *
63 |
64 | [flake8]
65 | # W503 Is supposed to be off by default but in the latest pycodestyle isn't.
66 | # Also, both openstacksdk and Donald Knuth disagree with the rule. Line
67 | # breaks should occur before the binary operator for readability.
68 | # H4 are rules for docstrings. Maybe we should clean them?
69 | # E501,E402,H301 are ignored so we can import the existing
70 | # modules unchanged and then clean them in subsequent patches.
71 | ignore = W503,H4,E501,E402,H301
72 | show-source = True
73 | exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,ansible_collections
74 |
75 | [testenv:ansible_{2_9,2_11,2_12,2_16,2_18,latest}]
76 | allowlist_externals = bash
77 | commands =
78 | bash {toxinidir}/ci/run-ansible-tests-collection.sh -e {envdir} {posargs}
79 | deps =
80 | -c{env:TOX_CONSTRAINTS_FILE:{toxinidir}/tests/constraints-none.txt}
81 | ansible_latest: -r{toxinidir}/tests/requirements.txt
82 | ansible_2_9: -r{toxinidir}/tests/requirements-ansible-2.9.txt
83 | ansible_2_11: -r{toxinidir}/tests/requirements-ansible-2.11.txt
84 | ansible_2_12: -r{toxinidir}/tests/requirements-ansible-2.12.txt
85 | ansible_2_16: -r{toxinidir}/tests/requirements-ansible-2.16.txt
86 | ansible_2_18: -r{toxinidir}/tests/requirements-ansible-2.18.txt
87 | # Need to pass some env vars for the Ansible playbooks
88 | passenv =
89 | HOME
90 | USER
91 | ANSIBLE_*
92 |
93 | [testenv:galaxy_release]
94 | allowlist_externals = mkdir rm sed
95 | commands =
96 | rm -rf /tmp/collection_built/
97 | mkdir -p /tmp/collection_built/
98 | sed -i "s/version:.*/version: {env:VERSION_TAG}/" {toxinidir}/galaxy.yml
99 | ansible-galaxy collection build {toxinidir} --output-path /tmp/collection_built/ --force
100 | ansible-galaxy collection publish /tmp/collection_built/openstack-cloud-{env:VERSION_TAG}.tar.gz --token {env:API_GALAXY_TOKEN}
101 | deps =
102 | ansible-core
103 |
--------------------------------------------------------------------------------
/plugins/modules/project_info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | # Copyright (c) 2016 Hewlett-Packard Enterprise Corporation
5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 |
7 | DOCUMENTATION = r'''
8 | ---
9 | module: project_info
10 | short_description: Retrieve information about one or more OpenStack projects
11 | author: OpenStack Ansible SIG
12 | description:
13 | - Retrieve information about a one or more OpenStack projects
14 | options:
15 | name:
16 | description:
17 | - Name or ID of the project.
18 | type: str
19 | domain:
20 | description:
21 | - Name or ID of the domain containing the project.
22 | type: str
23 | filters:
24 | description:
25 | - A dictionary of meta data to use for filtering projects.
26 | - Elements of I(filters) are passed as query parameters to
27 | OpenStack Identity API.
28 | type: dict
29 | extends_documentation_fragment:
30 | - openstack.cloud.openstack
31 | '''
32 |
33 | EXAMPLES = r'''
34 | - name: Fetch all Identity (Keystone) projects
35 | openstack.cloud.project_info:
36 | cloud: awesomecloud
37 |
38 | - name: Fetch all projects with a name
39 | openstack.cloud.project_info:
40 | cloud: awesomecloud
41 | name: demoproject
42 |
43 | - name: Fetch all projects with a name in a domain
44 | openstack.cloud.project_info:
45 | cloud: awesomecloud
46 | name: demoproject
47 | domain: admindomain
48 |
49 | - name: Fetch all disabled projects
50 | openstack.cloud.project_info:
51 | cloud: awesomecloud
52 | filters:
53 | is_enabled: false
54 | '''
55 |
56 | RETURN = r'''
57 | projects:
58 | description: List of dictionaries describing Identity (Keystone) projects.
59 | elements: dict
60 | returned: always, but can be empty
61 | type: list
62 | contains:
63 | description:
64 | description: Project description
65 | type: str
66 | sample: "demodescription"
67 | domain_id:
68 | description: Domain ID to which the project belongs
69 | type: str
70 | sample: "default"
71 | id:
72 | description: Project ID
73 | type: str
74 | sample: "f59382db809c43139982ca4189404650"
75 | is_domain:
76 | description: Indicates whether the project also acts as a domain.
77 | type: bool
78 | is_enabled:
79 | description: Indicates whether the project is enabled
80 | type: bool
81 | name:
82 | description: Project name
83 | type: str
84 | sample: "demoproject"
85 | options:
86 | description: The resource options for the project
87 | type: dict
88 | parent_id:
89 | description: The ID of the parent of the project
90 | type: str
91 | tags:
92 | description: A list of associated tags
93 | type: list
94 | elements: str
95 | '''
96 |
97 | from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
98 |
99 |
100 | class IdentityProjectInfoModule(OpenStackModule):
101 | argument_spec = dict(
102 | domain=dict(),
103 | name=dict(),
104 | filters=dict(type='dict'),
105 | )
106 | module_kwargs = dict(
107 | supports_check_mode=True
108 | )
109 |
110 | def run(self):
111 | filters = self.params['filters'] or {}
112 |
113 | domain_name_or_id = self.params['domain']
114 | if domain_name_or_id is not None:
115 | domain = self.conn.identity.find_domain(domain_name_or_id)
116 |
117 | if not domain:
118 | self.exit_json(changed=False, projects=[])
119 |
120 | filters['domain_id'] = domain.id
121 |
122 | projects = self.conn.search_projects(name_or_id=self.params['name'],
123 | filters=filters)
124 |
125 | self.exit_json(changed=False,
126 | projects=[p.to_dict(computed=False) for p in projects])
127 |
128 |
129 | def main():
130 | module = IdentityProjectInfoModule()
131 | module()
132 |
133 |
134 | if __name__ == '__main__':
135 | main()
136 |
--------------------------------------------------------------------------------
/ci/roles/quota/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - module_defaults:
3 | group/openstack.cloud.openstack:
4 | cloud: "{{ cloud }}"
5 | name: "{{ test_project }}"
6 | # Backward compatibility with Ansible 2.9
7 | openstack.cloud.project:
8 | cloud: "{{ cloud }}"
9 | name: "{{ test_project }}"
10 | openstack.cloud.quota:
11 | cloud: "{{ cloud }}"
12 | name: "{{ test_project }}"
13 | block:
14 | - name: Create test project
15 | openstack.cloud.project:
16 | state: present
17 |
18 | - name: Clear quotas before tests
19 | openstack.cloud.quota:
20 | state: absent
21 | register: default_quotas
22 |
23 | - name: Set network quota
24 | openstack.cloud.quota: "{{ test_network_quota }}"
25 | register: quotas
26 |
27 | - name: Assert changed
28 | assert:
29 | that: quotas is changed
30 |
31 | - name: Assert field values
32 | assert:
33 | that: quotas.quotas.network[item.key] == item.value
34 | loop: "{{ test_network_quota | dict2items }}"
35 |
36 | - name: Set network quota again
37 | openstack.cloud.quota: "{{ test_network_quota }}"
38 | register: quotas
39 |
40 | - name: Assert not changed
41 | assert:
42 | that: quotas is not changed
43 |
44 | - name: Set volume quotas
45 | openstack.cloud.quota: "{{ test_volume_quota }}"
46 | register: quotas
47 |
48 | - name: Assert changed
49 | assert:
50 | that: quotas is changed
51 |
52 | - name: Assert field values
53 | assert:
54 | that: quotas.quotas.volume[item.key] == item.value
55 | loop: "{{ test_volume_quota | dict2items }}"
56 |
57 | - name: Set volume quotas again
58 | openstack.cloud.quota: "{{ test_volume_quota }}"
59 | register: quotas
60 |
61 | - name: Assert not changed
62 | assert:
63 | that: quotas is not changed
64 |
65 | - name: Set compute quotas
66 | openstack.cloud.quota: "{{ test_compute_quota }}"
67 | register: quotas
68 |
69 | - name: Assert changed
70 | assert:
71 | that: quotas is changed
72 |
73 | - name: Assert field values
74 | assert:
75 | that: quotas.quotas.compute[item.key] == item.value
76 | loop: "{{ test_compute_quota | dict2items }}"
77 |
78 | - name: Set compute quotas again
79 | openstack.cloud.quota: "{{ test_compute_quota }}"
80 | register: quotas
81 |
82 | - name: Unset all quotas
83 | openstack.cloud.quota:
84 | state: absent
85 | register: quotas
86 |
87 | - name: Assert defaults restore
88 | assert:
89 | that: quotas.quotas == default_quotas.quotas
90 |
91 | - name: Set all quotas at once
92 | openstack.cloud.quota:
93 | "{{ [test_network_quota, test_volume_quota, test_compute_quota] | combine }}"
94 | register: quotas
95 |
96 | - name: Assert changed
97 | assert:
98 | that: quotas is changed
99 |
100 | - name: Assert volume values
101 | assert:
102 | that: quotas.quotas.volume[item.key] == item.value
103 | loop: "{{ test_volume_quota | dict2items }}"
104 |
105 | - name: Assert network values
106 | assert:
107 | that: quotas.quotas.network[item.key] == item.value
108 | loop: "{{ test_network_quota | dict2items }}"
109 |
110 | - name: Assert compute values
111 | assert:
112 | that: quotas.quotas.compute[item.key] == item.value
113 | loop: "{{ test_compute_quota | dict2items }}"
114 |
115 | - name: Set all quotas at once again
116 | openstack.cloud.quota:
117 | "{{ [test_network_quota, test_volume_quota, test_compute_quota] | combine }}"
118 | register: quotas
119 |
120 | - name: Assert not changed
121 | assert:
122 | that: quotas is not changed
123 |
124 | - name: Unset all quotas
125 | openstack.cloud.quota:
126 | state: absent
127 | register: quotas
128 |
129 | - name: Delete test project
130 | openstack.cloud.project:
131 | state: absent
132 |
133 | - import_tasks: loadbalancer.yml
134 | tags:
135 | - loadbalancer
136 |
137 |
--------------------------------------------------------------------------------