Rename all of the modules

This is separate from the previous patch - it's just the results
of running the script so we can review the two a little independently.
We should probably squash them.

Change-Id: I838f15cf4a32455a5be20033c8ddc27db6ca15c0
This commit is contained in:
Monty Taylor 2020-05-12 10:20:09 -05:00
parent 52905480b8
commit e47c4671c7
154 changed files with 15307 additions and 15029 deletions

View File

@ -1,6 +1,6 @@
---
- name: Authenticate to the cloud
openstack.cloud.os_auth:
openstack.cloud.auth:
cloud={{ cloud }}
- debug: var=service_catalog

View File

@ -1,6 +1,6 @@
---
- name: List all profiles
openstack.cloud.os_client_config:
openstack.cloud.config:
register: list
# WARNING: This will output sensitive authentication information!!!!

View File

@ -1,19 +1,19 @@
---
- name: Create group
openstack.cloud.os_group:
openstack.cloud.identity_group:
cloud: "{{ cloud }}"
state: present
name: "{{ group_name }}"
- name: Update group
openstack.cloud.os_group:
openstack.cloud.identity_group:
cloud: "{{ cloud }}"
state: present
name: "{{ group_name }}"
description: "updated description"
- name: Delete group
openstack.cloud.os_group:
openstack.cloud.identity_group:
cloud: "{{ cloud }}"
state: absent
name: "{{ group_name }}"

View File

@ -7,7 +7,7 @@
shell: truncate -s 1048576 {{ tmp_file.stdout }}
- name: Create raw image (defaults)
openstack.cloud.os_image:
openstack.cloud.image:
cloud: "{{ cloud }}"
state: present
name: "{{ image_name }}"
@ -18,13 +18,13 @@
- debug: var=image
- name: Delete raw image (defaults)
openstack.cloud.os_image:
openstack.cloud.image:
cloud: "{{ cloud }}"
state: absent
name: "{{ image_name }}"
- name: Create raw image (complex)
openstack.cloud.os_image:
openstack.cloud.image:
cloud: "{{ cloud }}"
state: present
name: "{{ image_name }}"
@ -43,7 +43,7 @@
- debug: var=image
- name: Delete raw image (complex)
openstack.cloud.os_image:
openstack.cloud.image:
cloud: "{{ cloud }}"
state: absent
name: "{{ image_name }}"

View File

@ -1,6 +1,6 @@
---
- name: Create keypair (non-existing)
openstack.cloud.os_keypair:
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
@ -14,7 +14,7 @@
- keypair.key.public_key is defined and keypair.key.public_key
- name: Delete keypair (non-existing)
openstack.cloud.os_keypair:
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
@ -26,27 +26,27 @@
ssh_key_file: .ssh/shade_id_rsa
- name: Create keypair (file)
openstack.cloud.os_keypair:
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
public_key_file: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
- name: Delete keypair (file)
openstack.cloud.os_keypair:
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
- name: Create keypair (key)
openstack.cloud.os_keypair:
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: present
public_key: "{{ lookup('file', '~/.ssh/shade_id_rsa.pub') }}"
- name: Delete keypair (key)
openstack.cloud.os_keypair:
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent

View File

@ -1,19 +1,19 @@
---
- name: Create keystone domain
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
state: present
name: "{{ domain_name }}"
description: "test description"
- name: Update keystone domain
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
name: "{{ domain_name }}"
description: "updated description"
- name: Delete keystone domain
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
state: absent
name: "{{ domain_name }}"

View File

@ -9,23 +9,23 @@
# meta/action_groups.yml glue seems to be missing
# group/os:
# cloud: "{{ cloud }}"
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
cloud: "{{ cloud }}"
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
cloud: "{{ cloud }}"
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
cloud: "{{ cloud }}"
idp_id: "{{ idp_name }}"
openstack.cloud.os_keystone_federation_protocol_info:
openstack.cloud.keystone_federation_protocol_info:
cloud: "{{ cloud }}"
idp_id: "{{ idp_name }}"
block:
# ========================================================================
# Initial setup
- name: 'Create test Domain'
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
name: '{{ domain_name }}'
register: create_domain
- assert:
@ -37,7 +37,7 @@
domain_id: '{{ create_domain.id }}'
- name: 'Create test Identity Provider'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
domain_id: '{{ domain_id }}'
@ -47,7 +47,7 @@
- create_idp is successful
- name: 'Create test mapping (1)'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name_1 }}'
rules: '{{ mapping_rules_1 }}'
@ -56,7 +56,7 @@
that:
- create_mapping is successful
- name: 'Create test mapping (2)'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name_2 }}'
rules: '{{ mapping_rules_2 }}'
@ -68,7 +68,7 @@
# We *should* have a blank slate to start with, but we also shouldn't
# explode if I(state=absent) and the IDP doesn't exist
- name: "Ensure Protocol doesn't exist to start"
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'absent'
name: '{{ protocol_name }}'
register: delete_protocol
@ -81,7 +81,7 @@
- name: 'Create protocol - CHECK MODE'
check_mode: yes
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name }}'
mapping_id: '{{ mapping_name_1 }}'
@ -92,7 +92,7 @@
- create_protocol is changed
- name: 'Fetch Protocol info (should be absent)'
openstack.cloud.os_keystone_federation_protocol_info:
openstack.cloud.keystone_federation_protocol_info:
name: '{{ protocol_name }}'
register: protocol_info
ignore_errors: yes
@ -101,7 +101,7 @@
- protocol_info is failed
- name: 'Create protocol'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name }}'
mapping_id: '{{ mapping_name_1 }}'
@ -124,7 +124,7 @@
- name: 'Create protocol (retry - no change) - CHECK MODE'
check_mode: yes
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name }}'
mapping_id: '{{ mapping_name_1 }}'
@ -135,7 +135,7 @@
- create_protocol is not changed
- name: 'Create protocol (retry - no change)'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name }}'
mapping_id: '{{ mapping_name_1 }}'
@ -161,7 +161,7 @@
- name: 'Update protocol - CHECK MODE'
check_mode: yes
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name }}'
mapping_id: '{{ mapping_name_2 }}'
@ -172,7 +172,7 @@
- update_protocol is changed
- name: 'Update protocol'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name }}'
mapping_id: '{{ mapping_name_2 }}'
@ -195,7 +195,7 @@
- name: 'Update protocol (retry - no change) - CHECK MODE'
check_mode: yes
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name }}'
mapping_id: '{{ mapping_name_2 }}'
@ -206,7 +206,7 @@
- update_protocol is not changed
- name: 'Update protocol (retry - no change)'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name }}'
mapping_id: '{{ mapping_name_2 }}'
@ -228,10 +228,10 @@
protocol: '{{ update_protocol.protocol }}'
# ========================================================================
# Create second protocol to test os_keystone_federation_protocol_info
# Create second protocol to test openstack.cloud.keystone_federation_protocol_info
- name: 'Create protocol (2)'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'present'
name: '{{ protocol_name_2 }}'
mapping_id: '{{ mapping_name_1 }}'
@ -253,10 +253,10 @@
protocol: '{{ create_protocol_2.protocol }}'
# ========================================================================
# Basic tests of os_keystone_federation_protocol_info
# Basic tests of openstack.cloud.keystone_federation_protocol_info
- name: 'Fetch Protocol info (a specific protocol)'
openstack.cloud.os_keystone_federation_protocol_info:
openstack.cloud.keystone_federation_protocol_info:
name: '{{ protocol_name }}'
register: protocol_info
- assert:
@ -276,7 +276,7 @@
protocol: '{{ protocol_info.protocols[0] }}'
- name: 'Fetch Protocol info (all protocols on our test IDP)'
openstack.cloud.os_keystone_federation_protocol_info: {}
openstack.cloud.keystone_federation_protocol_info: {}
# idp_id defined in defaults at the start
register: protocol_info
- assert:
@ -311,7 +311,7 @@
- name: 'Delete protocol - CHECK MODE'
check_mode: yes
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'absent'
name: '{{ protocol_name }}'
register: update_protocol
@ -321,7 +321,7 @@
- update_protocol is changed
- name: 'Delete protocol'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'absent'
name: '{{ protocol_name }}'
register: update_protocol
@ -332,7 +332,7 @@
- name: 'Delete protocol (retry - no change) - CHECK MODE'
check_mode: yes
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'absent'
name: '{{ protocol_name }}'
register: update_protocol
@ -342,7 +342,7 @@
- update_protocol is not changed
- name: 'Delete protocol (retry - no change)'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'absent'
name: '{{ protocol_name }}'
register: update_protocol
@ -355,39 +355,39 @@
# Clean up after ourselves
always:
- name: 'Delete protocol'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'absent'
name: '{{ protocol_name }}'
idp_id: '{{ idp_name }}'
ignore_errors: yes
- name: 'Delete protocol (2)'
openstack.cloud.os_keystone_federation_protocol:
openstack.cloud.keystone_federation_protocol:
state: 'absent'
name: '{{ protocol_name_2 }}'
idp_id: '{{ idp_name }}'
ignore_errors: yes
- name: 'Delete mapping 1'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name_1 }}'
ignore_errors: yes
- name: 'Delete mapping 2'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name_2 }}'
ignore_errors: yes
- name: 'Delete idp'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name }}'
ignore_errors: yes
- name: 'Delete domain'
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
state: 'absent'
name: '{{ domain_name }}'
ignore_errors: yes

View File

@ -9,17 +9,17 @@
# meta/action_groups.yml glue seems to be missing
# group/os:
# cloud: "{{ cloud }}"
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
cloud: "{{ cloud }}"
openstack.cloud.os_keystone_identity_provider_info:
openstack.cloud.federation_idp_info:
cloud: "{{ cloud }}"
block:
# ========================================================================
# Initial setup
- name: 'Create test domain'
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
name: '{{ domain_name }}'
register: create_domain
- assert:
@ -33,7 +33,7 @@
# We *should* have a blank slate to start with, but we also shouldn't
# explode if I(state=absent) and the IDP doesn't exist
- name: "Ensure IDP doesn't exist to start"
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name }}'
register: delete_idp
@ -46,7 +46,7 @@
- name: 'Create IDP - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
domain_id: '{{ domain_id }}'
@ -57,7 +57,7 @@
- create_idp is changed
- name: 'Fetch identity_provider info (provider should be absent)'
openstack.cloud.os_keystone_identity_provider_info:
openstack.cloud.federation_idp_info:
name: '{{ idp_name }}'
register: identity_provider_info
ignore_errors: yes
@ -66,7 +66,7 @@
- identity_provider_info is failed
- name: 'Create IDP'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
domain_id: '{{ domain_id }}'
@ -94,7 +94,7 @@
idp: '{{ create_identity_provider.identity_provider }}'
- name: 'Fetch IDP info - with name'
openstack.cloud.os_keystone_identity_provider_info:
openstack.cloud.federation_idp_info:
name: '{{ idp_name }}'
register: identity_provider_info
- assert:
@ -121,7 +121,7 @@
idp: '{{ identity_provider_info.identity_providers[0] }}'
- name: 'Fetch identity_provider info - without name'
openstack.cloud.os_keystone_identity_provider_info: {}
openstack.cloud.federation_idp_info: {}
register: identity_provider_info
- assert:
that:
@ -143,7 +143,7 @@
- name: 'Create identity_provider (retry - no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
domain_id: '{{ domain_id }}'
@ -154,7 +154,7 @@
- create_identity_provider is not changed
- name: 'Create identity_provider (retry - no change)'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
domain_id: '{{ domain_id }}'
@ -186,7 +186,7 @@
- name: 'Update IDP set description - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
description: '{{ idp_description }}'
@ -197,7 +197,7 @@
- update_identity_provider is changed
- name: 'Update IDP set description'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
description: '{{ idp_description }}'
@ -226,7 +226,7 @@
- name: 'Update IDP set description (retry - no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
description: '{{ idp_description }}'
@ -237,7 +237,7 @@
- update_identity_provider is not changed
- name: 'Update IDP set description (retry - no change)'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
description: '{{ idp_description }}'
@ -267,7 +267,7 @@
- name: 'Update IDP set Remote IDs - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
remote_ids: '{{ remote_ids_1 }}'
@ -278,7 +278,7 @@
- update_identity_provider is changed
- name: 'Update IDP set Remote IDs'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
remote_ids: '{{ remote_ids_1 }}'
@ -307,7 +307,7 @@
- name: 'Update IDP set Remote IDs (retry - no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
remote_ids: '{{ remote_ids_1 }}'
@ -318,7 +318,7 @@
- update_identity_provider is not changed
- name: 'Update IDP set Remote IDs (retry - no change)'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
remote_ids: '{{ remote_ids_1 }}'
@ -348,7 +348,7 @@
- name: 'Update IDP set Disabled - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
enabled: False
@ -359,7 +359,7 @@
- update_identity_provider is changed
- name: 'Update IDP set Disabled'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
enabled: False
@ -388,7 +388,7 @@
- name: 'Update IDP set Disabled (retry - no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
enabled: False
@ -399,7 +399,7 @@
- update_identity_provider is not changed
- name: 'Update IDP set Disabled (retry - no change)'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
enabled: False
@ -429,7 +429,7 @@
# If we don't specify anything to change, then nothing should change...
- name: 'Minimal call to IDP (no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
register: update_identity_provider
@ -439,7 +439,7 @@
- update_identity_provider is not changed
- name: 'Minimal call to IDP (no change)'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
register: update_identity_provider
@ -470,7 +470,7 @@
- name: 'Update all updatable IDP parameters - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
description: '{{ idp_description_2 }}'
@ -483,7 +483,7 @@
- update_identity_provider is changed
- name: 'Update all updatable IDP parameters'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
description: '{{ idp_description_2 }}'
@ -514,7 +514,7 @@
- name: 'Update all updatable IDP parameters (no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
description: '{{ idp_description_2 }}'
@ -527,7 +527,7 @@
- update_identity_provider is not changed
- name: 'Update all updatable IDP parameters (no change)'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name }}'
description: '{{ idp_description_2 }}'
@ -561,7 +561,7 @@
- name: 'Create complex IDP - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name_2 }}'
domain_id: '{{ domain_id }}'
@ -575,7 +575,7 @@
- create_identity_provider is changed
- name: 'Create complex IDP'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name_2 }}'
domain_id: '{{ domain_id }}'
@ -607,7 +607,7 @@
- name: 'Create complex IDP (retry - no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name_2 }}'
domain_id: '{{ domain_id }}'
@ -621,7 +621,7 @@
- create_identity_provider is not changed
- name: 'Create complex IDP'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'present'
name: '{{ idp_name_2 }}'
domain_id: '{{ domain_id }}'
@ -653,7 +653,7 @@
# Attempt to ensure that if we search we only get the one we expect
- name: 'Fetch Complex IDP info - with name'
openstack.cloud.os_keystone_identity_provider_info:
openstack.cloud.federation_idp_info:
name: '{{ idp_name_2 }}'
register: identity_provider_info
- assert:
@ -680,7 +680,7 @@
# Ensure that if we do search we get both of the results we expect
- name: 'Fetch multiple IDP info - without name'
openstack.cloud.os_keystone_identity_provider_info: {}
openstack.cloud.federation_idp_info: {}
register: identity_provider_info
- assert:
that:
@ -722,7 +722,7 @@
- name: 'Delete identity_provider - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name }}'
register: delete_identity_provider
@ -732,7 +732,7 @@
- delete_identity_provider is changed
- name: 'Delete identity_provider'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name }}'
register: delete_identity_provider
@ -743,7 +743,7 @@
- name: 'Delete identity_provider (retry - no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name }}'
register: delete_identity_provider
@ -753,7 +753,7 @@
- delete_identity_provider is not changed
- name: 'Delete identity_provider (retry - no change) '
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name }}'
register: delete_identity_provider
@ -763,7 +763,7 @@
- delete_identity_provider is not changed
- name: 'Fetch identity_provider info after deletion'
openstack.cloud.os_keystone_identity_provider_info:
openstack.cloud.federation_idp_info:
name: '{{ idp_name }}'
register: identity_provider_info
ignore_errors: True
@ -772,7 +772,7 @@
- identity_provider_info is failed
- name: 'Delete second identity_provider'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name_2 }}'
register: delete_identity_provider
@ -783,19 +783,19 @@
always:
- name: 'Delete idp'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name }}'
ignore_errors: yes
- name: 'Delete second identity_provider'
openstack.cloud.os_keystone_identity_provider:
openstack.cloud.federation_idp:
state: 'absent'
name: '{{ idp_name_2 }}'
ignore_errors: yes
- name: 'Delete domain'
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
state: 'absent'
name: '{{ domain_name }}'
ignore_errors: yes

View File

@ -3,13 +3,13 @@
# meta/action_groups.yml glue seems to be missing
# group/os:
# cloud: "{{ cloud }}"
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
cloud: "{{ cloud }}"
openstack.cloud.os_keystone_mapping_info:
openstack.cloud.federation_mapping_info:
cloud: "{{ cloud }}"
block:
- name: "Ensure mapping doesn't exist to start"
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name }}'
register: delete_mapping
@ -18,7 +18,7 @@
- delete_mapping is successful
- name: 'Create mapping - CHECK_MODE'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name }}'
rules: '{{ mapping_rules_1 }}'
@ -30,7 +30,7 @@
- create_mapping is changed
- name: 'Fetch mapping info (mapping should be absent)'
openstack.cloud.os_keystone_mapping_info:
openstack.cloud.federation_mapping_info:
name: '{{ mapping_name }}'
register: mapping_info
ignore_errors: yes
@ -39,7 +39,7 @@
- mapping_info is failed
- name: 'Create mapping'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name }}'
rules: '{{ mapping_rules_1 }}'
@ -56,7 +56,7 @@
- create_mapping.mapping.rules | length == 1
- name: 'Fetch mapping info - with name'
openstack.cloud.os_keystone_mapping_info:
openstack.cloud.federation_mapping_info:
name: '{{ mapping_name }}'
register: mapping_info
- assert:
@ -74,7 +74,7 @@
mapping_0: '{{ mapping_info.mappings[0] }}'
- name: 'Fetch mapping info - without name'
openstack.cloud.os_keystone_mapping_info: {}
openstack.cloud.federation_mapping_info: {}
register: mapping_info
- assert:
that:
@ -92,7 +92,7 @@
mapping_0: '{{ mapping_info.mappings[0] }}'
- name: 'Create mapping (retry - no change) - CHECK_MODE'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name }}'
rules: '{{ mapping_rules_1 }}'
@ -104,7 +104,7 @@
- create_mapping is not changed
- name: 'Create mapping (retry - no change)'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name }}'
rules: '{{ mapping_rules_1 }}'
@ -121,7 +121,7 @@
- create_mapping.mapping.rules | length == 1
- name: 'Update mapping - CHECK_MODE'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name }}'
rules: '{{ mapping_rules_2 }}'
@ -133,7 +133,7 @@
- update_mapping is changed
- name: 'Update mapping'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name }}'
rules: '{{ mapping_rules_2 }}'
@ -150,7 +150,7 @@
- update_mapping.mapping.rules | length == 1
- name: 'Update mapping (retry - no change)'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name }}'
rules: '{{ mapping_rules_2 }}'
@ -167,7 +167,7 @@
- update_mapping.mapping.rules | length == 1
- name: 'Create second mapping'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'present'
name: '{{ mapping_name_2 }}'
rules: '{{ mapping_rules_1 }}'
@ -184,7 +184,7 @@
- create_mapping.mapping.rules | length == 1
- name: 'Fetch mapping (2) info - with name'
openstack.cloud.os_keystone_mapping_info:
openstack.cloud.federation_mapping_info:
name: '{{ mapping_name_2 }}'
register: mapping_info
- assert:
@ -202,7 +202,7 @@
mapping_0: '{{ mapping_info.mappings[0] }}'
- name: 'Fetch mapping info - without name'
openstack.cloud.os_keystone_mapping_info: {}
openstack.cloud.federation_mapping_info: {}
register: mapping_info
- assert:
that:
@ -226,7 +226,7 @@
mapping_1: '{{ mapping_info.mappings[1] }}'
- name: 'Delete mapping - CHECK_MODE'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name }}'
register: delete_mapping
@ -237,7 +237,7 @@
- delete_mapping is changed
- name: 'Delete mapping'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name }}'
register: delete_mapping
@ -247,7 +247,7 @@
- delete_mapping is changed
- name: 'Delete mapping (retry - no change) - CHECK_MODE'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name }}'
register: delete_mapping
@ -258,7 +258,7 @@
- delete_mapping is not changed
- name: 'Delete mapping (retry - no change) '
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name }}'
register: delete_mapping
@ -268,7 +268,7 @@
- delete_mapping is not changed
- name: 'Fetch mapping info after deletion'
openstack.cloud.os_keystone_mapping_info:
openstack.cloud.federation_mapping_info:
name: '{{ mapping_name }}'
register: mapping_info
ignore_errors: True
@ -277,7 +277,7 @@
- mapping_info is failed
- name: 'Delete second mapping'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name_2 }}'
register: delete_mapping
@ -288,13 +288,13 @@
always:
- name: 'Delete mapping'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name }}'
ignore_errors: yes
- name: 'Delete second mapping'
openstack.cloud.os_keystone_mapping:
openstack.cloud.federation_mapping:
state: 'absent'
name: '{{ mapping_name_2 }}'
ignore_errors: yes

View File

@ -1,12 +1,12 @@
---
- name: Create keystone role
openstack.cloud.os_keystone_role:
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: present
name: "{{ role_name }}"
- name: Delete keystone role
openstack.cloud.os_keystone_role:
openstack.cloud.identity_role:
cloud: "{{ cloud }}"
state: absent
name: "{{ role_name }}"

View File

@ -1,6 +1,6 @@
---
- name: Create network
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
state: present
@ -8,7 +8,7 @@
external: "{{ network_external }}"
- name: Delete network
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
state: absent

View File

@ -1,6 +1,6 @@
---
- name: Create public flavor
openstack.cloud.os_nova_flavor:
openstack.cloud.compute_flavor:
cloud: "{{ cloud }}"
state: present
name: ansible_public_flavor
@ -13,13 +13,13 @@
flavorid: 12345
- name: Delete public flavor
openstack.cloud.os_nova_flavor:
openstack.cloud.compute_flavor:
cloud: "{{ cloud }}"
state: absent
name: ansible_public_flavor
- name: Create private flavor
openstack.cloud.os_nova_flavor:
openstack.cloud.compute_flavor:
cloud: "{{ cloud }}"
state: present
name: ansible_private_flavor
@ -32,13 +32,13 @@
flavorid: 12345
- name: Delete private flavor
openstack.cloud.os_nova_flavor:
openstack.cloud.compute_flavor:
cloud: "{{ cloud }}"
state: absent
name: ansible_private_flavor
- name: Create flavor (defaults)
openstack.cloud.os_nova_flavor:
openstack.cloud.compute_flavor:
cloud: "{{ cloud }}"
state: present
name: ansible_defaults_flavor
@ -47,7 +47,7 @@
disk: 10
- name: Delete flavor (defaults)
openstack.cloud.os_nova_flavor:
openstack.cloud.compute_flavor:
cloud: "{{ cloud }}"
state: absent
name: ansible_defaults_flavor

View File

@ -4,14 +4,14 @@
register: tmp_file
- name: Create container
openstack.cloud.os_object:
openstack.cloud.object:
cloud: "{{ cloud }}"
state: present
container: ansible_container
container_access: private
- name: Put object
openstack.cloud.os_object:
openstack.cloud.object:
cloud: "{{ cloud }}"
state: present
name: ansible_object
@ -19,14 +19,14 @@
container: ansible_container
- name: Delete object
openstack.cloud.os_object:
openstack.cloud.object:
cloud: "{{ cloud }}"
state: absent
name: ansible_object
container: ansible_container
- name: Delete container
openstack.cloud.os_object:
openstack.cloud.object:
cloud: "{{ cloud }}"
state: absent
container: ansible_container

View File

@ -1,13 +1,13 @@
---
- name: Create network
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
state: present
name: "{{ network_name }}"
external: "{{ network_external }}"
- name: Create subnet
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: present
name: "{{ subnet_name }}"
@ -15,7 +15,7 @@
cidr: 10.5.5.0/24
- name: Create port (no security group or default security group)
openstack.cloud.os_port:
openstack.cloud.port:
cloud: "{{ cloud }}"
state: present
name: "{{ port_name }}"
@ -28,20 +28,20 @@
- debug: var=port
- name: Delete port (no security group or default security group)
openstack.cloud.os_port:
openstack.cloud.port:
cloud: "{{ cloud }}"
state: absent
name: "{{ port_name }}"
- name: Create security group
openstack.cloud.os_security_group:
openstack.cloud.security_group:
cloud: "{{ cloud }}"
state: present
name: "{{ secgroup_name }}"
description: Test group
- name: Create port (with security group)
openstack.cloud.os_port:
openstack.cloud.port:
cloud: "{{ cloud }}"
state: present
name: "{{ port_name }}"
@ -55,13 +55,13 @@
- debug: var=port
- name: Delete port (with security group)
openstack.cloud.os_port:
openstack.cloud.port:
cloud: "{{ cloud }}"
state: absent
name: "{{ port_name }}"
- name: Create port (with allowed_address_pairs and extra_dhcp_opts)
openstack.cloud.os_port:
openstack.cloud.port:
cloud: "{{ cloud }}"
state: present
name: "{{ port_name }}"
@ -77,25 +77,25 @@
- debug: var=port
- name: Delete port (with allowed_address_pairs and extra_dhcp_opts)
openstack.cloud.os_port:
openstack.cloud.port:
cloud: "{{ cloud }}"
state: absent
name: "{{ port_name }}"
- name: Delete security group
openstack.cloud.os_security_group:
openstack.cloud.security_group:
cloud: "{{ cloud }}"
state: absent
name: "{{ secgroup_name }}"
- name: Delete subnet
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: absent
name: "{{ subnet_name }}"
- name: Delete network
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
state: absent
name: "{{ network_name }}"

View File

@ -1,6 +1,6 @@
---
- name: Create project
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -12,7 +12,7 @@
- debug: var=project
- name: Update project
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -22,7 +22,7 @@
- debug: var=updatedproject
- name: Delete project
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: absent
name: ansible_project

View File

@ -1,7 +1,7 @@
---
- name: 'Create project with properties - CHECK_MODE'
check_mode: yes
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -17,7 +17,7 @@
- create_project_cm is changed
- name: 'Create project with properties'
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -37,7 +37,7 @@
- name: 'Create project with properties (retry - no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -53,7 +53,7 @@
- create_project_retry_cm is not changed
- name: 'Create project with properties (retry - no change)'
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -73,7 +73,7 @@
- name: 'Update project with properties - CHECK_MODE'
check_mode: yes
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -87,7 +87,7 @@
- updated_project_cm is changed
- name: 'Update project with properties'
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -105,7 +105,7 @@
- name: 'Update project with properties (retry - no change) - CHECK_MODE'
check_mode: yes
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -119,7 +119,7 @@
- updated_project_retry_cm is not changed
- name: 'Update project with properties (retry - no change)'
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: present
name: ansible_project
@ -136,7 +136,7 @@
- updated_project_retry["project"].dummy_key == dummy_value_updated
- name: Delete project with properties
openstack.cloud.os_project:
openstack.cloud.project:
cloud: "{{ cloud }}"
state: absent
name: ansible_project

View File

@ -1,14 +1,14 @@
---
# Regular user operation
- name: Create internal network
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
state: present
name: "{{ network_name }}"
external: false
- name: Create subnet1
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: present
network_name: "{{ network_name }}"
@ -16,13 +16,13 @@
cidr: 10.7.7.0/24
- name: Create router
openstack.cloud.os_router:
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
- name: Update router (add interface)
openstack.cloud.os_router:
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
@ -30,7 +30,7 @@
- shade_subnet1
- name: Gather routers info
openstack.cloud.os_routers_info:
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
filters:
@ -45,7 +45,7 @@
# Admin operation
- name: Create external network
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
state: present
name: "{{ external_network_name }}"
@ -54,7 +54,7 @@
- network_external
- name: Create subnet2
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: present
network_name: "{{ external_network_name }}"
@ -64,7 +64,7 @@
- network_external
- name: Update router (add external gateway)
openstack.cloud.os_router:
openstack.cloud.router:
cloud: "{{ cloud }}"
state: present
name: "{{ router_name }}"
@ -75,7 +75,7 @@
- network_external
- name: Gather routers info
openstack.cloud.os_routers_info:
openstack.cloud.routers_info:
cloud: "{{ cloud }}"
name: "{{ router_name }}"
filters:
@ -89,19 +89,19 @@
- (result.openstack_routers.0.interfaces_info|length) == 1
- name: Delete router
openstack.cloud.os_router:
openstack.cloud.router:
cloud: "{{ cloud }}"
state: absent
name: "{{ router_name }}"
- name: Delete subnet1
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: absent
name: shade_subnet1
- name: Delete subnet2
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
state: absent
name: shade_subnet2
@ -109,13 +109,13 @@
- network_external
- name: Delete internal network
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
state: absent
name: "{{ network_name }}"
- name: Delete external network
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
state: absent
name: "{{ external_network_name }}"

View File

@ -1,13 +1,13 @@
---
- name: Create security group
openstack.cloud.os_security_group:
openstack.cloud.security_group:
cloud: "{{ cloud }}"
name: "{{ secgroup_name }}"
state: present
description: Created from Ansible playbook
- name: Create empty ICMP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
@ -15,7 +15,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Create -1 ICMP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
@ -25,7 +25,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Create empty TCP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
@ -33,7 +33,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Create empty UDP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
@ -41,7 +41,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Create HTTP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
@ -51,7 +51,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Create egress rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
@ -62,7 +62,7 @@
direction: egress
- name: Delete empty ICMP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: absent
@ -70,7 +70,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Delete -1 ICMP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: absent
@ -80,7 +80,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Delete empty TCP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: absent
@ -88,7 +88,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Delete empty UDP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: absent
@ -96,7 +96,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Delete HTTP rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: absent
@ -106,7 +106,7 @@
remote_ip_prefix: 0.0.0.0/0
- name: Delete egress rule
openstack.cloud.os_security_group_rule:
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: absent
@ -117,7 +117,7 @@
direction: egress
- name: Delete security group
openstack.cloud.os_security_group:
openstack.cloud.security_group:
cloud: "{{ cloud }}"
name: "{{ secgroup_name }}"
state: absent

View File

@ -1,6 +1,6 @@
---
- name: Create server with meta as CSV
openstack.cloud.os_server:
openstack.cloud.server:
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
@ -15,14 +15,14 @@
- debug: var=server
- name: Delete server with meta as CSV
openstack.cloud.os_server:
openstack.cloud.server:
cloud: "{{ cloud }}"
state: absent
name: "{{ server_name }}"
wait: true
- name: Create server with meta as dict
openstack.cloud.os_server:
openstack.cloud.server:
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
@ -39,14 +39,14 @@
- debug: var=server
- name: Delete server with meta as dict
openstack.cloud.os_server:
openstack.cloud.server:
cloud: "{{ cloud }}"
state: absent
name: "{{ server_name }}"
wait: true
- name: Create server (FIP from pool/network)
openstack.cloud.os_server:
openstack.cloud.server:
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
@ -61,14 +61,14 @@
- debug: var=server
- name: Delete server (FIP from pool/network)
openstack.cloud.os_server:
openstack.cloud.server:
cloud: "{{ cloud }}"
state: absent
name: "{{ server_name }}"
wait: true
- name: Create server from volume
openstack.cloud.os_server:
openstack.cloud.server:
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
@ -85,7 +85,7 @@
- debug: var=server
- name: Delete server with volume
openstack.cloud.os_server:
openstack.cloud.server:
cloud: "{{ cloud }}"
state: absent
name: "{{ server_name }}"

View File

@ -1,12 +1,12 @@
---
- name: Create network {{ network_name }}
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
state: present
- name: Create subnet {{ subnet_name }} on network {{ network_name }}
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
network_name: "{{ network_name }}"
name: "{{ subnet_name }}"
@ -21,7 +21,7 @@
allocation_pool_end: 192.168.0.254
- name: Update subnet
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
network_name: "{{ network_name }}"
name: "{{ subnet_name }}"
@ -31,13 +31,13 @@
cidr: 192.168.0.0/24
- name: Delete subnet {{ subnet_name }}
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
name: "{{ subnet_name }}"
state: absent
- name: Delete network {{ network_name }}
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
state: absent

View File

@ -1,12 +1,12 @@
---
- name: Create network {{ network_name }}
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
state: present
- name: Create subnet {{ subnet_name }} on network {{ network_name }}
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
network_name: "{{ network_name }}"
enable_dhcp: "{{ enable_subnet_dhcp }}"
@ -18,7 +18,7 @@
allocation_pool_end: 192.168.0.4
- name: Update subnet {{ subnet_name }} allocation pools
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
network_name: "{{ network_name }}"
name: "{{ subnet_name }}"
@ -28,7 +28,7 @@
allocation_pool_end: 192.168.0.8
- name: Get Subnet Info
openstack.cloud.os_subnets_info:
openstack.cloud.subnets_info:
cloud: "{{ cloud }}"
name: "{{ subnet_name }}"
register: subnet_result
@ -51,13 +51,13 @@
- {start: '192.168.0.5', end: '192.168.0.8'}
- name: Delete subnet {{ subnet_name }}
openstack.cloud.os_subnet:
openstack.cloud.subnet:
cloud: "{{ cloud }}"
name: "{{ subnet_name }}"
state: absent
- name: Delete network {{ network_name }}
openstack.cloud.os_network:
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
state: absent

View File

@ -1,6 +1,6 @@
---
- name: Create user
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
@ -13,7 +13,7 @@
- debug: var=user
- name: Update user
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
@ -24,7 +24,7 @@
- debug: var=updateduser
- name: Delete user
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: ansible_user

View File

@ -1,6 +1,6 @@
---
- name: Create user
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: ansible_user
@ -11,21 +11,21 @@
register: user
- name: Assign user to nonadmins group
openstack.cloud.os_user_group:
openstack.cloud.group_assignment:
cloud: "{{ cloud }}"
state: present
user: ansible_user
group: nonadmins
- name: Remove user from nonadmins group
openstack.cloud.os_user_group:
openstack.cloud.group_assignment:
cloud: "{{ cloud }}"
state: absent
user: ansible_user
group: nonadmins
- name: Delete user
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: ansible_user

View File

@ -1,96 +1,96 @@
- name: Create domain
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
state: present
name: "{{ domain_name }}"
register: domain
- name: Create group in default domain
openstack.cloud.os_group:
openstack.cloud.identity_group:
cloud: "{{ cloud }}"
state: present
name: "{{ group_name }}"
domain_id: default
- name: Create group in specific domain
openstack.cloud.os_group:
openstack.cloud.identity_group:
cloud: "{{ cloud }}"
state: present
name: "{{ group_name }}"
domain_id: "{{ domain.id }}"
- name: Create user in default domain
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: "{{ user_name }}"
domain: default
- name: Create user in specific domain
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: present
name: "{{ user_name }}"
domain: "{{ domain.id }}"
- name: Assign role to group in default domain
openstack.cloud.os_user_role:
openstack.cloud.role_assignment:
cloud: "{{ cloud }}"
role: "{{ keystone_role_name }}"
group: "{{ group_name }}"
domain: default
- name: Assign role to group in specific domain
openstack.cloud.os_user_role:
openstack.cloud.role_assignment:
cloud: "{{ cloud }}"
role: "{{ keystone_role_name }}"
group: "{{ group_name }}"
domain: "{{ domain.id }}"
- name: Assign role to user in default domain
openstack.cloud.os_user_role:
openstack.cloud.role_assignment:
cloud: "{{ cloud }}"
role: "{{ keystone_role_name }}"
user: "{{ user_name }}"
domain: default
- name: Assign role to user in specific domain
openstack.cloud.os_user_role:
openstack.cloud.role_assignment:
cloud: "{{ cloud }}"
role: "{{ keystone_role_name }}"
user: "{{ user_name }}"
domain: "{{ domain.id }}"
- name: Delete group in default domain
openstack.cloud.os_group:
openstack.cloud.identity_group:
cloud: "{{ cloud }}"
state: absent
name: "{{ group_name }}"
domain_id: default
- name: Delete group in specific domain
openstack.cloud.os_group:
openstack.cloud.identity_group:
cloud: "{{ cloud }}"
state: absent
name: "{{ group_name }}"
domain_id: "{{ domain.id }}"
- name: Delete user in default domain
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: "{{ user_name }}"
domain: default
- name: Delete user in specific domain
openstack.cloud.os_user:
openstack.cloud.identity_user:
cloud: "{{ cloud }}"
state: absent
name: "{{ user_name }}"
domain: "{{ domain.id }}"
- name: Delete domain
openstack.cloud.os_keystone_domain:
openstack.cloud.identity_domain:
cloud: "{{ cloud }}"
state: absent
name: "{{ domain_name }}"

View File

@ -1,6 +1,6 @@
---
- name: Create volume
openstack.cloud.os_volume:
openstack.cloud.volume:
cloud: "{{ cloud }}"
state: present
size: 1
@ -11,7 +11,7 @@
- debug: var=vol
- name: Delete volume
openstack.cloud.os_volume:
openstack.cloud.volume:
cloud: "{{ cloud }}"
state: absent
display_name: ansible_volume

View File

@ -7,7 +7,7 @@
- { role: auth, tags: auth }
- { role: client_config, tags: client_config }
- { role: group, tags: group }
# TODO(mordred) Reenable this once the fixed os_image winds up in an
# TODO(mordred) Reenable this once the fixed openstack.cloud.image winds up in an
# upstream ansible release.
# - { role: image, tags: image }
- { role: keypair, tags: keypair }

View File

@ -1,38 +1,240 @@
openstack:
- auth
- baremetal_inspect
- baremetal_inspect
- baremetal_node
- baremetal_node
- baremetal_node_action
- baremetal_node_action
- catalog_endpoint
- catalog_service
- catalog_service
- coe_cluster
- coe_cluster_template
- compute_flavor
- compute_flavor
- compute_flavor
- compute_flavor_info
- compute_flavor_info
- config
- config
- dns_zone
- dns_zone
- endpoint
- endpoint
- federation_idp
- federation_idp
- federation_idp_info
- federation_idp_info
- federation_mapping
- federation_mapping
- federation_mapping_info
- federation_mapping_info
- floating_ip
- group_assignment
- group_assignment
- host_aggregate
- host_aggregate
- identity_domain
- identity_domain
- identity_domain_info
- identity_domain_info
- identity_group
- identity_group
- identity_group_info
- identity_group_info
- identity_role
- identity_role
- identity_user
- identity_user
- identity_user_info
- identity_user_info
- image
- image_info
- keypair
- keystone_federation_protocol
- keystone_federation_protocol_info
- lb_listener
- lb_listener
- lb_member
- lb_member
- lb_pool
- lb_pool
- loadbalancer
- network
- networks_info
- object
- port
- port_info
- project
- project_access
- project_info
- quota
- recordset
- role_assignment
- role_assignment
- router
- routers_info
- security_group
- security_group_rule
- server
- server_action
- server_group
- server_info
- server_metadata
- server_volume
- stack
- subnet
- subnets_info
- volume
- volume_snapshot
os:
- auth
- baremetal_inspect
- baremetal_inspect
- baremetal_node
- baremetal_node
- baremetal_node_action
- baremetal_node_action
- catalog_endpoint
- catalog_service
- catalog_service
- coe_cluster
- coe_cluster_template
- compute_flavor
- compute_flavor
- compute_flavor
- compute_flavor_info
- compute_flavor_info
- config
- config
- dns_zone
- dns_zone
- endpoint
- endpoint
- federation_idp
- federation_idp
- federation_idp_info
- federation_idp_info
- federation_mapping
- federation_mapping
- federation_mapping_info
- federation_mapping_info
- floating_ip
- group_assignment
- group_assignment
- host_aggregate
- host_aggregate
- identity_domain
- identity_domain
- identity_domain_info
- identity_domain_info
- identity_group
- identity_group
- identity_group_info
- identity_group_info
- identity_role
- identity_role
- identity_user
- identity_user
- identity_user_info
- identity_user_info
- image
- image_info
- keypair
- keystone_federation_protocol
- keystone_federation_protocol_info
- lb_listener
- lb_listener
- lb_member
- lb_member
- lb_pool
- lb_pool
- loadbalancer
- network
- networks_info
- object
- port
- port_info
- project
- project_access
- project_info
- quota
- recordset
- role_assignment
- role_assignment
- router
- routers_info
- security_group
- security_group_rule
- server
- server_action
- server_group
- server_info
- server_metadata
- server_volume
- stack
- subnet
- subnets_info
- volume
- volume_snapshot
- os_auth
- os_client_config
- os_client_config
- os_coe_cluster
- os_coe_cluster_template
- os_endpoint
- os_flavor
- os_flavor_info
- os_flavor_info
- os_floating_ip
- os_group
- os_group
- os_group_info
- os_group_info
- os_image
- os_image_info
- os_ironic
- os_ironic
- os_ironic_inspect
- os_ironic_inspect
- os_ironic_node
- os_ironic_node
- os_keypair
- os_keystone_domain
- os_keystone_domain
- os_keystone_domain_info
- os_keystone_domain_info
- os_keystone_endpoint
- os_keystone_identity_provider
- os_keystone_identity_provider_info
- os_keystone_mapping
- os_keystone_mapping_info
- os_keystone_endpoint
- os_keystone_federation_protocol
- os_keystone_federation_protocol_info
- os_keystone_identity_provider
- os_keystone_identity_provider
- os_keystone_identity_provider_info
- os_keystone_identity_provider_info
- os_keystone_mapping
- os_keystone_mapping
- os_keystone_mapping_info
- os_keystone_mapping_info
- os_keystone_role
- os_keystone_role
- os_keystone_service
- os_keystone_service
- os_listener
- os_listener
- os_loadbalancer
- os_member
- os_member
- os_network
- os_networks_info
- os_nova_flavor
- os_nova_flavor
- os_nova_host_aggregate
- os_nova_host_aggregate
- os_object
- os_pool
- os_pool
- os_port
- os_port_info
- os_project
@ -54,9 +256,14 @@ os:
- os_subnet
- os_subnets_info
- os_user
- os_user
- os_user_group
- os_user_group
- os_user_info
- os_user_info
- os_user_role
- os_user_role
- os_volume
- os_volume_snapshot
- os_zone
- os_zone

View File

@ -20,6 +20,16 @@ plugin_routing:
removal_date: TBD
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.coe_cluster_template
redirect: openstack.cloud.coe_cluster_template
os_endpoint:
deprecation:
removal_date: TBD
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.catalog_endpoint
redirect: openstack.cloud.catalog_endpoint
os_flavor:
deprecation:
removal_date: TBD
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.compute_flavor
redirect: openstack.cloud.compute_flavor
os_flavor_info:
deprecation:
removal_date: TBD

73
plugins/modules/auth.py Normal file
View File

@ -0,0 +1,73 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: auth
short_description: Retrieve an auth token
author: "Monty Taylor (@emonty)"
description:
- Retrieve an auth token from an OpenStack Cloud
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Authenticate to the cloud and retrieve the service catalog
openstack.cloud.auth:
cloud: rax-dfw
- name: Show service catalog
debug:
var: service_catalog
'''
RETURN = '''
auth_token:
description: Openstack API Auth Token
returned: success
type: str
service_catalog:
description: A dictionary of available API endpoints
returned: success
type: dict
'''
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec()
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:
module.exit_json(
changed=False,
ansible_facts=dict(
auth_token=cloud.auth_token,
service_catalog=cloud.service_catalog))
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
if __name__ == '__main__':
main()

View File

@ -0,0 +1,152 @@
#!/usr/bin/python
# coding: utf-8 -*-
# (c) 2015-2016, Hewlett Packard Enterprise Development Company LP
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: baremetal_inspect
short_description: Explicitly triggers baremetal node introspection in ironic.
author: "Julia Kreger (@juliakreger)"
description:
- Requests Ironic to set a node into inspect state in order to collect metadata regarding the node.
This command may be out of band or in-band depending on the ironic driver configuration.
This is only possible on nodes in 'manageable' and 'available' state.
options:
mac:
description:
- unique mac address that is used to attempt to identify the host.
type: str
uuid:
description:
- globally unique identifier (UUID) to identify the host.
type: str
name:
description:
- unique name identifier to identify the host in Ironic.
type: str
ironic_url:
description:
- If noauth mode is utilized, this is required to be set to the endpoint URL for the Ironic API.
Use with "auth" and "auth_type" settings set to None.
type: str
timeout:
description:
- A timeout in seconds to tell the role to wait for the node to complete introspection if wait is set to True.
default: 1200
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
ansible_facts:
description: Dictionary of new facts representing discovered properties of the node..
returned: changed
type: complex
contains:
memory_mb:
description: Amount of node memory as updated in the node properties
type: str
sample: "1024"
cpu_arch:
description: Detected CPU architecture type
type: str
sample: "x86_64"
local_gb:
description: Total size of local disk storage as updated in node properties.
type: str
sample: "10"
cpus:
description: Count of cpu cores defined in the updated node properties.
type: str
sample: "1"
'''
EXAMPLES = '''
# Invoke node inspection
- openstack.cloud.baremetal_inspect:
name: "testnode1"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _choose_id_value(module):
if module.params['uuid']:
return module.params['uuid']
if module.params['name']:
return module.params['name']
return None
def main():
argument_spec = openstack_full_argument_spec(
auth_type=dict(required=False),
uuid=dict(required=False),
name=dict(required=False),
mac=dict(required=False),
ironic_url=dict(required=False),
timeout=dict(default=1200, type='int', required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if (
module.params['auth_type'] in [None, 'None']
and module.params['ironic_url'] is None
):
module.fail_json(msg="Authentication appears to be disabled, "
"Please define an ironic_url parameter")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None']
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
sdk, cloud = openstack_cloud_from_module(module)
try:
if module.params['name'] or module.params['uuid']:
server = cloud.get_machine(_choose_id_value(module))
elif module.params['mac']:
server = cloud.get_machine_by_mac(module.params['mac'])
else:
module.fail_json(msg="The worlds did not align, "
"the host was not found as "
"no name, uuid, or mac was "
"defined.")
if server:
cloud.inspect_machine(server['uuid'], module.params['wait'])
# TODO(TheJulia): diff properties, ?and ports? and determine
# if a change occurred. In theory, the node is always changed
# if introspection is able to update the record.
module.exit_json(changed=True,
ansible_facts=server['properties'])
else:
module.fail_json(msg="node not found.")
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,380 @@
#!/usr/bin/python
# coding: utf-8 -*-
# (c) 2014, Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: baremetal_node
short_description: Create/Delete Bare Metal Resources from OpenStack
author: "Monty Taylor (@emonty)"
description:
- Create or Remove Ironic nodes from OpenStack.
options:
state:
description:
- Indicates desired state of the resource
choices: ['present', 'absent']
default: present
type: str
uuid:
description:
- globally unique identifier (UUID) to be given to the resource. Will
be auto-generated if not specified, and name is specified.
- Definition of a UUID will always take precedence to a name value.
type: str
name:
description:
- unique name identifier to be given to the resource.
type: str
driver:
description:
- The name of the Ironic Driver to use with this node.
- Required when I(state=present)
type: str
chassis_uuid:
description:
- Associate the node with a pre-defined chassis.
type: str
ironic_url:
description:
- If noauth mode is utilized, this is required to be set to the
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
settings set to None.
type: str
driver_info:
description:
- Information for this server's driver. Will vary based on which
driver is in use. Any sub-field which is populated will be validated
during creation.
required: true
type: dict
suboptions:
power:
description:
- Information necessary to turn this server on / off.
This often includes such things as IPMI username, password, and IP address.
required: true
deploy:
description:
- Information necessary to deploy this server directly, without using Nova. THIS IS NOT RECOMMENDED.
console:
description:
- Information necessary to connect to this server's serial console. Not all drivers support this.
management:
description:
- Information necessary to interact with this server's management interface. May be shared by power_info in some cases.
required: true
nics:
description:
- 'A list of network interface cards, eg, " - mac: aa:bb:cc:aa:bb:cc"'
required: true
type: list
elements: dict
suboptions:
mac:
description: The MAC address of the network interface card.
type: str
required: true
properties:
description:
- Definition of the physical characteristics of this server, used for scheduling purposes
type: dict
suboptions:
cpu_arch:
description:
- CPU architecture (x86_64, i686, ...)
default: x86_64
cpus:
description:
- Number of CPU cores this machine has
default: 1
ram:
description:
- amount of RAM this machine has, in MB
default: 1
disk_size:
description:
- size of first storage device in this machine (typically /dev/sda), in GB
default: 1
capabilities:
description:
- special capabilities for the node, such as boot_option, node_role etc
(see U(https://docs.openstack.org/ironic/latest/install/advanced.html)
for more information)
default: ""
root_device:
description:
- Root disk device hints for deployment.
- See U(https://docs.openstack.org/ironic/latest/install/advanced.html#specifying-the-disk-for-deployment-root-device-hints)
for allowed hints.
default: ""
skip_update_of_masked_password:
description:
- Allows the code that would assert changes to nodes to skip the
update if the change is a single line consisting of the password
field.
- As of Kilo, by default, passwords are always masked to API
requests, which means the logic as a result always attempts to
re-assert the password field.
- C(skip_update_of_driver_password) is deprecated alias and will be removed in 2.14.
type: bool
default: 'no'
aliases: [ skip_update_of_driver_password ]
requirements:
- "python >= 3.6"
- "openstacksdk"
- "jsonpatch"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Enroll a node with some basic properties and driver info
- openstack.cloud.baremetal_node:
cloud: "devstack"
driver: "pxe_ipmitool"
uuid: "00000000-0000-0000-0000-000000000002"
properties:
cpus: 2
cpu_arch: "x86_64"
ram: 8192
disk_size: 64
capabilities: "boot_option:local"
root_device:
wwn: "0x4000cca77fc4dba1"
nics:
- mac: "aa:bb:cc:aa:bb:cc"
- mac: "dd:ee:ff:dd:ee:ff"
driver_info:
power:
ipmi_address: "1.2.3.4"
ipmi_username: "admin"
ipmi_password: "adminpass"
chassis_uuid: "00000000-0000-0000-0000-000000000001"
'''
try:
import jsonpatch
HAS_JSONPATCH = True
except ImportError:
HAS_JSONPATCH = False
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _parse_properties(module):
p = module.params['properties']
props = dict(
cpu_arch=p.get('cpu_arch') if p.get('cpu_arch') else 'x86_64',
cpus=p.get('cpus') if p.get('cpus') else 1,
memory_mb=p.get('ram') if p.get('ram') else 1,
local_gb=p.get('disk_size') if p.get('disk_size') else 1,
capabilities=p.get('capabilities') if p.get('capabilities') else '',
root_device=p.get('root_device') if p.get('root_device') else '',
)
return props
def _parse_driver_info(sdk, module):
p = module.params['driver_info']
info = p.get('power')
if not info:
raise sdk.exceptions.OpenStackCloudException(
"driver_info['power'] is required")
if p.get('console'):
info.update(p.get('console'))
if p.get('management'):
info.update(p.get('management'))
if p.get('deploy'):
info.update(p.get('deploy'))
return info
def _choose_id_value(module):
if module.params['uuid']:
return module.params['uuid']
if module.params['name']:
return module.params['name']
return None
def _choose_if_password_only(module, patch):
if len(patch) == 1:
if 'password' in patch[0]['path'] and module.params['skip_update_of_masked_password']:
# Return false to abort update as the password appears
# to be the only element in the patch.
return False
return True
def _exit_node_not_updated(module, server):
module.exit_json(
changed=False,
result="Node not updated",
uuid=server['uuid'],
provision_state=server['provision_state']
)
def main():
argument_spec = openstack_full_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
driver=dict(required=False),
driver_info=dict(type='dict', required=True),
nics=dict(type='list', required=True, elements="dict"),
properties=dict(type='dict', default={}),
ironic_url=dict(required=False),
chassis_uuid=dict(required=False),
skip_update_of_masked_password=dict(
required=False,
type='bool',
aliases=['skip_update_of_driver_password'],
deprecated_aliases=[dict(name='skip_update_of_driver_password', version='2.14')]
),
state=dict(required=False, default='present', choices=['present', 'absent'])
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if not HAS_JSONPATCH:
module.fail_json(msg='jsonpatch is required for this module')
if (
module.params['auth_type'] in [None, 'None']
and module.params['ironic_url'] is None
):
module.fail_json(msg="Authentication appears to be disabled, "
"Please define an ironic_url parameter")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None']
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
node_id = _choose_id_value(module)
sdk, cloud = openstack_cloud_from_module(module)
try:
server = cloud.get_machine(node_id)
if module.params['state'] == 'present':
if module.params['driver'] is None:
module.fail_json(msg="A driver must be defined in order "
"to set a node to present.")
properties = _parse_properties(module)
driver_info = _parse_driver_info(sdk, module)
kwargs = dict(
driver=module.params['driver'],
properties=properties,
driver_info=driver_info,
name=module.params['name'],
)
if module.params['chassis_uuid']:
kwargs['chassis_uuid'] = module.params['chassis_uuid']
if server is None:
# Note(TheJulia): Add a specific UUID to the request if
# present in order to be able to re-use kwargs for if
# the node already exists logic, since uuid cannot be
# updated.
if module.params['uuid']:
kwargs['uuid'] = module.params['uuid']
server = cloud.register_machine(module.params['nics'],
**kwargs)
module.exit_json(changed=True, uuid=server['uuid'],
provision_state=server['provision_state'])
else:
# TODO(TheJulia): Presently this does not support updating
# nics. Support needs to be added.
#
# Note(TheJulia): This message should never get logged
# however we cannot realistically proceed if neither a
# name or uuid was supplied to begin with.
if not node_id:
module.fail_json(msg="A uuid or name value "
"must be defined")
# Note(TheJulia): Constructing the configuration to compare
# against. The items listed in the server_config block can
# be updated via the API.
server_config = dict(
driver=server['driver'],
properties=server['properties'],
driver_info=server['driver_info'],
name=server['name'],
)
# Add the pre-existing chassis_uuid only if
# it is present in the server configuration.
if hasattr(server, 'chassis_uuid'):
server_config['chassis_uuid'] = server['chassis_uuid']
# Note(TheJulia): If a password is defined and concealed, a
# patch will always be generated and re-asserted.
patch = jsonpatch.JsonPatch.from_diff(server_config, kwargs)
if not patch:
_exit_node_not_updated(module, server)
elif _choose_if_password_only(module, list(patch)):
# Note(TheJulia): Normally we would allow the general
# exception catch below, however this allows a specific
# message.
try:
server = cloud.patch_machine(
server['uuid'],
list(patch))
except Exception as e:
module.fail_json(msg="Failed to update node, "
"Error: %s" % e.message)
# Enumerate out a list of changed paths.
change_list = []
for change in list(patch):
change_list.append(change['path'])
module.exit_json(changed=True,
result="Node Updated",
changes=change_list,
uuid=server['uuid'],
provision_state=server['provision_state'])
# Return not updated by default as the conditions were not met
# to update.
_exit_node_not_updated(module, server)
if module.params['state'] == 'absent':
if not node_id:
module.fail_json(msg="A uuid or name value must be defined "
"in order to remove a node.")
if server is not None:
cloud.unregister_machine(module.params['nics'],
server['uuid'])
module.exit_json(changed=True, result="deleted")
else:
module.exit_json(changed=False, result="Server not found")
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,379 @@
#!/usr/bin/python
# coding: utf-8 -*-
# (c) 2015, Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: baremetal_node_action
short_description: Activate/Deactivate Bare Metal Resources from OpenStack
author: "Monty Taylor (@emonty)"
description:
- Deploy to nodes controlled by Ironic.
options:
name:
description:
- Name of the node to create.
type: str
state:
description:
- Indicates desired state of the resource.
- I(state) can be C('present'), C('absent'), C('maintenance') or C('off').
default: present
type: str
deploy:
description:
- Indicates if the resource should be deployed. Allows for deployment
logic to be disengaged and control of the node power or maintenance
state to be changed.
type: str
default: 'yes'
uuid:
description:
- globally unique identifier (UUID) to be given to the resource.
type: str
ironic_url:
description:
- If noauth mode is utilized, this is required to be set to the
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
settings set to None.
type: str
config_drive:
description:
- A configdrive file or HTTP(S) URL that will be passed along to the
node.
type: raw
instance_info:
description:
- Definition of the instance information which is used to deploy
the node. This information is only required when an instance is
set to present.
type: dict
suboptions:
image_source:
description:
- An HTTP(S) URL where the image can be retrieved from.
image_checksum:
description:
- The checksum of image_source.
image_disk_format:
description:
- The type of image that has been requested to be deployed.
power:
description:
- A setting to allow power state to be asserted allowing nodes
that are not yet deployed to be powered on, and nodes that
are deployed to be powered off.
- I(power) can be C('present'), C('absent'), C('maintenance') or C('off').
default: present
type: str
maintenance:
description:
- A setting to allow the direct control if a node is in
maintenance mode.
- I(maintenance) can be C('yes'), C('no'), C('True'), or C('False').
type: str
maintenance_reason:
description:
- A string expression regarding the reason a node is in a
maintenance mode.
type: str
wait:
description:
- A boolean value instructing the module to wait for node
activation or deactivation to complete before returning.
type: bool
default: 'no'
timeout:
description:
- An integer value representing the number of seconds to
wait for the node activation or deactivation to complete.
default: 1800
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Activate a node by booting an image with a configdrive attached
- openstack.cloud.baremetal_node_action:
cloud: "openstack"
uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
state: present
power: present
deploy: True
maintenance: False
config_drive: "http://192.168.1.1/host-configdrive.iso"
instance_info:
image_source: "http://192.168.1.1/deploy_image.img"
image_checksum: "356a6b55ecc511a20c33c946c4e678af"
image_disk_format: "qcow"
delegate_to: localhost
# Activate a node by booting an image with a configdrive json object
- openstack.cloud.baremetal_node_action:
uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
auth_type: None
ironic_url: "http://192.168.1.1:6385/"
config_drive:
meta_data:
hostname: node1
public_keys:
default: ssh-rsa AAA...BBB==
instance_info:
image_source: "http://192.168.1.1/deploy_image.img"
image_checksum: "356a6b55ecc511a20c33c946c4e678af"
image_disk_format: "qcow"
delegate_to: localhost
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _choose_id_value(module):
if module.params['uuid']:
return module.params['uuid']
if module.params['name']:
return module.params['name']
return None
def _is_true(value):
true_values = [True, 'yes', 'Yes', 'True', 'true', 'present', 'on']
if value in true_values:
return True
return False
def _is_false(value):
false_values = [False, None, 'no', 'No', 'False', 'false', 'absent', 'off']
if value in false_values:
return True
return False
def _check_set_maintenance(module, cloud, node):
if _is_true(module.params['maintenance']):
if _is_false(node['maintenance']):
cloud.set_machine_maintenance_state(
node['uuid'],
True,
reason=module.params['maintenance_reason'])
module.exit_json(changed=True, msg="Node has been set into "
"maintenance mode")
else:
# User has requested maintenance state, node is already in the
# desired state, checking to see if the reason has changed.
if (str(node['maintenance_reason']) not in
str(module.params['maintenance_reason'])):
cloud.set_machine_maintenance_state(
node['uuid'],
True,
reason=module.params['maintenance_reason'])
module.exit_json(changed=True, msg="Node maintenance reason "
"updated, cannot take any "
"additional action.")
elif _is_false(module.params['maintenance']):
if node['maintenance'] is True:
cloud.remove_machine_from_maintenance(node['uuid'])
return True
else:
module.fail_json(msg="maintenance parameter was set but a valid "
"the value was not recognized.")
return False
def _check_set_power_state(module, cloud, node):
if 'power on' in str(node['power_state']):
if _is_false(module.params['power']):
# User has requested the node be powered off.
cloud.set_machine_power_off(node['uuid'])
module.exit_json(changed=True, msg="Power requested off")
if 'power off' in str(node['power_state']):
if (
_is_false(module.params['power'])
and _is_false(module.params['state'])
):
return False
if (
_is_false(module.params['power'])
and _is_false(module.params['state'])
):
module.exit_json(
changed=False,
msg="Power for node is %s, node must be reactivated "
"OR set to state absent"
)
# In the event the power has been toggled on and
# deployment has been requested, we need to skip this
# step.
if (
_is_true(module.params['power'])
and _is_false(module.params['deploy'])
):
# Node is powered down when it is not awaiting to be provisioned
cloud.set_machine_power_on(node['uuid'])
return True
# Default False if no action has been taken.
return False
def main():
argument_spec = openstack_full_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
instance_info=dict(type='dict', required=False),
config_drive=dict(type='raw', required=False),
ironic_url=dict(required=False),
state=dict(required=False, default='present'),
maintenance=dict(required=False),
maintenance_reason=dict(required=False),
power=dict(required=False, default='present'),
deploy=dict(required=False, default='yes'),
wait=dict(type='bool', required=False, default=False),
timeout=dict(required=False, type='int', default=1800),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if (
module.params['auth_type'] in [None, 'None']
and module.params['ironic_url'] is None
):
module.fail_json(msg="Authentication appears disabled, Please "
"define an ironic_url parameter")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None']
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
if (
module.params['config_drive']
and not isinstance(module.params['config_drive'], (str, dict))
):
config_drive_type = type(module.params['config_drive'])
msg = ('argument config_drive is of type %s and we expected'
' str or dict') % config_drive_type
module.fail_json(msg=msg)
node_id = _choose_id_value(module)
if not node_id:
module.fail_json(msg="A uuid or name value must be defined "
"to use this module.")
sdk, cloud = openstack_cloud_from_module(module)
try:
node = cloud.get_machine(node_id)
if node is None:
module.fail_json(msg="node not found")
uuid = node['uuid']
instance_info = module.params['instance_info']
changed = False
wait = module.params['wait']
timeout = module.params['timeout']
# User has requested desired state to be in maintenance state.
if module.params['state'] == 'maintenance':
module.params['maintenance'] = True
if node['provision_state'] in [
'cleaning',
'deleting',
'wait call-back']:
module.fail_json(msg="Node is in %s state, cannot act upon the "
"request as the node is in a transition "
"state" % node['provision_state'])
# TODO(TheJulia) This is in-development code, that requires
# code in the shade library that is still in development.
if _check_set_maintenance(module, cloud, node):
if node['provision_state'] in 'active':
module.exit_json(changed=True,
result="Maintenance state changed")
changed = True
node = cloud.get_machine(node_id)
if _check_set_power_state(module, cloud, node):
changed = True
node = cloud.get_machine(node_id)
if _is_true(module.params['state']):
if _is_false(module.params['deploy']):
module.exit_json(
changed=changed,
result="User request has explicitly disabled "
"deployment logic"
)
if 'active' in node['provision_state']:
module.exit_json(
changed=changed,
result="Node already in an active state."
)
if instance_info is None:
module.fail_json(
changed=changed,
msg="When setting an instance to present, "
"instance_info is a required variable.")
# TODO(TheJulia): Update instance info, however info is
# deployment specific. Perhaps consider adding rebuild
# support, although there is a known desire to remove
# rebuild support from Ironic at some point in the future.
cloud.update_machine(uuid, instance_info=instance_info)
cloud.validate_node(uuid)
if not wait:
cloud.activate_node(uuid, module.params['config_drive'])
else:
cloud.activate_node(
uuid,
configdrive=module.params['config_drive'],
wait=wait,
timeout=timeout)
# TODO(TheJulia): Add more error checking..
module.exit_json(changed=changed, result="node activated")
elif _is_false(module.params['state']):
if node['provision_state'] not in "deleted":
cloud.update_machine(uuid, instance_info={})
if not wait:
cloud.deactivate_node(uuid)
else:
cloud.deactivate_node(
uuid,
wait=wait,
timeout=timeout)
module.exit_json(changed=True, result="deleted")
else:
module.exit_json(changed=False, result="node not found")
else:
module.fail_json(msg="State must be present, absent, "
"maintenance, off")
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,195 @@
#!/usr/bin/python
# Copyright 2016 Sam Yaple
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: catalog_service
short_description: Manage OpenStack Identity services
author: "Sam Yaple (@SamYaple)"
description:
- Create, update, or delete OpenStack Identity service. If a service
with the supplied name already exists, it will be updated with the
new description and enabled attributes.
options:
name:
description:
- Name of the service
required: true
type: str
description:
description:
- Description of the service
type: str
enabled:
description:
- Is the service enabled
type: bool
default: 'yes'
service_type:
description:
- The type of service
required: true
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a service for glance
- openstack.cloud.catalog_service:
cloud: mycloud
state: present
name: glance
service_type: image
description: OpenStack Image Service
# Delete a service
- openstack.cloud.catalog_service:
cloud: mycloud
state: absent
name: glance
service_type: image
'''
RETURN = '''
service:
description: Dictionary describing the service.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Service ID.
type: str
sample: "3292f020780b4d5baf27ff7e1d224c44"
name:
description: Service name.
type: str
sample: "glance"
service_type:
description: Service type.
type: str
sample: "image"
description:
description: Service description.
type: str
sample: "OpenStack Image Service"
enabled:
description: Service status.
type: bool
sample: True
id:
description: The service ID.
returned: On success when I(state) is 'present'
type: str
sample: "3292f020780b4d5baf27ff7e1d224c44"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _needs_update(module, service):
if service.enabled != module.params['enabled']:
return True
if service.description is not None and \
service.description != module.params['description']:
return True
return False
def _system_state_change(module, service):
state = module.params['state']
if state == 'absent' and service:
return True
if state == 'present':
if service is None:
return True
return _needs_update(module, service)
return False
def main():
argument_spec = openstack_full_argument_spec(
description=dict(default=None),
enabled=dict(default=True, type='bool'),
name=dict(required=True),
service_type=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
description = module.params['description']
enabled = module.params['enabled']
name = module.params['name']
state = module.params['state']
service_type = module.params['service_type']
sdk, cloud = openstack_cloud_from_module(module)
try:
services = cloud.search_services(name_or_id=name,
filters=dict(type=service_type))
if len(services) > 1:
module.fail_json(msg='Service name %s and type %s are not unique' %
(name, service_type))
elif len(services) == 1:
service = services[0]
else:
service = None
if module.check_mode:
module.exit_json(changed=_system_state_change(module, service))
if state == 'present':
if service is None:
service = cloud.create_service(name=name, description=description,
type=service_type, enabled=True)
changed = True
else:
if _needs_update(module, service):
service = cloud.update_service(
service.id, name=name, type=service_type, enabled=enabled,
description=description)
changed = True
else:
changed = False
module.exit_json(changed=changed, service=service, id=service.id)
elif state == 'absent':
if service is None:
changed = False
else:
cloud.delete_service(service.id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,299 @@
#!/usr/bin/python
# Copyright (c) 2018 Catalyst IT Ltd.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: coe_cluster
short_description: Add/Remove COE cluster from OpenStack Cloud
author: "Feilong Wang (@flwang)"
description:
- Add or Remove COE cluster from the OpenStack Container Infra service.
options:
cluster_template_id:
description:
- The template ID of cluster template.
required: true
type: str
discovery_url:
description:
- Url used for cluster node discovery
type: str
docker_volume_size:
description:
- The size in GB of the docker volume
type: int
flavor_id:
description:
- The flavor of the minion node for this ClusterTemplate
type: str
keypair:
description:
- Name of the keypair to use.
type: str
labels:
description:
- One or more key/value pairs
type: raw
master_flavor_id:
description:
- The flavor of the master node for this ClusterTemplate
type: str
master_count:
description:
- The number of master nodes for this cluster
default: 1
type: int
name:
description:
- Name that has to be given to the cluster template
required: true
type: str
node_count:
description:
- The number of nodes for this cluster
default: 1
type: int
state:
description:
- Indicate desired state of the resource.
choices: [present, absent]
default: present
type: str
timeout:
description:
- Timeout for creating the cluster in minutes. Default to 60 mins
if not set
default: 60
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
id:
description: The cluster UUID.
returned: On success when I(state) is 'present'
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
cluster:
description: Dictionary describing the cluster.
returned: On success when I(state) is 'present'
type: complex
contains:
api_address:
description:
- Api address of cluster master node
type: str
sample: https://172.24.4.30:6443
cluster_template_id:
description: The cluster_template UUID
type: str
sample: '7b1418c8-cea8-48fc-995d-52b66af9a9aa'
coe_version:
description:
- Version of the COE software currently running in this cluster
type: str
sample: v1.11.1
container_version:
description:
- "Version of the container software. Example: docker version."
type: str
sample: 1.12.6
created_at:
description:
- The time in UTC at which the cluster is created
type: str
sample: "2018-08-16T10:29:45+00:00"
create_timeout:
description:
- Timeout for creating the cluster in minutes. Default to 60 if
not set.
type: int
sample: 60
discovery_url:
description:
- Url used for cluster node discovery
type: str
sample: https://discovery.etcd.io/a42ee38e7113f31f4d6324f24367aae5
faults:
description:
- Fault info collected from the Heat resources of this cluster
type: dict
sample: {'0': 'ResourceInError: resources[0].resources...'}
flavor_id:
description:
- The flavor of the minion node for this cluster
type: str
sample: c1.c1r1
keypair:
description:
- Name of the keypair to use.
type: str
sample: mykey
labels:
description: One or more key/value pairs
type: dict
sample: {'key1': 'value1', 'key2': 'value2'}
master_addresses:
description:
- IP addresses of cluster master nodes
type: list
sample: ['172.24.4.5']
master_count:
description:
- The number of master nodes for this cluster.
type: int
sample: 1
master_flavor_id:
description:
- The flavor of the master node for this cluster
type: str
sample: c1.c1r1
name:
description:
- Name that has to be given to the cluster
type: str
sample: k8scluster
node_addresses:
description:
- IP addresses of cluster slave nodes
type: list
sample: ['172.24.4.8']
node_count:
description:
- The number of master nodes for this cluster.
type: int
sample: 1
stack_id:
description:
- Stack id of the Heat stack
type: str
sample: '07767ec6-85f5-44cb-bd63-242a8e7f0d9d'
status:
description: Status of the cluster from the heat stack
type: str
sample: 'CREATE_COMLETE'
status_reason:
description:
- Status reason of the cluster from the heat stack
type: str
sample: 'Stack CREATE completed successfully'
updated_at:
description:
- The time in UTC at which the cluster is updated
type: str
sample: '2018-08-16T10:39:25+00:00'
id:
description:
- Unique UUID for this cluster
type: str
sample: '86246a4d-a16c-4a58-9e96ad7719fe0f9d'
'''
EXAMPLES = '''
# Create a new Kubernetes cluster
- openstack.cloud.coe_cluster:
name: k8s
cluster_template_id: k8s-ha
keypair: mykey
master_count: 3
node_count: 5
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _parse_labels(labels):
if isinstance(labels, str):
labels_dict = {}
for kv_str in labels.split(","):
k, v = kv_str.split("=")
labels_dict[k] = v
return labels_dict
if not labels:
return {}
return labels
def main():
argument_spec = openstack_full_argument_spec(
cluster_template_id=dict(required=True),
discovery_url=dict(default=None),
docker_volume_size=dict(type='int'),
flavor_id=dict(default=None),
keypair=dict(default=None),
labels=dict(default=None, type='raw'),
master_count=dict(type='int', default=1),
master_flavor_id=dict(default=None),
name=dict(required=True),
node_count=dict(type='int', default=1),
state=dict(default='present', choices=['absent', 'present']),
timeout=dict(type='int', default=60),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
params = module.params.copy()
state = module.params['state']
name = module.params['name']
cluster_template_id = module.params['cluster_template_id']
kwargs = dict(
discovery_url=module.params['discovery_url'],
docker_volume_size=module.params['docker_volume_size'],
flavor_id=module.params['flavor_id'],
keypair=module.params['keypair'],
labels=_parse_labels(params['labels']),
master_count=module.params['master_count'],
master_flavor_id=module.params['master_flavor_id'],
node_count=module.params['node_count'],
create_timeout=module.params['timeout'],
)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = False
cluster = cloud.get_coe_cluster(name_or_id=name, filters={'cluster_template_id': cluster_template_id})
if state == 'present':
if not cluster:
cluster = cloud.create_coe_cluster(name, cluster_template_id=cluster_template_id, **kwargs)
changed = True
else:
changed = False
# NOTE (brtknr): At present, create_coe_cluster request returns
# cluster_id as `uuid` whereas get_coe_cluster request returns the
# same field as `id`. This behaviour may change in the future
# therefore try `id` first then `uuid`.
cluster_id = cluster.get('id', cluster.get('uuid'))
cluster['id'] = cluster['uuid'] = cluster_id
module.exit_json(changed=changed, cluster=cluster, id=cluster_id)
elif state == 'absent':
if not cluster:
module.exit_json(changed=False)
else:
cloud.delete_coe_cluster(name)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,394 @@
#!/usr/bin/python
# Copyright (c) 2018 Catalyst IT Ltd.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: coe_cluster_template
short_description: Add/Remove COE cluster template from OpenStack Cloud
author: "Feilong Wang (@flwang)"
description:
- Add or Remove COE cluster template from the OpenStack Container Infra
service.
options:
coe:
description:
- The Container Orchestration Engine for this clustertemplate
choices: [kubernetes, swarm, mesos]
type: str
required: true
dns_nameserver:
description:
- The DNS nameserver address
default: '8.8.8.8'
type: str
docker_storage_driver:
description:
- Docker storage driver
choices: [devicemapper, overlay, overlay2]
type: str
docker_volume_size:
description:
- The size in GB of the docker volume
type: int
external_network_id:
description:
- The external network to attach to the Cluster
type: str
fixed_network:
description:
- The fixed network name to attach to the Cluster
type: str
fixed_subnet:
description:
- The fixed subnet name to attach to the Cluster
type: str
flavor_id:
description:
- The flavor of the minion node for this ClusterTemplate
type: str
floating_ip_enabled:
description:
- Indicates whether created clusters should have a floating ip or not
type: bool
default: true
keypair_id:
description:
- Name or ID of the keypair to use.
type: str
image_id:
description:
- Image id the cluster will be based on
type: str
required: true
labels:
description:
- One or more key/value pairs
type: raw
http_proxy:
description:
- Address of a proxy that will receive all HTTP requests and relay them
The format is a URL including a port number
type: str
https_proxy:
description:
- Address of a proxy that will receive all HTTPS requests and relay
them. The format is a URL including a port number
type: str
master_flavor_id:
description:
- The flavor of the master node for this ClusterTemplate
type: str
master_lb_enabled:
description:
- Indicates whether created clusters should have a load balancer
for master nodes or not
type: bool
default: 'no'
name:
description:
- Name that has to be given to the cluster template
required: true
type: str
network_driver:
description:
- The name of the driver used for instantiating container networks
choices: [flannel, calico, docker]
type: str
no_proxy:
description:
- A comma separated list of IPs for which proxies should not be
used in the cluster
type: str
public:
description:
- Indicates whether the ClusterTemplate is public or not
type: bool
default: 'no'
registry_enabled:
description:
- Indicates whether the docker registry is enabled
type: bool
default: 'no'
server_type:
description:
- Server type for this ClusterTemplate
choices: [vm, bm]
default: vm
type: str
state:
description:
- Indicate desired state of the resource.
choices: [present, absent]
default: present
type: str
tls_disabled:
description:
- Indicates whether the TLS should be disabled
type: bool
default: 'no'
volume_driver:
description:
- The name of the driver used for instantiating container volumes
choices: [cinder, rexray]
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
id:
description: The cluster UUID.
returned: On success when I(state) is 'present'
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
cluster_template:
description: Dictionary describing the template.
returned: On success when I(state) is 'present'
type: complex
contains:
coe:
description: The Container Orchestration Engine for this clustertemplate
type: str
sample: kubernetes
dns_nameserver:
description: The DNS nameserver address
type: str
sample: '8.8.8.8'
docker_storage_driver:
description: Docker storage driver
type: str
sample: devicemapper
docker_volume_size:
description: The size in GB of the docker volume
type: int
sample: 5
external_network_id:
description: The external network to attach to the Cluster
type: str
sample: public
fixed_network:
description: The fixed network name to attach to the Cluster
type: str
sample: 07767ec6-85f5-44cb-bd63-242a8e7f0d9d
fixed_subnet:
description:
- The fixed subnet name to attach to the Cluster
type: str
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0d9d
flavor_id:
description:
- The flavor of the minion node for this ClusterTemplate
type: str
sample: c1.c1r1
floating_ip_enabled:
description:
- Indicates whether created clusters should have a floating ip or not
type: bool
sample: true
keypair_id:
description:
- Name or ID of the keypair to use.
type: str
sample: mykey
image_id:
description:
- Image id the cluster will be based on
type: str
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0e9d
labels:
description: One or more key/value pairs
type: dict
sample: {'key1': 'value1', 'key2': 'value2'}
http_proxy:
description:
- Address of a proxy that will receive all HTTP requests and relay them
The format is a URL including a port number
type: str
sample: http://10.0.0.11:9090
https_proxy:
description:
- Address of a proxy that will receive all HTTPS requests and relay
them. The format is a URL including a port number
type: str
sample: https://10.0.0.10:8443
master_flavor_id:
description:
- The flavor of the master node for this ClusterTemplate
type: str
sample: c1.c1r1
master_lb_enabled:
description:
- Indicates whether created clusters should have a load balancer
for master nodes or not
type: bool
sample: true
name:
description:
- Name that has to be given to the cluster template
type: str
sample: k8scluster
network_driver:
description:
- The name of the driver used for instantiating container networks
type: str
sample: calico
no_proxy:
description:
- A comma separated list of IPs for which proxies should not be
used in the cluster
type: str
sample: 10.0.0.4,10.0.0.5
public:
description:
- Indicates whether the ClusterTemplate is public or not
type: bool
sample: false
registry_enabled:
description:
- Indicates whether the docker registry is enabled
type: bool
sample: false
server_type:
description:
- Server type for this ClusterTemplate
type: str
sample: vm
tls_disabled:
description:
- Indicates whether the TLS should be disabled
type: bool
sample: false
volume_driver:
description:
- The name of the driver used for instantiating container volumes
type: str
sample: cinder
'''
EXAMPLES = '''
# Create a new Kubernetes cluster template
- openstack.cloud.coe_cluster_template:
name: k8s
coe: kubernetes
keypair_id: mykey
image_id: 2a8c9888-9054-4b06-a1ca-2bb61f9adb72
public: no
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _parse_labels(labels):
if isinstance(labels, str):
labels_dict = {}
for kv_str in labels.split(","):
k, v = kv_str.split("=")
labels_dict[k] = v
return labels_dict
if not labels:
return {}
return labels
def main():
argument_spec = openstack_full_argument_spec(
coe=dict(required=True, choices=['kubernetes', 'swarm', 'mesos']),
dns_nameserver=dict(default='8.8.8.8'),
docker_storage_driver=dict(choices=['devicemapper', 'overlay', 'overlay2']),
docker_volume_size=dict(type='int'),
external_network_id=dict(default=None),
fixed_network=dict(default=None),
fixed_subnet=dict(default=None),
flavor_id=dict(default=None),
floating_ip_enabled=dict(type='bool', default=True),
keypair_id=dict(default=None),
image_id=dict(required=True),
labels=dict(default=None, type='raw'),
http_proxy=dict(default=None),
https_proxy=dict(default=None),
master_lb_enabled=dict(type='bool', default=False),
master_flavor_id=dict(default=None),
name=dict(required=True),
network_driver=dict(choices=['flannel', 'calico', 'docker']),
no_proxy=dict(default=None),
public=dict(type='bool', default=False),
registry_enabled=dict(type='bool', default=False),
server_type=dict(default="vm", choices=['vm', 'bm']),
state=dict(default='present', choices=['absent', 'present']),
tls_disabled=dict(type='bool', default=False),
volume_driver=dict(choices=['cinder', 'rexray']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
params = module.params.copy()
state = module.params['state']
name = module.params['name']
coe = module.params['coe']
image_id = module.params['image_id']
kwargs = dict(
dns_nameserver=module.params['dns_nameserver'],
docker_storage_driver=module.params['docker_storage_driver'],
docker_volume_size=module.params['docker_volume_size'],
external_network_id=module.params['external_network_id'],
fixed_network=module.params['fixed_network'],
fixed_subnet=module.params['fixed_subnet'],
flavor_id=module.params['flavor_id'],
floating_ip_enabled=module.params['floating_ip_enabled'],
keypair_id=module.params['keypair_id'],
labels=_parse_labels(params['labels']),
http_proxy=module.params['http_proxy'],
https_proxy=module.params['https_proxy'],
master_lb_enabled=module.params['master_lb_enabled'],
master_flavor_id=module.params['master_flavor_id'],
network_driver=module.params['network_driver'],
no_proxy=module.params['no_proxy'],
public=module.params['public'],
registry_enabled=module.params['registry_enabled'],
server_type=module.params['server_type'],
tls_disabled=module.params['tls_disabled'],
volume_driver=module.params['volume_driver'],
)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = False
template = cloud.get_coe_cluster_template(name_or_id=name, filters={'coe': coe, 'image_id': image_id})
if state == 'present':
if not template:
template = cloud.create_coe_cluster_template(name, coe=coe, image_id=image_id, **kwargs)
changed = True
else:
changed = False
module.exit_json(changed=changed, cluster_template=template, id=template['uuid'])
elif state == 'absent':
if not template:
module.exit_json(changed=False)
else:
cloud.delete_coe_cluster_template(name)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,280 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: compute_flavor
short_description: Manage OpenStack compute flavors
author: "David Shrewsbury (@Shrews)"
description:
- Add or remove flavors from OpenStack.
options:
state:
description:
- Indicate desired state of the resource. When I(state) is 'present',
then I(ram), I(vcpus), and I(disk) are all required. There are no
default values for those parameters.
choices: ['present', 'absent']
default: present
type: str
name:
description:
- Flavor name.
required: true
type: str
ram:
description:
- Amount of memory, in MB.
type: int
vcpus:
description:
- Number of virtual CPUs.
type: int
disk:
description:
- Size of local disk, in GB.
default: 0
type: int
ephemeral:
description:
- Ephemeral space size, in GB.
default: 0
type: int
swap:
description:
- Swap space size, in MB.
default: 0
type: int
rxtx_factor:
description:
- RX/TX factor.
default: 1.0
type: float
is_public:
description:
- Make flavor accessible to the public.
type: bool
default: 'yes'
flavorid:
description:
- ID for the flavor. This is optional as a unique UUID will be
assigned if a value is not specified.
default: "auto"
type: str
extra_specs:
description:
- Metadata dictionary
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: "Create 'tiny' flavor with 1024MB of RAM, 1 virtual CPU, and 10GB of local disk, and 10GB of ephemeral."
openstack.cloud.compute_flavor:
cloud: mycloud
state: present
name: tiny
ram: 1024
vcpus: 1
disk: 10
ephemeral: 10
- name: "Delete 'tiny' flavor"
openstack.cloud.compute_flavor:
cloud: mycloud
state: absent
name: tiny
- name: Create flavor with metadata
openstack.cloud.compute_flavor:
cloud: mycloud
state: present
name: tiny
ram: 1024
vcpus: 1
disk: 10
extra_specs:
"quota:disk_read_iops_sec": 5000
"aggregate_instance_extra_specs:pinned": false
'''
RETURN = '''
flavor:
description: Dictionary describing the flavor.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Flavor ID.
returned: success
type: str
sample: "515256b8-7027-4d73-aa54-4e30a4a4a339"
name:
description: Flavor name.
returned: success
type: str
sample: "tiny"
disk:
description: Size of local disk, in GB.
returned: success
type: int
sample: 10
ephemeral:
description: Ephemeral space size, in GB.
returned: success
type: int
sample: 10
ram:
description: Amount of memory, in MB.
returned: success
type: int
sample: 1024
swap:
description: Swap space size, in MB.
returned: success
type: int
sample: 100
vcpus:
description: Number of virtual CPUs.
returned: success
type: int
sample: 2
is_public:
description: Make flavor accessible to the public.
returned: success
type: bool
sample: true
extra_specs:
description: Flavor metadata
returned: success
type: dict
sample:
"quota:disk_read_iops_sec": 5000
"aggregate_instance_extra_specs:pinned": false
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _system_state_change(module, flavor):
state = module.params['state']
if state == 'present' and not flavor:
return True
if state == 'absent' and flavor:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
state=dict(required=False, default='present',
choices=['absent', 'present']),
name=dict(required=True),
# required when state is 'present'
ram=dict(required=False, type='int'),
vcpus=dict(required=False, type='int'),
disk=dict(required=False, default=0, type='int'),
ephemeral=dict(required=False, default=0, type='int'),
swap=dict(required=False, default=0, type='int'),
rxtx_factor=dict(required=False, default=1.0, type='float'),
is_public=dict(required=False, default=True, type='bool'),
flavorid=dict(required=False, default="auto"),
extra_specs=dict(required=False, default=None, type='dict'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
required_if=[
('state', 'present', ['ram', 'vcpus', 'disk'])
],
**module_kwargs)
state = module.params['state']
name = module.params['name']
extra_specs = module.params['extra_specs'] or {}
sdk, cloud = openstack_cloud_from_module(module)
try:
flavor = cloud.get_flavor(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(module, flavor))
if state == 'present':
old_extra_specs = {}
require_update = False
if flavor:
old_extra_specs = flavor['extra_specs']
for param_key in ['ram', 'vcpus', 'disk', 'ephemeral', 'swap', 'rxtx_factor', 'is_public']:
if module.params[param_key] != flavor[param_key]:
require_update = True
break
if flavor and require_update:
cloud.delete_flavor(name)
flavor = None
if not flavor:
flavor = cloud.create_flavor(
name=name,
ram=module.params['ram'],
vcpus=module.params['vcpus'],
disk=module.params['disk'],
flavorid=module.params['flavorid'],
ephemeral=module.params['ephemeral'],
swap=module.params['swap'],
rxtx_factor=module.params['rxtx_factor'],
is_public=module.params['is_public']
)
changed = True
else:
changed = False
new_extra_specs = dict([(k, str(v)) for k, v in extra_specs.items()])
unset_keys = set(old_extra_specs.keys()) - set(extra_specs.keys())
if unset_keys and not require_update:
cloud.unset_flavor_specs(flavor['id'], unset_keys)
if old_extra_specs != new_extra_specs:
cloud.set_flavor_specs(flavor['id'], extra_specs)
changed = (changed or old_extra_specs != new_extra_specs)
module.exit_json(changed=changed,
flavor=flavor,
id=flavor['id'])
elif state == 'absent':
if flavor:
cloud.delete_flavor(name)
module.exit_json(changed=True)
module.exit_json(changed=False)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,233 @@
#!/usr/bin/python
# Copyright (c) 2015 IBM
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: compute_flavor_info
short_description: Retrieve information about one or more flavors
author: "David Shrewsbury (@Shrews)"
description:
- Retrieve information about available OpenStack instance flavors. By default,
information about ALL flavors are retrieved. Filters can be applied to get
information for only matching flavors. For example, you can filter on the
amount of RAM available to the flavor, or the number of virtual CPUs
available to the flavor, or both. When specifying multiple filters,
*ALL* filters must match on a flavor before that flavor is returned as
a fact.
- This module was called C(openstack.cloud.compute_flavor_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(openstack.cloud.compute_flavor_info) module no longer returns C(ansible_facts)!
notes:
- The result contains a list of unsorted flavors.
options:
name:
description:
- A flavor name. Cannot be used with I(ram) or I(vcpus) or I(ephemeral).
type: str
ram:
description:
- "A string used for filtering flavors based on the amount of RAM
(in MB) desired. This string accepts the following special values:
'MIN' (return flavors with the minimum amount of RAM), and 'MAX'
(return flavors with the maximum amount of RAM)."
- "A specific amount of RAM may also be specified. Any flavors with this
exact amount of RAM will be returned."
- "A range of acceptable RAM may be given using a special syntax. Simply
prefix the amount of RAM with one of these acceptable range values:
'<', '>', '<=', '>='. These values represent less than, greater than,
less than or equal to, and greater than or equal to, respectively."
type: str
vcpus:
description:
- A string used for filtering flavors based on the number of virtual
CPUs desired. Format is the same as the I(ram) parameter.
type: str
limit:
description:
- Limits the number of flavors returned. All matching flavors are
returned by default.
type: int
ephemeral:
description:
- A string used for filtering flavors based on the amount of ephemeral
storage. Format is the same as the I(ram) parameter
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Gather information about all available flavors
- openstack.cloud.compute_flavor_info:
cloud: mycloud
register: result
- debug:
msg: "{{ result.openstack_flavors }}"
# Gather information for the flavor named "xlarge-flavor"
- openstack.cloud.compute_flavor_info:
cloud: mycloud
name: "xlarge-flavor"
# Get all flavors that have exactly 512 MB of RAM.
- openstack.cloud.compute_flavor_info:
cloud: mycloud
ram: "512"
# Get all flavors that have 1024 MB or more of RAM.
- openstack.cloud.compute_flavor_info:
cloud: mycloud
ram: ">=1024"
# Get a single flavor that has the minimum amount of RAM. Using the 'limit'
# option will guarantee only a single flavor is returned.
- openstack.cloud.compute_flavor_info:
cloud: mycloud
ram: "MIN"
limit: 1
# Get all flavors with 1024 MB of RAM or more, AND exactly 2 virtual CPUs.
- openstack.cloud.compute_flavor_info:
cloud: mycloud
ram: ">=1024"
vcpus: "2"
# Get all flavors with 1024 MB of RAM or more, exactly 2 virtual CPUs, and
# less than 30gb of ephemeral storage.
- openstack.cloud.compute_flavor_info:
cloud: mycloud
ram: ">=1024"
vcpus: "2"
ephemeral: "<30"
'''
RETURN = '''
openstack_flavors:
description: Dictionary describing the flavors.
returned: On success.
type: complex
contains:
id:
description: Flavor ID.
returned: success
type: str
sample: "515256b8-7027-4d73-aa54-4e30a4a4a339"
name:
description: Flavor name.
returned: success
type: str
sample: "tiny"
disk:
description: Size of local disk, in GB.
returned: success
type: int
sample: 10
ephemeral:
description: Ephemeral space size, in GB.
returned: success
type: int
sample: 10
ram:
description: Amount of memory, in MB.
returned: success
type: int
sample: 1024
swap:
description: Swap space size, in MB.
returned: success
type: int
sample: 100
vcpus:
description: Number of virtual CPUs.
returned: success
type: int
sample: 2
is_public:
description: Make flavor accessible to the public.
returned: success
type: bool
sample: true
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
ram=dict(required=False, default=None),
vcpus=dict(required=False, default=None),
limit=dict(required=False, default=None, type='int'),
ephemeral=dict(required=False, default=None),
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[
['name', 'ram'],
['name', 'vcpus'],
['name', 'ephemeral']
]
)
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'openstack.cloud.compute_flavor_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.compute_flavor_facts' module has been renamed to 'openstack.cloud.compute_flavor_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
name = module.params['name']
vcpus = module.params['vcpus']
ram = module.params['ram']
ephemeral = module.params['ephemeral']
limit = module.params['limit']
filters = {}
if vcpus:
filters['vcpus'] = vcpus
if ram:
filters['ram'] = ram
if ephemeral:
filters['ephemeral'] = ephemeral
sdk, cloud = openstack_cloud_from_module(module)
try:
if name:
flavors = cloud.search_flavors(filters={'name': name})
else:
flavors = cloud.list_flavors()
if filters:
flavors = cloud.range_search(flavors, filters)
if limit is not None:
flavors = flavors[:limit]
if is_old_facts:
module.exit_json(changed=False,
ansible_facts=dict(openstack_flavors=flavors))
else:
module.exit_json(changed=False,
openstack_flavors=flavors)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

82
plugins/modules/config.py Normal file
View File

@ -0,0 +1,82 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: config
short_description: Get OpenStack Client config
description:
- Get I(openstack) client config data from clouds.yaml or environment
notes:
- Facts are placed in the C(openstack.clouds) variable.
options:
clouds:
description:
- List of clouds to limit the return list to. No value means return
information on all configured clouds
required: false
default: []
type: list
elements: str
requirements:
- "python >= 3.6"
- "openstacksdk"
author: "Monty Taylor (@emonty)"
'''
EXAMPLES = '''
- name: Get list of clouds that do not support security groups
openstack.cloud.config:
- debug:
var: "{{ item }}"
with_items: "{{ openstack.clouds | rejectattr('secgroup_source', 'none') | list }}"
- name: Get the information back just about the mordred cloud
openstack.cloud.config:
clouds:
- mordred
'''
try:
import openstack.config
from openstack import exceptions
HAS_OPENSTACKSDK = True
except ImportError:
HAS_OPENSTACKSDK = False
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(argument_spec=dict(
clouds=dict(required=False, type='list', default=[], elements='str'),
))
if not HAS_OPENSTACKSDK:
module.fail_json(msg='openstacksdk is required for this module')
p = module.params
try:
config = openstack.config.OpenStackConfig()
clouds = []
for cloud in config.get_all_clouds():
if not p['clouds'] or cloud.name in p['clouds']:
cloud.config['name'] = cloud.name
clouds.append(cloud.config)
module.exit_json(ansible_facts=dict(openstack=dict(clouds=clouds)))
except exceptions.ConfigException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

248
plugins/modules/dns_zone.py Normal file
View File

@ -0,0 +1,248 @@
#!/usr/bin/python
# Copyright (c) 2016 Hewlett-Packard Enterprise
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: dns_zone
short_description: Manage OpenStack DNS zones
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
description:
- Manage OpenStack DNS zones. Zones can be created, deleted or
updated. Only the I(email), I(description), I(ttl) and I(masters) values
can be updated.
options:
name:
description:
- Zone name
required: true
type: str
zone_type:
description:
- Zone type
choices: [primary, secondary]
type: str
email:
description:
- Email of the zone owner (only applies if zone_type is primary)
type: str
description:
description:
- Zone description
type: str
ttl:
description:
- TTL (Time To Live) value in seconds
type: int
masters:
description:
- Master nameservers (only applies if zone_type is secondary)
type: list
elements: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a zone named "example.net"
- openstack.cloud.dns_zone:
cloud: mycloud
state: present
name: example.net.
zone_type: primary
email: test@example.net
description: Test zone
ttl: 3600
# Update the TTL on existing "example.net." zone
- openstack.cloud.dns_zone:
cloud: mycloud
state: present
name: example.net.
ttl: 7200
# Delete zone named "example.net."
- openstack.cloud.dns_zone:
cloud: mycloud
state: absent
name: example.net.
'''
RETURN = '''
zone:
description: Dictionary describing the zone.
returned: On success when I(state) is 'present'.
type: complex
contains:
id:
description: Unique zone ID
type: str
sample: "c1c530a3-3619-46f3-b0f6-236927b2618c"
name:
description: Zone name
type: str
sample: "example.net."
type:
description: Zone type
type: str
sample: "PRIMARY"
email:
description: Zone owner email
type: str
sample: "test@example.net"
description:
description: Zone description
type: str
sample: "Test description"
ttl:
description: Zone TTL value
type: int
sample: 3600
masters:
description: Zone master nameservers
type: list
sample: []
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _system_state_change(state, email, description, ttl, masters, zone):
if state == 'present':
if not zone:
return True
if email is not None and zone.email != email:
return True
if description is not None and zone.description != description:
return True
if ttl is not None and zone.ttl != ttl:
return True
if masters is not None and zone.masters != masters:
return True
if state == 'absent' and zone:
return True
return False
def _wait(timeout, cloud, zone, state, module, sdk):
"""Wait for a zone to reach the desired state for the given state."""
for count in sdk.utils.iterate_timeout(
timeout,
"Timeout waiting for zone to be %s" % state):
if (state == 'absent' and zone is None) or (state == 'present' and zone and zone.status == 'ACTIVE'):
return
try:
zone = cloud.get_zone(zone.id)
except Exception:
continue
if zone and zone.status == 'ERROR':
module.fail_json(msg="Zone reached ERROR state while waiting for it to be %s" % state)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
zone_type=dict(required=False, choices=['primary', 'secondary']),
email=dict(required=False, default=None),
description=dict(required=False, default=None),
ttl=dict(required=False, default=None, type='int'),
masters=dict(required=False, default=None, type='list', elements='str'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
name = module.params.get('name')
state = module.params.get('state')
wait = module.params.get('wait')
timeout = module.params.get('timeout')
sdk, cloud = openstack_cloud_from_module(module)
try:
zone = cloud.get_zone(name)
if state == 'present':
zone_type = module.params.get('zone_type')
email = module.params.get('email')
description = module.params.get('description')
ttl = module.params.get('ttl')
masters = module.params.get('masters')
if module.check_mode:
module.exit_json(changed=_system_state_change(state, email,
description, ttl,
masters, zone))
if zone is None:
zone = cloud.create_zone(
name=name, zone_type=zone_type, email=email,
description=description, ttl=ttl, masters=masters)
changed = True
else:
if masters is None:
masters = []
pre_update_zone = zone
changed = _system_state_change(state, email,
description, ttl,
masters, pre_update_zone)
if changed:
zone = cloud.update_zone(
name, email=email,
description=description,
ttl=ttl, masters=masters)
if wait:
_wait(timeout, cloud, zone, state, module, sdk)
module.exit_json(changed=changed, zone=zone)
elif state == 'absent':
if module.check_mode:
module.exit_json(changed=_system_state_change(state, None,
None, None,
None, zone))
if zone is None:
changed = False
else:
cloud.delete_zone(name)
changed = True
if wait:
_wait(timeout, cloud, zone, state, module, sdk)
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

215
plugins/modules/endpoint.py Normal file
View File

@ -0,0 +1,215 @@
#!/usr/bin/python
# Copyright: (c) 2017, VEXXHOST, Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: endpoint
short_description: Manage OpenStack Identity service endpoints
author:
- Mohammed Naser (@mnaser)
- Alberto Murillo (@albertomurillo)
description:
- Create, update, or delete OpenStack Identity service endpoints. If a
service with the same combination of I(service), I(interface) and I(region)
exist, the I(url) and I(state) (C(present) or C(absent)) will be updated.
options:
service:
description:
- Name or id of the service.
required: true
type: str
endpoint_interface:
description:
- Interface of the service.
choices: [admin, public, internal]
required: true
type: str
url:
description:
- URL of the service.
required: true
type: str
region:
description:
- Region that the service belongs to. Note that I(region_name) is used for authentication.
type: str
enabled:
description:
- Is the service enabled.
default: True
type: bool
state:
description:
- Should the resource be C(present) or C(absent).
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.13.0"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Create a service for glance
openstack.cloud.endpoint:
cloud: mycloud
service: glance
endpoint_interface: public
url: http://controller:9292
region: RegionOne
state: present
- name: Delete a service for nova
openstack.cloud.endpoint:
cloud: mycloud
service: nova
endpoint_interface: public
region: RegionOne
state: absent
'''
RETURN = '''
endpoint:
description: Dictionary describing the endpoint.
returned: On success when I(state) is C(present)
type: complex
contains:
id:
description: Endpoint ID.
type: str
sample: 3292f020780b4d5baf27ff7e1d224c44
region:
description: Region Name.
type: str
sample: RegionOne
service_id:
description: Service ID.
type: str
sample: b91f1318f735494a825a55388ee118f3
interface:
description: Endpoint Interface.
type: str
sample: public
url:
description: Service URL.
type: str
sample: http://controller:9292
enabled:
description: Service status.
type: bool
sample: True
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _needs_update(module, endpoint):
if endpoint.enabled != module.params['enabled']:
return True
if endpoint.url != module.params['url']:
return True
return False
def _system_state_change(module, endpoint):
state = module.params['state']
if state == 'absent' and endpoint:
return True
if state == 'present':
if endpoint is None:
return True
return _needs_update(module, endpoint)
return False
def main():
argument_spec = openstack_full_argument_spec(
service=dict(type='str', required=True),
endpoint_interface=dict(type='str', required=True, choices=['admin', 'public', 'internal']),
url=dict(type='str', required=True),
region=dict(type='str'),
enabled=dict(type='bool', default=True),
state=dict(type='str', default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
service_name_or_id = module.params['service']
interface = module.params['endpoint_interface']
url = module.params['url']
region = module.params['region']
enabled = module.params['enabled']
state = module.params['state']
sdk, cloud = openstack_cloud_from_module(module)
try:
service = cloud.get_service(service_name_or_id)
if service is None:
module.fail_json(msg='Service %s does not exist' % service_name_or_id)
filters = dict(service_id=service.id, interface=interface)
if region is not None:
filters['region'] = region
endpoints = cloud.search_endpoints(filters=filters)
if len(endpoints) > 1:
module.fail_json(msg='Service %s, interface %s and region %s are '
'not unique' %
(service_name_or_id, interface, region))
elif len(endpoints) == 1:
endpoint = endpoints[0]
else:
endpoint = None
if module.check_mode:
module.exit_json(changed=_system_state_change(module, endpoint))
if state == 'present':
if endpoint is None:
result = cloud.create_endpoint(service_name_or_id=service,
url=url, interface=interface,
region=region, enabled=enabled)
endpoint = result[0]
changed = True
else:
if _needs_update(module, endpoint):
endpoint = cloud.update_endpoint(
endpoint.id, url=url, enabled=enabled)
changed = True
else:
changed = False
module.exit_json(changed=changed, endpoint=endpoint)
elif state == 'absent':
if endpoint is None:
changed = False
else:
cloud.delete_endpoint(endpoint.id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,248 @@
#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: federation_idp
short_description: manage a federation Identity Provider
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
description:
- Manage a federation Identity Provider.
options:
name:
description:
- The name of the Identity Provider.
type: str
required: true
aliases: ['id']
state:
description:
- Whether the Identity Provider should be C(present) or C(absent).
choices: ['present', 'absent']
default: present
type: str
description:
description:
- The description of the Identity Provider.
type: str
domain_id:
description:
- The ID of a domain that is associated with the Identity Provider.
Federated users that authenticate with the Identity Provider will be
created under the domain specified.
- Required when creating a new Identity Provider.
type: str
enabled:
description:
- Whether the Identity Provider is enabled or not.
- Will default to C(true) when creating a new Identity Provider.
type: bool
aliases: ['is_enabled']
remote_ids:
description:
- "List of the unique Identity Provider's remote IDs."
- Will default to an empty list when creating a new Identity Provider.
type: list
elements: str
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Create an identity provider
openstack.cloud.federation_idp:
cloud: example_cloud
name: example_provider
domain_id: 0123456789abcdef0123456789abcdef
description: 'My example IDP'
remote_ids:
- 'https://auth.example.com/auth/realms/ExampleRealm'
- name: Delete an identity provider
openstack.cloud.federation_idp:
cloud: example_cloud
name: example_provider
state: absent
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
def normalize_idp(idp):
"""
Normalizes the IDP definitions so that the outputs are consistent with the
parameters
- "enabled" (parameter) == "is_enabled" (SDK)
- "name" (parameter) == "id" (SDK)
"""
if idp is None:
return None
_idp = idp.to_dict()
_idp['enabled'] = idp['is_enabled']
_idp['name'] = idp['id']
return _idp
def delete_identity_provider(module, sdk, cloud, idp):
"""
Delete an existing Identity Provider
returns: the "Changed" state
"""
if idp is None:
return False
if module.check_mode:
return True
try:
cloud.identity.delete_identity_provider(idp)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to delete identity provider: {0}'.format(str(ex)))
return True
def create_identity_provider(module, sdk, cloud, name):
"""
Create a new Identity Provider
returns: the "Changed" state and the new identity provider
"""
if module.check_mode:
return True, None
description = module.params.get('description')
enabled = module.params.get('enabled')
domain_id = module.params.get('domain_id')
remote_ids = module.params.get('remote_ids')
if enabled is None:
enabled = True
if remote_ids is None:
remote_ids = []
attributes = {
'domain_id': domain_id,
'enabled': enabled,
'remote_ids': remote_ids,
}
if description is not None:
attributes['description'] = description
try:
idp = cloud.identity.create_identity_provider(id=name, **attributes)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to create identity provider: {0}'.format(str(ex)))
return (True, idp)
def update_identity_provider(module, sdk, cloud, idp):
"""
Update an existing Identity Provider
returns: the "Changed" state and the new identity provider
"""
description = module.params.get('description')
enabled = module.params.get('enabled')
domain_id = module.params.get('domain_id')
remote_ids = module.params.get('remote_ids')
attributes = {}
if (description is not None) and (description != idp.description):
attributes['description'] = description
if (enabled is not None) and (enabled != idp.is_enabled):
attributes['enabled'] = enabled
if (domain_id is not None) and (domain_id != idp.domain_id):
attributes['domain_id'] = domain_id
if (remote_ids is not None) and (remote_ids != idp.remote_ids):
attributes['remote_ids'] = remote_ids
if not attributes:
return False, idp
if module.check_mode:
return True, None
try:
new_idp = cloud.identity.update_identity_provider(idp, **attributes)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to update identity provider: {0}'.format(str(ex)))
return (True, new_idp)
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
name=dict(required=True, aliases=['id']),
state=dict(default='present', choices=['absent', 'present']),
description=dict(),
domain_id=dict(),
enabled=dict(type='bool', aliases=['is_enabled']),
remote_ids=dict(type='list', elements='str'),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
state = module.params.get('state')
changed = False
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
try:
idp = cloud.identity.get_identity_provider(name)
except sdk.exceptions.ResourceNotFound:
idp = None
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get identity provider: {0}'.format(str(ex)))
if state == 'absent':
if idp is not None:
changed = delete_identity_provider(module, sdk, cloud, idp)
module.exit_json(changed=changed)
# state == 'present'
else:
if idp is None:
if module.params.get('domain_id') is None:
module.fail_json(msg='A domain_id must be passed when creating'
' an identity provider')
(changed, idp) = create_identity_provider(module, sdk, cloud, name)
idp = normalize_idp(idp)
module.exit_json(changed=changed, identity_provider=idp)
(changed, new_idp) = update_identity_provider(module, sdk, cloud, idp)
new_idp = normalize_idp(new_idp)
module.exit_json(changed=changed, identity_provider=new_idp)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,107 @@
#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: federation_idp_info
short_description: Get the information about the available federation identity
providers
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
description:
- Fetch a federation identity provider.
options:
name:
description:
- The name of the identity provider to fetch.
- If I(name) is specified, the module will return failed if the identity
provider doesn't exist.
type: str
aliases: ['id']
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Fetch a specific identity provider
openstack.cloud.federation_idp_info:
cloud: example_cloud
name: example_provider
- name: Fetch all providers
openstack.cloud.federation_idp_info:
cloud: example_cloud
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
def normalize_idp(idp):
"""
Normalizes the IDP definitions so that the outputs are consistent with the
parameters
- "enabled" (parameter) == "is_enabled" (SDK)
- "name" (parameter) == "id" (SDK)
"""
if idp is None:
return
_idp = idp.to_dict()
_idp['enabled'] = idp['is_enabled']
_idp['name'] = idp['id']
return _idp
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
name=dict(aliases=['id']),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
if name:
try:
idp = normalize_idp(cloud.identity.get_identity_provider(name))
except sdk.exceptions.ResourceNotFound:
module.fail_json(msg='Failed to find identity provider')
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get identity provider: {0}'.format(str(ex)))
module.exit_json(changed=False, identity_providers=[idp])
else:
try:
providers = list(map(normalize_idp, cloud.identity.identity_providers()))
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to list identity providers: {0}'.format(str(ex)))
module.exit_json(changed=False, identity_providers=providers)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,224 @@
#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: federation_mapping
short_description: Manage a federation mapping
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
description:
- Manage a federation mapping.
options:
name:
description:
- The name of the mapping to manage.
required: true
type: str
aliases: ['id']
state:
description:
- Whether the mapping should be C(present) or C(absent).
choices: ['present', 'absent']
default: present
type: str
rules:
description:
- The rules that comprise the mapping. These are pairs of I(local) and
I(remote) definitions. For more details on how these work please see
the OpenStack documentation
U(https://docs.openstack.org/keystone/latest/admin/federation/mapping_combinations.html).
- Required if I(state=present)
type: list
elements: dict
suboptions:
local:
description:
- Information on what local attributes will be mapped.
required: true
type: list
elements: dict
remote:
description:
- Information on what remote attributes will be mapped.
required: true
type: list
elements: dict
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Create a new mapping
openstack.cloud.federation_mapping:
cloud: example_cloud
name: example_mapping
rules:
- local:
- user:
name: '{0}'
- group:
id: '0cd5e9'
remote:
- type: UserName
- type: orgPersonType
any_one_of:
- Contractor
- SubContractor
- name: Delete a mapping
openstack.cloud.federation_mapping:
name: example_mapping
state: absent
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
def normalize_mapping(mapping):
"""
Normalizes the mapping definitions so that the outputs are consistent with
the parameters
- "name" (parameter) == "id" (SDK)
"""
if mapping is None:
return None
_mapping = mapping.to_dict()
_mapping['name'] = mapping['id']
return _mapping
def create_mapping(module, sdk, cloud, name):
"""
Attempt to create a Mapping
returns: A tuple containing the "Changed" state and the created mapping
"""
if module.check_mode:
return (True, None)
rules = module.params.get('rules')
try:
mapping = cloud.identity.create_mapping(id=name, rules=rules)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to create mapping: {0}'.format(str(ex)))
return (True, mapping)
def delete_mapping(module, sdk, cloud, mapping):
"""
Attempt to delete a Mapping
returns: the "Changed" state
"""
if mapping is None:
return False
if module.check_mode:
return True
try:
cloud.identity.delete_mapping(mapping)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to delete mapping: {0}'.format(str(ex)))
return True
def update_mapping(module, sdk, cloud, mapping):
"""
Attempt to delete a Mapping
returns: The "Changed" state and the the new mapping
"""
current_rules = mapping.rules
new_rules = module.params.get('rules')
# Nothing to do
if current_rules == new_rules:
return (False, mapping)
if module.check_mode:
return (True, None)
try:
new_mapping = cloud.identity.update_mapping(mapping, rules=new_rules)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to update mapping: {0}'.format(str(ex)))
return (True, new_mapping)
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
name=dict(required=True, aliases=['id']),
state=dict(default='present', choices=['absent', 'present']),
rules=dict(type='list', elements='dict', options=dict(
local=dict(required=True, type='list', elements='dict'),
remote=dict(required=True, type='list', elements='dict')
)),
)
module_kwargs = openstack_module_kwargs(
required_if=[('state', 'present', ['rules'])]
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
state = module.params.get('state')
changed = False
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
try:
mapping = cloud.identity.get_mapping(name)
except sdk.exceptions.ResourceNotFound:
mapping = None
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to fetch mapping: {0}'.format(str(ex)))
if state == 'absent':
if mapping is not None:
changed = delete_mapping(module, sdk, cloud, mapping)
module.exit_json(changed=changed)
# state == 'present'
else:
if len(module.params.get('rules')) < 1:
module.fail_json(msg='At least one rule must be passed')
if mapping is None:
(changed, mapping) = create_mapping(module, sdk, cloud, name)
mapping = normalize_mapping(mapping)
module.exit_json(changed=changed, mapping=mapping)
else:
(changed, new_mapping) = update_mapping(module, sdk, cloud, mapping)
new_mapping = normalize_mapping(new_mapping)
module.exit_json(mapping=new_mapping, changed=changed)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,104 @@
#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: federation_mapping_info
short_description: Get the information about the available federation mappings
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
description:
- Fetch a federation mapping.
options:
name:
description:
- The name of the mapping to fetch.
- If I(name) is specified, the module will return failed if the mapping
doesn't exist.
type: str
aliases: ['id']
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Fetch a specific mapping
openstack.cloud.federation_mapping_info:
cloud: example_cloud
name: example_mapping
- name: Fetch all mappings
openstack.cloud.federation_mapping_info:
cloud: example_cloud
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
def normalize_mapping(mapping):
"""
Normalizes the mapping definitions so that the outputs are consistent with the
parameters
- "name" (parameter) == "id" (SDK)
"""
if mapping is None:
return None
_mapping = mapping.to_dict()
_mapping['name'] = mapping['id']
return _mapping
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
name=dict(aliases=['id']),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
if name:
try:
mapping = normalize_mapping(cloud.identity.get_mapping(name))
except sdk.exceptions.ResourceNotFound:
module.fail_json(msg='Failed to find mapping')
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get mapping: {0}'.format(str(ex)))
module.exit_json(changed=False, mappings=[mapping])
else:
try:
mappings = list(map(normalize_mapping, cloud.identity.mappings()))
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to list mappings: {0}'.format(str(ex)))
module.exit_json(changed=False, mappings=mappings)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,261 @@
#!/usr/bin/python
# Copyright: (c) 2015, Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: floating_ip
author: Davide Guerri (@dguerri) <davide.guerri@hp.com>
short_description: Add/Remove floating IP from an instance
description:
- Add or Remove a floating IP to an instance.
- Returns the floating IP when attaching only if I(wait=true).
options:
server:
description:
- The name or ID of the instance to which the IP address
should be assigned.
required: true
type: str
network:
description:
- The name or ID of a neutron external network or a nova pool name.
type: str
floating_ip_address:
description:
- A floating IP address to attach or to detach. Required only if I(state)
is absent. When I(state) is present can be used to specify a IP address
to attach.
type: str
reuse:
description:
- When I(state) is present, and I(floating_ip_address) is not present,
this parameter can be used to specify whether we should try to reuse
a floating IP address already allocated to the project.
type: bool
default: 'no'
fixed_address:
description:
- To which fixed IP of server the floating IP address should be
attached to.
type: str
nat_destination:
description:
- The name or id of a neutron private network that the fixed IP to
attach floating IP is on
aliases: ["fixed_network", "internal_network"]
type: str
wait:
description:
- When attaching a floating IP address, specify whether to wait for it to appear as attached.
- Must be set to C(yes) for the module to return the value of the floating IP.
type: bool
default: 'no'
timeout:
description:
- Time to wait for an IP address to appear as attached. See wait.
required: false
default: 60
type: int
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
purge:
description:
- When I(state) is absent, indicates whether or not to delete the floating
IP completely, or only detach it from the server. Default is to detach only.
type: bool
default: 'no'
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Assign a floating IP to the fist interface of `cattle001` from an exiting
# external network or nova pool. A new floating IP from the first available
# external network is allocated to the project.
- openstack.cloud.floating_ip:
cloud: dguerri
server: cattle001
# Assign a new floating IP to the instance fixed ip `192.0.2.3` of
# `cattle001`. If a free floating IP is already allocated to the project, it is
# reused; if not, a new one is created.
- openstack.cloud.floating_ip:
cloud: dguerri
state: present
reuse: yes
server: cattle001
network: ext_net
fixed_address: 192.0.2.3
wait: true
timeout: 180
# Assign a new floating IP from the network `ext_net` to the instance fixed
# ip in network `private_net` of `cattle001`.
- openstack.cloud.floating_ip:
cloud: dguerri
state: present
server: cattle001
network: ext_net
nat_destination: private_net
wait: true
timeout: 180
# Detach a floating IP address from a server
- openstack.cloud.floating_ip:
cloud: dguerri
state: absent
floating_ip_address: 203.0.113.2
server: cattle001
'''
from ansible.module_utils.basic import AnsibleModule, remove_values
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _get_floating_ip(cloud, floating_ip_address):
f_ips = cloud.search_floating_ips(
filters={'floating_ip_address': floating_ip_address})
if not f_ips:
return None
return f_ips[0]
def main():
argument_spec = openstack_full_argument_spec(
server=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
network=dict(required=False, default=None),
floating_ip_address=dict(required=False, default=None),
reuse=dict(required=False, type='bool', default=False),
fixed_address=dict(required=False, default=None),
nat_destination=dict(required=False, default=None,
aliases=['fixed_network', 'internal_network']),
wait=dict(required=False, type='bool', default=False),
timeout=dict(required=False, type='int', default=60),
purge=dict(required=False, type='bool', default=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
server_name_or_id = module.params['server']
state = module.params['state']
network = module.params['network']
floating_ip_address = module.params['floating_ip_address']
reuse = module.params['reuse']
fixed_address = module.params['fixed_address']
nat_destination = module.params['nat_destination']
wait = module.params['wait']
timeout = module.params['timeout']
purge = module.params['purge']
sdk, cloud = openstack_cloud_from_module(module)
try:
server = cloud.get_server(server_name_or_id)
if server is None:
module.fail_json(
msg="server {0} not found".format(server_name_or_id))
if state == 'present':
# If f_ip already assigned to server, check that it matches
# requirements.
public_ip = cloud.get_server_public_ip(server)
f_ip = _get_floating_ip(cloud, public_ip) if public_ip else public_ip
if f_ip:
if network:
network_id = cloud.get_network(name_or_id=network)["id"]
else:
network_id = None
# check if we have floating ip on given nat_destination network
if nat_destination:
nat_floating_addrs = [
addr for addr in server.addresses.get(
cloud.get_network(nat_destination)['name'], [])
if addr['addr'] == public_ip
and addr['OS-EXT-IPS:type'] == 'floating'
]
if len(nat_floating_addrs) == 0:
module.fail_json(msg="server {server} already has a "
"floating-ip on a different "
"nat-destination than '{nat_destination}'"
.format(server=server_name_or_id,
nat_destination=nat_destination))
if all([fixed_address, f_ip.fixed_ip_address == fixed_address,
network, f_ip.network != network_id]):
# Current state definitely conflicts with requirements
module.fail_json(msg="server {server} already has a "
"floating-ip on requested "
"interface but it doesn't match "
"requested network {network}: {fip}"
.format(server=server_name_or_id,
network=network,
fip=remove_values(f_ip,
module.no_log_values)))
if not network or f_ip.network == network_id:
# Requirements are met
module.exit_json(changed=False, floating_ip=f_ip)
# Requirements are vague enough to ignore existing f_ip and try
# to create a new f_ip to the server.
server = cloud.add_ips_to_server(
server=server, ips=floating_ip_address, ip_pool=network,
reuse=reuse, fixed_address=fixed_address, wait=wait,
timeout=timeout, nat_destination=nat_destination)
fip_address = cloud.get_server_public_ip(server)
# Update the floating IP status
f_ip = _get_floating_ip(cloud, fip_address)
module.exit_json(changed=True, floating_ip=f_ip)
elif state == 'absent':
if floating_ip_address is None:
if not server_name_or_id:
module.fail_json(msg="either server or floating_ip_address are required")
server = cloud.get_server(server_name_or_id)
floating_ip_address = cloud.get_server_public_ip(server)
f_ip = _get_floating_ip(cloud, floating_ip_address)
if not f_ip:
# Nothing to detach
module.exit_json(changed=False)
changed = False
if f_ip["fixed_ip_address"]:
cloud.detach_ip_from_server(
server_id=server['id'], floating_ip_id=f_ip['id'])
# Update the floating IP status
f_ip = cloud.get_floating_ip(id=f_ip['id'])
changed = True
if purge:
cloud.delete_floating_ip(f_ip['id'])
module.exit_json(changed=True)
module.exit_json(changed=changed, floating_ip=f_ip)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,106 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: group_assignment
short_description: Associate OpenStack Identity users and groups
author: "Monty Taylor (@emonty)"
description:
- Add and remove users from groups
options:
user:
description:
- Name or id for the user
required: true
type: str
group:
description:
- Name or id for the group.
required: true
type: str
state:
description:
- Should the user be present or absent in the group
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Add the demo user to the demo group
- openstack.cloud.group_assignment:
cloud: mycloud
user: demo
group: demo
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _system_state_change(state, in_group):
if state == 'present' and not in_group:
return True
if state == 'absent' and in_group:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
user=dict(required=True),
group=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
user = module.params['user']
group = module.params['group']
state = module.params['state']
sdk, cloud = openstack_cloud_from_module(module)
try:
in_group = cloud.is_user_in_group(user, group)
if module.check_mode:
module.exit_json(changed=_system_state_change(state, in_group))
changed = False
if state == 'present':
if not in_group:
cloud.add_user_to_group(user, group)
changed = True
elif state == 'absent':
if in_group:
cloud.remove_user_from_group(user, group)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,224 @@
#!/usr/bin/python
# Copyright 2016 Jakub Jursa <jakub.jursa1@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: host_aggregate
short_description: Manage OpenStack host aggregates
author: "Jakub Jursa (@kuboj)"
description:
- Create, update, or delete OpenStack host aggregates. If a aggregate
with the supplied name already exists, it will be updated with the
new name, new availability zone, new metadata and new list of hosts.
options:
name:
description: Name of the aggregate.
required: true
type: str
metadata:
description: Metadata dict.
type: dict
availability_zone:
description: Availability zone to create aggregate into.
type: str
hosts:
description: List of hosts to set for an aggregate.
type: list
elements: str
purge_hosts:
description: Whether hosts not in I(hosts) should be removed from the aggregate
type: bool
default: true
state:
description: Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a host aggregate
- openstack.cloud.host_aggregate:
cloud: mycloud
state: present
name: db_aggregate
hosts:
- host1
- host2
metadata:
type: dbcluster
# Add an additional host to the aggregate
- openstack.cloud.host_aggregate:
cloud: mycloud
state: present
name: db_aggregate
hosts:
- host3
purge_hosts: false
metadata:
type: dbcluster
# Delete an aggregate
- openstack.cloud.host_aggregate:
cloud: mycloud
state: absent
name: db_aggregate
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _needs_update(module, aggregate):
new_metadata = (module.params['metadata'] or {})
if module.params['availability_zone'] is not None:
new_metadata['availability_zone'] = module.params['availability_zone']
if module.params['name'] != aggregate.name:
return True
if module.params['hosts'] is not None:
if module.params['purge_hosts']:
if set(module.params['hosts']) != set(aggregate.hosts):
return True
else:
intersection = set(module.params['hosts']).intersection(set(aggregate.hosts))
if set(module.params['hosts']) != intersection:
return True
if module.params['availability_zone'] is not None:
if module.params['availability_zone'] != aggregate.availability_zone:
return True
if module.params['metadata'] is not None:
if new_metadata != aggregate.metadata:
return True
return False
def _system_state_change(module, aggregate):
state = module.params['state']
if state == 'absent' and aggregate:
return True
if state == 'present':
if aggregate is None:
return True
return _needs_update(module, aggregate)
return False
def _update_hosts(cloud, aggregate, hosts, purge_hosts):
if hosts is None:
return
hosts_to_add = set(hosts) - set(aggregate.hosts)
for i in hosts_to_add:
cloud.add_host_to_aggregate(aggregate.id, i)
if not purge_hosts:
return
hosts_to_remove = set(aggregate.hosts) - set(hosts)
for i in hosts_to_remove:
cloud.remove_host_from_aggregate(aggregate.id, i)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
metadata=dict(required=False, default=None, type='dict'),
availability_zone=dict(required=False, default=None),
hosts=dict(required=False, default=None, type='list', elements='str'),
purge_hosts=dict(default=True, type='bool'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
name = module.params['name']
metadata = module.params['metadata']
availability_zone = module.params['availability_zone']
hosts = module.params['hosts']
purge_hosts = module.params['purge_hosts']
state = module.params['state']
if metadata is not None:
metadata.pop('availability_zone', None)
sdk, cloud = openstack_cloud_from_module(module)
try:
aggregates = cloud.search_aggregates(name_or_id=name)
if len(aggregates) == 1:
aggregate = aggregates[0]
elif len(aggregates) == 0:
aggregate = None
else:
raise Exception("Should not happen")
if module.check_mode:
module.exit_json(changed=_system_state_change(module, aggregate))
if state == 'present':
if aggregate is None:
aggregate = cloud.create_aggregate(name=name,
availability_zone=availability_zone)
_update_hosts(cloud, aggregate, hosts, False)
if metadata:
cloud.set_aggregate_metadata(aggregate.id, metadata)
changed = True
else:
if _needs_update(module, aggregate):
if availability_zone is not None:
aggregate = cloud.update_aggregate(aggregate.id, name=name,
availability_zone=availability_zone)
if metadata is not None:
metas = metadata
for i in (set(aggregate.metadata.keys()) - set(metadata.keys())):
if i != 'availability_zone':
metas[i] = None
cloud.set_aggregate_metadata(aggregate.id, metas)
_update_hosts(cloud, aggregate, hosts, purge_hosts)
changed = True
else:
changed = False
module.exit_json(changed=changed)
elif state == 'absent':
if aggregate is None:
changed = False
else:
_update_hosts(cloud, aggregate, [], True)
cloud.delete_aggregate(aggregate.id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,185 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: identity_domain
short_description: Manage OpenStack Identity Domains
author:
- Monty Taylor (@emonty)
- Haneef Ali (@haneefs)
description:
- Create, update, or delete OpenStack Identity domains. If a domain
with the supplied name already exists, it will be updated with the
new description and enabled attributes.
options:
name:
description:
- Name that has to be given to the instance
required: true
type: str
description:
description:
- Description of the domain
type: str
enabled:
description:
- Is the domain enabled
type: bool
default: 'yes'
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a domain
- openstack.cloud.identity_domain:
cloud: mycloud
state: present
name: demo
description: Demo Domain
# Delete a domain
- openstack.cloud.identity_domain:
cloud: mycloud
state: absent
name: demo
'''
RETURN = '''
domain:
description: Dictionary describing the domain.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Domain ID.
type: str
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
name:
description: Domain name.
type: str
sample: "demo"
description:
description: Domain description.
type: str
sample: "Demo Domain"
enabled:
description: Domain description.
type: bool
sample: True
id:
description: The domain ID.
returned: On success when I(state) is 'present'
type: str
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _needs_update(module, domain):
if module.params['description'] is not None and \
domain.description != module.params['description']:
return True
if domain.enabled != module.params['enabled']:
return True
return False
def _system_state_change(module, domain):
state = module.params['state']
if state == 'absent' and domain:
return True
if state == 'present':
if domain is None:
return True
return _needs_update(module, domain)
return False
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
description=dict(default=None),
enabled=dict(default=True, type='bool'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
name = module.params['name']
description = module.params['description']
enabled = module.params['enabled']
state = module.params['state']
sdk, cloud = openstack_cloud_from_module(module)
try:
domains = cloud.search_domains(filters=dict(name=name))
if len(domains) > 1:
module.fail_json(msg='Domain name %s is not unique' % name)
elif len(domains) == 1:
domain = domains[0]
else:
domain = None
if module.check_mode:
module.exit_json(changed=_system_state_change(module, domain))
if state == 'present':
if domain is None:
domain = cloud.create_domain(
name=name, description=description, enabled=enabled)
changed = True
else:
if _needs_update(module, domain):
domain = cloud.update_domain(
domain.id, name=name, description=description,
enabled=enabled)
changed = True
else:
changed = False
module.exit_json(changed=changed, domain=domain, id=domain.id)
elif state == 'absent':
if domain is None:
changed = False
else:
cloud.delete_domain(domain.id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,139 @@
#!/usr/bin/python
# Copyright (c) 2016 Hewlett-Packard Enterprise Corporation
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: identity_domain_info
short_description: Retrieve information about one or more OpenStack domains
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
description:
- Retrieve information about a one or more OpenStack domains
- This module was called C(openstack.cloud.identity_domain_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(openstack.cloud.identity_domain_info) module no longer returns C(ansible_facts)!
options:
name:
description:
- Name or ID of the domain
type: str
filters:
description:
- A dictionary of meta data to use for further filtering. Elements of
this dictionary may be additional dictionaries.
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Gather information about previously created domain
- openstack.cloud.identity_domain_info:
cloud: awesomecloud
register: result
- debug:
msg: "{{ result.openstack_domains }}"
# Gather information about a previously created domain by name
- openstack.cloud.identity_domain_info:
cloud: awesomecloud
name: demodomain
register: result
- debug:
msg: "{{ result.openstack_domains }}"
# Gather information about a previously created domain with filter
- openstack.cloud.identity_domain_info:
cloud: awesomecloud
name: demodomain
filters:
enabled: false
register: result
- debug:
msg: "{{ result.openstack_domains }}"
'''
RETURN = '''
openstack_domains:
description: has all the OpenStack information about domains
returned: always, but can be null
type: complex
contains:
id:
description: Unique UUID.
returned: success
type: str
name:
description: Name given to the domain.
returned: success
type: str
description:
description: Description of the domain.
returned: success
type: str
enabled:
description: Flag to indicate if the domain is enabled.
returned: success
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[
['name', 'filters'],
]
)
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'openstack.cloud.identity_domain_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.identity_domain_facts' module has been renamed to 'openstack.cloud.identity_domain_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
filters = module.params['filters']
if name:
# Let's suppose user is passing domain ID
try:
domains = opcloud.get_domain(name)
except Exception:
domains = opcloud.search_domains(filters={'name': name})
else:
domains = opcloud.search_domains(filters)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_domains=domains))
else:
module.exit_json(changed=False, openstack_domains=domains)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,167 @@
#!/usr/bin/python
# Copyright (c) 2016 IBM
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: identity_group
short_description: Manage OpenStack Identity Groups
author: "Monty Taylor (@emonty), David Shrewsbury (@Shrews)"
description:
- Manage OpenStack Identity Groups. Groups can be created, deleted or
updated. Only the I(description) value can be updated.
options:
name:
description:
- Group name
required: true
type: str
description:
description:
- Group description
type: str
domain_id:
description:
- Domain id to create the group in if the cloud supports domains.
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a group named "demo"
- openstack.cloud.identity_group:
cloud: mycloud
state: present
name: demo
description: "Demo Group"
domain_id: demoid
# Update the description on existing "demo" group
- openstack.cloud.identity_group:
cloud: mycloud
state: present
name: demo
description: "Something else"
domain_id: demoid
# Delete group named "demo"
- openstack.cloud.identity_group:
cloud: mycloud
state: absent
name: demo
'''
RETURN = '''
group:
description: Dictionary describing the group.
returned: On success when I(state) is 'present'.
type: complex
contains:
id:
description: Unique group ID
type: str
sample: "ee6156ff04c645f481a6738311aea0b0"
name:
description: Group name
type: str
sample: "demo"
description:
description: Group description
type: str
sample: "Demo Group"
domain_id:
description: Domain for the group
type: str
sample: "default"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _system_state_change(state, description, group):
if state == 'present' and not group:
return True
if state == 'present' and description is not None and group.description != description:
return True
if state == 'absent' and group:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
description=dict(required=False, default=None),
domain_id=dict(required=False, default=None),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
name = module.params.get('name')
description = module.params.get('description')
state = module.params.get('state')
domain_id = module.params.pop('domain_id')
sdk, cloud = openstack_cloud_from_module(module)
try:
if domain_id:
group = cloud.get_group(name, filters={'domain_id': domain_id})
else:
group = cloud.get_group(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(state, description, group))
if state == 'present':
if group is None:
group = cloud.create_group(
name=name, description=description, domain=domain_id)
changed = True
else:
if description is not None and group.description != description:
group = cloud.update_group(
group.id, description=description)
changed = True
else:
changed = False
module.exit_json(changed=changed, group=group)
elif state == 'absent':
if group is None:
changed = False
else:
cloud.delete_group(group.id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,164 @@
#!/usr/bin/python
# Copyright (c) 2019, Phillipe Smith <phillipelnx@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: identity_group_info
short_description: Retrieve info about one or more OpenStack groups
author: "Phillipe Smith (@phsmith)"
description:
- Retrieve info about a one or more OpenStack groups.
options:
name:
description:
- Name or ID of the group.
type: str
domain:
description:
- Name or ID of the domain containing the group if the cloud supports domains
type: str
filters:
description:
- A dictionary of meta data to use for further filtering. Elements of
this dictionary may be additional dictionaries.
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Gather info about previously created groups
- name: gather info
hosts: localhost
tasks:
- name: Gather info about previously created groups
openstack.cloud.identity_group_info:
cloud: awesomecloud
register: openstack_groups
- debug:
var: openstack_groups
# Gather info about a previously created group by name
- name: gather info
hosts: localhost
tasks:
- name: Gather info about a previously created group by name
openstack.cloud.identity_group_info:
cloud: awesomecloud
name: demogroup
register: openstack_groups
- debug:
var: openstack_groups
# Gather info about a previously created group in a specific domain
- name: gather info
hosts: localhost
tasks:
- name: Gather info about a previously created group in a specific domain
openstack.cloud.identity_group_info:
cloud: awesomecloud
name: demogroup
domain: admindomain
register: openstack_groups
- debug:
var: openstack_groups
# Gather info about a previously created group in a specific domain with filter
- name: gather info
hosts: localhost
tasks:
- name: Gather info about a previously created group in a specific domain with filter
openstack.cloud.identity_group_info:
cloud: awesomecloud
name: demogroup
domain: admindomain
filters:
enabled: False
register: openstack_groups
- debug:
var: openstack_groups
'''
RETURN = '''
openstack_groups:
description: Dictionary describing all the matching groups.
returned: always, but can be null
type: complex
contains:
name:
description: Name given to the group.
returned: success
type: str
description:
description: Description of the group.
returned: success
type: str
id:
description: Unique UUID.
returned: success
type: str
domain_id:
description: Domain ID containing the group (keystone v3 clouds only)
returned: success
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, openstack_cloud_from_module
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
domain=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module = AnsibleModule(argument_spec)
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
domain = module.params['domain']
filters = module.params['filters']
if domain:
try:
# We assume admin is passing domain id
dom = opcloud.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
dom = opcloud.search_domains(filters={'name': domain})
if dom:
domain = dom[0]['id']
else:
module.fail_json(msg='Domain name or ID does not exist')
if not filters:
filters = {}
groups = opcloud.search_groups(name, filters, domain_id=domain)
module.exit_json(changed=False, groups=groups)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,125 @@
#!/usr/bin/python
# Copyright (c) 2016 IBM
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: identity_role
short_description: Manage OpenStack Identity Roles
author:
- Monty Taylor (@emonty)
- David Shrewsbury (@Shrews)
description:
- Manage OpenStack Identity Roles.
options:
name:
description:
- Role Name
required: true
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a role named "demo"
- openstack.cloud.identity_role:
cloud: mycloud
state: present
name: demo
# Delete the role named "demo"
- openstack.cloud.identity_role:
cloud: mycloud
state: absent
name: demo
'''
RETURN = '''
role:
description: Dictionary describing the role.
returned: On success when I(state) is 'present'.
type: complex
contains:
id:
description: Unique role ID.
type: str
sample: "677bfab34c844a01b88a217aa12ec4c2"
name:
description: Role name.
type: str
sample: "demo"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _system_state_change(state, role):
if state == 'present' and not role:
return True
if state == 'absent' and role:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
name = module.params.get('name')
state = module.params.get('state')
sdk, cloud = openstack_cloud_from_module(module)
try:
role = cloud.get_role(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(state, role))
if state == 'present':
if role is None:
role = cloud.create_role(name)
changed = True
else:
changed = False
module.exit_json(changed=changed, role=role)
elif state == 'absent':
if role is None:
changed = False
else:
cloud.delete_role(name)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,299 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: identity_user
short_description: Manage OpenStack Identity Users
author: David Shrewsbury (@Shrews)
description:
- Manage OpenStack Identity users. Users can be created,
updated or deleted using this module. A user will be updated
if I(name) matches an existing user and I(state) is present.
The value for I(name) cannot be updated without deleting and
re-creating the user.
options:
name:
description:
- Username for the user
required: true
type: str
password:
description:
- Password for the user
type: str
update_password:
required: false
choices: ['always', 'on_create']
description:
- C(always) will attempt to update password. C(on_create) will only
set the password for newly created users.
type: str
email:
description:
- Email address for the user
type: str
description:
description:
- Description about the user
type: str
default_project:
description:
- Project name or ID that the user should be associated with by default
type: str
domain:
description:
- Domain to create the user in if the cloud supports domains
type: str
enabled:
description:
- Is the user enabled
type: bool
default: 'yes'
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a user
- openstack.cloud.identity_user:
cloud: mycloud
state: present
name: demouser
password: secret
email: demo@example.com
domain: default
default_project: demo
# Delete a user
- openstack.cloud.identity_user:
cloud: mycloud
state: absent
name: demouser
# Create a user but don't update password if user exists
- openstack.cloud.identity_user:
cloud: mycloud
state: present
name: demouser
password: secret
update_password: on_create
email: demo@example.com
domain: default
default_project: demo
# Create a user without password
- openstack.cloud.identity_user:
cloud: mycloud
state: present
name: demouser
email: demo@example.com
domain: default
default_project: demo
'''
RETURN = '''
user:
description: Dictionary describing the user.
returned: On success when I(state) is 'present'
type: complex
contains:
default_project_id:
description: User default project ID. Only present with Keystone >= v3.
type: str
sample: "4427115787be45f08f0ec22a03bfc735"
domain_id:
description: User domain ID. Only present with Keystone >= v3.
type: str
sample: "default"
email:
description: User email address
type: str
sample: "demo@example.com"
id:
description: User ID
type: str
sample: "f59382db809c43139982ca4189404650"
name:
description: User name
type: str
sample: "demouser"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _needs_update(params_dict, user):
for k in params_dict:
if k not in ('password', 'update_password') and user[k] != params_dict[k]:
return True
# We don't get password back in the user object, so assume any supplied
# password is a change.
if (
params_dict['password'] is not None
and params_dict['update_password'] == 'always'
):
return True
return False
def _get_domain_id(cloud, domain):
try:
# We assume admin is passing domain id
domain_id = cloud.get_domain(domain)['id']
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
try:
domain_id = cloud.search_domains(filters={'name': domain})[0]['id']
except Exception:
# Ok, let's hope the user is non-admin and passing a sane id
domain_id = domain
return domain_id
def _get_default_project_id(cloud, default_project, domain_id, module):
project = cloud.get_project(default_project, domain_id=domain_id)
if not project:
module.fail_json(msg='Default project %s is not valid' % default_project)
return project['id']
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
password=dict(required=False, default=None, no_log=True),
email=dict(required=False, default=None),
default_project=dict(required=False, default=None),
description=dict(type='str'),
domain=dict(required=False, default=None),
enabled=dict(default=True, type='bool'),
state=dict(default='present', choices=['absent', 'present']),
update_password=dict(default=None, choices=['always', 'on_create']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(
argument_spec,
**module_kwargs)
name = module.params['name']
password = module.params.get('password')
email = module.params['email']
default_project = module.params['default_project']
domain = module.params['domain']
enabled = module.params['enabled']
state = module.params['state']
update_password = module.params['update_password']
description = module.params['description']
sdk, cloud = openstack_cloud_from_module(module)
try:
domain_id = None
if domain:
domain_id = _get_domain_id(cloud, domain)
user = cloud.get_user(name, domain_id=domain_id)
else:
user = cloud.get_user(name)
if state == 'present':
if update_password in ('always', 'on_create'):
if not password:
msg = "update_password is %s but a password value is missing" % update_password
module.fail_json(msg=msg)
default_project_id = None
if default_project:
default_project_id = _get_default_project_id(cloud, default_project, domain_id, module)
if user is None:
if description is not None:
user = cloud.create_user(
name=name, password=password, email=email,
default_project=default_project_id, domain_id=domain_id,
enabled=enabled, description=description)
else:
user = cloud.create_user(
name=name, password=password, email=email,
default_project=default_project_id, domain_id=domain_id,
enabled=enabled)
changed = True
else:
params_dict = {'email': email, 'enabled': enabled,
'password': password,
'update_password': update_password}
if description is not None:
params_dict['description'] = description
if domain_id is not None:
params_dict['domain_id'] = domain_id
if default_project_id is not None:
params_dict['default_project_id'] = default_project_id
if _needs_update(params_dict, user):
if update_password == 'always':
if description is not None:
user = cloud.update_user(
user['id'], password=password, email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled, description=description)
else:
user = cloud.update_user(
user['id'], password=password, email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled)
else:
if description is not None:
user = cloud.update_user(
user['id'], email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled, description=description)
else:
user = cloud.update_user(
user['id'], email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled)
changed = True
else:
changed = False
module.exit_json(changed=changed, user=user)
elif state == 'absent':
if user is None:
changed = False
else:
if domain:
cloud.delete_user(user['id'], domain_id=domain_id)
else:
cloud.delete_user(user['id'])
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,174 @@
#!/usr/bin/python
# Copyright (c) 2016 Hewlett-Packard Enterprise Corporation
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: identity_user_info
short_description: Retrieve information about one or more OpenStack users
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
description:
- Retrieve information about a one or more OpenStack users
- This module was called C(openstack.cloud.identity_user_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(openstack.cloud.identity_user_info) module no longer returns C(ansible_facts)!
options:
name:
description:
- Name or ID of the user
type: str
domain:
description:
- Name or ID of the domain containing the user if the cloud supports domains
type: str
filters:
description:
- A dictionary of meta data to use for further filtering. Elements of
this dictionary may be additional dictionaries.
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Gather information about previously created users
- openstack.cloud.identity_user_info:
cloud: awesomecloud
register: result
- debug:
msg: "{{ result.openstack_users }}"
# Gather information about a previously created user by name
- openstack.cloud.identity_user_info:
cloud: awesomecloud
name: demouser
register: result
- debug:
msg: "{{ result.openstack_users }}"
# Gather information about a previously created user in a specific domain
- openstack.cloud.identity_user_info:
cloud: awesomecloud
name: demouser
domain: admindomain
register: result
- debug:
msg: "{{ result.openstack_users }}"
# Gather information about a previously created user in a specific domain with filter
- openstack.cloud.identity_user_info:
cloud: awesomecloud
name: demouser
domain: admindomain
filters:
enabled: False
register: result
- debug:
msg: "{{ result.openstack_users }}"
'''
RETURN = '''
openstack_users:
description: has all the OpenStack information about users
returned: always, but can be null
type: complex
contains:
id:
description: Unique UUID.
returned: success
type: str
name:
description: Name given to the user.
returned: success
type: str
enabled:
description: Flag to indicate if the user is enabled
returned: success
type: bool
domain_id:
description: Domain ID containing the user
returned: success
type: str
default_project_id:
description: Default project ID of the user
returned: success
type: str
email:
description: Email of the user
returned: success
type: str
username:
description: Username of the user
returned: success
type: str
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_full_argument_spec,
openstack_cloud_from_module,
)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
domain=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name == 'openstack.cloud.identity_user_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.identity_user_facts' module has been renamed to 'openstack.cloud.identity_user_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
domain = module.params['domain']
filters = module.params['filters']
if domain:
try:
# We assume admin is passing domain id
dom = opcloud.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
dom = opcloud.search_domains(filters={'name': domain})
if dom:
domain = dom[0]['id']
else:
module.fail_json(msg='Domain name or ID does not exist')
if not filters:
filters = {}
filters['domain_id'] = domain
users = opcloud.search_users(name, filters)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_users=users))
else:
module.exit_json(changed=False, openstack_users=users)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

246
plugins/modules/image.py Normal file
View File

@ -0,0 +1,246 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2013, Benno Joy <benno@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# TODO(mordred): we need to support "location"(v1) and "locations"(v2)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: image
short_description: Add/Delete images from OpenStack Cloud
author: "Monty Taylor (@emonty)"
description:
- Add or Remove images from the OpenStack Image Repository
options:
name:
description:
- The name of the image when uploading - or the name/ID of the image if deleting
required: true
type: str
id:
description:
- The ID of the image when uploading an image
type: str
checksum:
description:
- The checksum of the image
type: str
disk_format:
description:
- The format of the disk that is getting uploaded
default: qcow2
choices: ['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']
type: str
container_format:
description:
- The format of the container
default: bare
choices: ['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']
type: str
owner:
description:
- The owner of the image
type: str
min_disk:
description:
- The minimum disk space (in GB) required to boot this image
type: int
min_ram:
description:
- The minimum ram (in MB) required to boot this image
type: int
is_public:
description:
- Whether the image can be accessed publicly. Note that publicizing an image requires admin role by default.
type: bool
default: false
protected:
description:
- Prevent image from being deleted
type: bool
default: 'no'
filename:
description:
- The path to the file which has to be uploaded
type: str
ramdisk:
description:
- The name of an existing ramdisk image that will be associated with this image
type: str
kernel:
description:
- The name of an existing kernel image that will be associated with this image
type: str
properties:
description:
- Additional properties to be associated with this image
default: {}
type: dict
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
volume:
description:
- ID of a volume to create an image from.
- The volume must be in AVAILABLE state.
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Upload an image from a local file named cirros-0.3.0-x86_64-disk.img
- openstack.cloud.image:
auth:
auth_url: https://identity.example.com
username: admin
password: passme
project_name: admin
openstack.cloud.identity_user_domain_name: Default
openstack.cloud.project_domain_name: Default
name: cirros
container_format: bare
disk_format: qcow2
state: present
filename: cirros-0.3.0-x86_64-disk.img
kernel: cirros-vmlinuz
ramdisk: cirros-initrd
properties:
cpu_arch: x86_64
distro: ubuntu
# Create image from volume attached to an instance
- name: create volume snapshot
openstack.cloud.volume_snapshot:
auth:
"{{ auth }}"
display_name: myvol_snapshot
volume: myvol
force: yes
register: myvol_snapshot
- name: create volume from snapshot
openstack.cloud.volume:
auth:
"{{ auth }}"
size: "{{ myvol_snapshot.snapshot.size }}"
snapshot_id: "{{ myvol_snapshot.snapshot.id }}"
display_name: myvol_snapshot_volume
wait: yes
register: myvol_snapshot_volume
- name: create image from volume snapshot
openstack.cloud.image:
auth:
"{{ auth }}"
volume: "{{ myvol_snapshot_volume.volume.id }}"
name: myvol_image
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
id=dict(default=None),
checksum=dict(default=None),
disk_format=dict(default='qcow2', choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']),
container_format=dict(default='bare', choices=['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']),
owner=dict(default=None),
min_disk=dict(type='int', default=0),
min_ram=dict(type='int', default=0),
is_public=dict(type='bool', default=False),
protected=dict(type='bool', default=False),
filename=dict(default=None),
ramdisk=dict(default=None),
kernel=dict(default=None),
properties=dict(type='dict', default={}),
volume=dict(default=None),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[['filename', 'volume']],
)
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = False
if module.params['id']:
image = cloud.get_image(name_or_id=module.params['id'])
elif module.params['checksum']:
image = cloud.get_image(name_or_id=module.params['name'], filters={'checksum': module.params['checksum']})
else:
image = cloud.get_image(name_or_id=module.params['name'])
if module.params['state'] == 'present':
if not image:
kwargs = {}
if module.params['id'] is not None:
kwargs['id'] = module.params['id']
image = cloud.create_image(
name=module.params['name'],
filename=module.params['filename'],
disk_format=module.params['disk_format'],
container_format=module.params['container_format'],
wait=module.params['wait'],
timeout=module.params['timeout'],
is_public=module.params['is_public'],
protected=module.params['protected'],
min_disk=module.params['min_disk'],
min_ram=module.params['min_ram'],
volume=module.params['volume'],
**kwargs
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed, image=image, id=image.id)
cloud.update_image_properties(
image=image,
kernel=module.params['kernel'],
ramdisk=module.params['ramdisk'],
protected=module.params['protected'],
**module.params['properties'])
image = cloud.get_image(name_or_id=image.id)
module.exit_json(changed=changed, image=image, id=image.id)
elif module.params['state'] == 'absent':
if not image:
changed = False
else:
cloud.delete_image(
name_or_id=module.params['name'],
wait=module.params['wait'],
timeout=module.params['timeout'])
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,192 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: image_info
short_description: Retrieve information about an image within OpenStack.
author: "Davide Agnello (@dagnello)"
description:
- Retrieve information about a image image from OpenStack.
- This module was called C(openstack.cloud.image_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(openstack.cloud.image_info) module no longer returns C(ansible_facts)!
options:
image:
description:
- Name or ID of the image
required: false
type: str
properties:
description:
- Dict of properties of the images used for query
type: dict
required: false
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Gather information about a previously created image named image1
openstack.cloud.image_info:
auth:
auth_url: https://identity.example.com
username: user
password: password
project_name: someproject
image: image1
register: result
- name: Show openstack information
debug:
msg: "{{ result.openstack_image }}"
# Show all available Openstack images
- name: Retrieve all available Openstack images
openstack.cloud.image_info:
register: result
- name: Show images
debug:
msg: "{{ result.openstack_image }}"
# Show images matching requested properties
- name: Retrieve images having properties with desired values
openstack.cloud.image_facts:
properties:
some_property: some_value
OtherProp: OtherVal
- name: Show images
debug:
msg: "{{ result.openstack_image }}"
'''
RETURN = '''
openstack_image:
description: has all the openstack information about the image
returned: always, but can be null
type: complex
contains:
id:
description: Unique UUID.
returned: success
type: str
name:
description: Name given to the image.
returned: success
type: str
status:
description: Image status.
returned: success
type: str
created_at:
description: Image created at timestamp.
returned: success
type: str
deleted:
description: Image deleted flag.
returned: success
type: bool
container_format:
description: Container format of the image.
returned: success
type: str
min_ram:
description: Min amount of RAM required for this image.
returned: success
type: int
disk_format:
description: Disk format of the image.
returned: success
type: str
updated_at:
description: Image updated at timestamp.
returned: success
type: str
properties:
description: Additional properties associated with the image.
returned: success
type: dict
min_disk:
description: Min amount of disk space required for this image.
returned: success
type: int
protected:
description: Image protected flag.
returned: success
type: bool
checksum:
description: Checksum for the image.
returned: success
type: str
owner:
description: Owner for the image.
returned: success
type: str
is_public:
description: Is public flag of the image.
returned: success
type: bool
deleted_at:
description: Image deleted at timestamp.
returned: success
type: str
size:
description: Size of the image.
returned: success
type: int
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
image=dict(required=False),
properties=dict(default=None, type='dict'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'openstack.cloud.image_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.image_facts' module has been renamed to 'openstack.cloud.image_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, cloud = openstack_cloud_from_module(module)
try:
if module.params['image']:
image = cloud.get_image(module.params['image'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_image=image))
else:
module.exit_json(changed=False, openstack_image=image)
else:
images = cloud.search_images(filters=module.params['properties'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_image=images))
else:
module.exit_json(changed=False, openstack_image=images)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

167
plugins/modules/keypair.py Normal file
View File

@ -0,0 +1,167 @@
#!/usr/bin/python
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2013, Benno Joy <benno@ansible.com>
# Copyright (c) 2013, John Dewey <john@dewey.ws>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: keypair
short_description: Add/Delete a keypair from OpenStack
author: "Benno Joy (@bennojoy)"
description:
- Add or Remove key pair from OpenStack
options:
name:
description:
- Name that has to be given to the key pair
required: true
type: str
public_key:
description:
- The public key that would be uploaded to nova and injected into VMs
upon creation.
type: str
public_key_file:
description:
- Path to local file containing ssh public key. Mutually exclusive
with public_key.
type: str
state:
description:
- Should the resource be present or absent. If state is replace and
the key exists but has different content, delete it and recreate it
with the new content.
choices: [present, absent, replace]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Creates a key pair with the running users public key
- openstack.cloud.keypair:
cloud: mordred
state: present
name: ansible_key
public_key_file: /home/me/.ssh/id_rsa.pub
# Creates a new key pair and the private key returned after the run.
- openstack.cloud.keypair:
cloud: rax-dfw
state: present
name: ansible_key
'''
RETURN = '''
id:
description: Unique UUID.
returned: success
type: str
name:
description: Name given to the keypair.
returned: success
type: str
public_key:
description: The public key value for the keypair.
returned: success
type: str
private_key:
description: The private key value for the keypair.
returned: Only when a keypair is generated for the user (e.g., when creating one
and a public key is not specified).
type: str
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _system_state_change(module, keypair):
state = module.params['state']
if state == 'present' and not keypair:
return True
if state == 'absent' and keypair:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
public_key=dict(default=None),
public_key_file=dict(default=None),
state=dict(default='present',
choices=['absent', 'present', 'replace']),
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[['public_key', 'public_key_file']])
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
state = module.params['state']
name = module.params['name']
public_key = module.params['public_key']
if module.params['public_key_file']:
with open(module.params['public_key_file']) as public_key_fh:
public_key = public_key_fh.read().rstrip()
sdk, cloud = openstack_cloud_from_module(module)
try:
keypair = cloud.get_keypair(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(module, keypair))
if state in ('present', 'replace'):
if keypair and keypair['name'] == name:
if public_key and (public_key != keypair['public_key']):
if state == 'present':
module.fail_json(
msg="Key name %s present but key hash not the same"
" as offered. Delete key first." % name
)
else:
cloud.delete_keypair(name)
keypair = cloud.create_keypair(name, public_key)
changed = True
else:
changed = False
else:
keypair = cloud.create_keypair(name, public_key)
changed = True
module.exit_json(changed=changed,
key=keypair,
id=keypair['id'])
elif state == 'absent':
if keypair:
cloud.delete_keypair(name)
module.exit_json(changed=True)
module.exit_json(changed=False)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,217 @@
#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: keystone_federation_protocol
short_description: manage a federation Protocol
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
description:
- Manage a federation Protocol.
options:
name:
description:
- The name of the Protocol.
type: str
required: true
aliases: ['id']
state:
description:
- Whether the protocol should be C(present) or C(absent).
choices: ['present', 'absent']
default: present
type: str
idp_id:
description:
- The name of the Identity Provider this Protocol is associated with.
aliases: ['idp_name']
required: true
type: str
mapping_id:
description:
- The name of the Mapping to use for this Protocol.'
- Required when creating a new Protocol.
type: str
aliases: ['mapping_name']
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Create a protocol
openstack.cloud.keystone_federation_protocol:
cloud: example_cloud
name: example_protocol
idp_id: example_idp
mapping_id: example_mapping
- name: Delete a protocol
openstack.cloud.keystone_federation_protocol:
cloud: example_cloud
name: example_protocol
idp_id: example_idp
state: absent
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
def normalize_protocol(protocol):
"""
Normalizes the protocol definitions so that the outputs are consistent with the
parameters
- "name" (parameter) == "id" (SDK)
"""
if protocol is None:
return None
_protocol = protocol.to_dict()
_protocol['name'] = protocol['id']
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
_protocol['idp_id'] = protocol['idp_id']
return _protocol
def delete_protocol(module, sdk, cloud, protocol):
"""
Delete an existing Protocol
returns: the "Changed" state
"""
if protocol is None:
return False
if module.check_mode:
return True
try:
cloud.identity.delete_federation_protocol(None, protocol)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to delete protocol: {0}'.format(str(ex)))
return True
def create_protocol(module, sdk, cloud, name):
"""
Create a new Protocol
returns: the "Changed" state and the new protocol
"""
if module.check_mode:
return True, None
idp_name = module.params.get('idp_id')
mapping_id = module.params.get('mapping_id')
attributes = {
'idp_id': idp_name,
'mapping_id': mapping_id,
}
try:
protocol = cloud.identity.create_federation_protocol(id=name, **attributes)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to create protocol: {0}'.format(str(ex)))
return (True, protocol)
def update_protocol(module, sdk, cloud, protocol):
"""
Update an existing Protocol
returns: the "Changed" state and the new protocol
"""
mapping_id = module.params.get('mapping_id')
attributes = {}
if (mapping_id is not None) and (mapping_id != protocol.mapping_id):
attributes['mapping_id'] = mapping_id
if not attributes:
return False, protocol
if module.check_mode:
return True, None
try:
new_protocol = cloud.identity.update_federation_protocol(None, protocol, **attributes)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to update protocol: {0}'.format(str(ex)))
return (True, new_protocol)
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
name=dict(required=True, aliases=['id']),
state=dict(default='present', choices=['absent', 'present']),
idp_id=dict(required=True, aliases=['idp_name']),
mapping_id=dict(aliases=['mapping_name']),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
state = module.params.get('state')
idp = module.params.get('idp_id')
changed = False
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
try:
protocol = cloud.identity.get_federation_protocol(idp, name)
except sdk.exceptions.ResourceNotFound:
protocol = None
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get protocol: {0}'.format(str(ex)))
if state == 'absent':
if protocol is not None:
changed = delete_protocol(module, sdk, cloud, protocol)
module.exit_json(changed=changed)
# state == 'present'
else:
if protocol is None:
if module.params.get('mapping_id') is None:
module.fail_json(msg='A mapping_id must be passed when creating'
' a protocol')
(changed, protocol) = create_protocol(module, sdk, cloud, name)
protocol = normalize_protocol(protocol)
module.exit_json(changed=changed, protocol=protocol)
else:
(changed, new_protocol) = update_protocol(module, sdk, cloud, protocol)
new_protocol = normalize_protocol(new_protocol)
module.exit_json(changed=changed, protocol=new_protocol)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,116 @@
#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: keystone_federation_protocol_info
short_description: get information about federation Protocols
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
description:
- Get information about federation Protocols.
options:
name:
description:
- The name of the Protocol.
type: str
aliases: ['id']
idp_id:
description:
- The name of the Identity Provider this Protocol is associated with.
aliases: ['idp_name']
required: true
type: str
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.44"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Describe a protocol
openstack.cloud.keystone_federation_protocol_info:
cloud: example_cloud
name: example_protocol
idp_id: example_idp
mapping_name: example_mapping
- name: Describe all protocols attached to an IDP
openstack.cloud.keystone_federation_protocol_info:
cloud: example_cloud
idp_id: example_idp
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
def normalize_protocol(protocol):
"""
Normalizes the protocol definitions so that the outputs are consistent with the
parameters
- "name" (parameter) == "id" (SDK)
"""
if protocol is None:
return None
_protocol = protocol.to_dict()
_protocol['name'] = protocol['id']
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
_protocol['idp_id'] = protocol['idp_id']
return _protocol
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
name=dict(aliases=['id']),
idp_id=dict(required=True, aliases=['idp_name']),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
idp = module.params.get('idp_id')
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
if name:
try:
protocol = cloud.identity.get_federation_protocol(idp, name)
protocol = normalize_protocol(protocol)
except sdk.exceptions.ResourceNotFound:
module.fail_json(msg='Failed to find protocol')
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get protocol: {0}'.format(str(ex)))
module.exit_json(changed=False, protocols=[protocol])
else:
try:
protocols = list(map(normalize_protocol, cloud.identity.federation_protocols(idp)))
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to list protocols: {0}'.format(str(ex)))
module.exit_json(changed=False, protocols=protocols)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,260 @@
#!/usr/bin/python
# Copyright (c) 2018 Catalyst Cloud Ltd.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: lb_listener
short_description: Add/Delete a listener for a load balancer from OpenStack Cloud
author: "Lingxian Kong (@lingxiankong)"
description:
- Add or Remove a listener for a load balancer from the OpenStack load-balancer service.
options:
name:
description:
- Name that has to be given to the listener
required: true
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
loadbalancer:
description:
- The name or id of the load balancer that this listener belongs to.
required: true
type: str
protocol:
description:
- The protocol for the listener.
choices: [HTTP, HTTPS, TCP, TERMINATED_HTTPS]
default: HTTP
type: str
protocol_port:
description:
- The protocol port number for the listener.
default: 80
type: int
wait:
description:
- If the module should wait for the load balancer to be ACTIVE.
type: bool
default: 'yes'
timeout:
description:
- The amount of time the module should wait for the load balancer to get
into ACTIVE state.
default: 180
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
id:
description: The listener UUID.
returned: On success when I(state) is 'present'
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
listener:
description: Dictionary describing the listener.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Unique UUID.
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
name:
description: Name given to the listener.
type: str
sample: "test"
description:
description: The listener description.
type: str
sample: "description"
load_balancer_id:
description: The load balancer UUID this listener belongs to.
type: str
sample: "b32eef7e-d2a6-4ea4-a301-60a873f89b3b"
loadbalancers:
description: A list of load balancer IDs..
type: list
sample: [{"id": "b32eef7e-d2a6-4ea4-a301-60a873f89b3b"}]
provisioning_status:
description: The provisioning status of the listener.
type: str
sample: "ACTIVE"
operating_status:
description: The operating status of the listener.
type: str
sample: "ONLINE"
is_admin_state_up:
description: The administrative state of the listener.
type: bool
sample: true
protocol:
description: The protocol for the listener.
type: str
sample: "HTTP"
protocol_port:
description: The protocol port number for the listener.
type: int
sample: 80
'''
EXAMPLES = '''
# Create a listener, wait for the loadbalancer to be active.
- openstack.cloud.lb_listener:
cloud: mycloud
endpoint_type: admin
state: present
name: test-listener
loadbalancer: test-loadbalancer
protocol: HTTP
protocol_port: 8080
# Create a listener, do not wait for the loadbalancer to be active.
- openstack.cloud.lb_listener:
cloud: mycloud
endpoint_type: admin
state: present
name: test-listener
loadbalancer: test-loadbalancer
protocol: HTTP
protocol_port: 8080
wait: no
# Delete a listener
- openstack.cloud.lb_listener:
cloud: mycloud
endpoint_type: admin
state: absent
name: test-listener
loadbalancer: test-loadbalancer
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _lb_wait_for_status(module, cloud, lb, status, failures, interval=5):
"""Wait for load balancer to be in a particular provisioning status."""
timeout = module.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
lb = cloud.load_balancer.get_load_balancer(lb.id)
if lb.provisioning_status == status:
return None
if lb.provisioning_status in failures:
module.fail_json(
msg="Load Balancer %s transitioned to failure state %s" %
(lb.id, lb.provisioning_status)
)
time.sleep(interval)
total_sleep += interval
module.fail_json(
msg="Timeout waiting for Load Balancer %s to transition to %s" %
(lb.id, status)
)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
loadbalancer=dict(required=True),
protocol=dict(default='HTTP',
choices=['HTTP', 'HTTPS', 'TCP', 'TERMINATED_HTTPS']),
protocol_port=dict(default=80, type='int', required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
loadbalancer = module.params['loadbalancer']
loadbalancer_id = None
try:
changed = False
listener = cloud.load_balancer.find_listener(
name_or_id=module.params['name'])
if module.params['state'] == 'present':
if not listener:
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
if not lb:
module.fail_json(
msg='load balancer %s is not found' % loadbalancer
)
loadbalancer_id = lb.id
listener = cloud.load_balancer.create_listener(
name=module.params['name'],
loadbalancer_id=loadbalancer_id,
protocol=module.params['protocol'],
protocol_port=module.params['protocol_port'],
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed,
listener=listener.to_dict(),
id=listener.id)
if module.params['wait']:
# Check in case the listener already exists.
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
if not lb:
module.fail_json(
msg='load balancer %s is not found' % loadbalancer
)
_lb_wait_for_status(module, cloud, lb, "ACTIVE", ["ERROR"])
module.exit_json(changed=changed, listener=listener.to_dict(),
id=listener.id)
elif module.params['state'] == 'absent':
if not listener:
changed = False
else:
cloud.load_balancer.delete_listener(listener)
changed = True
if module.params['wait']:
# Wait for the load balancer to be active after deleting
# the listener.
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
if not lb:
module.fail_json(
msg='load balancer %s is not found' % loadbalancer
)
_lb_wait_for_status(module, cloud, lb, "ACTIVE", ["ERROR"])
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,232 @@
#!/usr/bin/python
# Copyright (c) 2018 Catalyst Cloud Ltd.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: lb_member
short_description: Add/Delete a member for a pool in load balancer from OpenStack Cloud
author: "Lingxian Kong (@lingxiankong)"
description:
- Add or Remove a member for a pool from the OpenStack load-balancer service.
options:
name:
description:
- Name that has to be given to the member
required: true
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
pool:
description:
- The name or id of the pool that this member belongs to.
required: true
type: str
protocol_port:
description:
- The protocol port number for the member.
default: 80
type: int
address:
description:
- The IP address of the member.
type: str
subnet_id:
description:
- The subnet ID the member service is accessible from.
type: str
wait:
description:
- If the module should wait for the load balancer to be ACTIVE.
type: bool
default: 'yes'
timeout:
description:
- The amount of time the module should wait for the load balancer to get
into ACTIVE state.
default: 180
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
id:
description: The member UUID.
returned: On success when I(state) is 'present'
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
member:
description: Dictionary describing the member.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Unique UUID.
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
name:
description: Name given to the member.
type: str
sample: "test"
description:
description: The member description.
type: str
sample: "description"
provisioning_status:
description: The provisioning status of the member.
type: str
sample: "ACTIVE"
operating_status:
description: The operating status of the member.
type: str
sample: "ONLINE"
is_admin_state_up:
description: The administrative state of the member.
type: bool
sample: true
protocol_port:
description: The protocol port number for the member.
type: int
sample: 80
subnet_id:
description: The subnet ID the member service is accessible from.
type: str
sample: "489247fa-9c25-11e8-9679-00224d6b7bc1"
address:
description: The IP address of the backend member server.
type: str
sample: "192.168.2.10"
'''
EXAMPLES = '''
# Create a member, wait for the member to be created.
- openstack.cloud.lb_member:
cloud: mycloud
endpoint_type: admin
state: present
name: test-member
pool: test-pool
address: 192.168.10.3
protocol_port: 8080
# Delete a listener
- openstack.cloud.lb_member:
cloud: mycloud
endpoint_type: admin
state: absent
name: test-member
pool: test-pool
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _wait_for_member_status(module, cloud, pool_id, member_id, status,
failures, interval=5):
timeout = module.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
member = cloud.load_balancer.get_member(member_id, pool_id)
provisioning_status = member.provisioning_status
if provisioning_status == status:
return member
if provisioning_status in failures:
module.fail_json(
msg="Member %s transitioned to failure state %s" %
(member_id, provisioning_status)
)
time.sleep(interval)
total_sleep += interval
module.fail_json(
msg="Timeout waiting for member %s to transition to %s" %
(member_id, status)
)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
pool=dict(required=True),
address=dict(default=None),
protocol_port=dict(default=80, type='int'),
subnet_id=dict(default=None),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
name = module.params['name']
pool = module.params['pool']
try:
changed = False
pool_ret = cloud.load_balancer.find_pool(name_or_id=pool)
if not pool_ret:
module.fail_json(msg='pool %s is not found' % pool)
pool_id = pool_ret.id
member = cloud.load_balancer.find_member(name, pool_id)
if module.params['state'] == 'present':
if not member:
member = cloud.load_balancer.create_member(
pool_ret,
address=module.params['address'],
name=name,
protocol_port=module.params['protocol_port'],
subnet_id=module.params['subnet_id']
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed,
member=member.to_dict(),
id=member.id)
if module.params['wait']:
member = _wait_for_member_status(module, cloud, pool_id,
member.id, "ACTIVE",
["ERROR"])
module.exit_json(changed=changed, member=member.to_dict(),
id=member.id)
elif module.params['state'] == 'absent':
if member:
cloud.load_balancer.delete_member(member, pool_ret)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

271
plugins/modules/lb_pool.py Normal file
View File

@ -0,0 +1,271 @@
#!/usr/bin/python
# Copyright (c) 2018 Catalyst Cloud Ltd.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: lb_pool
short_description: Add/Delete a pool in the load balancing service from OpenStack Cloud
author: "Lingxian Kong (@lingxiankong)"
description:
- Add or Remove a pool from the OpenStack load-balancer service.
options:
name:
description:
- Name that has to be given to the pool
required: true
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
loadbalancer:
description:
- The name or id of the load balancer that this pool belongs to.
Either loadbalancer or listener must be specified for pool creation.
type: str
listener:
description:
- The name or id of the listener that this pool belongs to.
Either loadbalancer or listener must be specified for pool creation.
type: str
protocol:
description:
- The protocol for the pool.
choices: [HTTP, HTTPS, PROXY, TCP, UDP]
default: HTTP
type: str
lb_algorithm:
description:
- The load balancing algorithm for the pool.
choices: [LEAST_CONNECTIONS, ROUND_ROBIN, SOURCE_IP]
default: ROUND_ROBIN
type: str
wait:
description:
- If the module should wait for the pool to be ACTIVE.
type: bool
default: 'yes'
timeout:
description:
- The amount of time the module should wait for the pool to get
into ACTIVE state.
default: 180
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
id:
description: The pool UUID.
returned: On success when I(state) is 'present'
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
listener:
description: Dictionary describing the pool.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Unique UUID.
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
name:
description: Name given to the pool.
type: str
sample: "test"
description:
description: The pool description.
type: str
sample: "description"
loadbalancers:
description: A list of load balancer IDs.
type: list
sample: [{"id": "b32eef7e-d2a6-4ea4-a301-60a873f89b3b"}]
listeners:
description: A list of listener IDs.
type: list
sample: [{"id": "b32eef7e-d2a6-4ea4-a301-60a873f89b3b"}]
members:
description: A list of member IDs.
type: list
sample: [{"id": "b32eef7e-d2a6-4ea4-a301-60a873f89b3b"}]
loadbalancer_id:
description: The load balancer ID the pool belongs to. This field is set when the pool doesn't belong to any listener in the load balancer.
type: str
sample: "7c4be3f8-9c2f-11e8-83b3-44a8422643a4"
listener_id:
description: The listener ID the pool belongs to.
type: str
sample: "956aa716-9c2f-11e8-83b3-44a8422643a4"
provisioning_status:
description: The provisioning status of the pool.
type: str
sample: "ACTIVE"
operating_status:
description: The operating status of the pool.
type: str
sample: "ONLINE"
is_admin_state_up:
description: The administrative state of the pool.
type: bool
sample: true
protocol:
description: The protocol for the pool.
type: str
sample: "HTTP"
lb_algorithm:
description: The load balancing algorithm for the pool.
type: str
sample: "ROUND_ROBIN"
'''
EXAMPLES = '''
# Create a pool, wait for the pool to be active.
- openstack.cloud.lb_pool:
cloud: mycloud
endpoint_type: admin
state: present
name: test-pool
loadbalancer: test-loadbalancer
protocol: HTTP
lb_algorithm: ROUND_ROBIN
# Delete a pool
- openstack.cloud.lb_pool:
cloud: mycloud
endpoint_type: admin
state: absent
name: test-pool
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, \
openstack_module_kwargs, openstack_cloud_from_module
def _wait_for_pool_status(module, cloud, pool_id, status, failures,
interval=5):
timeout = module.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
pool = cloud.load_balancer.get_pool(pool_id)
provisioning_status = pool.provisioning_status
if provisioning_status == status:
return pool
if provisioning_status in failures:
module.fail_json(
msg="pool %s transitioned to failure state %s" %
(pool_id, provisioning_status)
)
time.sleep(interval)
total_sleep += interval
module.fail_json(
msg="timeout waiting for pool %s to transition to %s" %
(pool_id, status)
)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
loadbalancer=dict(default=None),
listener=dict(default=None),
protocol=dict(default='HTTP',
choices=['HTTP', 'HTTPS', 'TCP', 'UDP', 'PROXY']),
lb_algorithm=dict(
default='ROUND_ROBIN',
choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP']
)
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[['loadbalancer', 'listener']]
)
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
loadbalancer = module.params['loadbalancer']
listener = module.params['listener']
try:
changed = False
pool = cloud.load_balancer.find_pool(name_or_id=module.params['name'])
if module.params['state'] == 'present':
if not pool:
loadbalancer_id = None
if not (loadbalancer or listener):
module.fail_json(
msg="either loadbalancer or listener must be provided"
)
if loadbalancer:
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
if not lb:
module.fail_json(msg='load balancer %s is not '
'found' % loadbalancer)
loadbalancer_id = lb.id
listener_id = None
if listener:
listener_ret = cloud.load_balancer.find_listener(listener)
if not listener_ret:
module.fail_json(msg='listener %s is not found'
% listener)
listener_id = listener_ret.id
pool = cloud.load_balancer.create_pool(
name=module.params['name'],
loadbalancer_id=loadbalancer_id,
listener_id=listener_id,
protocol=module.params['protocol'],
lb_algorithm=module.params['lb_algorithm']
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed,
pool=pool.to_dict(),
id=pool.id)
if module.params['wait']:
pool = _wait_for_pool_status(module, cloud, pool.id, "ACTIVE",
["ERROR"])
module.exit_json(changed=changed, pool=pool.to_dict(),
id=pool.id)
elif module.params['state'] == 'absent':
if pool:
cloud.load_balancer.delete_pool(pool)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,614 @@
#!/usr/bin/python
# Copyright (c) 2018 Catalyst Cloud Ltd.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: loadbalancer
short_description: Add/Delete load balancer from OpenStack Cloud
author: "Lingxian Kong (@lingxiankong)"
description:
- Add or Remove load balancer from the OpenStack load-balancer
service(Octavia). Load balancer update is not supported for now.
options:
name:
description:
- Name that has to be given to the load balancer
required: true
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
vip_network:
description:
- The name or id of the network for the virtual IP of the load balancer.
One of I(vip_network), I(vip_subnet), or I(vip_port) must be specified
for creation.
type: str
vip_subnet:
description:
- The name or id of the subnet for the virtual IP of the load balancer.
One of I(vip_network), I(vip_subnet), or I(vip_port) must be specified
for creation.
type: str
vip_port:
description:
- The name or id of the load balancer virtual IP port. One of
I(vip_network), I(vip_subnet), or I(vip_port) must be specified for
creation.
type: str
vip_address:
description:
- IP address of the load balancer virtual IP.
type: str
public_ip_address:
description:
- Public IP address associated with the VIP.
type: str
auto_public_ip:
description:
- Allocate a public IP address and associate with the VIP automatically.
type: bool
default: 'no'
public_network:
description:
- The name or ID of a Neutron external network.
type: str
delete_public_ip:
description:
- When C(state=absent) and this option is true, any public IP address
associated with the VIP will be deleted along with the load balancer.
type: bool
default: 'no'
listeners:
description:
- A list of listeners that attached to the load balancer.
suboptions:
name:
description:
- The listener name or ID.
protocol:
description:
- The protocol for the listener.
default: HTTP
protocol_port:
description:
- The protocol port number for the listener.
default: 80
pool:
description:
- The pool attached to the listener.
suboptions:
name:
description:
- The pool name or ID.
protocol:
description:
- The protocol for the pool.
default: HTTP
lb_algorithm:
description:
- The load balancing algorithm for the pool.
default: ROUND_ROBIN
members:
description:
- A list of members that added to the pool.
suboptions:
name:
description:
- The member name or ID.
address:
description:
- The IP address of the member.
protocol_port:
description:
- The protocol port number for the member.
default: 80
subnet:
description:
- The name or ID of the subnet the member service is
accessible from.
elements: dict
type: list
wait:
description:
- If the module should wait for the load balancer to be created or
deleted.
type: bool
default: 'yes'
timeout:
description:
- The amount of time the module should wait.
default: 180
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
id:
description: The load balancer UUID.
returned: On success when C(state=present)
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
loadbalancer:
description: Dictionary describing the load balancer.
returned: On success when C(state=present)
type: complex
contains:
id:
description: Unique UUID.
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
name:
description: Name given to the load balancer.
type: str
sample: "lingxian_test"
vip_network_id:
description: Network ID the load balancer virtual IP port belongs in.
type: str
sample: "f171db43-56fd-41cf-82d7-4e91d741762e"
vip_subnet_id:
description: Subnet ID the load balancer virtual IP port belongs in.
type: str
sample: "c53e3c70-9d62-409a-9f71-db148e7aa853"
vip_port_id:
description: The load balancer virtual IP port ID.
type: str
sample: "2061395c-1c01-47ab-b925-c91b93df9c1d"
vip_address:
description: The load balancer virtual IP address.
type: str
sample: "192.168.2.88"
public_vip_address:
description: The load balancer public VIP address.
type: str
sample: "10.17.8.254"
provisioning_status:
description: The provisioning status of the load balancer.
type: str
sample: "ACTIVE"
operating_status:
description: The operating status of the load balancer.
type: str
sample: "ONLINE"
is_admin_state_up:
description: The administrative state of the load balancer.
type: bool
sample: true
listeners:
description: The associated listener IDs, if any.
type: list
sample: [{"id": "7aa1b380-beec-459c-a8a7-3a4fb6d30645"}, {"id": "692d06b8-c4f8-4bdb-b2a3-5a263cc23ba6"}]
pools:
description: The associated pool IDs, if any.
type: list
sample: [{"id": "27b78d92-cee1-4646-b831-e3b90a7fa714"}, {"id": "befc1fb5-1992-4697-bdb9-eee330989344"}]
'''
EXAMPLES = '''
# Create a load balancer by specifying the VIP subnet.
- openstack.cloud.loadbalancer:
auth:
auth_url: https://identity.example.com
username: admin
password: passme
project_name: admin
state: present
name: my_lb
vip_subnet: my_subnet
timeout: 150
# Create a load balancer by specifying the VIP network and the IP address.
- openstack.cloud.loadbalancer:
auth:
auth_url: https://identity.example.com
username: admin
password: passme
project_name: admin
state: present
name: my_lb
vip_network: my_network
vip_address: 192.168.0.11
# Create a load balancer together with its sub-resources in the 'all in one'
# way. A public IP address is also allocated to the load balancer VIP.
- openstack.cloud.loadbalancer:
auth:
auth_url: https://identity.example.com
username: admin
password: passme
project_name: admin
name: lingxian_test
state: present
vip_subnet: kong_subnet
auto_public_ip: yes
public_network: public
listeners:
- name: lingxian_80
protocol: TCP
protocol_port: 80
pool:
name: lingxian_80_pool
protocol: TCP
members:
- name: mywebserver1
address: 192.168.2.81
protocol_port: 80
subnet: webserver_subnet
- name: lingxian_8080
protocol: TCP
protocol_port: 8080
pool:
name: lingxian_8080-pool
protocol: TCP
members:
- name: mywebserver2
address: 192.168.2.82
protocol_port: 8080
wait: yes
timeout: 600
# Delete a load balancer(and all its related resources)
- openstack.cloud.loadbalancer:
auth:
auth_url: https://identity.example.com
username: admin
password: passme
project_name: admin
state: absent
name: my_lb
# Delete a load balancer(and all its related resources) together with the
# public IP address(if any) attached to it.
- openstack.cloud.loadbalancer:
auth:
auth_url: https://identity.example.com
username: admin
password: passme
project_name: admin
state: absent
name: my_lb
delete_public_ip: yes
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _wait_for_lb(module, cloud, lb, status, failures, interval=5):
"""Wait for load balancer to be in a particular provisioning status."""
timeout = module.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
lb = cloud.load_balancer.find_load_balancer(lb.id)
if lb:
if lb.provisioning_status == status:
return None
if lb.provisioning_status in failures:
module.fail_json(
msg="Load Balancer %s transitioned to failure state %s" %
(lb.id, lb.provisioning_status)
)
else:
if status == "DELETED":
return None
else:
module.fail_json(
msg="Load Balancer %s transitioned to DELETED" % lb.id
)
time.sleep(interval)
total_sleep += interval
module.fail_json(
msg="Timeout waiting for Load Balancer %s to transition to %s" %
(lb.id, status)
)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
vip_network=dict(required=False),
vip_subnet=dict(required=False),
vip_port=dict(required=False),
vip_address=dict(required=False),
listeners=dict(type='list', default=[], elements='dict'),
public_ip_address=dict(required=False, default=None),
auto_public_ip=dict(required=False, default=False, type='bool'),
public_network=dict(required=False),
delete_public_ip=dict(required=False, default=False, type='bool'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
vip_network = module.params['vip_network']
vip_subnet = module.params['vip_subnet']
vip_port = module.params['vip_port']
listeners = module.params['listeners']
public_vip_address = module.params['public_ip_address']
allocate_fip = module.params['auto_public_ip']
delete_fip = module.params['delete_public_ip']
public_network = module.params['public_network']
vip_network_id = None
vip_subnet_id = None
vip_port_id = None
try:
changed = False
lb = cloud.load_balancer.find_load_balancer(
name_or_id=module.params['name'])
if module.params['state'] == 'present':
if not lb:
if not (vip_network or vip_subnet or vip_port):
module.fail_json(
msg="One of vip_network, vip_subnet, or vip_port must "
"be specified for load balancer creation"
)
if vip_network:
network = cloud.get_network(vip_network)
if not network:
module.fail_json(
msg='network %s is not found' % vip_network
)
vip_network_id = network.id
if vip_subnet:
subnet = cloud.get_subnet(vip_subnet)
if not subnet:
module.fail_json(
msg='subnet %s is not found' % vip_subnet
)
vip_subnet_id = subnet.id
if vip_port:
port = cloud.get_port(vip_port)
if not port:
module.fail_json(
msg='port %s is not found' % vip_port
)
vip_port_id = port.id
lb = cloud.load_balancer.create_load_balancer(
name=module.params['name'],
vip_network_id=vip_network_id,
vip_subnet_id=vip_subnet_id,
vip_port_id=vip_port_id,
vip_address=module.params['vip_address'],
)
changed = True
if not listeners and not module.params['wait']:
module.exit_json(
changed=changed,
loadbalancer=lb.to_dict(),
id=lb.id
)
_wait_for_lb(module, cloud, lb, "ACTIVE", ["ERROR"])
for listener_def in listeners:
listener_name = listener_def.get("name")
pool_def = listener_def.get("pool")
if not listener_name:
module.fail_json(msg='listener name is required')
listener = cloud.load_balancer.find_listener(
name_or_id=listener_name
)
if not listener:
_wait_for_lb(module, cloud, lb, "ACTIVE", ["ERROR"])
protocol = listener_def.get("protocol", "HTTP")
protocol_port = listener_def.get("protocol_port", 80)
listener = cloud.load_balancer.create_listener(
name=listener_name,
loadbalancer_id=lb.id,
protocol=protocol,
protocol_port=protocol_port,
)
changed = True
# Ensure pool in the listener.
if pool_def:
pool_name = pool_def.get("name")
members = pool_def.get('members', [])
if not pool_name:
module.fail_json(msg='pool name is required')
pool = cloud.load_balancer.find_pool(name_or_id=pool_name)
if not pool:
_wait_for_lb(module, cloud, lb, "ACTIVE", ["ERROR"])
protocol = pool_def.get("protocol", "HTTP")
lb_algorithm = pool_def.get("lb_algorithm",
"ROUND_ROBIN")
pool = cloud.load_balancer.create_pool(
name=pool_name,
listener_id=listener.id,
protocol=protocol,
lb_algorithm=lb_algorithm
)
changed = True
# Ensure members in the pool
for member_def in members:
member_name = member_def.get("name")
if not member_name:
module.fail_json(msg='member name is required')
member = cloud.load_balancer.find_member(member_name,
pool.id)
if not member:
_wait_for_lb(module, cloud, lb, "ACTIVE",
["ERROR"])
address = member_def.get("address")
if not address:
module.fail_json(
msg='member address for member %s is '
'required' % member_name
)
subnet_id = member_def.get("subnet")
if subnet_id:
subnet = cloud.get_subnet(subnet_id)
if not subnet:
module.fail_json(
msg='subnet %s for member %s is not '
'found' % (subnet_id, member_name)
)
subnet_id = subnet.id
protocol_port = member_def.get("protocol_port", 80)
member = cloud.load_balancer.create_member(
pool,
name=member_name,
address=address,
protocol_port=protocol_port,
subnet_id=subnet_id
)
changed = True
# Associate public ip to the load balancer VIP. If
# public_vip_address is provided, use that IP, otherwise, either
# find an available public ip or create a new one.
fip = None
orig_public_ip = None
new_public_ip = None
if public_vip_address or allocate_fip:
ips = cloud.network.ips(
port_id=lb.vip_port_id,
fixed_ip_address=lb.vip_address
)
ips = list(ips)
if ips:
orig_public_ip = ips[0]
new_public_ip = orig_public_ip.floating_ip_address
if public_vip_address and public_vip_address != orig_public_ip:
fip = cloud.network.find_ip(public_vip_address)
if not fip:
module.fail_json(
msg='Public IP %s is unavailable' % public_vip_address
)
# Release origin public ip first
cloud.network.update_ip(
orig_public_ip,
fixed_ip_address=None,
port_id=None
)
# Associate new public ip
cloud.network.update_ip(
fip,
fixed_ip_address=lb.vip_address,
port_id=lb.vip_port_id
)
new_public_ip = public_vip_address
changed = True
elif allocate_fip and not orig_public_ip:
fip = cloud.network.find_available_ip()
if not fip:
if not public_network:
module.fail_json(msg="Public network is not provided")
pub_net = cloud.network.find_network(public_network)
if not pub_net:
module.fail_json(
msg='Public network %s not found' %
public_network
)
fip = cloud.network.create_ip(
floating_network_id=pub_net.id
)
cloud.network.update_ip(
fip,
fixed_ip_address=lb.vip_address,
port_id=lb.vip_port_id
)
new_public_ip = fip.floating_ip_address
changed = True
# Include public_vip_address in the result.
lb = cloud.load_balancer.find_load_balancer(name_or_id=lb.id)
lb_dict = lb.to_dict()
lb_dict.update({"public_vip_address": new_public_ip})
module.exit_json(
changed=changed,
loadbalancer=lb_dict,
id=lb.id
)
elif module.params['state'] == 'absent':
changed = False
public_vip_address = None
if lb:
if delete_fip:
ips = cloud.network.ips(
port_id=lb.vip_port_id,
fixed_ip_address=lb.vip_address
)
ips = list(ips)
if ips:
public_vip_address = ips[0]
# Deleting load balancer with `cascade=False` does not make
# sense because the deletion will always fail if there are
# sub-resources.
cloud.load_balancer.delete_load_balancer(lb, cascade=True)
changed = True
if module.params['wait']:
_wait_for_lb(module, cloud, lb, "DELETED", ["ERROR"])
if delete_fip and public_vip_address:
cloud.network.delete_ip(public_vip_address)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

263
plugins/modules/network.py Normal file
View File

@ -0,0 +1,263 @@
#!/usr/bin/python
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2013, Benno Joy <benno@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: network
short_description: Creates/removes networks from OpenStack
author: "Monty Taylor (@emonty)"
description:
- Add or remove network from OpenStack.
options:
name:
description:
- Name to be assigned to the network.
required: true
type: str
shared:
description:
- Whether this network is shared or not.
type: bool
default: 'no'
admin_state_up:
description:
- Whether the state should be marked as up or down.
type: bool
default: 'yes'
external:
description:
- Whether this network is externally accessible.
type: bool
default: 'no'
state:
description:
- Indicate desired state of the resource.
choices: ['present', 'absent']
default: present
type: str
provider_physical_network:
description:
- The physical network where this network object is implemented.
type: str
provider_network_type:
description:
- The type of physical network that maps to this network resource.
type: str
provider_segmentation_id:
description:
- An isolated segment on the physical network. The I(network_type)
attribute defines the segmentation model. For example, if the
I(network_type) value is vlan, this ID is a vlan identifier. If
the I(network_type) value is gre, this ID is a gre key.
type: int
project:
description:
- Project name or ID containing the network (name admin-only)
type: str
port_security_enabled:
description:
- Whether port security is enabled on the network or not.
Network will use OpenStack defaults if this option is
not utilised. Requires openstacksdk>=0.18.
type: bool
mtu:
description:
- The maximum transmission unit (MTU) value to address fragmentation.
Network will use OpenStack defaults if this option is
not provided. Requires openstacksdk>=0.18.
type: int
dns_domain:
description:
- The DNS domain value to set. Requires openstacksdk>=0.29.
Network will use Openstack defaults if this option is
not provided.
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create an externally accessible network named 'ext_network'.
- openstack.cloud.network:
cloud: mycloud
state: present
name: ext_network
external: true
'''
RETURN = '''
network:
description: Dictionary describing the network.
returned: On success when I(state) is 'present'.
type: complex
contains:
id:
description: Network ID.
type: str
sample: "4bb4f9a5-3bd2-4562-bf6a-d17a6341bb56"
name:
description: Network name.
type: str
sample: "ext_network"
shared:
description: Indicates whether this network is shared across all tenants.
type: bool
sample: false
status:
description: Network status.
type: str
sample: "ACTIVE"
mtu:
description: The MTU of a network resource.
type: int
sample: 0
dns_domain:
description: The DNS domain of a network resource.
type: str
sample: "sample.openstack.org."
admin_state_up:
description: The administrative state of the network.
type: bool
sample: true
port_security_enabled:
description: The port security status
type: bool
sample: true
router:external:
description: Indicates whether this network is externally accessible.
type: bool
sample: true
tenant_id:
description: The tenant ID.
type: str
sample: "06820f94b9f54b119636be2728d216fc"
subnets:
description: The associated subnets.
type: list
sample: []
"provider:physical_network":
description: The physical network where this network object is implemented.
type: str
sample: my_vlan_net
"provider:network_type":
description: The type of physical network that maps to this network resource.
type: str
sample: vlan
"provider:segmentation_id":
description: An isolated segment on the physical network.
type: str
sample: 101
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
shared=dict(default=False, type='bool'),
admin_state_up=dict(default=True, type='bool'),
external=dict(default=False, type='bool'),
provider_physical_network=dict(required=False),
provider_network_type=dict(required=False),
provider_segmentation_id=dict(required=False, type='int'),
state=dict(default='present', choices=['absent', 'present']),
project=dict(default=None),
port_security_enabled=dict(type='bool'),
mtu=dict(required=False, type='int'),
dns_domain=dict(required=False)
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
state = module.params['state']
name = module.params['name']
shared = module.params['shared']
admin_state_up = module.params['admin_state_up']
external = module.params['external']
provider_physical_network = module.params['provider_physical_network']
provider_network_type = module.params['provider_network_type']
provider_segmentation_id = module.params['provider_segmentation_id']
project = module.params['project']
net_create_kwargs = {}
min_version = None
if module.params['mtu'] is not None:
min_version = '0.18.0'
net_create_kwargs['mtu_size'] = module.params['mtu']
if module.params['port_security_enabled'] is not None:
min_version = '0.18.0'
net_create_kwargs['port_security_enabled'] = module.params['port_security_enabled']
if module.params['dns_domain'] is not None:
min_version = '0.29.0'
net_create_kwargs['dns_domain'] = module.params['dns_domain']
sdk, cloud = openstack_cloud_from_module(module, min_version)
try:
if project is not None:
proj = cloud.get_project(project)
if proj is None:
module.fail_json(msg='Project %s could not be found' % project)
project_id = proj['id']
filters = {'tenant_id': project_id}
else:
project_id = None
filters = None
net = cloud.get_network(name, filters=filters)
if state == 'present':
if not net:
provider = {}
if provider_physical_network:
provider['physical_network'] = provider_physical_network
if provider_network_type:
provider['network_type'] = provider_network_type
if provider_segmentation_id:
provider['segmentation_id'] = provider_segmentation_id
if project_id is not None:
net = cloud.create_network(name, shared, admin_state_up,
external, provider, project_id,
**net_create_kwargs)
else:
net = cloud.create_network(name, shared, admin_state_up,
external, provider,
**net_create_kwargs)
changed = True
else:
changed = False
module.exit_json(changed=changed, network=net, id=net['id'])
elif state == 'absent':
if not net:
module.exit_json(changed=False)
else:
cloud.delete_network(name)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,156 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: networks_info
short_description: Retrieve information about one or more OpenStack networks.
author: "Davide Agnello (@dagnello)"
description:
- Retrieve information about one or more networks from OpenStack.
- This module was called C(openstack.cloud.networks_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(openstack.cloud.networks_info) module no longer returns C(ansible_facts)!
options:
name:
description:
- Name or ID of the Network
required: false
type: str
filters:
description:
- A dictionary of meta data to use for further filtering. Elements of
this dictionary may be additional dictionaries.
required: false
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Gather information about previously created networks
openstack.cloud.networks_info:
auth:
auth_url: https://identity.example.com
username: user
password: password
project_name: someproject
register: result
- name: Show openstack networks
debug:
msg: "{{ result.openstack_networks }}"
- name: Gather information about a previously created network by name
openstack.cloud.networks_info:
auth:
auth_url: https://identity.example.com
username: user
password: password
project_name: someproject
name: network1
register: result
- name: Show openstack networks
debug:
msg: "{{ result.openstack_networks }}"
- name: Gather information about a previously created network with filter
# Note: name and filters parameters are Not mutually exclusive
openstack.cloud.networks_info:
auth:
auth_url: https://identity.example.com
username: user
password: password
project_name: someproject
filters:
tenant_id: 55e2ce24b2a245b09f181bf025724cbe
subnets:
- 057d4bdf-6d4d-4728-bb0f-5ac45a6f7400
- 443d4dc0-91d4-4998-b21c-357d10433483
register: result
- name: Show openstack networks
debug:
msg: "{{ result.openstack_networks }}"
'''
RETURN = '''
openstack_networks:
description: has all the openstack information about the networks
returned: always, but can be null
type: complex
contains:
id:
description: Unique UUID.
returned: success
type: str
name:
description: Name given to the network.
returned: success
type: str
status:
description: Network status.
returned: success
type: str
subnets:
description: Subnet(s) included in this network.
returned: success
type: list
elements: str
tenant_id:
description: Tenant id associated with this network.
returned: success
type: str
shared:
description: Network shared flag.
returned: success
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_full_argument_spec,
openstack_cloud_from_module,
)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None)
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name == 'openstack.cloud.networks_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.networks_facts' module has been renamed to 'openstack.cloud.networks_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, cloud = openstack_cloud_from_module(module)
try:
networks = cloud.search_networks(module.params['name'],
module.params['filters'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_networks=networks))
else:
module.exit_json(changed=False, openstack_networks=networks)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

129
plugins/modules/object.py Normal file
View File

@ -0,0 +1,129 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2013, Benno Joy <benno@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: object
short_description: Create or Delete objects and containers from OpenStack
author: "Monty Taylor (@emonty)"
description:
- Create or Delete objects and containers from OpenStack
options:
container:
description:
- The name of the container in which to create the object
required: true
type: str
name:
description:
- Name to be give to the object. If omitted, operations will be on
the entire container
required: false
type: str
filename:
description:
- Path to local file to be uploaded.
required: false
type: str
container_access:
description:
- desired container access level.
required: false
choices: ['private', 'public']
default: private
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: "Create a object named 'fstab' in the 'config' container"
openstack.cloud.object:
cloud: mordred
state: present
name: fstab
container: config
filename: /etc/fstab
- name: Delete a container called config and all of its contents
openstack.cloud.object:
cloud: rax-iad
state: absent
container: config
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def process_object(
cloud_obj, container, name, filename, container_access, **kwargs):
changed = False
container_obj = cloud_obj.get_container(container)
if kwargs['state'] == 'present':
if not container_obj:
container_obj = cloud_obj.create_container(container)
changed = True
if cloud_obj.get_container_access(container) != container_access:
cloud_obj.set_container_access(container, container_access)
changed = True
if name:
if cloud_obj.is_object_stale(container, name, filename):
cloud_obj.create_object(container, name, filename)
changed = True
else:
if container_obj:
if name:
if cloud_obj.get_object_metadata(container, name):
cloud_obj.delete_object(container, name)
changed = True
else:
cloud_obj.delete_container(container)
changed = True
return changed
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
container=dict(required=True),
filename=dict(required=False, default=None),
container_access=dict(default='private', choices=['private', 'public']),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = process_object(cloud, **module.params)
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -1,73 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_auth
short_description: Retrieve an auth token
author: "Monty Taylor (@emonty)"
description:
- Retrieve an auth token from an OpenStack Cloud
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Authenticate to the cloud and retrieve the service catalog
os_auth:
cloud: rax-dfw
- name: Show service catalog
debug:
var: service_catalog
'''
RETURN = '''
auth_token:
description: Openstack API Auth Token
returned: success
type: str
service_catalog:
description: A dictionary of available API endpoints
returned: success
type: dict
'''
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec()
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:
module.exit_json(
changed=False,
ansible_facts=dict(
auth_token=cloud.auth_token,
service_catalog=cloud.service_catalog))
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
if __name__ == '__main__':
main()

1
plugins/modules/os_auth.py Symbolic link
View File

@ -0,0 +1 @@
auth.py

View File

@ -1,82 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_client_config
short_description: Get OpenStack Client config
description:
- Get I(openstack) client config data from clouds.yaml or environment
notes:
- Facts are placed in the C(openstack.clouds) variable.
options:
clouds:
description:
- List of clouds to limit the return list to. No value means return
information on all configured clouds
required: false
default: []
type: list
elements: str
requirements:
- "python >= 3.6"
- "openstacksdk"
author: "Monty Taylor (@emonty)"
'''
EXAMPLES = '''
- name: Get list of clouds that do not support security groups
os_client_config:
- debug:
var: "{{ item }}"
with_items: "{{ openstack.clouds | rejectattr('secgroup_source', 'none') | list }}"
- name: Get the information back just about the mordred cloud
os_client_config:
clouds:
- mordred
'''
try:
import openstack.config
from openstack import exceptions
HAS_OPENSTACKSDK = True
except ImportError:
HAS_OPENSTACKSDK = False
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(argument_spec=dict(
clouds=dict(required=False, type='list', default=[], elements='str'),
))
if not HAS_OPENSTACKSDK:
module.fail_json(msg='openstacksdk is required for this module')
p = module.params
try:
config = openstack.config.OpenStackConfig()
clouds = []
for cloud in config.get_all_clouds():
if not p['clouds'] or cloud.name in p['clouds']:
cloud.config['name'] = cloud.name
clouds.append(cloud.config)
module.exit_json(ansible_facts=dict(openstack=dict(clouds=clouds)))
except exceptions.ConfigException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -0,0 +1 @@
config.py

View File

@ -1,299 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2018 Catalyst IT Ltd.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_coe_cluster
short_description: Add/Remove COE cluster from OpenStack Cloud
author: "Feilong Wang (@flwang)"
description:
- Add or Remove COE cluster from the OpenStack Container Infra service.
options:
cluster_template_id:
description:
- The template ID of cluster template.
required: true
type: str
discovery_url:
description:
- Url used for cluster node discovery
type: str
docker_volume_size:
description:
- The size in GB of the docker volume
type: int
flavor_id:
description:
- The flavor of the minion node for this ClusterTemplate
type: str
keypair:
description:
- Name of the keypair to use.
type: str
labels:
description:
- One or more key/value pairs
type: raw
master_flavor_id:
description:
- The flavor of the master node for this ClusterTemplate
type: str
master_count:
description:
- The number of master nodes for this cluster
default: 1
type: int
name:
description:
- Name that has to be given to the cluster template
required: true
type: str
node_count:
description:
- The number of nodes for this cluster
default: 1
type: int
state:
description:
- Indicate desired state of the resource.
choices: [present, absent]
default: present
type: str
timeout:
description:
- Timeout for creating the cluster in minutes. Default to 60 mins
if not set
default: 60
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
id:
description: The cluster UUID.
returned: On success when I(state) is 'present'
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
cluster:
description: Dictionary describing the cluster.
returned: On success when I(state) is 'present'
type: complex
contains:
api_address:
description:
- Api address of cluster master node
type: str
sample: https://172.24.4.30:6443
cluster_template_id:
description: The cluster_template UUID
type: str
sample: '7b1418c8-cea8-48fc-995d-52b66af9a9aa'
coe_version:
description:
- Version of the COE software currently running in this cluster
type: str
sample: v1.11.1
container_version:
description:
- "Version of the container software. Example: docker version."
type: str
sample: 1.12.6
created_at:
description:
- The time in UTC at which the cluster is created
type: str
sample: "2018-08-16T10:29:45+00:00"
create_timeout:
description:
- Timeout for creating the cluster in minutes. Default to 60 if
not set.
type: int
sample: 60
discovery_url:
description:
- Url used for cluster node discovery
type: str
sample: https://discovery.etcd.io/a42ee38e7113f31f4d6324f24367aae5
faults:
description:
- Fault info collected from the Heat resources of this cluster
type: dict
sample: {'0': 'ResourceInError: resources[0].resources...'}
flavor_id:
description:
- The flavor of the minion node for this cluster
type: str
sample: c1.c1r1
keypair:
description:
- Name of the keypair to use.
type: str
sample: mykey
labels:
description: One or more key/value pairs
type: dict
sample: {'key1': 'value1', 'key2': 'value2'}
master_addresses:
description:
- IP addresses of cluster master nodes
type: list
sample: ['172.24.4.5']
master_count:
description:
- The number of master nodes for this cluster.
type: int
sample: 1
master_flavor_id:
description:
- The flavor of the master node for this cluster
type: str
sample: c1.c1r1
name:
description:
- Name that has to be given to the cluster
type: str
sample: k8scluster
node_addresses:
description:
- IP addresses of cluster slave nodes
type: list
sample: ['172.24.4.8']
node_count:
description:
- The number of master nodes for this cluster.
type: int
sample: 1
stack_id:
description:
- Stack id of the Heat stack
type: str
sample: '07767ec6-85f5-44cb-bd63-242a8e7f0d9d'
status:
description: Status of the cluster from the heat stack
type: str
sample: 'CREATE_COMLETE'
status_reason:
description:
- Status reason of the cluster from the heat stack
type: str
sample: 'Stack CREATE completed successfully'
updated_at:
description:
- The time in UTC at which the cluster is updated
type: str
sample: '2018-08-16T10:39:25+00:00'
id:
description:
- Unique UUID for this cluster
type: str
sample: '86246a4d-a16c-4a58-9e96ad7719fe0f9d'
'''
EXAMPLES = '''
# Create a new Kubernetes cluster
- os_coe_cluster:
name: k8s
cluster_template_id: k8s-ha
keypair: mykey
master_count: 3
node_count: 5
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _parse_labels(labels):
if isinstance(labels, str):
labels_dict = {}
for kv_str in labels.split(","):
k, v = kv_str.split("=")
labels_dict[k] = v
return labels_dict
if not labels:
return {}
return labels
def main():
argument_spec = openstack_full_argument_spec(
cluster_template_id=dict(required=True),
discovery_url=dict(default=None),
docker_volume_size=dict(type='int'),
flavor_id=dict(default=None),
keypair=dict(default=None),
labels=dict(default=None, type='raw'),
master_count=dict(type='int', default=1),
master_flavor_id=dict(default=None),
name=dict(required=True),
node_count=dict(type='int', default=1),
state=dict(default='present', choices=['absent', 'present']),
timeout=dict(type='int', default=60),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
params = module.params.copy()
state = module.params['state']
name = module.params['name']
cluster_template_id = module.params['cluster_template_id']
kwargs = dict(
discovery_url=module.params['discovery_url'],
docker_volume_size=module.params['docker_volume_size'],
flavor_id=module.params['flavor_id'],
keypair=module.params['keypair'],
labels=_parse_labels(params['labels']),
master_count=module.params['master_count'],
master_flavor_id=module.params['master_flavor_id'],
node_count=module.params['node_count'],
create_timeout=module.params['timeout'],
)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = False
cluster = cloud.get_coe_cluster(name_or_id=name, filters={'cluster_template_id': cluster_template_id})
if state == 'present':
if not cluster:
cluster = cloud.create_coe_cluster(name, cluster_template_id=cluster_template_id, **kwargs)
changed = True
else:
changed = False
# NOTE (brtknr): At present, create_coe_cluster request returns
# cluster_id as `uuid` whereas get_coe_cluster request returns the
# same field as `id`. This behaviour may change in the future
# therefore try `id` first then `uuid`.
cluster_id = cluster.get('id', cluster.get('uuid'))
cluster['id'] = cluster['uuid'] = cluster_id
module.exit_json(changed=changed, cluster=cluster, id=cluster_id)
elif state == 'absent':
if not cluster:
module.exit_json(changed=False)
else:
cloud.delete_coe_cluster(name)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

View File

@ -0,0 +1 @@
coe_cluster.py

View File

@ -1,394 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2018 Catalyst IT Ltd.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_coe_cluster_template
short_description: Add/Remove COE cluster template from OpenStack Cloud
author: "Feilong Wang (@flwang)"
description:
- Add or Remove COE cluster template from the OpenStack Container Infra
service.
options:
coe:
description:
- The Container Orchestration Engine for this clustertemplate
choices: [kubernetes, swarm, mesos]
type: str
required: true
dns_nameserver:
description:
- The DNS nameserver address
default: '8.8.8.8'
type: str
docker_storage_driver:
description:
- Docker storage driver
choices: [devicemapper, overlay, overlay2]
type: str
docker_volume_size:
description:
- The size in GB of the docker volume
type: int
external_network_id:
description:
- The external network to attach to the Cluster
type: str
fixed_network:
description:
- The fixed network name to attach to the Cluster
type: str
fixed_subnet:
description:
- The fixed subnet name to attach to the Cluster
type: str
flavor_id:
description:
- The flavor of the minion node for this ClusterTemplate
type: str
floating_ip_enabled:
description:
- Indicates whether created clusters should have a floating ip or not
type: bool
default: true
keypair_id:
description:
- Name or ID of the keypair to use.
type: str
image_id:
description:
- Image id the cluster will be based on
type: str
required: true
labels:
description:
- One or more key/value pairs
type: raw
http_proxy:
description:
- Address of a proxy that will receive all HTTP requests and relay them
The format is a URL including a port number
type: str
https_proxy:
description:
- Address of a proxy that will receive all HTTPS requests and relay
them. The format is a URL including a port number
type: str
master_flavor_id:
description:
- The flavor of the master node for this ClusterTemplate
type: str
master_lb_enabled:
description:
- Indicates whether created clusters should have a load balancer
for master nodes or not
type: bool
default: 'no'
name:
description:
- Name that has to be given to the cluster template
required: true
type: str
network_driver:
description:
- The name of the driver used for instantiating container networks
choices: [flannel, calico, docker]
type: str
no_proxy:
description:
- A comma separated list of IPs for which proxies should not be
used in the cluster
type: str
public:
description:
- Indicates whether the ClusterTemplate is public or not
type: bool
default: 'no'
registry_enabled:
description:
- Indicates whether the docker registry is enabled
type: bool
default: 'no'
server_type:
description:
- Server type for this ClusterTemplate
choices: [vm, bm]
default: vm
type: str
state:
description:
- Indicate desired state of the resource.
choices: [present, absent]
default: present
type: str
tls_disabled:
description:
- Indicates whether the TLS should be disabled
type: bool
default: 'no'
volume_driver:
description:
- The name of the driver used for instantiating container volumes
choices: [cinder, rexray]
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
id:
description: The cluster UUID.
returned: On success when I(state) is 'present'
type: str
sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
cluster_template:
description: Dictionary describing the template.
returned: On success when I(state) is 'present'
type: complex
contains:
coe:
description: The Container Orchestration Engine for this clustertemplate
type: str
sample: kubernetes
dns_nameserver:
description: The DNS nameserver address
type: str
sample: '8.8.8.8'
docker_storage_driver:
description: Docker storage driver
type: str
sample: devicemapper
docker_volume_size:
description: The size in GB of the docker volume
type: int
sample: 5
external_network_id:
description: The external network to attach to the Cluster
type: str
sample: public
fixed_network:
description: The fixed network name to attach to the Cluster
type: str
sample: 07767ec6-85f5-44cb-bd63-242a8e7f0d9d
fixed_subnet:
description:
- The fixed subnet name to attach to the Cluster
type: str
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0d9d
flavor_id:
description:
- The flavor of the minion node for this ClusterTemplate
type: str
sample: c1.c1r1
floating_ip_enabled:
description:
- Indicates whether created clusters should have a floating ip or not
type: bool
sample: true
keypair_id:
description:
- Name or ID of the keypair to use.
type: str
sample: mykey
image_id:
description:
- Image id the cluster will be based on
type: str
sample: 05567ec6-85f5-44cb-bd63-242a8e7f0e9d
labels:
description: One or more key/value pairs
type: dict
sample: {'key1': 'value1', 'key2': 'value2'}
http_proxy:
description:
- Address of a proxy that will receive all HTTP requests and relay them
The format is a URL including a port number
type: str
sample: http://10.0.0.11:9090
https_proxy:
description:
- Address of a proxy that will receive all HTTPS requests and relay
them. The format is a URL including a port number
type: str
sample: https://10.0.0.10:8443
master_flavor_id:
description:
- The flavor of the master node for this ClusterTemplate
type: str
sample: c1.c1r1
master_lb_enabled:
description:
- Indicates whether created clusters should have a load balancer
for master nodes or not
type: bool
sample: true
name:
description:
- Name that has to be given to the cluster template
type: str
sample: k8scluster
network_driver:
description:
- The name of the driver used for instantiating container networks
type: str
sample: calico
no_proxy:
description:
- A comma separated list of IPs for which proxies should not be
used in the cluster
type: str
sample: 10.0.0.4,10.0.0.5
public:
description:
- Indicates whether the ClusterTemplate is public or not
type: bool
sample: false
registry_enabled:
description:
- Indicates whether the docker registry is enabled
type: bool
sample: false
server_type:
description:
- Server type for this ClusterTemplate
type: str
sample: vm
tls_disabled:
description:
- Indicates whether the TLS should be disabled
type: bool
sample: false
volume_driver:
description:
- The name of the driver used for instantiating container volumes
type: str
sample: cinder
'''
EXAMPLES = '''
# Create a new Kubernetes cluster template
- os_coe_cluster_template:
name: k8s
coe: kubernetes
keypair_id: mykey
image_id: 2a8c9888-9054-4b06-a1ca-2bb61f9adb72
public: no
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _parse_labels(labels):
if isinstance(labels, str):
labels_dict = {}
for kv_str in labels.split(","):
k, v = kv_str.split("=")
labels_dict[k] = v
return labels_dict
if not labels:
return {}
return labels
def main():
argument_spec = openstack_full_argument_spec(
coe=dict(required=True, choices=['kubernetes', 'swarm', 'mesos']),
dns_nameserver=dict(default='8.8.8.8'),
docker_storage_driver=dict(choices=['devicemapper', 'overlay', 'overlay2']),
docker_volume_size=dict(type='int'),
external_network_id=dict(default=None),
fixed_network=dict(default=None),
fixed_subnet=dict(default=None),
flavor_id=dict(default=None),
floating_ip_enabled=dict(type='bool', default=True),
keypair_id=dict(default=None),
image_id=dict(required=True),
labels=dict(default=None, type='raw'),
http_proxy=dict(default=None),
https_proxy=dict(default=None),
master_lb_enabled=dict(type='bool', default=False),
master_flavor_id=dict(default=None),
name=dict(required=True),
network_driver=dict(choices=['flannel', 'calico', 'docker']),
no_proxy=dict(default=None),
public=dict(type='bool', default=False),
registry_enabled=dict(type='bool', default=False),
server_type=dict(default="vm", choices=['vm', 'bm']),
state=dict(default='present', choices=['absent', 'present']),
tls_disabled=dict(type='bool', default=False),
volume_driver=dict(choices=['cinder', 'rexray']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
params = module.params.copy()
state = module.params['state']
name = module.params['name']
coe = module.params['coe']
image_id = module.params['image_id']
kwargs = dict(
dns_nameserver=module.params['dns_nameserver'],
docker_storage_driver=module.params['docker_storage_driver'],
docker_volume_size=module.params['docker_volume_size'],
external_network_id=module.params['external_network_id'],
fixed_network=module.params['fixed_network'],
fixed_subnet=module.params['fixed_subnet'],
flavor_id=module.params['flavor_id'],
floating_ip_enabled=module.params['floating_ip_enabled'],
keypair_id=module.params['keypair_id'],
labels=_parse_labels(params['labels']),
http_proxy=module.params['http_proxy'],
https_proxy=module.params['https_proxy'],
master_lb_enabled=module.params['master_lb_enabled'],
master_flavor_id=module.params['master_flavor_id'],
network_driver=module.params['network_driver'],
no_proxy=module.params['no_proxy'],
public=module.params['public'],
registry_enabled=module.params['registry_enabled'],
server_type=module.params['server_type'],
tls_disabled=module.params['tls_disabled'],
volume_driver=module.params['volume_driver'],
)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = False
template = cloud.get_coe_cluster_template(name_or_id=name, filters={'coe': coe, 'image_id': image_id})
if state == 'present':
if not template:
template = cloud.create_coe_cluster_template(name, coe=coe, image_id=image_id, **kwargs)
changed = True
else:
changed = False
module.exit_json(changed=changed, cluster_template=template, id=template['uuid'])
elif state == 'absent':
if not template:
module.exit_json(changed=False)
else:
cloud.delete_coe_cluster_template(name)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

View File

@ -0,0 +1 @@
coe_cluster_template.py

View File

@ -1,233 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2015 IBM
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_flavor_info
short_description: Retrieve information about one or more flavors
author: "David Shrewsbury (@Shrews)"
description:
- Retrieve information about available OpenStack instance flavors. By default,
information about ALL flavors are retrieved. Filters can be applied to get
information for only matching flavors. For example, you can filter on the
amount of RAM available to the flavor, or the number of virtual CPUs
available to the flavor, or both. When specifying multiple filters,
*ALL* filters must match on a flavor before that flavor is returned as
a fact.
- This module was called C(os_flavor_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(os_flavor_info) module no longer returns C(ansible_facts)!
notes:
- The result contains a list of unsorted flavors.
options:
name:
description:
- A flavor name. Cannot be used with I(ram) or I(vcpus) or I(ephemeral).
type: str
ram:
description:
- "A string used for filtering flavors based on the amount of RAM
(in MB) desired. This string accepts the following special values:
'MIN' (return flavors with the minimum amount of RAM), and 'MAX'
(return flavors with the maximum amount of RAM)."
- "A specific amount of RAM may also be specified. Any flavors with this
exact amount of RAM will be returned."
- "A range of acceptable RAM may be given using a special syntax. Simply
prefix the amount of RAM with one of these acceptable range values:
'<', '>', '<=', '>='. These values represent less than, greater than,
less than or equal to, and greater than or equal to, respectively."
type: str
vcpus:
description:
- A string used for filtering flavors based on the number of virtual
CPUs desired. Format is the same as the I(ram) parameter.
type: str
limit:
description:
- Limits the number of flavors returned. All matching flavors are
returned by default.
type: int
ephemeral:
description:
- A string used for filtering flavors based on the amount of ephemeral
storage. Format is the same as the I(ram) parameter
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Gather information about all available flavors
- os_flavor_info:
cloud: mycloud
register: result
- debug:
msg: "{{ result.openstack_flavors }}"
# Gather information for the flavor named "xlarge-flavor"
- os_flavor_info:
cloud: mycloud
name: "xlarge-flavor"
# Get all flavors that have exactly 512 MB of RAM.
- os_flavor_info:
cloud: mycloud
ram: "512"
# Get all flavors that have 1024 MB or more of RAM.
- os_flavor_info:
cloud: mycloud
ram: ">=1024"
# Get a single flavor that has the minimum amount of RAM. Using the 'limit'
# option will guarantee only a single flavor is returned.
- os_flavor_info:
cloud: mycloud
ram: "MIN"
limit: 1
# Get all flavors with 1024 MB of RAM or more, AND exactly 2 virtual CPUs.
- os_flavor_info:
cloud: mycloud
ram: ">=1024"
vcpus: "2"
# Get all flavors with 1024 MB of RAM or more, exactly 2 virtual CPUs, and
# less than 30gb of ephemeral storage.
- os_flavor_info:
cloud: mycloud
ram: ">=1024"
vcpus: "2"
ephemeral: "<30"
'''
RETURN = '''
openstack_flavors:
description: Dictionary describing the flavors.
returned: On success.
type: complex
contains:
id:
description: Flavor ID.
returned: success
type: str
sample: "515256b8-7027-4d73-aa54-4e30a4a4a339"
name:
description: Flavor name.
returned: success
type: str
sample: "tiny"
disk:
description: Size of local disk, in GB.
returned: success
type: int
sample: 10
ephemeral:
description: Ephemeral space size, in GB.
returned: success
type: int
sample: 10
ram:
description: Amount of memory, in MB.
returned: success
type: int
sample: 1024
swap:
description: Swap space size, in MB.
returned: success
type: int
sample: 100
vcpus:
description: Number of virtual CPUs.
returned: success
type: int
sample: 2
is_public:
description: Make flavor accessible to the public.
returned: success
type: bool
sample: true
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
ram=dict(required=False, default=None),
vcpus=dict(required=False, default=None),
limit=dict(required=False, default=None, type='int'),
ephemeral=dict(required=False, default=None),
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[
['name', 'ram'],
['name', 'vcpus'],
['name', 'ephemeral']
]
)
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'os_flavor_facts'
if is_old_facts:
module.deprecate("The 'os_flavor_facts' module has been renamed to 'os_flavor_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
name = module.params['name']
vcpus = module.params['vcpus']
ram = module.params['ram']
ephemeral = module.params['ephemeral']
limit = module.params['limit']
filters = {}
if vcpus:
filters['vcpus'] = vcpus
if ram:
filters['ram'] = ram
if ephemeral:
filters['ephemeral'] = ephemeral
sdk, cloud = openstack_cloud_from_module(module)
try:
if name:
flavors = cloud.search_flavors(filters={'name': name})
else:
flavors = cloud.list_flavors()
if filters:
flavors = cloud.range_search(flavors, filters)
if limit is not None:
flavors = flavors[:limit]
if is_old_facts:
module.exit_json(changed=False,
ansible_facts=dict(openstack_flavors=flavors))
else:
module.exit_json(changed=False,
openstack_flavors=flavors)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
compute_flavor_info.py

View File

@ -1,261 +0,0 @@
#!/usr/bin/python
# Copyright: (c) 2015, Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_floating_ip
author: Davide Guerri (@dguerri) <davide.guerri@hp.com>
short_description: Add/Remove floating IP from an instance
description:
- Add or Remove a floating IP to an instance.
- Returns the floating IP when attaching only if I(wait=true).
options:
server:
description:
- The name or ID of the instance to which the IP address
should be assigned.
required: true
type: str
network:
description:
- The name or ID of a neutron external network or a nova pool name.
type: str
floating_ip_address:
description:
- A floating IP address to attach or to detach. Required only if I(state)
is absent. When I(state) is present can be used to specify a IP address
to attach.
type: str
reuse:
description:
- When I(state) is present, and I(floating_ip_address) is not present,
this parameter can be used to specify whether we should try to reuse
a floating IP address already allocated to the project.
type: bool
default: 'no'
fixed_address:
description:
- To which fixed IP of server the floating IP address should be
attached to.
type: str
nat_destination:
description:
- The name or id of a neutron private network that the fixed IP to
attach floating IP is on
aliases: ["fixed_network", "internal_network"]
type: str
wait:
description:
- When attaching a floating IP address, specify whether to wait for it to appear as attached.
- Must be set to C(yes) for the module to return the value of the floating IP.
type: bool
default: 'no'
timeout:
description:
- Time to wait for an IP address to appear as attached. See wait.
required: false
default: 60
type: int
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
purge:
description:
- When I(state) is absent, indicates whether or not to delete the floating
IP completely, or only detach it from the server. Default is to detach only.
type: bool
default: 'no'
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Assign a floating IP to the fist interface of `cattle001` from an exiting
# external network or nova pool. A new floating IP from the first available
# external network is allocated to the project.
- os_floating_ip:
cloud: dguerri
server: cattle001
# Assign a new floating IP to the instance fixed ip `192.0.2.3` of
# `cattle001`. If a free floating IP is already allocated to the project, it is
# reused; if not, a new one is created.
- os_floating_ip:
cloud: dguerri
state: present
reuse: yes
server: cattle001
network: ext_net
fixed_address: 192.0.2.3
wait: true
timeout: 180
# Assign a new floating IP from the network `ext_net` to the instance fixed
# ip in network `private_net` of `cattle001`.
- os_floating_ip:
cloud: dguerri
state: present
server: cattle001
network: ext_net
nat_destination: private_net
wait: true
timeout: 180
# Detach a floating IP address from a server
- os_floating_ip:
cloud: dguerri
state: absent
floating_ip_address: 203.0.113.2
server: cattle001
'''
from ansible.module_utils.basic import AnsibleModule, remove_values
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _get_floating_ip(cloud, floating_ip_address):
f_ips = cloud.search_floating_ips(
filters={'floating_ip_address': floating_ip_address})
if not f_ips:
return None
return f_ips[0]
def main():
argument_spec = openstack_full_argument_spec(
server=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
network=dict(required=False, default=None),
floating_ip_address=dict(required=False, default=None),
reuse=dict(required=False, type='bool', default=False),
fixed_address=dict(required=False, default=None),
nat_destination=dict(required=False, default=None,
aliases=['fixed_network', 'internal_network']),
wait=dict(required=False, type='bool', default=False),
timeout=dict(required=False, type='int', default=60),
purge=dict(required=False, type='bool', default=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
server_name_or_id = module.params['server']
state = module.params['state']
network = module.params['network']
floating_ip_address = module.params['floating_ip_address']
reuse = module.params['reuse']
fixed_address = module.params['fixed_address']
nat_destination = module.params['nat_destination']
wait = module.params['wait']
timeout = module.params['timeout']
purge = module.params['purge']
sdk, cloud = openstack_cloud_from_module(module)
try:
server = cloud.get_server(server_name_or_id)
if server is None:
module.fail_json(
msg="server {0} not found".format(server_name_or_id))
if state == 'present':
# If f_ip already assigned to server, check that it matches
# requirements.
public_ip = cloud.get_server_public_ip(server)
f_ip = _get_floating_ip(cloud, public_ip) if public_ip else public_ip
if f_ip:
if network:
network_id = cloud.get_network(name_or_id=network)["id"]
else:
network_id = None
# check if we have floating ip on given nat_destination network
if nat_destination:
nat_floating_addrs = [
addr for addr in server.addresses.get(
cloud.get_network(nat_destination)['name'], [])
if addr['addr'] == public_ip
and addr['OS-EXT-IPS:type'] == 'floating'
]
if len(nat_floating_addrs) == 0:
module.fail_json(msg="server {server} already has a "
"floating-ip on a different "
"nat-destination than '{nat_destination}'"
.format(server=server_name_or_id,
nat_destination=nat_destination))
if all([fixed_address, f_ip.fixed_ip_address == fixed_address,
network, f_ip.network != network_id]):
# Current state definitely conflicts with requirements
module.fail_json(msg="server {server} already has a "
"floating-ip on requested "
"interface but it doesn't match "
"requested network {network}: {fip}"
.format(server=server_name_or_id,
network=network,
fip=remove_values(f_ip,
module.no_log_values)))
if not network or f_ip.network == network_id:
# Requirements are met
module.exit_json(changed=False, floating_ip=f_ip)
# Requirements are vague enough to ignore existing f_ip and try
# to create a new f_ip to the server.
server = cloud.add_ips_to_server(
server=server, ips=floating_ip_address, ip_pool=network,
reuse=reuse, fixed_address=fixed_address, wait=wait,
timeout=timeout, nat_destination=nat_destination)
fip_address = cloud.get_server_public_ip(server)
# Update the floating IP status
f_ip = _get_floating_ip(cloud, fip_address)
module.exit_json(changed=True, floating_ip=f_ip)
elif state == 'absent':
if floating_ip_address is None:
if not server_name_or_id:
module.fail_json(msg="either server or floating_ip_address are required")
server = cloud.get_server(server_name_or_id)
floating_ip_address = cloud.get_server_public_ip(server)
f_ip = _get_floating_ip(cloud, floating_ip_address)
if not f_ip:
# Nothing to detach
module.exit_json(changed=False)
changed = False
if f_ip["fixed_ip_address"]:
cloud.detach_ip_from_server(
server_id=server['id'], floating_ip_id=f_ip['id'])
# Update the floating IP status
f_ip = cloud.get_floating_ip(id=f_ip['id'])
changed = True
if purge:
cloud.delete_floating_ip(f_ip['id'])
module.exit_json(changed=True)
module.exit_json(changed=changed, floating_ip=f_ip)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
floating_ip.py

View File

@ -1,167 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2016 IBM
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_group
short_description: Manage OpenStack Identity Groups
author: "Monty Taylor (@emonty), David Shrewsbury (@Shrews)"
description:
- Manage OpenStack Identity Groups. Groups can be created, deleted or
updated. Only the I(description) value can be updated.
options:
name:
description:
- Group name
required: true
type: str
description:
description:
- Group description
type: str
domain_id:
description:
- Domain id to create the group in if the cloud supports domains.
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a group named "demo"
- os_group:
cloud: mycloud
state: present
name: demo
description: "Demo Group"
domain_id: demoid
# Update the description on existing "demo" group
- os_group:
cloud: mycloud
state: present
name: demo
description: "Something else"
domain_id: demoid
# Delete group named "demo"
- os_group:
cloud: mycloud
state: absent
name: demo
'''
RETURN = '''
group:
description: Dictionary describing the group.
returned: On success when I(state) is 'present'.
type: complex
contains:
id:
description: Unique group ID
type: str
sample: "ee6156ff04c645f481a6738311aea0b0"
name:
description: Group name
type: str
sample: "demo"
description:
description: Group description
type: str
sample: "Demo Group"
domain_id:
description: Domain for the group
type: str
sample: "default"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _system_state_change(state, description, group):
if state == 'present' and not group:
return True
if state == 'present' and description is not None and group.description != description:
return True
if state == 'absent' and group:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
description=dict(required=False, default=None),
domain_id=dict(required=False, default=None),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
name = module.params.get('name')
description = module.params.get('description')
state = module.params.get('state')
domain_id = module.params.pop('domain_id')
sdk, cloud = openstack_cloud_from_module(module)
try:
if domain_id:
group = cloud.get_group(name, filters={'domain_id': domain_id})
else:
group = cloud.get_group(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(state, description, group))
if state == 'present':
if group is None:
group = cloud.create_group(
name=name, description=description, domain=domain_id)
changed = True
else:
if description is not None and group.description != description:
group = cloud.update_group(
group.id, description=description)
changed = True
else:
changed = False
module.exit_json(changed=changed, group=group)
elif state == 'absent':
if group is None:
changed = False
else:
cloud.delete_group(group.id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

1
plugins/modules/os_group.py Symbolic link
View File

@ -0,0 +1 @@
identity_group.py

View File

@ -1,164 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2019, Phillipe Smith <phillipelnx@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_group_info
short_description: Retrieve info about one or more OpenStack groups
author: "Phillipe Smith (@phsmith)"
description:
- Retrieve info about a one or more OpenStack groups.
options:
name:
description:
- Name or ID of the group.
type: str
domain:
description:
- Name or ID of the domain containing the group if the cloud supports domains
type: str
filters:
description:
- A dictionary of meta data to use for further filtering. Elements of
this dictionary may be additional dictionaries.
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Gather info about previously created groups
- name: gather info
hosts: localhost
tasks:
- name: Gather info about previously created groups
os_group_info:
cloud: awesomecloud
register: openstack_groups
- debug:
var: openstack_groups
# Gather info about a previously created group by name
- name: gather info
hosts: localhost
tasks:
- name: Gather info about a previously created group by name
os_group_info:
cloud: awesomecloud
name: demogroup
register: openstack_groups
- debug:
var: openstack_groups
# Gather info about a previously created group in a specific domain
- name: gather info
hosts: localhost
tasks:
- name: Gather info about a previously created group in a specific domain
os_group_info:
cloud: awesomecloud
name: demogroup
domain: admindomain
register: openstack_groups
- debug:
var: openstack_groups
# Gather info about a previously created group in a specific domain with filter
- name: gather info
hosts: localhost
tasks:
- name: Gather info about a previously created group in a specific domain with filter
os_group_info:
cloud: awesomecloud
name: demogroup
domain: admindomain
filters:
enabled: False
register: openstack_groups
- debug:
var: openstack_groups
'''
RETURN = '''
openstack_groups:
description: Dictionary describing all the matching groups.
returned: always, but can be null
type: complex
contains:
name:
description: Name given to the group.
returned: success
type: str
description:
description: Description of the group.
returned: success
type: str
id:
description: Unique UUID.
returned: success
type: str
domain_id:
description: Domain ID containing the group (keystone v3 clouds only)
returned: success
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, openstack_cloud_from_module
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
domain=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module = AnsibleModule(argument_spec)
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
domain = module.params['domain']
filters = module.params['filters']
if domain:
try:
# We assume admin is passing domain id
dom = opcloud.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
dom = opcloud.search_domains(filters={'name': domain})
if dom:
domain = dom[0]['id']
else:
module.fail_json(msg='Domain name or ID does not exist')
if not filters:
filters = {}
groups = opcloud.search_groups(name, filters, domain_id=domain)
module.exit_json(changed=False, groups=groups)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
identity_group_info.py

View File

@ -1,246 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2013, Benno Joy <benno@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# TODO(mordred): we need to support "location"(v1) and "locations"(v2)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_image
short_description: Add/Delete images from OpenStack Cloud
author: "Monty Taylor (@emonty)"
description:
- Add or Remove images from the OpenStack Image Repository
options:
name:
description:
- The name of the image when uploading - or the name/ID of the image if deleting
required: true
type: str
id:
description:
- The ID of the image when uploading an image
type: str
checksum:
description:
- The checksum of the image
type: str
disk_format:
description:
- The format of the disk that is getting uploaded
default: qcow2
choices: ['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']
type: str
container_format:
description:
- The format of the container
default: bare
choices: ['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']
type: str
owner:
description:
- The owner of the image
type: str
min_disk:
description:
- The minimum disk space (in GB) required to boot this image
type: int
min_ram:
description:
- The minimum ram (in MB) required to boot this image
type: int
is_public:
description:
- Whether the image can be accessed publicly. Note that publicizing an image requires admin role by default.
type: bool
default: false
protected:
description:
- Prevent image from being deleted
type: bool
default: 'no'
filename:
description:
- The path to the file which has to be uploaded
type: str
ramdisk:
description:
- The name of an existing ramdisk image that will be associated with this image
type: str
kernel:
description:
- The name of an existing kernel image that will be associated with this image
type: str
properties:
description:
- Additional properties to be associated with this image
default: {}
type: dict
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
volume:
description:
- ID of a volume to create an image from.
- The volume must be in AVAILABLE state.
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Upload an image from a local file named cirros-0.3.0-x86_64-disk.img
- os_image:
auth:
auth_url: https://identity.example.com
username: admin
password: passme
project_name: admin
os_user_domain_name: Default
os_project_domain_name: Default
name: cirros
container_format: bare
disk_format: qcow2
state: present
filename: cirros-0.3.0-x86_64-disk.img
kernel: cirros-vmlinuz
ramdisk: cirros-initrd
properties:
cpu_arch: x86_64
distro: ubuntu
# Create image from volume attached to an instance
- name: create volume snapshot
os_volume_snapshot:
auth:
"{{ auth }}"
display_name: myvol_snapshot
volume: myvol
force: yes
register: myvol_snapshot
- name: create volume from snapshot
os_volume:
auth:
"{{ auth }}"
size: "{{ myvol_snapshot.snapshot.size }}"
snapshot_id: "{{ myvol_snapshot.snapshot.id }}"
display_name: myvol_snapshot_volume
wait: yes
register: myvol_snapshot_volume
- name: create image from volume snapshot
os_image:
auth:
"{{ auth }}"
volume: "{{ myvol_snapshot_volume.volume.id }}"
name: myvol_image
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
id=dict(default=None),
checksum=dict(default=None),
disk_format=dict(default='qcow2', choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']),
container_format=dict(default='bare', choices=['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']),
owner=dict(default=None),
min_disk=dict(type='int', default=0),
min_ram=dict(type='int', default=0),
is_public=dict(type='bool', default=False),
protected=dict(type='bool', default=False),
filename=dict(default=None),
ramdisk=dict(default=None),
kernel=dict(default=None),
properties=dict(type='dict', default={}),
volume=dict(default=None),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[['filename', 'volume']],
)
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = False
if module.params['id']:
image = cloud.get_image(name_or_id=module.params['id'])
elif module.params['checksum']:
image = cloud.get_image(name_or_id=module.params['name'], filters={'checksum': module.params['checksum']})
else:
image = cloud.get_image(name_or_id=module.params['name'])
if module.params['state'] == 'present':
if not image:
kwargs = {}
if module.params['id'] is not None:
kwargs['id'] = module.params['id']
image = cloud.create_image(
name=module.params['name'],
filename=module.params['filename'],
disk_format=module.params['disk_format'],
container_format=module.params['container_format'],
wait=module.params['wait'],
timeout=module.params['timeout'],
is_public=module.params['is_public'],
protected=module.params['protected'],
min_disk=module.params['min_disk'],
min_ram=module.params['min_ram'],
volume=module.params['volume'],
**kwargs
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed, image=image, id=image.id)
cloud.update_image_properties(
image=image,
kernel=module.params['kernel'],
ramdisk=module.params['ramdisk'],
protected=module.params['protected'],
**module.params['properties'])
image = cloud.get_image(name_or_id=image.id)
module.exit_json(changed=changed, image=image, id=image.id)
elif module.params['state'] == 'absent':
if not image:
changed = False
else:
cloud.delete_image(
name_or_id=module.params['name'],
wait=module.params['wait'],
timeout=module.params['timeout'])
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
if __name__ == "__main__":
main()

1
plugins/modules/os_image.py Symbolic link
View File

@ -0,0 +1 @@
image.py

View File

@ -1,192 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: os_image_info
short_description: Retrieve information about an image within OpenStack.
author: "Davide Agnello (@dagnello)"
description:
- Retrieve information about a image image from OpenStack.
- This module was called C(os_image_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(os_image_info) module no longer returns C(ansible_facts)!
options:
image:
description:
- Name or ID of the image
required: false
type: str
properties:
description:
- Dict of properties of the images used for query
type: dict
required: false
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Gather information about a previously created image named image1
os_image_info:
auth:
auth_url: https://identity.example.com
username: user
password: password
project_name: someproject
image: image1
register: result
- name: Show openstack information
debug:
msg: "{{ result.openstack_image }}"
# Show all available Openstack images
- name: Retrieve all available Openstack images
os_image_info:
register: result
- name: Show images
debug:
msg: "{{ result.openstack_image }}"
# Show images matching requested properties
- name: Retrieve images having properties with desired values
os_image_facts:
properties:
some_property: some_value
OtherProp: OtherVal
- name: Show images
debug:
msg: "{{ result.openstack_image }}"
'''
RETURN = '''
openstack_image:
description: has all the openstack information about the image
returned: always, but can be null
type: complex
contains:
id:
description: Unique UUID.
returned: success
type: str
name:
description: Name given to the image.
returned: success
type: str
status:
description: Image status.
returned: success
type: str
created_at:
description: Image created at timestamp.
returned: success
type: str
deleted:
description: Image deleted flag.
returned: success
type: bool
container_format:
description: Container format of the image.
returned: success
type: str
min_ram:
description: Min amount of RAM required for this image.
returned: success
type: int
disk_format:
description: Disk format of the image.
returned: success
type: str
updated_at:
description: Image updated at timestamp.
returned: success
type: str
properties:
description: Additional properties associated with the image.
returned: success
type: dict
min_disk:
description: Min amount of disk space required for this image.
returned: success
type: int
protected:
description: Image protected flag.
returned: success
type: bool
checksum:
description: Checksum for the image.
returned: success
type: str
owner:
description: Owner for the image.
returned: success
type: str
is_public:
description: Is public flag of the image.
returned: success
type: bool
deleted_at:
description: Image deleted at timestamp.
returned: success
type: str
size:
description: Size of the image.
returned: success
type: int
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
image=dict(required=False),
properties=dict(default=None, type='dict'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'os_image_facts'
if is_old_facts:
module.deprecate("The 'os_image_facts' module has been renamed to 'os_image_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, cloud = openstack_cloud_from_module(module)
try:
if module.params['image']:
image = cloud.get_image(module.params['image'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_image=image))
else:
module.exit_json(changed=False, openstack_image=image)
else:
images = cloud.search_images(filters=module.params['properties'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_image=images))
else:
module.exit_json(changed=False, openstack_image=images)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
image_info.py

View File

@ -1,380 +0,0 @@
#!/usr/bin/python
# coding: utf-8 -*-
# (c) 2014, Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_ironic
short_description: Create/Delete Bare Metal Resources from OpenStack
author: "Monty Taylor (@emonty)"
description:
- Create or Remove Ironic nodes from OpenStack.
options:
state:
description:
- Indicates desired state of the resource
choices: ['present', 'absent']
default: present
type: str
uuid:
description:
- globally unique identifier (UUID) to be given to the resource. Will
be auto-generated if not specified, and name is specified.
- Definition of a UUID will always take precedence to a name value.
type: str
name:
description:
- unique name identifier to be given to the resource.
type: str
driver:
description:
- The name of the Ironic Driver to use with this node.
- Required when I(state=present)
type: str
chassis_uuid:
description:
- Associate the node with a pre-defined chassis.
type: str
ironic_url:
description:
- If noauth mode is utilized, this is required to be set to the
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
settings set to None.
type: str
driver_info:
description:
- Information for this server's driver. Will vary based on which
driver is in use. Any sub-field which is populated will be validated
during creation.
required: true
type: dict
suboptions:
power:
description:
- Information necessary to turn this server on / off.
This often includes such things as IPMI username, password, and IP address.
required: true
deploy:
description:
- Information necessary to deploy this server directly, without using Nova. THIS IS NOT RECOMMENDED.
console:
description:
- Information necessary to connect to this server's serial console. Not all drivers support this.
management:
description:
- Information necessary to interact with this server's management interface. May be shared by power_info in some cases.
required: true
nics:
description:
- 'A list of network interface cards, eg, " - mac: aa:bb:cc:aa:bb:cc"'
required: true
type: list
elements: dict
suboptions:
mac:
description: The MAC address of the network interface card.
type: str
required: true
properties:
description:
- Definition of the physical characteristics of this server, used for scheduling purposes
type: dict
suboptions:
cpu_arch:
description:
- CPU architecture (x86_64, i686, ...)
default: x86_64
cpus:
description:
- Number of CPU cores this machine has
default: 1
ram:
description:
- amount of RAM this machine has, in MB
default: 1
disk_size:
description:
- size of first storage device in this machine (typically /dev/sda), in GB
default: 1
capabilities:
description:
- special capabilities for the node, such as boot_option, node_role etc
(see U(https://docs.openstack.org/ironic/latest/install/advanced.html)
for more information)
default: ""
root_device:
description:
- Root disk device hints for deployment.
- See U(https://docs.openstack.org/ironic/latest/install/advanced.html#specifying-the-disk-for-deployment-root-device-hints)
for allowed hints.
default: ""
skip_update_of_masked_password:
description:
- Allows the code that would assert changes to nodes to skip the
update if the change is a single line consisting of the password
field.
- As of Kilo, by default, passwords are always masked to API
requests, which means the logic as a result always attempts to
re-assert the password field.
- C(skip_update_of_driver_password) is deprecated alias and will be removed in 2.14.
type: bool
default: 'no'
aliases: [ skip_update_of_driver_password ]
requirements:
- "python >= 3.6"
- "openstacksdk"
- "jsonpatch"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Enroll a node with some basic properties and driver info
- os_ironic:
cloud: "devstack"
driver: "pxe_ipmitool"
uuid: "00000000-0000-0000-0000-000000000002"
properties:
cpus: 2
cpu_arch: "x86_64"
ram: 8192
disk_size: 64
capabilities: "boot_option:local"
root_device:
wwn: "0x4000cca77fc4dba1"
nics:
- mac: "aa:bb:cc:aa:bb:cc"
- mac: "dd:ee:ff:dd:ee:ff"
driver_info:
power:
ipmi_address: "1.2.3.4"
ipmi_username: "admin"
ipmi_password: "adminpass"
chassis_uuid: "00000000-0000-0000-0000-000000000001"
'''
try:
import jsonpatch
HAS_JSONPATCH = True
except ImportError:
HAS_JSONPATCH = False
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _parse_properties(module):
p = module.params['properties']
props = dict(
cpu_arch=p.get('cpu_arch') if p.get('cpu_arch') else 'x86_64',
cpus=p.get('cpus') if p.get('cpus') else 1,
memory_mb=p.get('ram') if p.get('ram') else 1,
local_gb=p.get('disk_size') if p.get('disk_size') else 1,
capabilities=p.get('capabilities') if p.get('capabilities') else '',
root_device=p.get('root_device') if p.get('root_device') else '',
)
return props
def _parse_driver_info(sdk, module):
p = module.params['driver_info']
info = p.get('power')
if not info:
raise sdk.exceptions.OpenStackCloudException(
"driver_info['power'] is required")
if p.get('console'):
info.update(p.get('console'))
if p.get('management'):
info.update(p.get('management'))
if p.get('deploy'):
info.update(p.get('deploy'))
return info
def _choose_id_value(module):
if module.params['uuid']:
return module.params['uuid']
if module.params['name']:
return module.params['name']
return None
def _choose_if_password_only(module, patch):
if len(patch) == 1:
if 'password' in patch[0]['path'] and module.params['skip_update_of_masked_password']:
# Return false to abort update as the password appears
# to be the only element in the patch.
return False
return True
def _exit_node_not_updated(module, server):
module.exit_json(
changed=False,
result="Node not updated",
uuid=server['uuid'],
provision_state=server['provision_state']
)
def main():
argument_spec = openstack_full_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
driver=dict(required=False),
driver_info=dict(type='dict', required=True),
nics=dict(type='list', required=True, elements="dict"),
properties=dict(type='dict', default={}),
ironic_url=dict(required=False),
chassis_uuid=dict(required=False),
skip_update_of_masked_password=dict(
required=False,
type='bool',
aliases=['skip_update_of_driver_password'],
deprecated_aliases=[dict(name='skip_update_of_driver_password', version='2.14')]
),
state=dict(required=False, default='present', choices=['present', 'absent'])
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if not HAS_JSONPATCH:
module.fail_json(msg='jsonpatch is required for this module')
if (
module.params['auth_type'] in [None, 'None']
and module.params['ironic_url'] is None
):
module.fail_json(msg="Authentication appears to be disabled, "
"Please define an ironic_url parameter")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None']
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
node_id = _choose_id_value(module)
sdk, cloud = openstack_cloud_from_module(module)
try:
server = cloud.get_machine(node_id)
if module.params['state'] == 'present':
if module.params['driver'] is None:
module.fail_json(msg="A driver must be defined in order "
"to set a node to present.")
properties = _parse_properties(module)
driver_info = _parse_driver_info(sdk, module)
kwargs = dict(
driver=module.params['driver'],
properties=properties,
driver_info=driver_info,
name=module.params['name'],
)
if module.params['chassis_uuid']:
kwargs['chassis_uuid'] = module.params['chassis_uuid']
if server is None:
# Note(TheJulia): Add a specific UUID to the request if
# present in order to be able to re-use kwargs for if
# the node already exists logic, since uuid cannot be
# updated.
if module.params['uuid']:
kwargs['uuid'] = module.params['uuid']
server = cloud.register_machine(module.params['nics'],
**kwargs)
module.exit_json(changed=True, uuid=server['uuid'],
provision_state=server['provision_state'])
else:
# TODO(TheJulia): Presently this does not support updating
# nics. Support needs to be added.
#
# Note(TheJulia): This message should never get logged
# however we cannot realistically proceed if neither a
# name or uuid was supplied to begin with.
if not node_id:
module.fail_json(msg="A uuid or name value "
"must be defined")
# Note(TheJulia): Constructing the configuration to compare
# against. The items listed in the server_config block can
# be updated via the API.
server_config = dict(
driver=server['driver'],
properties=server['properties'],
driver_info=server['driver_info'],
name=server['name'],
)
# Add the pre-existing chassis_uuid only if
# it is present in the server configuration.
if hasattr(server, 'chassis_uuid'):
server_config['chassis_uuid'] = server['chassis_uuid']
# Note(TheJulia): If a password is defined and concealed, a
# patch will always be generated and re-asserted.
patch = jsonpatch.JsonPatch.from_diff(server_config, kwargs)
if not patch:
_exit_node_not_updated(module, server)
elif _choose_if_password_only(module, list(patch)):
# Note(TheJulia): Normally we would allow the general
# exception catch below, however this allows a specific
# message.
try:
server = cloud.patch_machine(
server['uuid'],
list(patch))
except Exception as e:
module.fail_json(msg="Failed to update node, "
"Error: %s" % e.message)
# Enumerate out a list of changed paths.
change_list = []
for change in list(patch):
change_list.append(change['path'])
module.exit_json(changed=True,
result="Node Updated",
changes=change_list,
uuid=server['uuid'],
provision_state=server['provision_state'])
# Return not updated by default as the conditions were not met
# to update.
_exit_node_not_updated(module, server)
if module.params['state'] == 'absent':
if not node_id:
module.fail_json(msg="A uuid or name value must be defined "
"in order to remove a node.")
if server is not None:
cloud.unregister_machine(module.params['nics'],
server['uuid'])
module.exit_json(changed=True, result="deleted")
else:
module.exit_json(changed=False, result="Server not found")
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -0,0 +1 @@
baremetal_node.py

View File

@ -1,152 +0,0 @@
#!/usr/bin/python
# coding: utf-8 -*-
# (c) 2015-2016, Hewlett Packard Enterprise Development Company LP
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_ironic_inspect
short_description: Explicitly triggers baremetal node introspection in ironic.
author: "Julia Kreger (@juliakreger)"
description:
- Requests Ironic to set a node into inspect state in order to collect metadata regarding the node.
This command may be out of band or in-band depending on the ironic driver configuration.
This is only possible on nodes in 'manageable' and 'available' state.
options:
mac:
description:
- unique mac address that is used to attempt to identify the host.
type: str
uuid:
description:
- globally unique identifier (UUID) to identify the host.
type: str
name:
description:
- unique name identifier to identify the host in Ironic.
type: str
ironic_url:
description:
- If noauth mode is utilized, this is required to be set to the endpoint URL for the Ironic API.
Use with "auth" and "auth_type" settings set to None.
type: str
timeout:
description:
- A timeout in seconds to tell the role to wait for the node to complete introspection if wait is set to True.
default: 1200
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
ansible_facts:
description: Dictionary of new facts representing discovered properties of the node..
returned: changed
type: complex
contains:
memory_mb:
description: Amount of node memory as updated in the node properties
type: str
sample: "1024"
cpu_arch:
description: Detected CPU architecture type
type: str
sample: "x86_64"
local_gb:
description: Total size of local disk storage as updated in node properties.
type: str
sample: "10"
cpus:
description: Count of cpu cores defined in the updated node properties.
type: str
sample: "1"
'''
EXAMPLES = '''
# Invoke node inspection
- os_ironic_inspect:
name: "testnode1"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _choose_id_value(module):
if module.params['uuid']:
return module.params['uuid']
if module.params['name']:
return module.params['name']
return None
def main():
argument_spec = openstack_full_argument_spec(
auth_type=dict(required=False),
uuid=dict(required=False),
name=dict(required=False),
mac=dict(required=False),
ironic_url=dict(required=False),
timeout=dict(default=1200, type='int', required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if (
module.params['auth_type'] in [None, 'None']
and module.params['ironic_url'] is None
):
module.fail_json(msg="Authentication appears to be disabled, "
"Please define an ironic_url parameter")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None']
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
sdk, cloud = openstack_cloud_from_module(module)
try:
if module.params['name'] or module.params['uuid']:
server = cloud.get_machine(_choose_id_value(module))
elif module.params['mac']:
server = cloud.get_machine_by_mac(module.params['mac'])
else:
module.fail_json(msg="The worlds did not align, "
"the host was not found as "
"no name, uuid, or mac was "
"defined.")
if server:
cloud.inspect_machine(server['uuid'], module.params['wait'])
# TODO(TheJulia): diff properties, ?and ports? and determine
# if a change occurred. In theory, the node is always changed
# if introspection is able to update the record.
module.exit_json(changed=True,
ansible_facts=server['properties'])
else:
module.fail_json(msg="node not found.")
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -0,0 +1 @@
baremetal_inspect.py

View File

@ -1,379 +0,0 @@
#!/usr/bin/python
# coding: utf-8 -*-
# (c) 2015, Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_ironic_node
short_description: Activate/Deactivate Bare Metal Resources from OpenStack
author: "Monty Taylor (@emonty)"
description:
- Deploy to nodes controlled by Ironic.
options:
name:
description:
- Name of the node to create.
type: str
state:
description:
- Indicates desired state of the resource.
- I(state) can be C('present'), C('absent'), C('maintenance') or C('off').
default: present
type: str
deploy:
description:
- Indicates if the resource should be deployed. Allows for deployment
logic to be disengaged and control of the node power or maintenance
state to be changed.
type: str
default: 'yes'
uuid:
description:
- globally unique identifier (UUID) to be given to the resource.
type: str
ironic_url:
description:
- If noauth mode is utilized, this is required to be set to the
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
settings set to None.
type: str
config_drive:
description:
- A configdrive file or HTTP(S) URL that will be passed along to the
node.
type: raw
instance_info:
description:
- Definition of the instance information which is used to deploy
the node. This information is only required when an instance is
set to present.
type: dict
suboptions:
image_source:
description:
- An HTTP(S) URL where the image can be retrieved from.
image_checksum:
description:
- The checksum of image_source.
image_disk_format:
description:
- The type of image that has been requested to be deployed.
power:
description:
- A setting to allow power state to be asserted allowing nodes
that are not yet deployed to be powered on, and nodes that
are deployed to be powered off.
- I(power) can be C('present'), C('absent'), C('maintenance') or C('off').
default: present
type: str
maintenance:
description:
- A setting to allow the direct control if a node is in
maintenance mode.
- I(maintenance) can be C('yes'), C('no'), C('True'), or C('False').
type: str
maintenance_reason:
description:
- A string expression regarding the reason a node is in a
maintenance mode.
type: str
wait:
description:
- A boolean value instructing the module to wait for node
activation or deactivation to complete before returning.
type: bool
default: 'no'
timeout:
description:
- An integer value representing the number of seconds to
wait for the node activation or deactivation to complete.
default: 1800
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Activate a node by booting an image with a configdrive attached
- os_ironic_node:
cloud: "openstack"
uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
state: present
power: present
deploy: True
maintenance: False
config_drive: "http://192.168.1.1/host-configdrive.iso"
instance_info:
image_source: "http://192.168.1.1/deploy_image.img"
image_checksum: "356a6b55ecc511a20c33c946c4e678af"
image_disk_format: "qcow"
delegate_to: localhost
# Activate a node by booting an image with a configdrive json object
- os_ironic_node:
uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
auth_type: None
ironic_url: "http://192.168.1.1:6385/"
config_drive:
meta_data:
hostname: node1
public_keys:
default: ssh-rsa AAA...BBB==
instance_info:
image_source: "http://192.168.1.1/deploy_image.img"
image_checksum: "356a6b55ecc511a20c33c946c4e678af"
image_disk_format: "qcow"
delegate_to: localhost
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _choose_id_value(module):
if module.params['uuid']:
return module.params['uuid']
if module.params['name']:
return module.params['name']
return None
def _is_true(value):
true_values = [True, 'yes', 'Yes', 'True', 'true', 'present', 'on']
if value in true_values:
return True
return False
def _is_false(value):
false_values = [False, None, 'no', 'No', 'False', 'false', 'absent', 'off']
if value in false_values:
return True
return False
def _check_set_maintenance(module, cloud, node):
if _is_true(module.params['maintenance']):
if _is_false(node['maintenance']):
cloud.set_machine_maintenance_state(
node['uuid'],
True,
reason=module.params['maintenance_reason'])
module.exit_json(changed=True, msg="Node has been set into "
"maintenance mode")
else:
# User has requested maintenance state, node is already in the
# desired state, checking to see if the reason has changed.
if (str(node['maintenance_reason']) not in
str(module.params['maintenance_reason'])):
cloud.set_machine_maintenance_state(
node['uuid'],
True,
reason=module.params['maintenance_reason'])
module.exit_json(changed=True, msg="Node maintenance reason "
"updated, cannot take any "
"additional action.")
elif _is_false(module.params['maintenance']):
if node['maintenance'] is True:
cloud.remove_machine_from_maintenance(node['uuid'])
return True
else:
module.fail_json(msg="maintenance parameter was set but a valid "
"the value was not recognized.")
return False
def _check_set_power_state(module, cloud, node):
if 'power on' in str(node['power_state']):
if _is_false(module.params['power']):
# User has requested the node be powered off.
cloud.set_machine_power_off(node['uuid'])
module.exit_json(changed=True, msg="Power requested off")
if 'power off' in str(node['power_state']):
if (
_is_false(module.params['power'])
and _is_false(module.params['state'])
):
return False
if (
_is_false(module.params['power'])
and _is_false(module.params['state'])
):
module.exit_json(
changed=False,
msg="Power for node is %s, node must be reactivated "
"OR set to state absent"
)
# In the event the power has been toggled on and
# deployment has been requested, we need to skip this
# step.
if (
_is_true(module.params['power'])
and _is_false(module.params['deploy'])
):
# Node is powered down when it is not awaiting to be provisioned
cloud.set_machine_power_on(node['uuid'])
return True
# Default False if no action has been taken.
return False
def main():
argument_spec = openstack_full_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
instance_info=dict(type='dict', required=False),
config_drive=dict(type='raw', required=False),
ironic_url=dict(required=False),
state=dict(required=False, default='present'),
maintenance=dict(required=False),
maintenance_reason=dict(required=False),
power=dict(required=False, default='present'),
deploy=dict(required=False, default='yes'),
wait=dict(type='bool', required=False, default=False),
timeout=dict(required=False, type='int', default=1800),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if (
module.params['auth_type'] in [None, 'None']
and module.params['ironic_url'] is None
):
module.fail_json(msg="Authentication appears disabled, Please "
"define an ironic_url parameter")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None']
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
if (
module.params['config_drive']
and not isinstance(module.params['config_drive'], (str, dict))
):
config_drive_type = type(module.params['config_drive'])
msg = ('argument config_drive is of type %s and we expected'
' str or dict') % config_drive_type
module.fail_json(msg=msg)
node_id = _choose_id_value(module)
if not node_id:
module.fail_json(msg="A uuid or name value must be defined "
"to use this module.")
sdk, cloud = openstack_cloud_from_module(module)
try:
node = cloud.get_machine(node_id)
if node is None:
module.fail_json(msg="node not found")
uuid = node['uuid']
instance_info = module.params['instance_info']
changed = False
wait = module.params['wait']
timeout = module.params['timeout']
# User has requested desired state to be in maintenance state.
if module.params['state'] == 'maintenance':
module.params['maintenance'] = True
if node['provision_state'] in [
'cleaning',
'deleting',
'wait call-back']:
module.fail_json(msg="Node is in %s state, cannot act upon the "
"request as the node is in a transition "
"state" % node['provision_state'])
# TODO(TheJulia) This is in-development code, that requires
# code in the shade library that is still in development.
if _check_set_maintenance(module, cloud, node):
if node['provision_state'] in 'active':
module.exit_json(changed=True,
result="Maintenance state changed")
changed = True
node = cloud.get_machine(node_id)
if _check_set_power_state(module, cloud, node):
changed = True
node = cloud.get_machine(node_id)
if _is_true(module.params['state']):
if _is_false(module.params['deploy']):
module.exit_json(
changed=changed,
result="User request has explicitly disabled "
"deployment logic"
)
if 'active' in node['provision_state']:
module.exit_json(
changed=changed,
result="Node already in an active state."
)
if instance_info is None:
module.fail_json(
changed=changed,
msg="When setting an instance to present, "
"instance_info is a required variable.")
# TODO(TheJulia): Update instance info, however info is
# deployment specific. Perhaps consider adding rebuild
# support, although there is a known desire to remove
# rebuild support from Ironic at some point in the future.
cloud.update_machine(uuid, instance_info=instance_info)
cloud.validate_node(uuid)
if not wait:
cloud.activate_node(uuid, module.params['config_drive'])
else:
cloud.activate_node(
uuid,
configdrive=module.params['config_drive'],
wait=wait,
timeout=timeout)
# TODO(TheJulia): Add more error checking..
module.exit_json(changed=changed, result="node activated")
elif _is_false(module.params['state']):
if node['provision_state'] not in "deleted":
cloud.update_machine(uuid, instance_info={})
if not wait:
cloud.deactivate_node(uuid)
else:
cloud.deactivate_node(
uuid,
wait=wait,
timeout=timeout)
module.exit_json(changed=True, result="deleted")
else:
module.exit_json(changed=False, result="node not found")
else:
module.fail_json(msg="State must be present, absent, "
"maintenance, off")
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == "__main__":
main()

View File

@ -0,0 +1 @@
baremetal_node_action.py

View File

@ -1,167 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2013, Benno Joy <benno@ansible.com>
# Copyright (c) 2013, John Dewey <john@dewey.ws>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_keypair
short_description: Add/Delete a keypair from OpenStack
author: "Benno Joy (@bennojoy)"
description:
- Add or Remove key pair from OpenStack
options:
name:
description:
- Name that has to be given to the key pair
required: true
type: str
public_key:
description:
- The public key that would be uploaded to nova and injected into VMs
upon creation.
type: str
public_key_file:
description:
- Path to local file containing ssh public key. Mutually exclusive
with public_key.
type: str
state:
description:
- Should the resource be present or absent. If state is replace and
the key exists but has different content, delete it and recreate it
with the new content.
choices: [present, absent, replace]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Creates a key pair with the running users public key
- os_keypair:
cloud: mordred
state: present
name: ansible_key
public_key_file: /home/me/.ssh/id_rsa.pub
# Creates a new key pair and the private key returned after the run.
- os_keypair:
cloud: rax-dfw
state: present
name: ansible_key
'''
RETURN = '''
id:
description: Unique UUID.
returned: success
type: str
name:
description: Name given to the keypair.
returned: success
type: str
public_key:
description: The public key value for the keypair.
returned: success
type: str
private_key:
description: The private key value for the keypair.
returned: Only when a keypair is generated for the user (e.g., when creating one
and a public key is not specified).
type: str
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _system_state_change(module, keypair):
state = module.params['state']
if state == 'present' and not keypair:
return True
if state == 'absent' and keypair:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
public_key=dict(default=None),
public_key_file=dict(default=None),
state=dict(default='present',
choices=['absent', 'present', 'replace']),
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[['public_key', 'public_key_file']])
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
state = module.params['state']
name = module.params['name']
public_key = module.params['public_key']
if module.params['public_key_file']:
with open(module.params['public_key_file']) as public_key_fh:
public_key = public_key_fh.read().rstrip()
sdk, cloud = openstack_cloud_from_module(module)
try:
keypair = cloud.get_keypair(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(module, keypair))
if state in ('present', 'replace'):
if keypair and keypair['name'] == name:
if public_key and (public_key != keypair['public_key']):
if state == 'present':
module.fail_json(
msg="Key name %s present but key hash not the same"
" as offered. Delete key first." % name
)
else:
cloud.delete_keypair(name)
keypair = cloud.create_keypair(name, public_key)
changed = True
else:
changed = False
else:
keypair = cloud.create_keypair(name, public_key)
changed = True
module.exit_json(changed=changed,
key=keypair,
id=keypair['id'])
elif state == 'absent':
if keypair:
cloud.delete_keypair(name)
module.exit_json(changed=True)
module.exit_json(changed=False)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
keypair.py

View File

@ -1,185 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_keystone_domain
short_description: Manage OpenStack Identity Domains
author:
- Monty Taylor (@emonty)
- Haneef Ali (@haneefs)
description:
- Create, update, or delete OpenStack Identity domains. If a domain
with the supplied name already exists, it will be updated with the
new description and enabled attributes.
options:
name:
description:
- Name that has to be given to the instance
required: true
type: str
description:
description:
- Description of the domain
type: str
enabled:
description:
- Is the domain enabled
type: bool
default: 'yes'
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a domain
- os_keystone_domain:
cloud: mycloud
state: present
name: demo
description: Demo Domain
# Delete a domain
- os_keystone_domain:
cloud: mycloud
state: absent
name: demo
'''
RETURN = '''
domain:
description: Dictionary describing the domain.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Domain ID.
type: str
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
name:
description: Domain name.
type: str
sample: "demo"
description:
description: Domain description.
type: str
sample: "Demo Domain"
enabled:
description: Domain description.
type: bool
sample: True
id:
description: The domain ID.
returned: On success when I(state) is 'present'
type: str
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _needs_update(module, domain):
if module.params['description'] is not None and \
domain.description != module.params['description']:
return True
if domain.enabled != module.params['enabled']:
return True
return False
def _system_state_change(module, domain):
state = module.params['state']
if state == 'absent' and domain:
return True
if state == 'present':
if domain is None:
return True
return _needs_update(module, domain)
return False
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
description=dict(default=None),
enabled=dict(default=True, type='bool'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
name = module.params['name']
description = module.params['description']
enabled = module.params['enabled']
state = module.params['state']
sdk, cloud = openstack_cloud_from_module(module)
try:
domains = cloud.search_domains(filters=dict(name=name))
if len(domains) > 1:
module.fail_json(msg='Domain name %s is not unique' % name)
elif len(domains) == 1:
domain = domains[0]
else:
domain = None
if module.check_mode:
module.exit_json(changed=_system_state_change(module, domain))
if state == 'present':
if domain is None:
domain = cloud.create_domain(
name=name, description=description, enabled=enabled)
changed = True
else:
if _needs_update(module, domain):
domain = cloud.update_domain(
domain.id, name=name, description=description,
enabled=enabled)
changed = True
else:
changed = False
module.exit_json(changed=changed, domain=domain, id=domain.id)
elif state == 'absent':
if domain is None:
changed = False
else:
cloud.delete_domain(domain.id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
identity_domain.py

View File

@ -1,139 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2016 Hewlett-Packard Enterprise Corporation
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_keystone_domain_info
short_description: Retrieve information about one or more OpenStack domains
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
description:
- Retrieve information about a one or more OpenStack domains
- This module was called C(os_keystone_domain_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(os_keystone_domain_info) module no longer returns C(ansible_facts)!
options:
name:
description:
- Name or ID of the domain
type: str
filters:
description:
- A dictionary of meta data to use for further filtering. Elements of
this dictionary may be additional dictionaries.
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Gather information about previously created domain
- os_keystone_domain_info:
cloud: awesomecloud
register: result
- debug:
msg: "{{ result.openstack_domains }}"
# Gather information about a previously created domain by name
- os_keystone_domain_info:
cloud: awesomecloud
name: demodomain
register: result
- debug:
msg: "{{ result.openstack_domains }}"
# Gather information about a previously created domain with filter
- os_keystone_domain_info:
cloud: awesomecloud
name: demodomain
filters:
enabled: false
register: result
- debug:
msg: "{{ result.openstack_domains }}"
'''
RETURN = '''
openstack_domains:
description: has all the OpenStack information about domains
returned: always, but can be null
type: complex
contains:
id:
description: Unique UUID.
returned: success
type: str
name:
description: Name given to the domain.
returned: success
type: str
description:
description: Description of the domain.
returned: success
type: str
enabled:
description: Flag to indicate if the domain is enabled.
returned: success
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module_kwargs = openstack_module_kwargs(
mutually_exclusive=[
['name', 'filters'],
]
)
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'os_keystone_domain_facts'
if is_old_facts:
module.deprecate("The 'os_keystone_domain_facts' module has been renamed to 'os_keystone_domain_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
filters = module.params['filters']
if name:
# Let's suppose user is passing domain ID
try:
domains = opcloud.get_domain(name)
except Exception:
domains = opcloud.search_domains(filters={'name': name})
else:
domains = opcloud.search_domains(filters)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_domains=domains))
else:
module.exit_json(changed=False, openstack_domains=domains)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
identity_domain_info.py

View File

@ -1,215 +0,0 @@
#!/usr/bin/python
# Copyright: (c) 2017, VEXXHOST, Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: os_keystone_endpoint
short_description: Manage OpenStack Identity service endpoints
author:
- Mohammed Naser (@mnaser)
- Alberto Murillo (@albertomurillo)
description:
- Create, update, or delete OpenStack Identity service endpoints. If a
service with the same combination of I(service), I(interface) and I(region)
exist, the I(url) and I(state) (C(present) or C(absent)) will be updated.
options:
service:
description:
- Name or id of the service.
required: true
type: str
endpoint_interface:
description:
- Interface of the service.
choices: [admin, public, internal]
required: true
type: str
url:
description:
- URL of the service.
required: true
type: str
region:
description:
- Region that the service belongs to. Note that I(region_name) is used for authentication.
type: str
enabled:
description:
- Is the service enabled.
default: True
type: bool
state:
description:
- Should the resource be C(present) or C(absent).
choices: [present, absent]
default: present
type: str
requirements:
- "python >= 3.6"
- "openstacksdk >= 0.13.0"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Create a service for glance
os_keystone_endpoint:
cloud: mycloud
service: glance
endpoint_interface: public
url: http://controller:9292
region: RegionOne
state: present
- name: Delete a service for nova
os_keystone_endpoint:
cloud: mycloud
service: nova
endpoint_interface: public
region: RegionOne
state: absent
'''
RETURN = '''
endpoint:
description: Dictionary describing the endpoint.
returned: On success when I(state) is C(present)
type: complex
contains:
id:
description: Endpoint ID.
type: str
sample: 3292f020780b4d5baf27ff7e1d224c44
region:
description: Region Name.
type: str
sample: RegionOne
service_id:
description: Service ID.
type: str
sample: b91f1318f735494a825a55388ee118f3
interface:
description: Endpoint Interface.
type: str
sample: public
url:
description: Service URL.
type: str
sample: http://controller:9292
enabled:
description: Service status.
type: bool
sample: True
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
def _needs_update(module, endpoint):
if endpoint.enabled != module.params['enabled']:
return True
if endpoint.url != module.params['url']:
return True
return False
def _system_state_change(module, endpoint):
state = module.params['state']
if state == 'absent' and endpoint:
return True
if state == 'present':
if endpoint is None:
return True
return _needs_update(module, endpoint)
return False
def main():
argument_spec = openstack_full_argument_spec(
service=dict(type='str', required=True),
endpoint_interface=dict(type='str', required=True, choices=['admin', 'public', 'internal']),
url=dict(type='str', required=True),
region=dict(type='str'),
enabled=dict(type='bool', default=True),
state=dict(type='str', default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
service_name_or_id = module.params['service']
interface = module.params['endpoint_interface']
url = module.params['url']
region = module.params['region']
enabled = module.params['enabled']
state = module.params['state']
sdk, cloud = openstack_cloud_from_module(module)
try:
service = cloud.get_service(service_name_or_id)
if service is None:
module.fail_json(msg='Service %s does not exist' % service_name_or_id)
filters = dict(service_id=service.id, interface=interface)
if region is not None:
filters['region'] = region
endpoints = cloud.search_endpoints(filters=filters)
if len(endpoints) > 1:
module.fail_json(msg='Service %s, interface %s and region %s are '
'not unique' %
(service_name_or_id, interface, region))
elif len(endpoints) == 1:
endpoint = endpoints[0]
else:
endpoint = None
if module.check_mode:
module.exit_json(changed=_system_state_change(module, endpoint))
if state == 'present':
if endpoint is None:
result = cloud.create_endpoint(service_name_or_id=service,
url=url, interface=interface,
region=region, enabled=enabled)
endpoint = result[0]
changed = True
else:
if _needs_update(module, endpoint):
endpoint = cloud.update_endpoint(
endpoint.id, url=url, enabled=enabled)
changed = True
else:
changed = False
module.exit_json(changed=changed, endpoint=endpoint)
elif state == 'absent':
if endpoint is None:
changed = False
else:
cloud.delete_endpoint(endpoint.id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
endpoint.py

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