one_image/one_image_info: refactor (#8889)
* Refactor one_image * Refactor one_image_info * Add examples one_image * Add CHANGELOG fragment * Add integration tests for one_image * Add integration tests for one_image_info * Update one_image DOC * Update one_image_info DOC * Update one_image DOC * Update one_image_info DOC * Fix f-strings for one_image * Update CHANGELOG fragment * PR fixes * PR fixespull/8987/head
parent
e7ccbc2f18
commit
fea0ffa5aa
|
@ -0,0 +1,6 @@
|
||||||
|
minor_changes:
|
||||||
|
- one_image - add option ``persistent`` to manage image persistence (https://github.com/ansible-collections/community.general/issues/3578, https://github.com/ansible-collections/community.general/pull/8889).
|
||||||
|
- one_image - refactor code to make it more similar to ``one_template`` and ``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
|
||||||
|
- one_image_info - refactor code to make it more similar to ``one_template`` and ``one_vnet`` (https://github.com/ansible-collections/community.general/pull/8889).
|
||||||
|
- one_image - extend xsd scheme to make it return a lot more info about image (https://github.com/ansible-collections/community.general/pull/8889).
|
||||||
|
- one_image_info - extend xsd scheme to make it return a lot more info about image (https://github.com/ansible-collections/community.general/pull/8889).
|
|
@ -16,6 +16,7 @@ from ansible.module_utils.six import string_types
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
|
|
||||||
|
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
|
||||||
HAS_PYONE = True
|
HAS_PYONE = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -347,3 +348,90 @@ class OpenNebulaModule:
|
||||||
result: the Ansible result
|
result: the Ansible result
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("Method requires implementation")
|
raise NotImplementedError("Method requires implementation")
|
||||||
|
|
||||||
|
def get_image_list_id(self, image, element):
|
||||||
|
"""
|
||||||
|
This is a helper function for get_image_info to iterate over a simple list of objects
|
||||||
|
"""
|
||||||
|
list_of_id = []
|
||||||
|
|
||||||
|
if element == 'VMS':
|
||||||
|
image_list = image.VMS
|
||||||
|
if element == 'CLONES':
|
||||||
|
image_list = image.CLONES
|
||||||
|
if element == 'APP_CLONES':
|
||||||
|
image_list = image.APP_CLONES
|
||||||
|
|
||||||
|
for iter in image_list.ID:
|
||||||
|
list_of_id.append(
|
||||||
|
# These are optional so firstly check for presence
|
||||||
|
getattr(iter, 'ID', 'Null'),
|
||||||
|
)
|
||||||
|
return list_of_id
|
||||||
|
|
||||||
|
def get_image_snapshots_list(self, image):
|
||||||
|
"""
|
||||||
|
This is a helper function for get_image_info to iterate over a dictionary
|
||||||
|
"""
|
||||||
|
list_of_snapshots = []
|
||||||
|
|
||||||
|
for iter in image.SNAPSHOTS.SNAPSHOT:
|
||||||
|
list_of_snapshots.append({
|
||||||
|
'date': iter['DATE'],
|
||||||
|
'parent': iter['PARENT'],
|
||||||
|
'size': iter['SIZE'],
|
||||||
|
# These are optional so firstly check for presence
|
||||||
|
'allow_orhans': getattr(image.SNAPSHOTS, 'ALLOW_ORPHANS', 'Null'),
|
||||||
|
'children': getattr(iter, 'CHILDREN', 'Null'),
|
||||||
|
'active': getattr(iter, 'ACTIVE', 'Null'),
|
||||||
|
'name': getattr(iter, 'NAME', 'Null'),
|
||||||
|
})
|
||||||
|
return list_of_snapshots
|
||||||
|
|
||||||
|
def get_image_info(self, image):
|
||||||
|
"""
|
||||||
|
This method is used by one_image and one_image_info modules to retrieve
|
||||||
|
information from XSD scheme of an image
|
||||||
|
Returns: a copy of the parameters that includes the resolved parameters.
|
||||||
|
"""
|
||||||
|
info = {
|
||||||
|
'id': image.ID,
|
||||||
|
'name': image.NAME,
|
||||||
|
'state': IMAGE_STATES[image.STATE],
|
||||||
|
'running_vms': image.RUNNING_VMS,
|
||||||
|
'used': bool(image.RUNNING_VMS),
|
||||||
|
'user_name': image.UNAME,
|
||||||
|
'user_id': image.UID,
|
||||||
|
'group_name': image.GNAME,
|
||||||
|
'group_id': image.GID,
|
||||||
|
'permissions': {
|
||||||
|
'owner_u': image.PERMISSIONS.OWNER_U,
|
||||||
|
'owner_m': image.PERMISSIONS.OWNER_M,
|
||||||
|
'owner_a': image.PERMISSIONS.OWNER_A,
|
||||||
|
'group_u': image.PERMISSIONS.GROUP_U,
|
||||||
|
'group_m': image.PERMISSIONS.GROUP_M,
|
||||||
|
'group_a': image.PERMISSIONS.GROUP_A,
|
||||||
|
'other_u': image.PERMISSIONS.OTHER_U,
|
||||||
|
'other_m': image.PERMISSIONS.OTHER_M,
|
||||||
|
'other_a': image.PERMISSIONS.OTHER_A
|
||||||
|
},
|
||||||
|
'type': image.TYPE,
|
||||||
|
'disk_type': image.DISK_TYPE,
|
||||||
|
'persistent': image.PERSISTENT,
|
||||||
|
'regtime': image.REGTIME,
|
||||||
|
'source': image.SOURCE,
|
||||||
|
'path': image.PATH,
|
||||||
|
'fstype': getattr(image, 'FSTYPE', 'Null'),
|
||||||
|
'size': image.SIZE,
|
||||||
|
'cloning_ops': image.CLONING_OPS,
|
||||||
|
'cloning_id': image.CLONING_ID,
|
||||||
|
'target_snapshot': image.TARGET_SNAPSHOT,
|
||||||
|
'datastore_id': image.DATASTORE_ID,
|
||||||
|
'datastore': image.DATASTORE,
|
||||||
|
'vms': self.get_image_list_id(image, 'VMS'),
|
||||||
|
'clones': self.get_image_list_id(image, 'CLONES'),
|
||||||
|
'app_clones': self.get_image_list_id(image, 'APP_CLONES'),
|
||||||
|
'snapshots': self.get_image_snapshots_list(image),
|
||||||
|
'template': image.TEMPLATE,
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
|
|
@ -17,6 +17,7 @@ description:
|
||||||
requirements:
|
requirements:
|
||||||
- pyone
|
- pyone
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
|
- community.general.opennebula
|
||||||
- community.general.attributes
|
- community.general.attributes
|
||||||
attributes:
|
attributes:
|
||||||
check_mode:
|
check_mode:
|
||||||
|
@ -24,23 +25,6 @@ attributes:
|
||||||
diff_mode:
|
diff_mode:
|
||||||
support: none
|
support: none
|
||||||
options:
|
options:
|
||||||
api_url:
|
|
||||||
description:
|
|
||||||
- URL of the OpenNebula RPC server.
|
|
||||||
- It is recommended to use HTTPS so that the username/password are not
|
|
||||||
- transferred over the network unencrypted.
|
|
||||||
- If not set then the value of the E(ONE_URL) environment variable is used.
|
|
||||||
type: str
|
|
||||||
api_username:
|
|
||||||
description:
|
|
||||||
- Name of the user to login into the OpenNebula RPC server. If not set
|
|
||||||
- then the value of the E(ONE_USERNAME) environment variable is used.
|
|
||||||
type: str
|
|
||||||
api_password:
|
|
||||||
description:
|
|
||||||
- Password of the user to login into OpenNebula RPC server. If not set
|
|
||||||
- then the value of the E(ONE_PASSWORD) environment variable is used.
|
|
||||||
type: str
|
|
||||||
id:
|
id:
|
||||||
description:
|
description:
|
||||||
- A O(id) of the image you would like to manage.
|
- A O(id) of the image you would like to manage.
|
||||||
|
@ -67,6 +51,11 @@ options:
|
||||||
- A name that will be assigned to the existing or new image.
|
- A name that will be assigned to the existing or new image.
|
||||||
- In the case of cloning, by default O(new_name) will take the name of the origin image with the prefix 'Copy of'.
|
- In the case of cloning, by default O(new_name) will take the name of the origin image with the prefix 'Copy of'.
|
||||||
type: str
|
type: str
|
||||||
|
persistent:
|
||||||
|
description:
|
||||||
|
- Whether the image should be persistent or non-persistent.
|
||||||
|
type: bool
|
||||||
|
version_added: 9.5.0
|
||||||
author:
|
author:
|
||||||
- "Milan Ilic (@ilicmilan)"
|
- "Milan Ilic (@ilicmilan)"
|
||||||
'''
|
'''
|
||||||
|
@ -92,6 +81,11 @@ EXAMPLES = '''
|
||||||
id: 37
|
id: 37
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
|
- name: Make the IMAGE persistent
|
||||||
|
community.general.one_image:
|
||||||
|
id: 37
|
||||||
|
persistent: true
|
||||||
|
|
||||||
- name: Enable the IMAGE by name
|
- name: Enable the IMAGE by name
|
||||||
community.general.one_image:
|
community.general.one_image:
|
||||||
name: bar-image
|
name: bar-image
|
||||||
|
@ -114,300 +108,448 @@ RETURN = '''
|
||||||
id:
|
id:
|
||||||
description: image id
|
description: image id
|
||||||
type: int
|
type: int
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: 153
|
sample: 153
|
||||||
name:
|
name:
|
||||||
description: image name
|
description: image name
|
||||||
type: str
|
type: str
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: app1
|
sample: app1
|
||||||
group_id:
|
group_id:
|
||||||
description: image's group id
|
description: image's group id
|
||||||
type: int
|
type: int
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: 1
|
sample: 1
|
||||||
group_name:
|
group_name:
|
||||||
description: image's group name
|
description: image's group name
|
||||||
type: str
|
type: str
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: one-users
|
sample: one-users
|
||||||
owner_id:
|
owner_id:
|
||||||
description: image's owner id
|
description: image's owner id
|
||||||
type: int
|
type: int
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: 143
|
sample: 143
|
||||||
owner_name:
|
owner_name:
|
||||||
description: image's owner name
|
description: image's owner name
|
||||||
type: str
|
type: str
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: ansible-test
|
sample: ansible-test
|
||||||
state:
|
state:
|
||||||
description: state of image instance
|
description: state of image instance
|
||||||
type: str
|
type: str
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: READY
|
sample: READY
|
||||||
used:
|
used:
|
||||||
description: is image in use
|
description: is image in use
|
||||||
type: bool
|
type: bool
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: true
|
sample: true
|
||||||
running_vms:
|
running_vms:
|
||||||
description: count of running vms that use this image
|
description: count of running vms that use this image
|
||||||
type: int
|
type: int
|
||||||
returned: success
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
sample: 7
|
sample: 7
|
||||||
|
permissions:
|
||||||
|
description: The image's permissions.
|
||||||
|
type: dict
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
contains:
|
||||||
|
owner_u:
|
||||||
|
description: The image's owner USAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 1
|
||||||
|
owner_m:
|
||||||
|
description: The image's owner MANAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
owner_a:
|
||||||
|
description: The image's owner ADMIN permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
group_u:
|
||||||
|
description: The image's group USAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
group_m:
|
||||||
|
description: The image's group MANAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
group_a:
|
||||||
|
description: The image's group ADMIN permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
other_u:
|
||||||
|
description: The image's other users USAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
other_m:
|
||||||
|
description: The image's other users MANAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
other_a:
|
||||||
|
description: The image's other users ADMIN permissions
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
sample:
|
||||||
|
owner_u: 1
|
||||||
|
owner_m: 0
|
||||||
|
owner_a: 0
|
||||||
|
group_u: 0
|
||||||
|
group_m: 0
|
||||||
|
group_a: 0
|
||||||
|
other_u: 0
|
||||||
|
other_m: 0
|
||||||
|
other_a: 0
|
||||||
|
type:
|
||||||
|
description: The image's type.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
disk_type:
|
||||||
|
description: The image's format type.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
persistent:
|
||||||
|
description: The image's persistence status (1 means true, 0 means false).
|
||||||
|
type: int
|
||||||
|
sample: 1
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
source:
|
||||||
|
description: The image's source.
|
||||||
|
type: str
|
||||||
|
sample: /var/lib/one//datastores/100/somerandomstringxd
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
path:
|
||||||
|
description: The image's filesystem path.
|
||||||
|
type: str
|
||||||
|
sample: /var/tmp/hello.qcow2
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
fstype:
|
||||||
|
description: The image's filesystem type.
|
||||||
|
type: str
|
||||||
|
sample: ext4
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
size:
|
||||||
|
description: The image's size in MegaBytes.
|
||||||
|
type: int
|
||||||
|
sample: 10000
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
cloning_ops:
|
||||||
|
description: The image's cloning operations per second.
|
||||||
|
type: int
|
||||||
|
sample: 0
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
cloning_id:
|
||||||
|
description: The image's cloning ID.
|
||||||
|
type: int
|
||||||
|
sample: -1
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
target_snapshot:
|
||||||
|
description: The image's target snapshot.
|
||||||
|
type: int
|
||||||
|
sample: 1
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
datastore_id:
|
||||||
|
description: The image's datastore ID.
|
||||||
|
type: int
|
||||||
|
sample: 100
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
datastore:
|
||||||
|
description: The image's datastore name.
|
||||||
|
type: int
|
||||||
|
sample: image_datastore
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
vms:
|
||||||
|
description: The image's list of vm ID's.
|
||||||
|
type: list
|
||||||
|
elements: int
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
sample:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
version_added: 9.5.0
|
||||||
|
clones:
|
||||||
|
description: The image's list of clones ID's.
|
||||||
|
type: list
|
||||||
|
elements: int
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
sample:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
version_added: 9.5.0
|
||||||
|
app_clones:
|
||||||
|
description: The image's list of app_clones ID's.
|
||||||
|
type: list
|
||||||
|
elements: int
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
sample:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
version_added: 9.5.0
|
||||||
|
snapshots:
|
||||||
|
description: The image's list of snapshots.
|
||||||
|
type: list
|
||||||
|
returned: when O(state=present), O(state=cloned), or O(state=renamed)
|
||||||
|
version_added: 9.5.0
|
||||||
|
sample:
|
||||||
|
- date: 123123
|
||||||
|
parent: 1
|
||||||
|
size: 10228
|
||||||
|
allow_orphans: 1
|
||||||
|
children: 0
|
||||||
|
active: 1
|
||||||
|
name: SampleName
|
||||||
'''
|
'''
|
||||||
|
|
||||||
try:
|
|
||||||
import pyone
|
|
||||||
HAS_PYONE = True
|
|
||||||
except ImportError:
|
|
||||||
HAS_PYONE = False
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
def get_image(module, client, predicate):
|
|
||||||
# Filter -2 means fetch all images user can Use
|
|
||||||
pool = client.imagepool.info(-2, -1, -1, -1)
|
|
||||||
|
|
||||||
for image in pool.IMAGE:
|
|
||||||
if predicate(image):
|
|
||||||
return image
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_image_by_name(module, client, image_name):
|
|
||||||
return get_image(module, client, lambda image: (image.NAME == image_name))
|
|
||||||
|
|
||||||
|
|
||||||
def get_image_by_id(module, client, image_id):
|
|
||||||
return get_image(module, client, lambda image: (image.ID == image_id))
|
|
||||||
|
|
||||||
|
|
||||||
def get_image_instance(module, client, requested_id, requested_name):
|
|
||||||
if requested_id:
|
|
||||||
return get_image_by_id(module, client, requested_id)
|
|
||||||
else:
|
|
||||||
return get_image_by_name(module, client, requested_name)
|
|
||||||
|
|
||||||
|
|
||||||
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
|
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
|
||||||
|
|
||||||
|
|
||||||
def get_image_info(image):
|
class ImageModule(OpenNebulaModule):
|
||||||
info = {
|
def __init__(self):
|
||||||
'id': image.ID,
|
argument_spec = dict(
|
||||||
'name': image.NAME,
|
id=dict(type='int', required=False),
|
||||||
'state': IMAGE_STATES[image.STATE],
|
name=dict(type='str', required=False),
|
||||||
'running_vms': image.RUNNING_VMS,
|
state=dict(type='str', choices=['present', 'absent', 'cloned', 'renamed'], default='present'),
|
||||||
'used': bool(image.RUNNING_VMS),
|
enabled=dict(type='bool', required=False),
|
||||||
'user_name': image.UNAME,
|
new_name=dict(type='str', required=False),
|
||||||
'user_id': image.UID,
|
persistent=dict(type='bool', required=False),
|
||||||
'group_name': image.GNAME,
|
)
|
||||||
'group_id': image.GID,
|
required_if = [
|
||||||
}
|
['state', 'renamed', ['id']]
|
||||||
|
]
|
||||||
|
mutually_exclusive = [
|
||||||
|
['id', 'name'],
|
||||||
|
]
|
||||||
|
|
||||||
return info
|
OpenNebulaModule.__init__(self,
|
||||||
|
argument_spec,
|
||||||
|
supports_check_mode=True,
|
||||||
|
mutually_exclusive=mutually_exclusive,
|
||||||
|
required_if=required_if)
|
||||||
|
|
||||||
|
def run(self, one, module, result):
|
||||||
|
params = module.params
|
||||||
|
id = params.get('id')
|
||||||
|
name = params.get('name')
|
||||||
|
desired_state = params.get('state')
|
||||||
|
enabled = params.get('enabled')
|
||||||
|
new_name = params.get('new_name')
|
||||||
|
persistent = params.get('persistent')
|
||||||
|
|
||||||
def wait_for_state(module, client, image_id, wait_timeout, state_predicate):
|
self.result = {}
|
||||||
import time
|
|
||||||
start_time = time.time()
|
image = self.get_image_instance(id, name)
|
||||||
|
if not image and desired_state != 'absent':
|
||||||
|
# Using 'if id:' doesn't work properly when id=0
|
||||||
|
if id is not None:
|
||||||
|
module.fail_json(msg="There is no image with id=" + str(id))
|
||||||
|
elif name is not None:
|
||||||
|
module.fail_json(msg="There is no image with name=" + name)
|
||||||
|
|
||||||
|
if desired_state == 'absent':
|
||||||
|
self.result = self.delete_image(image)
|
||||||
|
else:
|
||||||
|
if persistent is not None:
|
||||||
|
self.result = self.change_persistence(image, persistent)
|
||||||
|
if enabled is not None:
|
||||||
|
self.result = self.enable_image(image, enabled)
|
||||||
|
if desired_state == "cloned":
|
||||||
|
self.result = self.clone_image(image, new_name)
|
||||||
|
elif desired_state == "renamed":
|
||||||
|
self.result = self.rename_image(image, new_name)
|
||||||
|
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_image(self, predicate):
|
||||||
|
# Filter -2 means fetch all images user can Use
|
||||||
|
pool = self.one.imagepool.info(-2, -1, -1, -1)
|
||||||
|
|
||||||
|
for image in pool.IMAGE:
|
||||||
|
if predicate(image):
|
||||||
|
return image
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_image_by_name(self, image_name):
|
||||||
|
return self.get_image(lambda image: (image.NAME == image_name))
|
||||||
|
|
||||||
|
def get_image_by_id(self, image_id):
|
||||||
|
return self.get_image(lambda image: (image.ID == image_id))
|
||||||
|
|
||||||
|
def get_image_instance(self, requested_id, requested_name):
|
||||||
|
# Using 'if requested_id:' doesn't work properly when requested_id=0
|
||||||
|
if requested_id is not None:
|
||||||
|
return self.get_image_by_id(requested_id)
|
||||||
|
else:
|
||||||
|
return self.get_image_by_name(requested_name)
|
||||||
|
|
||||||
|
def wait_for_ready(self, image_id, wait_timeout=60):
|
||||||
|
import time
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
while (time.time() - start_time) < wait_timeout:
|
||||||
|
image = self.one.image.info(image_id)
|
||||||
|
state = image.STATE
|
||||||
|
|
||||||
|
if state in [IMAGE_STATES.index('ERROR')]:
|
||||||
|
self.module.fail_json(msg="Got an ERROR state: " + image.TEMPLATE['ERROR'])
|
||||||
|
|
||||||
|
if state in [IMAGE_STATES.index('READY')]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
self.module.fail_json(msg="Wait timeout has expired!")
|
||||||
|
|
||||||
|
def wait_for_delete(self, image_id, wait_timeout=60):
|
||||||
|
import time
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
while (time.time() - start_time) < wait_timeout:
|
||||||
|
# It might be already deleted by the time this function is called
|
||||||
|
try:
|
||||||
|
image = self.one.image.info(image_id)
|
||||||
|
except Exception:
|
||||||
|
check_image = self.get_image_instance(image_id)
|
||||||
|
if not check_image:
|
||||||
|
return True
|
||||||
|
|
||||||
|
state = image.STATE
|
||||||
|
|
||||||
|
if state in [IMAGE_STATES.index('DELETE')]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
self.module.fail_json(msg="Wait timeout has expired!")
|
||||||
|
|
||||||
|
def enable_image(self, image, enable):
|
||||||
|
image = self.one.image.info(image.ID)
|
||||||
|
changed = False
|
||||||
|
|
||||||
while (time.time() - start_time) < wait_timeout:
|
|
||||||
image = client.image.info(image_id)
|
|
||||||
state = image.STATE
|
state = image.STATE
|
||||||
|
|
||||||
if state_predicate(state):
|
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
|
||||||
return image
|
if enable:
|
||||||
|
self.module.fail_json(msg="Cannot enable " + IMAGE_STATES[state] + " image!")
|
||||||
|
else:
|
||||||
|
self.module.fail_json(msg="Cannot disable " + IMAGE_STATES[state] + " image!")
|
||||||
|
|
||||||
time.sleep(1)
|
if ((enable and state != IMAGE_STATES.index('READY')) or
|
||||||
|
(not enable and state != IMAGE_STATES.index('DISABLED'))):
|
||||||
|
changed = True
|
||||||
|
|
||||||
module.fail_json(msg="Wait timeout has expired!")
|
if changed and not self.module.check_mode:
|
||||||
|
self.one.image.enable(image.ID, enable)
|
||||||
|
|
||||||
|
result = OpenNebulaModule.get_image_info(image)
|
||||||
|
result['changed'] = changed
|
||||||
|
|
||||||
def wait_for_ready(module, client, image_id, wait_timeout=60):
|
|
||||||
return wait_for_state(module, client, image_id, wait_timeout, lambda state: (state in [IMAGE_STATES.index('READY')]))
|
|
||||||
|
|
||||||
|
|
||||||
def wait_for_delete(module, client, image_id, wait_timeout=60):
|
|
||||||
return wait_for_state(module, client, image_id, wait_timeout, lambda state: (state in [IMAGE_STATES.index('DELETE')]))
|
|
||||||
|
|
||||||
|
|
||||||
def enable_image(module, client, image, enable):
|
|
||||||
image = client.image.info(image.ID)
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
state = image.STATE
|
|
||||||
|
|
||||||
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
|
|
||||||
if enable:
|
|
||||||
module.fail_json(msg="Cannot enable " + IMAGE_STATES[state] + " image!")
|
|
||||||
else:
|
|
||||||
module.fail_json(msg="Cannot disable " + IMAGE_STATES[state] + " image!")
|
|
||||||
|
|
||||||
if ((enable and state != IMAGE_STATES.index('READY')) or
|
|
||||||
(not enable and state != IMAGE_STATES.index('DISABLED'))):
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if changed and not module.check_mode:
|
|
||||||
client.image.enable(image.ID, enable)
|
|
||||||
|
|
||||||
result = get_image_info(image)
|
|
||||||
result['changed'] = changed
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def clone_image(module, client, image, new_name):
|
|
||||||
if new_name is None:
|
|
||||||
new_name = "Copy of " + image.NAME
|
|
||||||
|
|
||||||
tmp_image = get_image_by_name(module, client, new_name)
|
|
||||||
if tmp_image:
|
|
||||||
result = get_image_info(tmp_image)
|
|
||||||
result['changed'] = False
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
if image.STATE == IMAGE_STATES.index('DISABLED'):
|
def change_persistence(self, image, enable):
|
||||||
module.fail_json(msg="Cannot clone DISABLED image")
|
image = self.one.image.info(image.ID)
|
||||||
|
changed = False
|
||||||
|
|
||||||
if not module.check_mode:
|
state = image.STATE
|
||||||
new_id = client.image.clone(image.ID, new_name)
|
|
||||||
wait_for_ready(module, client, new_id)
|
|
||||||
image = client.image.info(new_id)
|
|
||||||
|
|
||||||
result = get_image_info(image)
|
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
|
||||||
result['changed'] = True
|
if enable:
|
||||||
|
self.module.fail_json(msg="Cannot enable persistence for " + IMAGE_STATES[state] + " image!")
|
||||||
|
else:
|
||||||
|
self.module.fail_json(msg="Cannot disable persistence for " + IMAGE_STATES[state] + " image!")
|
||||||
|
|
||||||
return result
|
if ((enable and state != IMAGE_STATES.index('READY')) or
|
||||||
|
(not enable and state != IMAGE_STATES.index('DISABLED'))):
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if changed and not self.module.check_mode:
|
||||||
|
self.one.image.persistent(image.ID, enable)
|
||||||
|
|
||||||
def rename_image(module, client, image, new_name):
|
result = OpenNebulaModule.get_image_info(image)
|
||||||
if new_name is None:
|
result['changed'] = changed
|
||||||
module.fail_json(msg="'new_name' option has to be specified when the state is 'renamed'")
|
|
||||||
|
|
||||||
if new_name == image.NAME:
|
|
||||||
result = get_image_info(image)
|
|
||||||
result['changed'] = False
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
tmp_image = get_image_by_name(module, client, new_name)
|
def clone_image(self, image, new_name):
|
||||||
if tmp_image:
|
if new_name is None:
|
||||||
module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.ID))
|
new_name = "Copy of " + image.NAME
|
||||||
|
|
||||||
if not module.check_mode:
|
tmp_image = self.get_image_by_name(new_name)
|
||||||
client.image.rename(image.ID, new_name)
|
if tmp_image:
|
||||||
|
result = OpenNebulaModule.get_image_info(tmp_image)
|
||||||
|
result['changed'] = False
|
||||||
|
return result
|
||||||
|
|
||||||
result = get_image_info(image)
|
if image.STATE == IMAGE_STATES.index('DISABLED'):
|
||||||
result['changed'] = True
|
self.module.fail_json(msg="Cannot clone DISABLED image")
|
||||||
return result
|
|
||||||
|
|
||||||
|
if not self.module.check_mode:
|
||||||
|
new_id = self.one.image.clone(image.ID, new_name)
|
||||||
|
self.wait_for_ready(new_id)
|
||||||
|
image = self.one.image.info(new_id)
|
||||||
|
|
||||||
def delete_image(module, client, image):
|
result = OpenNebulaModule.get_image_info(image)
|
||||||
|
result['changed'] = True
|
||||||
|
|
||||||
if not image:
|
return result
|
||||||
return {'changed': False}
|
|
||||||
|
|
||||||
if image.RUNNING_VMS > 0:
|
def rename_image(self, image, new_name):
|
||||||
module.fail_json(msg="Cannot delete image. There are " + str(image.RUNNING_VMS) + " VMs using it.")
|
if new_name is None:
|
||||||
|
self.module.fail_json(msg="'new_name' option has to be specified when the state is 'renamed'")
|
||||||
|
|
||||||
if not module.check_mode:
|
if new_name == image.NAME:
|
||||||
client.image.delete(image.ID)
|
result = OpenNebulaModule.get_image_info(image)
|
||||||
wait_for_delete(module, client, image.ID)
|
result['changed'] = False
|
||||||
|
return result
|
||||||
|
|
||||||
return {'changed': True}
|
tmp_image = self.get_image_by_name(new_name)
|
||||||
|
if tmp_image:
|
||||||
|
self.module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.ID))
|
||||||
|
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.one.image.rename(image.ID, new_name)
|
||||||
|
|
||||||
def get_connection_info(module):
|
result = OpenNebulaModule.get_image_info(image)
|
||||||
|
result['changed'] = True
|
||||||
|
return result
|
||||||
|
|
||||||
url = module.params.get('api_url')
|
def delete_image(self, image):
|
||||||
username = module.params.get('api_username')
|
if not image:
|
||||||
password = module.params.get('api_password')
|
return {'changed': False}
|
||||||
|
|
||||||
if not url:
|
if image.RUNNING_VMS > 0:
|
||||||
url = os.environ.get('ONE_URL')
|
self.module.fail_json(msg="Cannot delete image. There are " + str(image.RUNNING_VMS) + " VMs using it.")
|
||||||
|
|
||||||
if not username:
|
if not self.module.check_mode:
|
||||||
username = os.environ.get('ONE_USERNAME')
|
self.one.image.delete(image.ID)
|
||||||
|
self.wait_for_delete(image.ID)
|
||||||
|
|
||||||
if not password:
|
return {'changed': True}
|
||||||
password = os.environ.get('ONE_PASSWORD')
|
|
||||||
|
|
||||||
if not (url and username and password):
|
|
||||||
module.fail_json(msg="One or more connection parameters (api_url, api_username, api_password) were not specified")
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
auth_params = namedtuple('auth', ('url', 'username', 'password'))
|
|
||||||
|
|
||||||
return auth_params(url=url, username=username, password=password)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
fields = {
|
ImageModule().run_module()
|
||||||
"api_url": {"required": False, "type": "str"},
|
|
||||||
"api_username": {"required": False, "type": "str"},
|
|
||||||
"api_password": {"required": False, "type": "str", "no_log": True},
|
|
||||||
"id": {"required": False, "type": "int"},
|
|
||||||
"name": {"required": False, "type": "str"},
|
|
||||||
"state": {
|
|
||||||
"default": "present",
|
|
||||||
"choices": ['present', 'absent', 'cloned', 'renamed'],
|
|
||||||
"type": "str"
|
|
||||||
},
|
|
||||||
"enabled": {"required": False, "type": "bool"},
|
|
||||||
"new_name": {"required": False, "type": "str"},
|
|
||||||
}
|
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=fields,
|
|
||||||
mutually_exclusive=[['id', 'name']],
|
|
||||||
supports_check_mode=True)
|
|
||||||
|
|
||||||
if not HAS_PYONE:
|
|
||||||
module.fail_json(msg='This module requires pyone to work!')
|
|
||||||
|
|
||||||
auth = get_connection_info(module)
|
|
||||||
params = module.params
|
|
||||||
id = params.get('id')
|
|
||||||
name = params.get('name')
|
|
||||||
state = params.get('state')
|
|
||||||
enabled = params.get('enabled')
|
|
||||||
new_name = params.get('new_name')
|
|
||||||
client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)
|
|
||||||
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
if not id and state == 'renamed':
|
|
||||||
module.fail_json(msg="Option 'id' is required when the state is 'renamed'")
|
|
||||||
|
|
||||||
image = get_image_instance(module, client, id, name)
|
|
||||||
if not image and state != 'absent':
|
|
||||||
if id:
|
|
||||||
module.fail_json(msg="There is no image with id=" + str(id))
|
|
||||||
else:
|
|
||||||
module.fail_json(msg="There is no image with name=" + name)
|
|
||||||
|
|
||||||
if state == 'absent':
|
|
||||||
result = delete_image(module, client, image)
|
|
||||||
else:
|
|
||||||
result = get_image_info(image)
|
|
||||||
changed = False
|
|
||||||
result['changed'] = False
|
|
||||||
|
|
||||||
if enabled is not None:
|
|
||||||
result = enable_image(module, client, image, enabled)
|
|
||||||
if state == "cloned":
|
|
||||||
result = clone_image(module, client, image, new_name)
|
|
||||||
elif state == "renamed":
|
|
||||||
result = rename_image(module, client, image, new_name)
|
|
||||||
|
|
||||||
changed = changed or result['changed']
|
|
||||||
result['changed'] = changed
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -17,29 +17,14 @@ description:
|
||||||
requirements:
|
requirements:
|
||||||
- pyone
|
- pyone
|
||||||
extends_documentation_fragment:
|
extends_documentation_fragment:
|
||||||
|
- community.general.opennebula
|
||||||
- community.general.attributes
|
- community.general.attributes
|
||||||
- community.general.attributes.info_module
|
- community.general.attributes.info_module
|
||||||
options:
|
options:
|
||||||
api_url:
|
|
||||||
description:
|
|
||||||
- URL of the OpenNebula RPC server.
|
|
||||||
- It is recommended to use HTTPS so that the username/password are not
|
|
||||||
- transferred over the network unencrypted.
|
|
||||||
- If not set then the value of the E(ONE_URL) environment variable is used.
|
|
||||||
type: str
|
|
||||||
api_username:
|
|
||||||
description:
|
|
||||||
- Name of the user to login into the OpenNebula RPC server. If not set
|
|
||||||
- then the value of the E(ONE_USERNAME) environment variable is used.
|
|
||||||
type: str
|
|
||||||
api_password:
|
|
||||||
description:
|
|
||||||
- Password of the user to login into OpenNebula RPC server. If not set
|
|
||||||
- then the value of the E(ONE_PASSWORD) environment variable is used.
|
|
||||||
type: str
|
|
||||||
ids:
|
ids:
|
||||||
description:
|
description:
|
||||||
- A list of images ids whose facts you want to gather.
|
- A list of images ids whose facts you want to gather.
|
||||||
|
- Module can use integers too.
|
||||||
aliases: ['id']
|
aliases: ['id']
|
||||||
type: list
|
type: list
|
||||||
elements: str
|
elements: str
|
||||||
|
@ -66,9 +51,16 @@ EXAMPLES = '''
|
||||||
msg: result
|
msg: result
|
||||||
|
|
||||||
- name: Gather facts about an image using ID
|
- name: Gather facts about an image using ID
|
||||||
|
community.general.one_image_info:
|
||||||
|
ids: 123
|
||||||
|
|
||||||
|
- name: Gather facts about an image using list of ID
|
||||||
community.general.one_image_info:
|
community.general.one_image_info:
|
||||||
ids:
|
ids:
|
||||||
- 123
|
- 123
|
||||||
|
- 456
|
||||||
|
- 789
|
||||||
|
- 0
|
||||||
|
|
||||||
- name: Gather facts about an image using the name
|
- name: Gather facts about an image using the name
|
||||||
community.general.one_image_info:
|
community.general.one_image_info:
|
||||||
|
@ -93,182 +85,285 @@ images:
|
||||||
returned: success
|
returned: success
|
||||||
contains:
|
contains:
|
||||||
id:
|
id:
|
||||||
description: image id
|
description: The image's id.
|
||||||
type: int
|
type: int
|
||||||
sample: 153
|
sample: 153
|
||||||
name:
|
name:
|
||||||
description: image name
|
description: The image's name.
|
||||||
type: str
|
type: str
|
||||||
sample: app1
|
sample: app1
|
||||||
group_id:
|
group_id:
|
||||||
description: image's group id
|
description: The image's group id
|
||||||
type: int
|
type: int
|
||||||
sample: 1
|
sample: 1
|
||||||
group_name:
|
group_name:
|
||||||
description: image's group name
|
description: The image's group name.
|
||||||
type: str
|
type: str
|
||||||
sample: one-users
|
sample: one-users
|
||||||
owner_id:
|
owner_id:
|
||||||
description: image's owner id
|
description: The image's owner id.
|
||||||
type: int
|
type: int
|
||||||
sample: 143
|
sample: 143
|
||||||
owner_name:
|
owner_name:
|
||||||
description: image's owner name
|
description: The image's owner name.
|
||||||
type: str
|
type: str
|
||||||
sample: ansible-test
|
sample: ansible-test
|
||||||
state:
|
state:
|
||||||
description: state of image instance
|
description: The image's state.
|
||||||
type: str
|
type: str
|
||||||
sample: READY
|
sample: READY
|
||||||
used:
|
used:
|
||||||
description: is image in use
|
description: The image's usage status.
|
||||||
type: bool
|
type: bool
|
||||||
sample: true
|
sample: true
|
||||||
running_vms:
|
running_vms:
|
||||||
description: count of running vms that use this image
|
description: The image's count of running vms that use this image.
|
||||||
type: int
|
type: int
|
||||||
sample: 7
|
sample: 7
|
||||||
|
permissions:
|
||||||
|
description: The image's permissions.
|
||||||
|
type: dict
|
||||||
|
version_added: 9.5.0
|
||||||
|
contains:
|
||||||
|
owner_u:
|
||||||
|
description: The image's owner USAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 1
|
||||||
|
owner_m:
|
||||||
|
description: The image's owner MANAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
owner_a:
|
||||||
|
description: The image's owner ADMIN permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
group_u:
|
||||||
|
description: The image's group USAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
group_m:
|
||||||
|
description: The image's group MANAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
group_a:
|
||||||
|
description: The image's group ADMIN permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
other_u:
|
||||||
|
description: The image's other users USAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
other_m:
|
||||||
|
description: The image's other users MANAGE permissions.
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
other_a:
|
||||||
|
description: The image's other users ADMIN permissions
|
||||||
|
type: str
|
||||||
|
sample: 0
|
||||||
|
sample:
|
||||||
|
owner_u: 1
|
||||||
|
owner_m: 0
|
||||||
|
owner_a: 0
|
||||||
|
group_u: 0
|
||||||
|
group_m: 0
|
||||||
|
group_a: 0
|
||||||
|
other_u: 0
|
||||||
|
other_m: 0
|
||||||
|
other_a: 0
|
||||||
|
type:
|
||||||
|
description: The image's type.
|
||||||
|
type: int
|
||||||
|
sample: 0
|
||||||
|
version_added: 9.5.0
|
||||||
|
disk_type:
|
||||||
|
description: The image's format type.
|
||||||
|
type: int
|
||||||
|
sample: 0
|
||||||
|
version_added: 9.5.0
|
||||||
|
persistent:
|
||||||
|
description: The image's persistence status (1 means true, 0 means false).
|
||||||
|
type: int
|
||||||
|
sample: 1
|
||||||
|
version_added: 9.5.0
|
||||||
|
source:
|
||||||
|
description: The image's source.
|
||||||
|
type: str
|
||||||
|
sample: /var/lib/one//datastores/100/somerandomstringxd
|
||||||
|
version_added: 9.5.0
|
||||||
|
path:
|
||||||
|
description: The image's filesystem path.
|
||||||
|
type: str
|
||||||
|
sample: /var/tmp/hello.qcow2
|
||||||
|
version_added: 9.5.0
|
||||||
|
fstype:
|
||||||
|
description: The image's filesystem type.
|
||||||
|
type: str
|
||||||
|
sample: ext4
|
||||||
|
version_added: 9.5.0
|
||||||
|
size:
|
||||||
|
description: The image's size in MegaBytes.
|
||||||
|
type: int
|
||||||
|
sample: 10000
|
||||||
|
version_added: 9.5.0
|
||||||
|
cloning_ops:
|
||||||
|
description: The image's cloning operations per second.
|
||||||
|
type: int
|
||||||
|
sample: 0
|
||||||
|
version_added: 9.5.0
|
||||||
|
cloning_id:
|
||||||
|
description: The image's cloning ID.
|
||||||
|
type: int
|
||||||
|
sample: -1
|
||||||
|
version_added: 9.5.0
|
||||||
|
target_snapshot:
|
||||||
|
description: The image's target snapshot.
|
||||||
|
type: int
|
||||||
|
sample: 1
|
||||||
|
version_added: 9.5.0
|
||||||
|
datastore_id:
|
||||||
|
description: The image's datastore ID.
|
||||||
|
type: int
|
||||||
|
sample: 100
|
||||||
|
version_added: 9.5.0
|
||||||
|
datastore:
|
||||||
|
description: The image's datastore name.
|
||||||
|
type: int
|
||||||
|
sample: image_datastore
|
||||||
|
version_added: 9.5.0
|
||||||
|
vms:
|
||||||
|
description: The image's list of vm ID's.
|
||||||
|
type: list
|
||||||
|
elements: int
|
||||||
|
version_added: 9.5.0
|
||||||
|
sample:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
clones:
|
||||||
|
description: The image's list of clones ID's.
|
||||||
|
type: list
|
||||||
|
elements: int
|
||||||
|
version_added: 9.5.0
|
||||||
|
sample:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
app_clones:
|
||||||
|
description: The image's list of app_clones ID's.
|
||||||
|
type: list
|
||||||
|
elements: int
|
||||||
|
version_added: 9.5.0
|
||||||
|
sample:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
snapshots:
|
||||||
|
description: The image's list of snapshots.
|
||||||
|
type: list
|
||||||
|
version_added: 9.5.0
|
||||||
|
sample:
|
||||||
|
- date: 123123
|
||||||
|
parent: 1
|
||||||
|
size: 10228
|
||||||
|
allow_orphans: 1
|
||||||
|
children: 0
|
||||||
|
active: 1
|
||||||
|
name: SampleName
|
||||||
'''
|
'''
|
||||||
|
|
||||||
try:
|
|
||||||
import pyone
|
|
||||||
HAS_PYONE = True
|
|
||||||
except ImportError:
|
|
||||||
HAS_PYONE = False
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_images(client):
|
|
||||||
pool = client.imagepool.info(-2, -1, -1, -1)
|
|
||||||
# Filter -2 means fetch all images user can Use
|
|
||||||
|
|
||||||
return pool
|
|
||||||
|
|
||||||
|
|
||||||
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
|
IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE', 'DELETE', 'USED_PERS', 'LOCKED_USED', 'LOCKED_USED_PERS']
|
||||||
|
|
||||||
|
|
||||||
def get_image_info(image):
|
class ImageInfoModule(OpenNebulaModule):
|
||||||
info = {
|
def __init__(self):
|
||||||
'id': image.ID,
|
argument_spec = dict(
|
||||||
'name': image.NAME,
|
ids=dict(type='list', aliases=['id'], elements='str', required=False),
|
||||||
'state': IMAGE_STATES[image.STATE],
|
name=dict(type='str', required=False),
|
||||||
'running_vms': image.RUNNING_VMS,
|
)
|
||||||
'used': bool(image.RUNNING_VMS),
|
mutually_exclusive = [
|
||||||
'user_name': image.UNAME,
|
['ids', 'name'],
|
||||||
'user_id': image.UID,
|
]
|
||||||
'group_name': image.GNAME,
|
|
||||||
'group_id': image.GID,
|
|
||||||
}
|
|
||||||
return info
|
|
||||||
|
|
||||||
|
OpenNebulaModule.__init__(self,
|
||||||
|
argument_spec,
|
||||||
|
supports_check_mode=True,
|
||||||
|
mutually_exclusive=mutually_exclusive)
|
||||||
|
|
||||||
def get_images_by_ids(module, client, ids):
|
def run(self, one, module, result):
|
||||||
images = []
|
params = module.params
|
||||||
pool = get_all_images(client)
|
ids = params.get('ids')
|
||||||
|
name = params.get('name')
|
||||||
|
|
||||||
for image in pool.IMAGE:
|
if ids:
|
||||||
if str(image.ID) in ids:
|
images = self.get_images_by_ids(ids)
|
||||||
images.append(image)
|
elif name:
|
||||||
ids.remove(str(image.ID))
|
images = self.get_images_by_name(name)
|
||||||
if len(ids) == 0:
|
else:
|
||||||
|
images = self.get_all_images().IMAGE
|
||||||
|
|
||||||
|
self.result = {
|
||||||
|
'images': [OpenNebulaModule.get_image_info(image) for image in images]
|
||||||
|
}
|
||||||
|
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def get_all_images(self):
|
||||||
|
pool = self.one.imagepool.info(-2, -1, -1, -1)
|
||||||
|
# Filter -2 means fetch all images user can Use
|
||||||
|
|
||||||
|
return pool
|
||||||
|
|
||||||
|
def get_images_by_ids(self, ids):
|
||||||
|
images = []
|
||||||
|
pool = self.get_all_images()
|
||||||
|
|
||||||
|
for image in pool.IMAGE:
|
||||||
|
if str(image.ID) in ids:
|
||||||
|
images.append(image)
|
||||||
|
ids.remove(str(image.ID))
|
||||||
|
if len(ids) == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
if len(ids) > 0:
|
||||||
|
self.module.fail_json(msg='There is no IMAGE(s) with id(s)=' + ', '.join('{id}'.format(id=str(image_id)) for image_id in ids))
|
||||||
|
|
||||||
|
return images
|
||||||
|
|
||||||
|
def get_images_by_name(self, name_pattern):
|
||||||
|
images = []
|
||||||
|
pattern = None
|
||||||
|
|
||||||
|
pool = self.get_all_images()
|
||||||
|
|
||||||
|
if name_pattern.startswith('~'):
|
||||||
|
import re
|
||||||
|
if name_pattern[1] == '*':
|
||||||
|
pattern = re.compile(name_pattern[2:], re.IGNORECASE)
|
||||||
|
else:
|
||||||
|
pattern = re.compile(name_pattern[1:])
|
||||||
|
|
||||||
|
for image in pool.IMAGE:
|
||||||
|
if pattern is not None:
|
||||||
|
if pattern.match(image.NAME):
|
||||||
|
images.append(image)
|
||||||
|
elif name_pattern == image.NAME:
|
||||||
|
images.append(image)
|
||||||
break
|
break
|
||||||
|
|
||||||
if len(ids) > 0:
|
# if the specific name is indicated
|
||||||
module.fail_json(msg='There is no IMAGE(s) with id(s)=' + ', '.join('{id}'.format(id=str(image_id)) for image_id in ids))
|
if pattern is None and len(images) == 0:
|
||||||
|
self.module.fail_json(msg="There is no IMAGE with name=" + name_pattern)
|
||||||
|
|
||||||
return images
|
return images
|
||||||
|
|
||||||
|
|
||||||
def get_images_by_name(module, client, name_pattern):
|
|
||||||
|
|
||||||
images = []
|
|
||||||
pattern = None
|
|
||||||
|
|
||||||
pool = get_all_images(client)
|
|
||||||
|
|
||||||
if name_pattern.startswith('~'):
|
|
||||||
import re
|
|
||||||
if name_pattern[1] == '*':
|
|
||||||
pattern = re.compile(name_pattern[2:], re.IGNORECASE)
|
|
||||||
else:
|
|
||||||
pattern = re.compile(name_pattern[1:])
|
|
||||||
|
|
||||||
for image in pool.IMAGE:
|
|
||||||
if pattern is not None:
|
|
||||||
if pattern.match(image.NAME):
|
|
||||||
images.append(image)
|
|
||||||
elif name_pattern == image.NAME:
|
|
||||||
images.append(image)
|
|
||||||
break
|
|
||||||
|
|
||||||
# if the specific name is indicated
|
|
||||||
if pattern is None and len(images) == 0:
|
|
||||||
module.fail_json(msg="There is no IMAGE with name=" + name_pattern)
|
|
||||||
|
|
||||||
return images
|
|
||||||
|
|
||||||
|
|
||||||
def get_connection_info(module):
|
|
||||||
|
|
||||||
url = module.params.get('api_url')
|
|
||||||
username = module.params.get('api_username')
|
|
||||||
password = module.params.get('api_password')
|
|
||||||
|
|
||||||
if not url:
|
|
||||||
url = os.environ.get('ONE_URL')
|
|
||||||
|
|
||||||
if not username:
|
|
||||||
username = os.environ.get('ONE_USERNAME')
|
|
||||||
|
|
||||||
if not password:
|
|
||||||
password = os.environ.get('ONE_PASSWORD')
|
|
||||||
|
|
||||||
if not (url and username and password):
|
|
||||||
module.fail_json(msg="One or more connection parameters (api_url, api_username, api_password) were not specified")
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
auth_params = namedtuple('auth', ('url', 'username', 'password'))
|
|
||||||
|
|
||||||
return auth_params(url=url, username=username, password=password)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
fields = {
|
ImageInfoModule().run_module()
|
||||||
"api_url": {"required": False, "type": "str"},
|
|
||||||
"api_username": {"required": False, "type": "str"},
|
|
||||||
"api_password": {"required": False, "type": "str", "no_log": True},
|
|
||||||
"ids": {"required": False, "aliases": ['id'], "type": "list", "elements": "str"},
|
|
||||||
"name": {"required": False, "type": "str"},
|
|
||||||
}
|
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=fields,
|
|
||||||
mutually_exclusive=[['ids', 'name']],
|
|
||||||
supports_check_mode=True)
|
|
||||||
|
|
||||||
if not HAS_PYONE:
|
|
||||||
module.fail_json(msg='This module requires pyone to work!')
|
|
||||||
|
|
||||||
auth = get_connection_info(module)
|
|
||||||
params = module.params
|
|
||||||
ids = params.get('ids')
|
|
||||||
name = params.get('name')
|
|
||||||
client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)
|
|
||||||
|
|
||||||
if ids:
|
|
||||||
images = get_images_by_ids(module, client, ids)
|
|
||||||
elif name:
|
|
||||||
images = get_images_by_name(module, client, name)
|
|
||||||
else:
|
|
||||||
images = get_all_images(client).IMAGE
|
|
||||||
|
|
||||||
result = {
|
|
||||||
'images': [get_image_info(image) for image in images],
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Copyright (c) Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
azp/generic/1
|
||||||
|
cloud/opennebula
|
||||||
|
disabled # FIXME - when this is fixed, also re-enable the generic tests in CI!
|
|
@ -0,0 +1,210 @@
|
||||||
|
---
|
||||||
|
####################################################################
|
||||||
|
# WARNING: These are designed specifically for Ansible tests #
|
||||||
|
# and should not be used as examples of how to write Ansible roles #
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
# Copyright (c) Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
# Checks for existence
|
||||||
|
- name: Make sure image is present by ID
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is present
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: Make sure image is present by ID
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
name: my_image
|
||||||
|
state: present
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is present
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
# Updating an image
|
||||||
|
- name: Clone image without name
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
state: cloned
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is cloned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Clone image with name
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
state: renamed
|
||||||
|
new_name: new_image
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is cloned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Disable image
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
enabled: false
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network is disabled
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Enable image
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
enabled: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network is enabled
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Make image persistent
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
persistent: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network is persistent
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Make image non-persistent
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
persistent: false
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network is non-persistent
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
# Testing idempotence using the same tasks
|
||||||
|
- name: Make image non-persistent
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
persistent: false
|
||||||
|
enabled: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network not changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
# Delete images
|
||||||
|
- name: Deleting non-existing image
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 228
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network not changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: Delete an existing image
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
state: absent
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image was deleted
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
# Trying to run with wrong arguments
|
||||||
|
- name: Try to use name and ID at the same time
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
name: name
|
||||||
|
register: result
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Assert that task failed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is failed
|
||||||
|
|
||||||
|
- name: Try to rename image without specifying new name
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
state: rename
|
||||||
|
register: result
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Assert that task failed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is failed
|
||||||
|
|
||||||
|
- name: Try to rename image without specifying new name
|
||||||
|
one_image:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
state: rename
|
||||||
|
register: result
|
||||||
|
ignore_errors: true
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Copyright (c) Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
azp/generic/1
|
||||||
|
cloud/opennebula
|
||||||
|
disabled # FIXME - when this is fixed, also re-enable the generic tests in CI!
|
|
@ -0,0 +1,192 @@
|
||||||
|
---
|
||||||
|
####################################################################
|
||||||
|
# WARNING: These are designed specifically for Ansible tests #
|
||||||
|
# and should not be used as examples of how to write Ansible roles #
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
# Copyright (c) Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
# Checks for existence
|
||||||
|
- name: Get info by ID
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is present
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: Get info by list of ID
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
ids:
|
||||||
|
- 2
|
||||||
|
- 2
|
||||||
|
- 8
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is present
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: Get info by list of ID
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
name: somename
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is present
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: Gather all info
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that images are present
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: Gather info by regex
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
name: '~my_image-[0-9].*'
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that images are present
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
- name: Gather info by regex and ignore upper/lower cases
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
name: '~*my_image-[0-9].*'
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that images are present
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is not changed
|
||||||
|
|
||||||
|
# Updating an image
|
||||||
|
- name: Clone image without name
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
state: cloned
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is cloned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Clone image with name
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
state: renamed
|
||||||
|
new_name: new_image
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that image is cloned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Disable image
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
enabled: false
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network is disabled
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Enable image
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
enabled: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network is enabled
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Make image persistent
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
persistent: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network is persistent
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
- name: Make image non-persistent
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
persistent: false
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that network is non-persistent
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
|
||||||
|
# Testing errors
|
||||||
|
- name: Try to use name and ID a the same time
|
||||||
|
one_image_info:
|
||||||
|
api_url: "{{ opennebula_url }}"
|
||||||
|
api_username: "{{ opennebula_username }}"
|
||||||
|
api_password: "{{ opennebula_password }}"
|
||||||
|
id: 0
|
||||||
|
name: somename
|
||||||
|
register: result
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Assert that network not changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is failed
|
Loading…
Reference in New Issue