cloud: ovirt: Add download image support to disks module (#22101)
This PR fixes: https://github.com/ansible/ansible/issues/22088
This commit is contained in:
parent
ef79932d16
commit
7d397e7d3e
2 changed files with 112 additions and 42 deletions
|
@ -135,7 +135,7 @@ def create_connection(auth):
|
|||
:return: Python SDK connection
|
||||
"""
|
||||
|
||||
connection = sdk.Connection(
|
||||
return sdk.Connection(
|
||||
url=auth.get('url'),
|
||||
username=auth.get('username'),
|
||||
password=auth.get('password'),
|
||||
|
@ -144,15 +144,6 @@ def create_connection(auth):
|
|||
token=auth.get('token', None),
|
||||
kerberos=auth.get('kerberos', None),
|
||||
)
|
||||
api_version = LooseVersion(engine_version(connection))
|
||||
python_sdk_version = LooseVersion(sdk_version.VERSION)
|
||||
if python_sdk_version < api_version:
|
||||
raise Exception(
|
||||
"Your SDK version is lower than engine version, please use same "
|
||||
"version of the SDK as engine, or unexpected errors may appear."
|
||||
)
|
||||
|
||||
return connection
|
||||
|
||||
|
||||
def convert_to_bytes(param):
|
||||
|
|
|
@ -50,7 +50,15 @@ options:
|
|||
- "Should the Virtual Machine disk be present/absent/attached/detached."
|
||||
choices: ['present', 'absent', 'attached', 'detached']
|
||||
default: 'present'
|
||||
image_path:
|
||||
download_image_path:
|
||||
description:
|
||||
- "Path on a file system where disk should be downloaded."
|
||||
- "Note that you must have an valid oVirt engine CA in your system trust store
|
||||
or you must provide it in C(ca_file) parameter."
|
||||
- "Note that the disk is not downloaded when the file already exists,
|
||||
but you can forcibly download the disk when using C(force) I (true)."
|
||||
version_added: "2.3"
|
||||
upload_image_path:
|
||||
description:
|
||||
- "Path to disk image, which should be uploaded."
|
||||
- "Note that currently we support only compability version 0.10 of the qcow disk."
|
||||
|
@ -159,6 +167,12 @@ EXAMPLES = '''
|
|||
format: cow
|
||||
image_path: /path/to/mydisk.qcow2
|
||||
storage_domain: data
|
||||
|
||||
# Download disk to local file system:
|
||||
# Since Ansible 2.3
|
||||
- ovirt_disks:
|
||||
id: 7de90f31-222c-436c-a1ca-7e655bd5b60c
|
||||
download_image_path: /home/user/mydisk.qcow2
|
||||
'''
|
||||
|
||||
|
||||
|
@ -207,6 +221,7 @@ from ansible.module_utils.ovirt import (
|
|||
convert_to_bytes,
|
||||
equal,
|
||||
follow_link,
|
||||
get_id_by_name,
|
||||
ovirt_full_argument_spec,
|
||||
search_by_name,
|
||||
wait,
|
||||
|
@ -225,14 +240,14 @@ def _search_by_lun(disks_service, lun_id):
|
|||
return res[0] if res else None
|
||||
|
||||
|
||||
def upload_disk_image(connection, module):
|
||||
size = os.path.getsize(module.params['image_path'])
|
||||
def transfer(connection, module, direction, transfer_func):
|
||||
transfers_service = connection.system_service().image_transfers_service()
|
||||
transfer = transfers_service.add(
|
||||
otypes.ImageTransfer(
|
||||
image=otypes.Image(
|
||||
id=module.params['id'],
|
||||
)
|
||||
),
|
||||
direction=direction,
|
||||
)
|
||||
)
|
||||
transfer_service = transfers_service.image_transfer_service(transfer.id)
|
||||
|
@ -244,11 +259,6 @@ def upload_disk_image(connection, module):
|
|||
time.sleep(module.params['poll_interval'])
|
||||
transfer = transfer_service.get()
|
||||
|
||||
# Set needed headers for uploading:
|
||||
upload_headers = {
|
||||
'Authorization': transfer.signed_ticket,
|
||||
}
|
||||
|
||||
proxy_url = urlparse(transfer.proxy_url)
|
||||
context = ssl.create_default_context()
|
||||
auth = module.params['auth']
|
||||
|
@ -264,22 +274,13 @@ def upload_disk_image(connection, module):
|
|||
context=context,
|
||||
)
|
||||
|
||||
with open(module.params['image_path'], "rb") as disk:
|
||||
chunk_size = 1024 * 1024 * 8
|
||||
pos = 0
|
||||
while pos < size:
|
||||
transfer_service.extend()
|
||||
upload_headers['Content-Range'] = "bytes %d-%d/%d" % (pos, min(pos + chunk_size, size) - 1, size)
|
||||
proxy_connection.request(
|
||||
'PUT',
|
||||
proxy_url.path,
|
||||
disk.read(chunk_size),
|
||||
headers=upload_headers,
|
||||
)
|
||||
r = proxy_connection.getresponse()
|
||||
if r.status >= 400:
|
||||
raise Exception("Failed to upload disk image.")
|
||||
pos += chunk_size
|
||||
transfer_func(
|
||||
transfer_service,
|
||||
proxy_connection,
|
||||
proxy_url,
|
||||
transfer.signed_ticket
|
||||
)
|
||||
return True
|
||||
finally:
|
||||
transfer_service.finalize()
|
||||
while transfer.phase in [
|
||||
|
@ -306,14 +307,78 @@ def upload_disk_image(connection, module):
|
|||
timeout=module.params['timeout'],
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def download_disk_image(connection, module):
|
||||
def _transfer(transfer_service, proxy_connection, proxy_url, transfer_ticket):
|
||||
disks_service = connection.system_service().disks_service()
|
||||
disk = disks_service.disk_service(module.params['id']).get()
|
||||
size = disk.provisioned_size
|
||||
transfer_headers = {
|
||||
'Authorization': transfer_ticket,
|
||||
}
|
||||
with open(module.params['download_image_path'], "wb") as mydisk:
|
||||
pos = 0
|
||||
MiB_per_request = 8
|
||||
chunk_size = 1024 * 1024 * MiB_per_request
|
||||
while pos < size:
|
||||
transfer_service.extend()
|
||||
transfer_headers['Range'] = 'bytes=%d-%d' % (pos, min(size, pos + chunk_size) - 1)
|
||||
proxy_connection.request(
|
||||
'GET',
|
||||
proxy_url.path,
|
||||
headers=transfer_headers,
|
||||
)
|
||||
r = proxy_connection.getresponse()
|
||||
if r.status >= 300:
|
||||
raise Exception("Error: %s" % r.read())
|
||||
|
||||
mydisk.write(r.read())
|
||||
pos += chunk_size
|
||||
return transfer(
|
||||
connection,
|
||||
module,
|
||||
otypes.ImageTransferDirection.DOWNLOAD,
|
||||
transfer_func=_transfer,
|
||||
)
|
||||
|
||||
|
||||
def upload_disk_image(connection, module):
|
||||
def _transfer(transfer_service, proxy_connection, proxy_url, transfer_ticket):
|
||||
path = module.params['upload_image_path']
|
||||
transfer_headers = {
|
||||
'Authorization': transfer_ticket,
|
||||
}
|
||||
with open(path, "rb") as disk:
|
||||
pos = 0
|
||||
MiB_per_request = 8
|
||||
size = os.path.getsize(path)
|
||||
chunk_size = 1024 * 1024 * MiB_per_request
|
||||
while pos < size:
|
||||
transfer_service.extend()
|
||||
transfer_headers['Content-Range'] = "bytes %d-%d/%d" % (pos, min(pos + chunk_size, size) - 1, size)
|
||||
proxy_connection.request(
|
||||
'PUT',
|
||||
proxy_url.path,
|
||||
disk.read(chunk_size),
|
||||
headers=transfer_headers,
|
||||
)
|
||||
r = proxy_connection.getresponse()
|
||||
if r.status >= 400:
|
||||
raise Exception("Failed to upload disk image.")
|
||||
pos += chunk_size
|
||||
return transfer(
|
||||
connection,
|
||||
module,
|
||||
otypes.ImageTransferDirection.UPLOAD,
|
||||
transfer_func=_transfer,
|
||||
)
|
||||
|
||||
|
||||
class DisksModule(BaseModule):
|
||||
|
||||
def build_entity(self):
|
||||
logical_unit = self._module.params.get('logical_unit')
|
||||
return otypes.Disk(
|
||||
disk = otypes.Disk(
|
||||
id=self._module.params.get('id'),
|
||||
name=self._module.params.get('name'),
|
||||
description=self._module.params.get('description'),
|
||||
|
@ -346,6 +411,12 @@ class DisksModule(BaseModule):
|
|||
],
|
||||
) if logical_unit else None,
|
||||
)
|
||||
if hasattr(disk, 'initial_size'):
|
||||
disk.initial_size = convert_to_bytes(
|
||||
self._module.params.get('size')
|
||||
)
|
||||
|
||||
return disk
|
||||
|
||||
def update_storage_domains(self, disk_id):
|
||||
changed = False
|
||||
|
@ -359,14 +430,14 @@ class DisksModule(BaseModule):
|
|||
|
||||
# Initiate move:
|
||||
if self._module.params['storage_domain']:
|
||||
new_disk_storage = search_by_name(sds_service, self._module.params['storage_domain'])
|
||||
new_disk_storage_id = get_id_by_name(sds_service, self._module.params['storage_domain'])
|
||||
changed = self.action(
|
||||
action='move',
|
||||
entity=disk,
|
||||
action_condition=lambda d: new_disk_storage.id != d.storage_domains[0].id,
|
||||
action_condition=lambda d: new_disk_storage_id != d.storage_domains[0].id,
|
||||
wait_condition=lambda d: d.status == otypes.DiskStatus.OK,
|
||||
storage_domain=otypes.StorageDomain(
|
||||
id=new_disk_storage.id,
|
||||
id=new_disk_storage_id,
|
||||
),
|
||||
post_action=lambda _: time.sleep(self._module.params['poll_interval']),
|
||||
)['changed']
|
||||
|
@ -435,7 +506,8 @@ def main():
|
|||
bootable=dict(default=None, type='bool'),
|
||||
shareable=dict(default=None, type='bool'),
|
||||
logical_unit=dict(default=None, type='dict'),
|
||||
image_path=dict(default=None),
|
||||
download_image_path=dict(default=None),
|
||||
upload_image_path=dict(default=None, aliases=['image_path']),
|
||||
force=dict(default=False, type='bool'),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
|
@ -475,9 +547,16 @@ def main():
|
|||
module.params['id'] = ret['id'] if disk is None else disk.id
|
||||
|
||||
# Upload disk image in case it's new disk or force parameter is passed:
|
||||
if module.params['image_path'] and (is_new_disk or module.params['force']):
|
||||
if module.params['upload_image_path'] and (is_new_disk or module.params['force']):
|
||||
uploaded = upload_disk_image(connection, module)
|
||||
ret['changed'] = ret['changed'] or uploaded
|
||||
# Download disk image in case it's file don't exist or force parameter is passed:
|
||||
if (
|
||||
module.params['download_image_path']
|
||||
and (not os.path.isfile(module.params['download_image_path']) or module.params['force'])
|
||||
):
|
||||
downloaded = download_disk_image(connection, module)
|
||||
ret['changed'] = ret['changed'] or downloaded
|
||||
elif state == 'absent':
|
||||
ret = disks_module.remove()
|
||||
|
||||
|
|
Loading…
Reference in a new issue