{"id":908,"sha1":"e2aa6c74b83b04133c8c32253ead626821a24d21","playbook":{"id":4,"items":{"plays":107,"tasks":2438,"results":2413,"hosts":13,"files":511,"records":0},"arguments":{"version":null,"verbosity":0,"private_key_file":null,"remote_user":null,"connection":"openstack.osa.ssh","timeout":null,"ssh_common_args":null,"sftp_extra_args":null,"scp_extra_args":null,"ssh_extra_args":null,"ask_pass":false,"connection_password_file":null,"force_handlers":true,"flush_cache":false,"become":false,"become_method":"sudo","become_user":null,"become_ask_pass":false,"become_password_file":null,"tags":["all"],"skip_tags":[],"check":false,"diff":false,"inventory":["/home/zuul/src/opendev.org/openstack/openstack-ansible/inventory/dynamic_inventory.py","/home/zuul/src/opendev.org/openstack/openstack-ansible/inventory/inventory.ini","/etc/openstack_deploy/inventory.ini"],"listhosts":false,"subset":null,"extra_vars":"Not saved by ARA as configured by 'ignored_arguments'","vault_ids":[],"ask_vault_pass":false,"vault_password_files":[],"forks":4,"module_path":null,"syntax":false,"listtasks":false,"listtags":false,"step":false,"start_at_task":null,"args":["setup-openstack.yml"]},"labels":[{"id":1,"name":"check:False"},{"id":2,"name":"tags:all"}],"started":"2025-12-14T10:21:40.790759Z","ended":"2025-12-14T11:05:36.775743Z","duration":"00:43:55.984984","name":null,"ansible_version":"2.18.6","client_version":"1.7.4","python_version":"3.13.5","server_version":"1.7.4","status":"completed","path":"/home/zuul/src/opendev.org/openstack/openstack-ansible/playbooks/setup-openstack.yml","controller":"aio1.openstack.local","user":"root"},"content":"---\n# Copyright 2023, Cleura AB.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n- name: Ensuring image has required properties set\n  ansible.builtin.assert:\n    quiet: true\n    that:\n      - \"'url' in image or 'filename' in image\"\n      - \"'name' in image\"\n  loop: \"{{ images }}\"\n  loop_control:\n    loop_var: image\n\n- name: Downloading image from remote url\n  vars:\n    images_for_download: \"{{ images | selectattr('url', 'defined') }}\"\n  when: images_for_download\n  block:\n    - name: Create temporary dir for the image download\n      ansible.builtin.tempfile:\n        state: directory\n        suffix: osa-images\n      register: tempdir\n      when: \"'image_download_path' not in openstack_resources_image\"\n\n    - name: Download image artifact file\n      vars:\n        loop_label:\n          name: \"{{ image['name'] }}\"\n          url: \"{{ image['url'] }}\"\n        download_checksum: \"{{ ('checksum_compressed' in image) | ternary(image.get('checksum_compressed', ''), image.get('checksum', '')) }}\"\n      ansible.builtin.get_url:\n        url: \"{{ image['url'] }}\"\n        dest: \"{{ openstack_resources_image['image_download_path'] | default(tempdir['path']) }}/{{ image['url'] | basename }}\"\n        mode: \"0644\"\n        checksum: \"{{ (download_checksum | bool) | ternary('md5:' ~ download_checksum, omit) }}\"\n      loop: \"{{ images_for_download }}\"\n      loop_control:\n        loop_var: image\n        label: \"{{ loop_label | to_json }}\"\n      async: \"{{ openstack_resources_image['image_async_timeout'] | default(600) }}\"\n      poll: 0\n      register: get_image_results\n\n    - name: Register get_image watch variable\n      ansible.builtin.set_fact:\n        fetch_status: []\n\n    - name: Register get_image watch variable\n      when: item.changed # noqa: no-handler\n      ansible.builtin.set_fact:\n        fetch_status: \"{{ fetch_status + [item.ansible_job_id] }}\"\n      loop: \"{{ get_image_results.results }}\"\n      loop_control:\n        label: \"{{ item['image']['name'] }}\"\n\n    # Try to sync an async tasks\n    - name: Waiting for image download to complete\n      ansible.builtin.async_status:\n        jid: \"{{ item }}\"\n      register: job_result\n      until: job_result.finished\n      retries: \"{{ openstack_resources_image['image_async_retries'] | default(200) }}\"\n      delay: \"{{ openstack_resources_image['image_async_delay'] | default(10) }}\"\n      loop: \"{{ fetch_status }}\"\n\n- name: Decompress compressed images\n  block:\n    - name: Install required packages for decompress images\n      vars:\n        _decompress_package_mapping:\n          gz:\n            debian: gzip\n            redhat: gzip\n          xz:\n            debian: xz-utils\n            redhat: xz\n      ansible.builtin.package:\n        name: \"{{ _decompress_package_mapping[item][ansible_facts['os_family'] | lower] }}\"\n        state: present\n      loop: \"{{ images | selectattr('compressed_format', 'defined') | map(attribute='compressed_format') | unique }}\"\n\n    - name: Decompress image file\n      vars:\n        _image_path: >-\n          {{ image['filename'] | default(openstack_resources_image['image_download_path'] | default(tempdir.path) ~ '/' ~ image['url'] | basename) }}\n        _decompress_format_binary:\n          gz: gunzip\n          xz:\n            - xz\n            - -d\n            - --memlimit=500MiB\n        _decompress_args:\n          - \"{{ _decompress_format_binary[image['compressed_format']] }}\"\n          - \"{{ _image_path }}\"\n      ansible.builtin.command:\n        argv: \"{{ _decompress_args | flatten }}\"\n        creates: \"{{ _image_path | split('.' ~ image['compressed_format']) | first }}\"\n      loop: \"{{ images | selectattr('compressed_format', 'defined') }}\"\n      loop_control:\n        loop_var: image\n\n- name: \"Uploading images\"\n  vars:\n    loop_label:\n      name: \"{{ image['name'] }}\"\n      state: \"{{ image['state'] | default('present') }}\"\n    _image_path: >-\n      {{ image['filename'] | default(openstack_resources_image['image_download_path'] | default(tempdir.path) ~ '/' ~ image['url'] | basename) }}\n  openstack.cloud.image:\n    checksum: \"{{ image['checksum'] | default(omit) }}\"\n    cloud: \"{{ openstack_resources_cloud_name }}\"\n    interface: \"{{ openstack_resources_interface }}\"\n    container_format: \"{{ image['container_format'] | default('bare') }}\"\n    disk_format: \"{{ image['disk_format'] | default(omit) }}\"\n    filename: \"{{ ('compressed_format' in image) | ternary(_image_path | split('.' ~ image.get('compressed_format', '')) | first, _image_path) }}\"\n    is_protected: \"{{ image['is_protected'] | default(omit) }}\"\n    kernel: \"{{ image['kernel'] | default(omit) }}\"\n    name: \"{{ image['name'] }}\"\n    owner: \"{{ image['owner'] | default(omit) }}\"\n    owner_domain: \"{{ image['owner_domain'] | default(omit) }}\"\n    min_disk: \"{{ image['min_disk'] | default(omit) }}\"\n    min_ram: \"{{ image['min_ram'] | default(omit) }}\"\n    properties: \"{{ image['properties'] | default(omit) }}\"\n    ramdisk: \"{{ image['ramdisk'] | default(omit) }}\"\n    state: \"{{ image['state'] | default('present') }}\"\n    tags: \"{{ image['tags'] | default(omit) }}\"\n    timeout: \"{{ openstack_resources_image['image_async_timeout'] | default(600) }}\"\n    visibility: \"{{ image['visibility'] | default('private') }}\"\n  loop: \"{{ images }}\"\n  loop_control:\n    loop_var: image\n    label: \"{{ loop_label | to_json }}\"\n  async: \"{{ openstack_resources_image['image_async_timeout'] | default(600) }}\"\n  poll: 0\n  register: upload_results\n\n- name: Register watch variable\n  ansible.builtin.set_fact:\n    upload_status: []\n\n- name: Register watch variable\n  when: item.changed # noqa: no-handler\n  ansible.builtin.set_fact:\n    upload_status: \"{{ upload_status + [item.ansible_job_id] }}\"\n  loop: \"{{ upload_results.results }}\"\n  loop_control:\n    label: \"{{ item['image']['name'] }}\"\n\n# Try to sync an async tasks\n- name: Waiting for upload to complete\n  ansible.builtin.async_status:\n    jid: \"{{ item }}\"\n  register: job_result\n  until: job_result.finished\n  retries: \"{{ openstack_resources_image['image_async_retries'] | default(200) }}\"\n  delay: \"{{ openstack_resources_image['image_async_delay'] | default(10) }}\"\n  loop: \"{{ upload_status }}\"\n\n- name: Removing tmpdir\n  ansible.builtin.file:\n    path: \"{{ tempdir.path }}\"\n    state: absent\n","created":"2025-12-14T11:00:39.418556Z","updated":"2025-12-14T11:00:39.418573Z","path":"/etc/ansible/ansible_collections/openstack/osa/roles/openstack_resources/tasks/image_upload.yml"}