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:
Ondra Machacek 2017-03-02 14:32:36 +01:00 committed by Ryan Brown
parent ef79932d16
commit 7d397e7d3e
2 changed files with 112 additions and 42 deletions

View file

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

View file

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