openstack-image-manager 0.20240318.0__py3-none-any.whl → 0.20240403.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,7 @@ import os
8
8
  import re
9
9
  import sys
10
10
  import typer
11
+ import typing
11
12
  from typing import Dict, Set
12
13
  import yamale
13
14
  import urllib.parse
@@ -202,12 +203,12 @@ class ImageManager:
202
203
  Read all files in etc/images/ and process each image
203
204
  Rename outdated images when not dry-running
204
205
  """
205
- logger.debug("cloud = %s" % self.CONF.cloud)
206
- logger.debug("dry-run = %s" % self.CONF.dry_run)
207
- logger.debug("images = %s" % self.CONF.images)
208
- logger.debug("tag = %s" % self.CONF.tag)
206
+ logger.debug(f"cloud = {self.CONF.cloud}")
207
+ logger.debug(f"dry-run = {self.CONF.dry_run}")
208
+ logger.debug(f"images = {self.CONF.images}")
209
+ logger.debug(f"tag = {self.CONF.tag}")
209
210
  logger.debug(
210
- "yes-i-really-know-what-i-do = %s" % self.CONF.yes_i_really_know_what_i_do
211
+ f"yes-i-really-know-what-i-do = {self.CONF.yes_i_really_know_what_i_do}"
211
212
  )
212
213
 
213
214
  # check local image definitions with yamale
@@ -280,13 +281,12 @@ class ImageManager:
280
281
  for required_key in REQUIRED_KEYS:
281
282
  if required_key not in image:
282
283
  logger.error(
283
- "'%s' lacks the necessary key %s"
284
- % (image["name"], required_key)
284
+ f"'{image['name']}' lacks the necessary key {required_key}"
285
285
  )
286
286
  self.exit_with_error = True
287
287
  continue
288
288
 
289
- logger.debug("Processing '%s'" % image["name"])
289
+ logger.debug(f"Processing '{image['name']}'")
290
290
 
291
291
  try:
292
292
  versions = dict()
@@ -331,7 +331,8 @@ class ImageManager:
331
331
  # strip any directory path for file: urls in order to
332
332
  # avoid exposing local filesystem details to other users
333
333
  if url.startswith("file:") and "/" in url:
334
- url = "file:%s" % url.rsplit("/", 1)[1]
334
+ urlfile = url.rsplit("/", 1)[1]
335
+ url = f"file:{urlfile}"
335
336
  versions[version["version"]]["meta"]["image_source"] = url
336
337
 
337
338
  if "build_date" in version:
@@ -352,7 +353,7 @@ class ImageManager:
352
353
  image["tags"].append(self.CONF.tag)
353
354
 
354
355
  if "os_distro" in image["meta"]:
355
- image["tags"].append("os:%s" % image["meta"]["os_distro"])
356
+ image["tags"].append(f"os:{image['meta']['os_distro']}")
356
357
 
357
358
  if "image_description" not in image["meta"]:
358
359
  image["meta"]["image_description"] = image["name"]
@@ -371,7 +372,7 @@ class ImageManager:
371
372
 
372
373
  def import_image(
373
374
  self, image: dict, name: str, url: str, versions: dict, version: str
374
- ) -> Image | None:
375
+ ) -> typing.Union[Image, None]:
375
376
  """
376
377
  Create a new image in Glance and upload it using the web-download method
377
378
 
@@ -382,7 +383,8 @@ class ImageManager:
382
383
  versions: versions dict generated by main()
383
384
  version: currently processed version
384
385
  """
385
- logger.info("Importing image %s" % name)
386
+ logger.info(f"Importing image {name}")
387
+ logger.info(f"Importing from URL {url}")
386
388
 
387
389
  properties = {
388
390
  "container_format": "bare",
@@ -403,16 +405,13 @@ class ImageManager:
403
405
  with local_file:
404
406
  try:
405
407
  logger.info(
406
- "Uploading local file '%s' as image %s"
407
- % (parsed_url.path, name)
408
+ f"Uploading local file '{parsed_url.path}' as image {name}"
408
409
  )
409
410
  new_image.data = local_file
410
411
  new_image.upload(self.conn.image)
411
412
  except Exception as e:
412
413
  self.conn.image.delete_image(new_image)
413
- logger.error(
414
- "Failed to upload local file for image %s\n%s" % (name, e)
415
- )
414
+ logger.error(f"Failed to upload local file for image {name}\n{e}")
416
415
  self.exit_with_error = True
417
416
  return None
418
417
  else:
@@ -435,13 +434,9 @@ class ImageManager:
435
434
  or image.owner == self.conn.current_project_id
436
435
  ):
437
436
  result[image.name] = image
438
- logger.debug(
439
- "Managed image '%s' (tags = %s)" % (image.name, image.tags)
440
- )
437
+ logger.debug(f"Managed image '{image.name}' (tags = {image.tags})")
441
438
  else:
442
- logger.debug(
443
- "Unmanaged image '%s' (tags = %s)" % (image.name, image.tags)
444
- )
439
+ logger.debug(f"Unmanaged image '{image.name}' (tags = {image.tags})")
445
440
 
446
441
  if self.CONF.use_os_hidden:
447
442
  for image in self.conn.image.images(**{"os_hidden": True}):
@@ -451,17 +446,15 @@ class ImageManager:
451
446
  ):
452
447
  result[image.name] = image
453
448
  logger.debug(
454
- "Managed hidden image '%s' (tags = %s)"
455
- % (image.name, image.tags)
449
+ f"Managed hidden image '{image.name}' (tags = {image.tags})"
456
450
  )
457
451
  else:
458
452
  logger.debug(
459
- "Unmanaged hidden image '%s' (tags = %s)"
460
- % (image.name, image.tags)
453
+ f"Unmanaged hidden image '{image.name}' (tags = {image.tags})"
461
454
  )
462
455
  return result
463
456
 
464
- def wait_for_image(self, image: Image) -> Image | None:
457
+ def wait_for_image(self, image: Image) -> typing.Union[Image, None]:
465
458
  """
466
459
  Wait for an imported image to reach "active" state
467
460
 
@@ -482,9 +475,7 @@ class ImageManager:
482
475
  # indefinitely stuck in "queued" state.
483
476
  if imported_image.status == "queued":
484
477
  if retry_attempts_for_queued_state < 0:
485
- logger.error(
486
- "Image %s seems stuck in queued state" % image.name
487
- )
478
+ logger.error(f"Image {image.name} seems stuck in queued state")
488
479
  self.exit_with_error = True
489
480
  return None
490
481
  else:
@@ -497,7 +488,7 @@ class ImageManager:
497
488
  else:
498
489
  return imported_image
499
490
  except Exception as e:
500
- logger.error("Exception while importing image %s\n%s" % (image.name, e))
491
+ logger.error(f"Exception while importing image {image.name}\n{e}")
501
492
  self.exit_with_error = True
502
493
 
503
494
  def process_image(
@@ -525,12 +516,12 @@ class ImageManager:
525
516
 
526
517
  for version in sorted_versions:
527
518
  if image["multi"]:
528
- name = "%s (%s)" % (image["name"], version)
519
+ name = f"{image['name']} ({version})"
529
520
  else:
530
- name = "%s %s" % (image["name"], version)
521
+ name = f"{image['name']} {version}"
531
522
 
532
- logger.info("Processing image '%s'" % name)
533
- logger.debug("Checking existence of '%s'" % name)
523
+ logger.info(f"Processing image '{name}'")
524
+ logger.debug(f"Checking existence of '{name}'")
534
525
  existence = name in cloud_images
535
526
 
536
527
  if existence and cloud_images[name].status != "active":
@@ -553,8 +544,7 @@ class ImageManager:
553
544
  )
554
545
  except KeyError:
555
546
  logger.error(
556
- "Image %s is missing property 'internal_version'"
557
- % image["name"]
547
+ f"Image {image['name']} is missing property 'internal_version'"
558
548
  )
559
549
 
560
550
  elif (
@@ -563,7 +553,7 @@ class ImageManager:
563
553
  and version == sorted_versions[-1]
564
554
  and not existence
565
555
  ):
566
- previous = "%s (%s)" % (image["name"], sorted_versions[-2])
556
+ previous = f"{image['name']} ({sorted_versions[-2]})"
567
557
  existence = previous in cloud_images and image["name"] in cloud_images
568
558
 
569
559
  elif (
@@ -584,8 +574,7 @@ class ImageManager:
584
574
  )
585
575
  if not upstream_checksum:
586
576
  logger.error(
587
- "Could not find checksum for image '%s', check the checksums_url"
588
- % image["name"]
577
+ f"Could not find checksum for image '{image['name']}', check the checksums_url"
589
578
  )
590
579
  return existing_images, imported_image, previous_image
591
580
 
@@ -596,11 +585,11 @@ class ImageManager:
596
585
  else ""
597
586
  )
598
587
  if image_checksum == upstream_checksum:
599
- logger.info("No new version for '%s'" % image["name"])
588
+ logger.info(f"No new version for '{image['name']}'")
600
589
  existing_images.add(image["name"])
601
590
  return existing_images, imported_image, previous_image
602
591
  else:
603
- logger.info("New version for '%s'" % image["name"])
592
+ logger.info(f"New version for '{image['name']}'")
604
593
  existence = False
605
594
  except KeyError:
606
595
  # when switching from a release pointer to a latest pointer, the image has no checksum property
@@ -619,8 +608,7 @@ class ImageManager:
619
608
  file_path = parsed_url.path
620
609
  if not (os.path.exists(file_path) and os.path.isfile(file_path)):
621
610
  logger.error(
622
- "Skipping '%s' due to file '%s' not found locally"
623
- % (name, file_path)
611
+ f"Skipping '{name}' due to file '{file_path}' not found locally"
624
612
  )
625
613
  self.exit_with_error = True
626
614
  return existing_images, imported_image, previous_image
@@ -628,12 +616,11 @@ class ImageManager:
628
616
  r = requests.head(url)
629
617
 
630
618
  if r.status_code in [200, 302]:
631
- logger.info("Tested URL %s: %s" % (url, r.status_code))
619
+ logger.info(f"Tested URL {url}: {r.status_code}")
632
620
  else:
633
- logger.error("Tested URL %s: %s" % (url, r.status_code))
621
+ logger.error(f"Tested URL {url}: {r.status_code}")
634
622
  logger.error(
635
- "Skipping '%s' due to HTTP status code %s"
636
- % (name, r.status_code)
623
+ f"Skipping '{name}' due to HTTP status code {r.status_code}"
637
624
  )
638
625
  self.exit_with_error = True
639
626
  return existing_images, imported_image, previous_image
@@ -647,8 +634,7 @@ class ImageManager:
647
634
  )
648
635
  if import_result:
649
636
  logger.info(
650
- "Import of '%s' successfully completed, reloading images"
651
- % name
637
+ f"Import of '{name}' successfully completed, reloading images"
652
638
  )
653
639
  cloud_images = self.get_images()
654
640
  imported_image = cloud_images.get(name, None)
@@ -659,8 +645,7 @@ class ImageManager:
659
645
 
660
646
  elif self.CONF.latest and version != sorted_versions[-1]:
661
647
  logger.info(
662
- "Skipping image '%s' (only importing the latest version from type multi)"
663
- % name
648
+ f"Skipping image '{name}' (only importing the latest version from type multi)"
664
649
  )
665
650
 
666
651
  if image["multi"]:
@@ -697,7 +682,7 @@ class ImageManager:
697
682
  image["meta"] = meta.copy()
698
683
 
699
684
  if name in cloud_images:
700
- logger.info("Checking parameters of '%s'" % name)
685
+ logger.info(f"Checking parameters of '{name}'")
701
686
 
702
687
  cloud_image = cloud_images[name]
703
688
  real_image_size = int(
@@ -708,8 +693,7 @@ class ImageManager:
708
693
 
709
694
  if "min_disk" in image and image["min_disk"] != cloud_image.min_disk:
710
695
  logger.info(
711
- "Setting min_disk: %s != %s"
712
- % (image["min_disk"], cloud_image.min_disk)
696
+ f"Setting min_disk: {image['min_disk']} != {cloud_image.min_disk}"
713
697
  )
714
698
  self.conn.image.update_image(
715
699
  cloud_image.id, **{"min_disk": int(image["min_disk"])}
@@ -718,15 +702,14 @@ class ImageManager:
718
702
  if (
719
703
  "min_disk" in image and real_image_size > image["min_disk"]
720
704
  ) or "min_disk" not in image:
721
- logger.info("Setting min_disk = %d" % real_image_size)
705
+ logger.info(f"Setting min_disk = {real_image_size}")
722
706
  self.conn.image.update_image(
723
707
  cloud_image.id, **{"min_disk": real_image_size}
724
708
  )
725
709
 
726
710
  if "min_ram" in image and image["min_ram"] != cloud_image.min_ram:
727
711
  logger.info(
728
- "Setting min_ram: %s != %s"
729
- % (image["min_ram"], cloud_image.min_ram)
712
+ f"Setting min_ram: {image['min_ram']} != {cloud_image.min_ram}"
730
713
  )
731
714
  self.conn.image.update_image(
732
715
  cloud_image.id, **{"min_ram": int(image["min_ram"])}
@@ -734,7 +717,7 @@ class ImageManager:
734
717
 
735
718
  if self.CONF.use_os_hidden:
736
719
  if "hidden" in versions[version]:
737
- logger.info("Setting os_hidden = %s" % versions[version]["hidden"])
720
+ logger.info(f"Setting os_hidden = {versions[version]['hidden']}")
738
721
  self.conn.image.update_image(
739
722
  cloud_image.id, **{"os_hidden": versions[version]["hidden"]}
740
723
  )
@@ -756,24 +739,23 @@ class ImageManager:
756
739
  )
757
740
  modify_date = modify_date.replace("-", "")
758
741
 
759
- logger.info("Setting internal_version = %s" % modify_date)
742
+ logger.info(f"Setting internal_version = {modify_date}")
760
743
  image["meta"]["internal_version"] = modify_date
761
744
  except Exception:
762
745
  logger.error(
763
- "Error when retrieving the modification date of image '%s'",
764
- image["name"],
746
+ f"Error when retrieving the modification date of image '{image['name']}'"
765
747
  )
766
- logger.info("Setting internal_version = %s" % version)
748
+ logger.info(f"Setting internal_version = {version}")
767
749
  image["meta"]["internal_version"] = version
768
750
  else:
769
- logger.info("Setting internal_version = %s" % version)
751
+ logger.info(f"Setting internal_version = {version}")
770
752
  image["meta"]["internal_version"] = version
771
753
 
772
- logger.info("Setting image_original_user = %s" % image["login"])
754
+ logger.info(f"Setting image_original_user = {image['login']}")
773
755
  image["meta"]["image_original_user"] = image["login"]
774
756
 
775
757
  if self.CONF.hypervisor:
776
- logger.info("Setting hypervisor type = %s" % self.CONF.hypervisor)
758
+ logger.info(f"Setting hypervisor type = {self.CONF.hypervisor}")
777
759
  image["meta"]["hypervisor_type"] = self.CONF.hypervisor
778
760
 
779
761
  if version == "latest" and upstream_checksum:
@@ -786,12 +768,12 @@ class ImageManager:
786
768
 
787
769
  for tag in image["tags"]:
788
770
  if tag not in cloud_image.tags:
789
- logger.info("Adding tag %s" % (tag))
771
+ logger.info(f"Adding tag {tag}")
790
772
  self.conn.image.add_tag(cloud_image.id, tag)
791
773
 
792
774
  for tag in cloud_image.tags:
793
775
  if tag not in image["tags"]:
794
- logger.info("Deleting tag %s" % (tag))
776
+ logger.info(f"Deleting tag {tag}")
795
777
  self.conn.image.remove_tag(cloud_image.id, tag)
796
778
 
797
779
  if "meta" in versions[version]:
@@ -803,8 +785,7 @@ class ImageManager:
803
785
  if property in image["meta"]:
804
786
  if image["meta"][property] != properties[property]:
805
787
  logger.info(
806
- "Setting property %s: %s != %s"
807
- % (property, properties[property], image["meta"][property])
788
+ f"Setting property {property}: {properties[property]} != {image['meta'][property]}"
808
789
  )
809
790
  self.conn.image.update_image(
810
791
  cloud_image.id, **{property: str(image["meta"][property])}
@@ -816,37 +797,37 @@ class ImageManager:
816
797
  "stores",
817
798
  ] or not property.startswith("os_"):
818
799
  # FIXME: handle deletion of properties
819
- logger.debug("Deleting property %s" % (property))
800
+ logger.debug(f"Deleting property {property}")
820
801
 
821
802
  for property in image["meta"]:
822
803
  if property not in properties:
823
804
  logger.info(
824
- "Setting property %s: %s" % (property, image["meta"][property])
805
+ f"Setting property {property}: {image['meta'][property]}"
825
806
  )
826
807
  self.conn.image.update_image(
827
808
  cloud_image.id, **{property: str(image["meta"][property])}
828
809
  )
829
810
 
830
- logger.info("Checking status of '%s'" % name)
811
+ logger.info(f"Checking status of '{name}'")
831
812
  if (
832
813
  cloud_image.status != image["status"]
833
814
  and image["status"] == "deactivated"
834
815
  ):
835
- logger.info("Deactivating image '%s'" % name)
816
+ logger.info(f"Deactivating image '{name}'")
836
817
  self.conn.image.deactivate_image(cloud_image.id)
837
818
 
838
819
  elif cloud_image.status != image["status"] and image["status"] == "active":
839
- logger.info("Reactivating image '%s'" % name)
820
+ logger.info(f"Reactivating image '{name}'")
840
821
  self.conn.image.reactivate_image(cloud_image.id)
841
822
 
842
- logger.info("Checking visibility of '%s'" % name)
823
+ logger.info(f"Checking visibility of '{name}'")
843
824
  if "visibility" in versions[version]:
844
825
  visibility = versions[version]["visibility"]
845
826
  else:
846
827
  visibility = image["visibility"]
847
828
 
848
829
  if cloud_image.visibility != visibility:
849
- logger.info("Setting visibility of '%s' to '%s'" % (name, visibility))
830
+ logger.info(f"Setting visibility of '{name}' to '{visibility}'")
850
831
  self.conn.image.update_image(cloud_image.id, visibility=visibility)
851
832
 
852
833
  def rename_images(
@@ -868,17 +849,17 @@ class ImageManager:
868
849
  cloud_images = self.get_images()
869
850
 
870
851
  if len(sorted_versions) > 1:
871
- latest = "%s (%s)" % (name, sorted_versions[-1])
872
- previous_latest = "%s (%s)" % (name, sorted_versions[-2])
852
+ latest = f"{name} ({sorted_versions[-1]})"
853
+ previous_latest = f"{name} ({sorted_versions[-2]})"
873
854
 
874
855
  if name in cloud_images and previous_latest not in cloud_images:
875
- logger.info("Renaming %s to %s" % (name, previous_latest))
856
+ logger.info(f"Renaming {name} to {previous_latest}")
876
857
  self.conn.image.update_image(
877
858
  cloud_images[name].id, name=previous_latest
878
859
  )
879
860
 
880
861
  if latest in cloud_images:
881
- logger.info("Renaming %s to %s" % (latest, name))
862
+ logger.info(f"Renaming {latest} to {name}")
882
863
  self.conn.image.update_image(cloud_images[latest].id, name=name)
883
864
 
884
865
  elif len(sorted_versions) == 1 and name in cloud_images:
@@ -891,34 +872,30 @@ class ImageManager:
891
872
  )
892
873
  create_date = create_date.replace("-", "")
893
874
 
894
- previous_latest = "%s (%s)" % (name, create_date)
875
+ previous_latest = f"{name} ({create_date})"
895
876
 
896
877
  logger.info(
897
- "Setting internal_version: %s for %s"
898
- % (create_date, previous_latest)
878
+ f"Setting internal_version: {create_date} for {previous_latest}"
899
879
  )
900
880
  self.conn.image.update_image(
901
881
  previous_image.id, **{"internal_version": create_date}
902
882
  )
903
883
  else:
904
- previous_latest = "%s (%s)" % (
905
- name,
906
- previous_image["properties"]["internal_version"],
884
+ previous_latest = (
885
+ f"{name} ({previous_image['properties']['internal_version']})"
907
886
  )
908
887
 
909
- logger.info("Renaming old latest '%s' to '%s'" % (name, previous_latest))
888
+ logger.info(f"Renaming old latest '{name}' to '{previous_latest}'")
910
889
  self.conn.image.update_image(previous_image.id, name=previous_latest)
911
890
 
912
- logger.info(
913
- "Renaming imported image '%s' to '%s'" % (imported_image.name, name)
914
- )
891
+ logger.info(f"Renaming imported image '{imported_image.name}' to '{name}'")
915
892
  self.conn.image.update_image(imported_image.id, name=name)
916
893
 
917
894
  elif len(sorted_versions) == 1:
918
- latest = "%s (%s)" % (name, sorted_versions[-1])
895
+ latest = f"{name} ({sorted_versions[-1]})"
919
896
 
920
897
  if latest in cloud_images:
921
- logger.info("Renaming %s to %s" % (latest, name))
898
+ logger.info(f"Renaming {latest} to {name}")
922
899
  self.conn.image.update_image(cloud_images[latest].id, name=name)
923
900
 
924
901
  def check_image_age(self) -> set:
@@ -1064,10 +1041,10 @@ class ImageManager:
1064
1041
  and not self.CONF.dry_run
1065
1042
  ):
1066
1043
  try:
1067
- logger.info("Deactivating image '%s'" % image)
1044
+ logger.info(f"Deactivating image '{image}'")
1068
1045
  self.conn.image.deactivate_image(cloud_image.id)
1069
1046
 
1070
- logger.info("Setting visibility of '%s' to 'community'" % image)
1047
+ logger.info(f"Setting visibility of '{image}' to 'community'")
1071
1048
  self.conn.image.update_image(
1072
1049
  cloud_image.id, visibility="community"
1073
1050
  )
@@ -1076,25 +1053,24 @@ class ImageManager:
1076
1053
  "keep" not in image_definition
1077
1054
  or not image_definition["keep"]
1078
1055
  ):
1079
- logger.info("Deleting %s" % image)
1056
+ logger.info(f"Deleting {image}")
1080
1057
  self.conn.image.delete_image(cloud_image.id)
1081
1058
  else:
1082
1059
  logger.info(
1083
- "Image '%s' will not be deleted, because 'keep' flag is True"
1084
- % image
1060
+ f"Image '{image}' will not be deleted, because 'keep' flag is True"
1085
1061
  )
1086
1062
  except Exception as e:
1087
1063
  logger.info(
1088
- "%s is still in use and cannot be deleted\n %s" % (image, e)
1064
+ f"{image} is still in use and cannot be deleted\n {e}"
1089
1065
  )
1090
1066
 
1091
1067
  else:
1092
1068
  logger.warning(
1093
- "Image %s should be deleted, but deletion is disabled" % image
1069
+ f"Image {image} should be deleted, but deletion is disabled"
1094
1070
  )
1095
1071
  try:
1096
1072
  if self.CONF.deactivate and not self.CONF.dry_run:
1097
- logger.info("Deactivating image '%s'" % image)
1073
+ logger.info(f"Deactivating image '{image}'")
1098
1074
  self.conn.image.deactivate_image(cloud_image.id)
1099
1075
 
1100
1076
  if (
@@ -1103,13 +1079,13 @@ class ImageManager:
1103
1079
  and cloud_image.visibility != "community"
1104
1080
  ):
1105
1081
  logger.info(
1106
- "Setting visibility of '%s' to 'community'" % image
1082
+ f"Setting visibility of '{image}' to 'community'"
1107
1083
  )
1108
1084
  self.conn.image.update_image(
1109
1085
  cloud_image.id, visibility="community"
1110
1086
  )
1111
1087
  except Exception as e:
1112
- logger.error("An Exception occurred: \n%s" % e)
1088
+ logger.error(f"An Exception occurred: \n{e}")
1113
1089
  self.exit_with_error = True
1114
1090
  elif counter[image_name] <= last:
1115
1091
  logger.info(
@@ -1120,12 +1096,12 @@ class ImageManager:
1120
1096
  and not self.CONF.dry_run
1121
1097
  and cloud_image.visibility != "community"
1122
1098
  ):
1123
- logger.info("Setting visibility of '%s' to 'community'" % image)
1099
+ logger.info(f"Setting visibility of '{image}' to 'community'")
1124
1100
  self.conn.image.update_image(cloud_image.id, visibility="community")
1125
1101
  elif (
1126
1102
  counter[image_name] < last and self.CONF.hide and not self.CONF.dry_run
1127
1103
  ):
1128
- logger.info("Setting visibility of '%s' to 'community'" % image)
1104
+ logger.info(f"Setting visibility of '{image}' to 'community'")
1129
1105
  self.conn.image.update_image(cloud_image.id, visibility="community")
1130
1106
  return unmanaged_images
1131
1107
 
@@ -1141,39 +1117,32 @@ class ImageManager:
1141
1117
  self.exit_with_error = True
1142
1118
  for result in e.results:
1143
1119
  logger.error(
1144
- "Error validating data '%s' with '%s'"
1145
- % (result.data, result.schema)
1120
+ f"Error validating data '{result.data}' with '{result.schema}'"
1146
1121
  )
1147
1122
  for error in result.errors:
1148
- logger.error("\t%s" % error)
1123
+ logger.error(f"\t{error}")
1149
1124
  else:
1150
- logger.debug("Image file %s is valid" % file)
1125
+ logger.debug(f"Image file {file} is valid")
1151
1126
  except FileNotFoundError:
1152
- logger.error("Invalid path '%s'" % self.CONF.images)
1127
+ logger.error(f"Invalid path '{self.CONF.images}'")
1153
1128
 
1154
1129
  def share_image_with_project(self, image, project):
1155
1130
  member = self.conn.image.find_member(project.id, image.id)
1156
1131
 
1157
1132
  if not member:
1158
- logger.info(
1159
- "add - %s - %s (%s)" % (image.name, project.name, project.domain_id)
1160
- )
1133
+ logger.info(f"add - {image.name} - {project.name} ({project.domain_id})")
1161
1134
  if not self.CONF.dry_run:
1162
1135
  member = self.conn.image.add_member(image.id, member_id=project.id)
1163
1136
 
1164
1137
  if not self.CONF.dry_run and member.status != "accepted":
1165
- logger.info(
1166
- "accept - %s - %s (%s)" % (image.name, project.name, project.domain_id)
1167
- )
1138
+ logger.info(f"accept - {image.name} - {project.name} ({project.domain_id})")
1168
1139
  self.conn.image.update_member(member, image.id, status="accepted")
1169
1140
 
1170
1141
  def unshare_image_with_project(self, image, project):
1171
1142
  member = self.conn.image.find_member(project.id, image.id)
1172
1143
 
1173
1144
  if member:
1174
- logger.info(
1175
- "del - %s - %s (%s)" % (image.name, project.name, project.domain_id)
1176
- )
1145
+ logger.info(f"del - {image.name} - {project.name} ({project.domain_id})")
1177
1146
  if not self.CONF.dry_run:
1178
1147
  self.conn.image.remove_member(member, image.id)
1179
1148
 
@@ -37,7 +37,7 @@ def get_latest_default(
37
37
 
38
38
  checksums = {}
39
39
  for line in result.text.split("\n"):
40
- cs = re.split("\s+", line) # noqa W605
40
+ cs = re.split(r"\s+", line)
41
41
  if shortname in ["rocky-8", "rocky-9"]:
42
42
  if len(cs) == 4 and cs[0] == "SHA256":
43
43
  checksums[latest_filename] = cs[3]
@@ -149,10 +149,10 @@ def mirror_image(
149
149
 
150
150
  try:
151
151
  client.stat_object(minio_bucket, os.path.join(dirname, new_filename))
152
- logger.info("'%s' available in '%s'" % (new_filename, dirname))
152
+ logger.info(f"'{new_filename}' available in '{dirname}'")
153
153
  except S3Error:
154
- logger.info("'%s' not yet available in '%s'" % (new_filename, dirname))
155
- logger.info("Downloading '%s'" % latest_url)
154
+ logger.info(f"'{new_filename}' not yet available in '{dirname}'")
155
+ logger.info(f"Downloading '{latest_url}'")
156
156
 
157
157
  response = requests.get(latest_url, stream=True)
158
158
  with open(os.path.basename(path.path), "wb") as fp:
@@ -160,13 +160,11 @@ def mirror_image(
160
160
  del response
161
161
 
162
162
  if fileextension in [".bz2", ".zip", ".xz", ".gz"]:
163
- logger.info("Decompressing '%s'" % os.path.basename(path.path))
163
+ logger.info(f"Decompressing '{os.path.basename(path.path)}'")
164
164
  patoolib.extract_archive(os.path.basename(path.path), outdir=".")
165
165
  os.remove(os.path.basename(path.path))
166
166
 
167
- logger.info(
168
- "Uploading '%s' to '%s' as '%s'" % (filename, dirname, new_filename)
169
- )
167
+ logger.info(f"Uploading '{filename}' to '{dirname}' as '{new_filename}'")
170
168
 
171
169
  client.fput_object(minio_bucket, os.path.join(dirname, new_filename), filename)
172
170
  os.remove(filename)
@@ -241,12 +239,25 @@ def update_image(
241
239
 
242
240
  minio_server = str(minio_server)
243
241
  minio_bucket = str(minio_bucket)
244
- new_url = f"https://{minio_server}/{swift_prefix}{minio_bucket}/{shortname}/{current_version}-{shortname}.{format}"
245
- logger.info(f"New URL is {new_url}")
246
- image["versions"][0]["url"] = new_url
242
+ mirror_url = f"https://{minio_server}/{swift_prefix}{minio_bucket}/{shortname}/{current_version}-{shortname}.{format}" # noqa E501
243
+ logger.info(f"New URL is {mirror_url}")
244
+
245
+ # If `mirror_url` is given, the manage.py script will
246
+ # use `mirror_url` for the download and will use `url`
247
+ # to set the `image_source` property. This way we keep
248
+ # track of the original source of the image.
249
+
250
+ image["versions"][0]["mirror_url"] = mirror_url
251
+
252
+ # We use `current_url` here and not `latest_url` to keep track
253
+ # of the original source of the image. Even if we know that `current_url`
254
+ # will not be available in the future. The `latest_url` will always
255
+ # be part of the image definition itself.
256
+
257
+ image["versions"][0]["url"] = current_url
247
258
 
248
259
  if dry_run:
249
- logger.info(f"Not mirroring {new_url}, dry-run enabled")
260
+ logger.info(f"Not mirroring {mirror_url}, dry-run enabled")
250
261
  else:
251
262
  mirror_image(
252
263
  image,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openstack-image-manager
3
- Version: 0.20240318.0
3
+ Version: 0.20240403.0
4
4
  Summary: OpenStack image manager
5
5
  Author-email: OSISM community <info@osism.tech>
6
6
  License: Apache License
@@ -232,13 +232,13 @@ Requires-Dist: patool ==2.2.0
232
232
  Requires-Dist: requests ==2.31.0
233
233
  Requires-Dist: ruamel.yaml ==0.18.6
234
234
  Requires-Dist: tabulate ==0.9.0
235
- Requires-Dist: typer[all] ==0.9.0
236
- Requires-Dist: yamale ==5.0.0
235
+ Requires-Dist: typer[all] ==0.12.0
236
+ Requires-Dist: yamale ==5.1.0
237
237
 
238
238
  # openstack-image-manager
239
239
 
240
240
  [![PyPi version](https://badgen.net/pypi/v/openstack-image-manager/)](https://pypi.org/project/openstack-image-manager/)
241
241
  [![PyPi license](https://badgen.net/pypi/license/openstack-image-manager/)](https://pypi.org/project/openstack-image-manager/)
242
- [![Documentation](https://img.shields.io/static/v1?label=&message=documentation&color=blue)](https://osism.github.io/docs/guides/operations-guide/openstack/day2-operations/image-manager)
242
+ [![Documentation](https://img.shields.io/static/v1?label=&message=documentation&color=blue)](https://osism.tech/docs/guides/operations-guide/openstack/day2-operations/image-manager)
243
243
 
244
244
  Easily manage and keep up to date a large number of images on an OpenStack environment
@@ -0,0 +1,11 @@
1
+ openstack_image_manager/__init__.py,sha256=z6lQHDMfCV8IkUz5pM1QYfQ37O2Rdy82jYovN8N9DIU,240
2
+ openstack_image_manager/manage.py,sha256=gh8Cf5XAKTOCoKL8jMe-Q6V_d3pQryLc2b457OQLZPI,46764
3
+ openstack_image_manager/mirror.py,sha256=_84-vAFfF1a3IuDBzluwTJWo20cyJEJaNIcxn3X87QI,4737
4
+ openstack_image_manager/table.py,sha256=cZ6Xuqp8uh2V5_uzT4KwWoiLAUdvsPZkrPjOXdxFeU4,1080
5
+ openstack_image_manager/update.py,sha256=MFx7wZ1kEQb4WXYfvdjgSCeyXflkJqbBBx-4z8pQQ4Y,11057
6
+ openstack_image_manager-0.20240403.0.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
7
+ openstack_image_manager-0.20240403.0.dist-info/METADATA,sha256=yydaIZqhbmwnh_HU0b6LRrouNba065I5W-1PueTV2NU,14871
8
+ openstack_image_manager-0.20240403.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
9
+ openstack_image_manager-0.20240403.0.dist-info/entry_points.txt,sha256=AEHPHHHZ3jAZfpvaI5ZzLi3DHb9vGQwL7TJcw_G_5nc,80
10
+ openstack_image_manager-0.20240403.0.dist-info/top_level.txt,sha256=iLfREddId51T97Dr9IGRQtJXKJgVy1PB6uHCaQk1j44,24
11
+ openstack_image_manager-0.20240403.0.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- openstack_image_manager/__init__.py,sha256=z6lQHDMfCV8IkUz5pM1QYfQ37O2Rdy82jYovN8N9DIU,240
2
- openstack_image_manager/manage.py,sha256=biCPWdV5-_4rI_6DtieOWe_HXrcnWP67hs1RzwnOdts,47688
3
- openstack_image_manager/mirror.py,sha256=_84-vAFfF1a3IuDBzluwTJWo20cyJEJaNIcxn3X87QI,4737
4
- openstack_image_manager/table.py,sha256=cZ6Xuqp8uh2V5_uzT4KwWoiLAUdvsPZkrPjOXdxFeU4,1080
5
- openstack_image_manager/update.py,sha256=RBH47LjWsWVKaYVwBxlDviSSZY6RxUyUQdrwtKG5bKs,10545
6
- openstack_image_manager-0.20240318.0.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
7
- openstack_image_manager-0.20240318.0.dist-info/METADATA,sha256=IEeNJuQY-DBWauuhBsj2CZzHGRWHLbGg4LeU6X_Ua1g,14875
8
- openstack_image_manager-0.20240318.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
9
- openstack_image_manager-0.20240318.0.dist-info/entry_points.txt,sha256=AEHPHHHZ3jAZfpvaI5ZzLi3DHb9vGQwL7TJcw_G_5nc,80
10
- openstack_image_manager-0.20240318.0.dist-info/top_level.txt,sha256=iLfREddId51T97Dr9IGRQtJXKJgVy1PB6uHCaQk1j44,24
11
- openstack_image_manager-0.20240318.0.dist-info/RECORD,,