diff --git a/roles/os_images/README.md b/roles/os_images/README.md index fd9a008..c8c3879 100644 --- a/roles/os_images/README.md +++ b/roles/os_images/README.md @@ -27,8 +27,8 @@ on the build host. `os_images_cache`: a path to a directory in which to cache build artefacts. It defaults to `~/disk_images` -`NOTE`: new images will NOT be built, even if changes are made in config, if an image is already cached. -Use `force_rebuild` flag in order to apply new config changes. +`NOTE`: new images will NOT be built, even if changes are made in config, if an image +is already cached. Use `force_rebuild` flag in order to apply new config changes. `os_images_auth_type`: OpenStack authentication endpoint and credentials. Defaults to `password`. @@ -57,25 +57,34 @@ mutually exclusive where each contain: This is a dict of the form of `KEY: VALUE`. * `packages`: (optional) list of packages to install in the image. * `size`: (optional) size to make the image filesystem. +* `architecture`: (optional) image CPU architecture to pass to diskimage-builder `-a`. + If unset, default to the diskimage-builder default architecture: `x86_64`, and upload + Glance images with the `cpu_arch: "x86_64"` image property. If architecture is set + to `arm64` or `aarch64`, Glance images with the `cpu_arch: "aarch64"` image property + When setting to other values, consider also setting `properties.cpu_arch` to a + corresponding value. * `properties`: (optional) dict of properties to set on the glance image. Common image properties are available [here](https://docs.openstack.org/glance/latest/user/common-image-properties.html). * `type`: (optional) image type. Default in DIB is qcow2. Image formats are available [here](https://docs.openstack.org/glance/latest/user/formats.html). -* `force_rebuild`: (optional) boolean flag indicating whether or not the image should always - be built (even if an existing image that name has been built before). The images on glance - will be replaced if `os_images_upload` is set to `True`. This defaults to +* `force_rebuild`: (optional) boolean flag indicating whether or not the image should + always be built (even if an existing image that name has been built before). The images + in glance will be replaced if `os_images_upload` is set to `True`. This defaults to `os_images_force_rebuild`if left unset. -* `is_public`: (optional) (deprecated - use `visibility`) whether the image should be set as visible to all - projects or kept private. Note that if both `is_public` and `visibility` are provided, `is_public` will - be preferred. +* `is_public`: (optional) (deprecated - use `visibility`) whether the image should be set + as visible to all projects or kept private. Note that if both `is_public` and `visibility` + are provided, `is_public` will be preferred. * `visibility`: (optional) Allowed values are 'public', 'private', 'shared' -or 'community'. Default is 'public' + or 'community'. Default is 'public' * `owner`: (optional) ID of the project that should own the uploaded image. `os_images_common`: A set of elements to include in every image listed. Defaults to `cloud-init enable-serial-console stable-interface-names`. +`os_images_common_properties`: A dict of Glance image properties to set on all images. +Defaults to an empty dict, and is overridden by `os_images_list.*.properties`. + `os_images_dib_pkg_name`: Optionally customise the name parameter passed to the ansible.builtin.pip module when installing diskimage-builder. This can be used to install diskimage-builder from version control. @@ -96,27 +105,50 @@ following parameters: `os_images_upload`: Whether to upload built images to Glance. Defaults to `True`. -`os_images_force_rebuild`: Whether or not to force a rebuild of the DIB image. The images on Glance -will be replaced with the newly built image if `os_images_upload` is set to `True`. Defaults to -`False`. +`os_images_force_rebuild`: Whether or not to force a rebuild of the DIB image. +The images on Glance will be replaced with the newly built image if `os_images_upload` +is set to `True`. Defaults to `False`. -`os_images_public`: (Deprecated - use `os_images_visibility`) Whether uploaded images are public. Defaults to `True` - note this requires admin permissions. +`os_images_public`: (Deprecated - use `os_images_visibility`) Whether uploaded +images are public. Defaults to `True` - note this requires admin permissions. -`os_images_visibility`: The visibility of images uploaded. One of `community`,`public` or `private`. If unset, defaults to `os_images_public` (requires admin permissions for anything other than `private`) +`os_images_visibility`: The visibility of images uploaded. One of `community`, +`public` or `private`. If unset, defaults to `os_images_public` (requires admin +permissions for anything other than `private`) -`os_images_venv`: Path to virtualenv in which to install python dependencies to upload images. +`os_images_venv`: Path to virtualenv in which to install python dependencies to +upload images. `os_images_dib_venv`: Path to virtualenv in which to install DIB to build images. `os_images_promote`: Whether or not to promote new images. Defaults to `False`. -`os_images_retire`: Whether or not to retire old images. Defaults to `os_image_promote`. May be necessary to set separately if you are promoting a new candidate image for which there is no existing one to retire, for example. +`os_images_retire`: Whether or not to retire old images. Defaults to `os_image_promote`. +May be necessary to set separately if you are promoting a new candidate image for which +there is no existing one to retire, for example. `os_images_build`: Whether or not to build the images. -`os_images_name_suffix`: Image suffix which would be removed during image promotion, for exmple: -rc, -dev, -test etc. Mandatory for promotion functionality. Empty by default. +`os_images_name_suffix`: Image suffix which would be removed during image promotion, for +exmple: -rc, -dev, -test etc. Mandatory for promotion functionality. Empty by default. + +`os_images_hide`: Whether or not to hide the images in Glance list. Hiding images is +available as an option in image retirement/promotion process. Defaults to `False`. + +Changing platform architecture in os_images +------------------------------------------- + +The target CPU architecture for each image defined in `os_images_list` may be set to any +architecture supported by diskimage-builder with the `architecture` parameter. + +If it is unset, an image with the default diskimage-builder architecture (`x86_64`) will +be built and optionally uploaded to Glance, with the Glance image property `cpu_arch` set +to `x86_64`. If it is set to `arm64` or `aarch64`, images will be uploaded to Glance with +the Glance image property `cpu_arch` set to `aarch64`. -`os_images_hide`: Whether or not to hide the images in Glance list. Hiding images is available as an option in image retirement/promotion process. Defaults to `False`. +If setting to a different `architecture`, consider also setting `properties.cpu_arch` to an +architecture +[supported by Glance](https://docs.openstack.org/glance/latest/admin/useful-image-properties.html#image-property-keys-and-values). Dependencies ------------ diff --git a/roles/os_images/defaults/main.yml b/roles/os_images/defaults/main.yml index 02c4033..41e5e54 100644 --- a/roles/os_images/defaults/main.yml +++ b/roles/os_images/defaults/main.yml @@ -45,8 +45,7 @@ os_images_common: cloud-init enable-serial-console stable-interface-names # type: qcow2 os_images_list: [] # Common properties to apply to all glance images. -os_images_common_properties: - cpu_arch: x86_64 +os_images_common_properties: {} # OpenStack authentication type: passed to the os_image Ansible module os_images_auth_type: password diff --git a/roles/os_images/tasks/images.yml b/roles/os_images/tasks/images.yml index d853ec7..2536d59 100644 --- a/roles/os_images/tasks/images.yml +++ b/roles/os_images/tasks/images.yml @@ -83,8 +83,9 @@ - name: Generate diskimage-builder images vars: dib_args: >- - {% if item.size is defined %}--image-size {{ item.size }}{% endif %} {% if item.type is defined %}-t {{ item.type }}{% endif %} {% if item.packages | default - %}-p {{ item.packages | join(',') }}{% endif %} {{ os_images_common }} {{ item.elements | join(' ') }} -o {{ item.name }} + {% if item.size is defined %}--image-size {{ item.size }}{% endif %} {% if item.type is defined %}-t {{ item.type }}{% endif %} + {% if item.packages | default %}-p {{ item.packages | join(',') }}{% endif %} {{ os_images_common }} {{ item.elements | join(' ') }} + -o {{ item.name }} {% if item.architecture is defined %}-a {{ item.architecture }}{% endif %} ansible.builtin.shell: . {{ os_images_dib_venv }}/bin/activate && disk-image-create {{ dib_args }} > {{ item.name }}.stdout 2> {{ item.name }}.stderr args: chdir: "{{ os_images_cache }}/{{ item.name }}" diff --git a/roles/os_images/tasks/prereqs.yml b/roles/os_images/tasks/prereqs.yml index 2301614..4d75345 100644 --- a/roles/os_images/tasks/prereqs.yml +++ b/roles/os_images/tasks/prereqs.yml @@ -29,3 +29,15 @@ state: directory mode: "0755" become: true + +- name: Run multiarch/qemu-user-static image to support cross-arch build + ansible.builtin.command: + # We already have a precheck for presence of docker executable so use it here + cmd: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + # Using --reset means that we always change something + changed_when: true + become: true + when: os_images_list | + selectattr("architecture", "defined") | + rejectattr("architecture", "equalto", ansible_facts.architecture) | + length > 0 diff --git a/roles/os_images/tasks/upload.yml b/roles/os_images/tasks/upload.yml index 8653098..4cbef18 100644 --- a/roles/os_images/tasks/upload.yml +++ b/roles/os_images/tasks/upload.yml @@ -110,10 +110,22 @@ container_format: bare disk_format: "{{ item.0.type | default('qcow2') }}" filename: "{{ os_images_cache }}/{{ item.0.name }}/{{ item.0.name }}.{{ item.0.type | default('qcow2') }}" - properties: "{{ os_images_common_properties | combine(item.0.properties | default({})) or omit }}" + properties: "{{ os_images_common_properties | combine(cpu_arch_properties) | combine(item.0.properties | default({})) or omit }}" kernel: "{{ item.1.id if is_baremetal else omit }}" ramdisk: "{{ item.2.id if is_baremetal else omit }}" vars: + # NOTE(m-anson): When architecture isn't defined for an + # image, assume that we should set cpu_arch: x86_64 as + # this is the diskimage-builder default. If an architecture + # of arm64 or aarch64 is defined, set cpu_arch: aarch64. In + # all other cases, leave it up to the operator to set + # properties.cpu_arch. + cpu_arch_properties: >- + {{ + {"cpu_arch": "aarch64"} + if (item.0.architecture is defined and item.0.architecture in ["arm64", "aarch64"]) + else ({"cpu_arch": "x86_64"} if item.0.architecture is not defined else {}) + }} is_baremetal: "{{ item.0.elements is defined and 'baremetal' in item.0.elements }}" visibility: "{{ item.0.visibility | default(item.0.is_public | ternary('public', 'private') if item.0.is_public is defined else os_images_visibility) }}" with_together: