-
Notifications
You must be signed in to change notification settings - Fork 9
Start writing user documentation for Image finding. #326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e40df25
Start writing user documentation for Image finding.
garloff a47a6af
Wording improvements
garloff 5508dcf
Tiny code improvements.
garloff 819538b
Comment out link to not yet existing standard version.
garloff b634819
Try to avoid matching double space in tables.
garloff 72d8451
Add to sidebar. (1st attempt.)
garloff 2961b4f
Link index file explicitly.
garloff 62fdcad
Merge branch 'main' into feat/add-iaas-image-selection
garloff 5c703f3
Merge branch 'main' into feat/add-iaas-image-selection
garloff 520d7fd
opentofu/HCL, first part.
garloff 404ffc0
Revert "Try to avoid matching double space in tables."
garloff f65f027
OK, make opentofu work with external helper.
garloff cb227fd
Add ansible approach.
garloff 517337b
Ansible tested now.
garloff ee5eb99
Break long line in shell example.
garloff a867890
Add link to second terraform bits. Fix one typo.
garloff 26c0035
Merge branch 'main' into feat/add-iaas-image-selection
garloff 2d7ea94
Merge branch 'main' into feat/add-iaas-image-selection
garloff 8ba58a4
Update usage output in find_img.py
garloff 729ffd3
Add screenshots from horizon and skyline.
garloff File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [flake8] | ||
| ignore = E501 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| #!/usr/bin/env python3 | ||
| # | ||
| # find_img.py | ||
| # | ||
| # Searches for an image with distribution and version and purpose | ||
| # | ||
| # (c) Kurt Garloff <s7n@garloff.de>, 7/2025 | ||
| # SPDX-License-Identifier: MIT | ||
| "This module finds the a distribution image with a given purpose" | ||
|
|
||
| import os | ||
| import sys | ||
| import openstack | ||
| # import logging | ||
|
|
||
|
|
||
| def warn(log, msg): | ||
| "warn output" | ||
| if log: | ||
| log.warn(msg) | ||
| else: | ||
| print(f"WARN: {msg}", file=sys.stderr) | ||
|
|
||
|
|
||
| def debug(log, msg): | ||
| "debug output" | ||
| if log: | ||
| log.debug(msg) | ||
| else: | ||
| print(f"DEBUG: {msg}", file=sys.stderr) | ||
|
|
||
|
|
||
| def img_sort_heuristic(images, distro, version, purpose): | ||
| """Sort list to prefer old names""" | ||
| # Do sorting magic (could be omitted) | ||
| newlist = [] | ||
| distro = distro.lower() | ||
| version = version.lower() | ||
| purpose = purpose.lower() | ||
| # 0th: Exact match old SCS naming scheme ("Ubuntu 24.04 Minimal") | ||
| for img in images: | ||
| newel = (img.id, img.name) | ||
| if img.name.lower() == f"{distro} {version} {purpose}": | ||
| newlist.append(newel) | ||
| elif img.name.lower() == f"{distro} {purpose} {version}": | ||
| newlist.append(newel) | ||
| # 1st: Exact match old SCS naming scheme ("Ubuntu 24.04") | ||
| for img in images: | ||
| newel = (img.id, img.name) | ||
| if img.name.lower() == f"{distro} {version}": | ||
| newlist.append(newel) | ||
| # 2nd: Fuzzy match old SCS naming scheme ("Ubuntu 24.04*") | ||
| for img in images: | ||
| newel = (img.id, img.name) | ||
| if img.name.lower().startswith(f"{distro} {version}") and newel not in newlist: | ||
| newlist.append(newel) | ||
| # 3rd: Even more fuzzy match old SCS naming scheme ("Ubuntu*24.04") | ||
| for img in images: | ||
| newel = (img.id, img.name) | ||
| if img.name.lower().startswith(f"{distro}") and img.name.lower().endswith(f"{version}") \ | ||
| and newel not in newlist: | ||
| newlist.append(newel) | ||
| # 4th: Rest | ||
| for img in images: | ||
| newel = (img.id, img.name) | ||
| if newel not in newlist: | ||
| newlist.append(newel) | ||
| return newlist | ||
|
|
||
|
|
||
| def find_image(conn, distro, version, purpose="generic", strict=False, log=None): | ||
| """Return a sorted list of ID,Name pairs that contain the wanted image. | ||
| Empty list indicates no image has been found. The list is sorted such | ||
| that (on SCS-compliant clouds), it will very likely contain the best | ||
| matching, most recent image as first element. | ||
| If strict is set, multiple matches are not allowed. | ||
| """ | ||
| ldistro = distro.lower() | ||
| # FIXME: The image.images() method only passes selected filters | ||
| purpose_out = purpose | ||
| images = [x for x in conn.image.images(os_distro=ldistro, os_version=version, | ||
| sort="name:desc,created_at:desc", visibility="public") | ||
| if x.properties.get("os_purpose") == purpose] | ||
| if len(images) == 0: | ||
| warn(log, f"No image found with os_distro={ldistro} os_version={version} os_purpose={purpose}") | ||
| purpose_out = "" | ||
| # images = list(conn.image.images(os_distro=ldistro, os_version=version, | ||
| # sort="name:desc,created_at:desc")) | ||
| images = [x for x in conn.image.images(os_distro=ldistro, os_version=version, | ||
| sort="name:desc,created_at:desc") | ||
| if "os_purpose" not in x.properties] | ||
| if len(images) == 0: | ||
| warn(log, f"No image found with os_distro={ldistro} os_version={version} without os_purpose") | ||
| return [] | ||
| # Now comes sorting magic for best backwards compatibility | ||
| if len(images) > 1: | ||
| debug(log, f"Several {purpose_out} images found with os_distro={ldistro} os_version={version}") | ||
| if strict: | ||
| return [] | ||
| return img_sort_heuristic(images, distro, version, purpose) | ||
| return [(img.id, img.name) for img in images] | ||
|
|
||
|
|
||
| def usage(): | ||
| "Usage hints (CLI)" | ||
| print("Usage: find-img.sh [-s] DISTRO VERSION [PURPOSE]", file=sys.stderr) | ||
| print("Returns all images matching, latest first, purpose defaulting to generic", file=sys.stderr) | ||
| print("[-s] sets strict mode where only one match is allowed.", file=sys.stderr) | ||
| print("You need to have OS_CLOUD set when running this", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| def main(argv): | ||
| "Main entry for CLI" | ||
| if len(argv) < 3: | ||
| usage() | ||
| try: | ||
| conn = openstack.connect(cloud=os.environ["OS_CLOUD"]) | ||
| except openstack.exceptions.ConfigException: | ||
| print(f"No valid entry for cloud {os.environ['OS_CLOUD']}", file=sys.stderr) | ||
| usage() | ||
| except KeyError: | ||
| print("OS_CLOUD environment not configured", file=sys.stderr) | ||
| usage() | ||
| conn.authorize() | ||
| purpose = "generic" | ||
| strict = False | ||
| if argv[1] == "-s": | ||
| argv = argv[1:] | ||
| strict = True | ||
| if len(argv) > 3: | ||
| purpose = argv[3] | ||
| images = find_image(conn, argv[1], argv[2], purpose, strict) | ||
| for img in images: | ||
| print(f"{img[0]} {img[1]}") | ||
| return len(images) == 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main(sys.argv)) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| #!/bin/bash | ||
| # | ||
| # Find Image by properties | ||
| # | ||
| # (c) Kurt Garloff <s7n@garloff.de>, 7/2025 | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| usage() | ||
| { | ||
| echo "Usage: find-img [-s] distro version [purpose]" | ||
| echo "Returns all images matching, latest first, purpose defaults to generic" | ||
| echo "If some images have the wanted purpose, only those will be shown" | ||
| } | ||
|
|
||
| get_images_raw() | ||
| { | ||
| # global OS_RESP | ||
| DIST=$(echo "$1" | tr A-Z a-z) | ||
| VERS="$2" | ||
| #VERS=$(echo "$2" | tr A-Z a-z) | ||
| shift; shift | ||
| #echo "DEBUG: openstack image list --property os_distro=$DIST --property os_version=$VERS $@ -f value -c ID -c Name --sort created_at:desc" | ||
| OS_RESP=$(openstack image list --property os_distro="$DIST" --property os_version="$VERS" $@ -f value -c ID -c Name --sort name:desc,created_at:desc) | ||
| } | ||
|
|
||
|
|
||
| img_sort_heuristic() | ||
| { | ||
| # Acts on global OS_RESP | ||
| # FIXME: We could do all sorts of advanced heuristics here, looking at the name etc. | ||
| # We only do a few pattern matches here | ||
| # distro version purpose | ||
| local NEW_RESP0=$(echo "$OS_RESP" | grep -i "^[0-9a-f\-]* $1 $2 $3\$") | ||
| # distro version purpose with extras appended | ||
| local NEW_RESP1=$(echo "$OS_RESP" | grep -i "^[0-9a-f\-]* $1 $2 $3" | grep -iv "^[0-9a-f\-]* $1 $2 $3\$") | ||
| # distro purpose version | ||
| local NEW_RESP2=$(echo "$OS_RESP" | grep -i "^[0-9a-f\-]* $1 $3 $2\$") | ||
| # distro purpose version with extras appended | ||
| local NEW_RESP3=$(echo "$OS_RESP" | grep -i "^[0-9a-f\-]* $1 $3 $2" | grep -iv "^[0-9a-f\-]* $1 $3 $2\$") | ||
| # distro version | ||
| local NEW_RESP4=$(echo "$OS_RESP" | grep -i "^[0-9a-f\-]* $1 $2\$") | ||
| # distro version with extras (but not purpose) | ||
| local NEW_RESP5=$(echo "$OS_RESP" | grep -i "^[0-9a-f\-]* $1 $2" | grep -iv "^[0-9a-f\-]* $1 $2\$" | grep -iv "^[0-9a-f\-]* $1 $2 $3") | ||
| # distro extra version (but extra != purpose) | ||
| local NEW_RESP6=$(echo "$OS_RESP" | grep -i "^[0-9a-f\-]* $1 .*$2\$" | grep -iv "^[0-9a-f\-]* $1 $3 $2\$" | grep -iv "$1 $2\$") | ||
| OS_RESP=$(echo -e "$NEW_RESP0\n$NEW_RESP1\n$NEW_RESP2\n$NEW_RESP3\n$NEW_RESP4\n$NEW_RESP5\n$NEW_RESP6" | sed '/^$/d') | ||
| } | ||
|
|
||
| get_images() | ||
| { | ||
| PURPOSE="${3:-generic}" | ||
| PURP="$PURPOSE" | ||
| get_images_raw "$1" "$2" --property os_purpose=$PURPOSE | ||
| if test -z "$OS_RESP"; then | ||
| echo "WARN: No image found with os_distro=$1 os_version=$2 os_purpose=$PURPOSE" 1>&2 | ||
| PURP="" | ||
| # We're screwed as we can not filter for the absence of os_purpose with CLI | ||
| # We could loop and do an image show and then flter out, but that's very slow | ||
| get_images_raw "$1" "$2" # --property os_purpose= | ||
| # FIXME: We need to filter out images with os_purpose property set | ||
| NEW_RESP="" | ||
| while read ID Name; do | ||
| PROPS=$(openstack image show $ID -f value -c properties) | ||
| if test $? != 0; then continue; fi | ||
| if echo "$PROPS" | grep os_purpose >/dev/null 2>&1; then continue; fi | ||
| NEW_RESP=$(echo -en "$NEW_RESP\n$ID $Name") | ||
| done < <(echo "$OS_RESP") | ||
| OS_RESP=$(echo "$NEW_RESP" | sed '/^$/d') | ||
| fi | ||
| NR_IMG=$(echo "$OS_RESP" | sed '/^$/d' | wc -l) | ||
| if test "$NR_IMG" = "0"; then echo "ERROR: No image found with os_distro=$1 os_version=$2" 1>&2; return 1 | ||
| elif test "$NR_IMG" = "1"; then return 0 | ||
| else | ||
| echo "DEBUG: Several $PURP images matching os_distro=$1 os_version=$2" 1>&2; | ||
| if test -n "$STRICT"; then return 1; fi | ||
| img_sort_heuristic "$1" "$2" "$PURPOSE" | ||
| return 0 | ||
| fi | ||
| } | ||
|
|
||
| if test -z "$OS_CLOUD" -a -z "$OS_AUTH_URL"; then | ||
| echo "You need to configure clouds.yaml/secure.yaml and set OS_CLOUD" 1>&2 | ||
| exit 2 | ||
| fi | ||
| if test "$1" = "-s"; then STRICT=1; shift; fi | ||
| if test -z "$1"; then usage; exit 1; fi | ||
|
|
||
| get_images "$@" | ||
| RC=$? | ||
| echo "$OS_RESP" | ||
| (exit $RC) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| #!/usr/bin/env tofu apply -auto-approve | ||
| # Pass variables with -var os_purpose=minimal | ||
| # (c) Kurt Garloff <s7n@garloff.de> | ||
| # SPDX-License-Identifier: MIT | ||
|
|
||
| variable "os_distro" { | ||
| type = string | ||
| default = "ubuntu" | ||
| } | ||
|
|
||
| variable "os_version" { | ||
| type = string | ||
| default = "24.04" | ||
| } | ||
|
|
||
| variable "os_purpose" { | ||
| type = string | ||
| default = "generic" | ||
| } | ||
|
|
||
| terraform { | ||
| required_providers { | ||
| openstack = { | ||
| source = "terraform-provider-openstack/openstack" | ||
| version = "~> 1.54.0" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| provider "openstack" { | ||
| # Your cloud config (or use environment variable OS_CLOUD) | ||
| } | ||
|
|
||
| data "openstack_images_image_v2" "my_image" { | ||
| most_recent = true | ||
|
|
||
| properties = { | ||
| os_distro = "${var.os_distro}" | ||
| os_version = "${var.os_version}" | ||
| os_purpose = "${var.os_purpose}" | ||
| } | ||
| #sort = "name:desc,created_at:desc" | ||
| #sort_key = "name" | ||
| #sort_direction = "desc" | ||
| } | ||
|
|
||
| # Output the results for inspection | ||
| output "selected_image_id" { | ||
| value = data.openstack_images_image_v2.my_image.id | ||
| } | ||
|
|
||
| output "selected_image_name" { | ||
| value = data.openstack_images_image_v2.my_image.name | ||
| } | ||
|
|
||
| output "selected_image_created_at" { | ||
| value = data.openstack_images_image_v2.my_image.created_at | ||
| } | ||
|
|
||
| output "selected_image_properties" { | ||
| value = { | ||
| os_distro = data.openstack_images_image_v2.my_image.properties.os_distro | ||
| os_version = data.openstack_images_image_v2.my_image.properties.os_version | ||
| os_purpose = data.openstack_images_image_v2.my_image.properties.os_purpose | ||
| } | ||
| } | ||
|
|
||
| output "selected_image_tags" { | ||
| value = data.openstack_images_image_v2.my_image.tags | ||
| } | ||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.