|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""Entropy checker |
| 3 | +
|
| 4 | +Check given cloud for conformance with SCS standard regarding |
| 5 | +entropy, to be found under /Standards/scs-0101-v1-entropy.md |
| 6 | +
|
| 7 | +Return code is 0 precisely when it could be verified that the standard is satisfied. |
| 8 | +Otherwise the return code is the number of errors that occurred (up to 127 due to OS |
| 9 | +restrictions); for further information, see the log messages on various channels: |
| 10 | + CRITICAL for problems preventing the test to complete, |
| 11 | + ERROR for violations of requirements, |
| 12 | + WARNING for violations of recommendations, |
| 13 | + DEBUG for background information and problems that don't hinder the test. |
| 14 | +""" |
| 15 | +import getopt |
| 16 | +import logging |
| 17 | +import os |
| 18 | +import sys |
| 19 | +import warnings |
| 20 | + |
| 21 | +import openstack |
| 22 | +import openstack.cloud |
| 23 | + |
| 24 | + |
| 25 | +try: |
| 26 | + from . import entropy_check |
| 27 | +except ImportError: |
| 28 | + import entropy_check |
| 29 | + |
| 30 | + |
| 31 | +logger = logging.getLogger(__name__) |
| 32 | + |
| 33 | + |
| 34 | +def print_usage(file=sys.stderr): |
| 35 | + """Help output""" |
| 36 | + print("""Usage: entropy-check.py [options] |
| 37 | +This tool checks the requested images and flavors according to the SCS Standard 0101 "Entropy". |
| 38 | +Options: |
| 39 | + [-c/--os-cloud OS_CLOUD] sets cloud environment (default from OS_CLOUD env) |
| 40 | + [-d/--debug] enables DEBUG logging channel |
| 41 | + [-i/--images IMAGE_LIST] sets images to be tested, separated by comma. |
| 42 | + [-V/--image-visibility VIS_LIST] filters images by visibility |
| 43 | + (default: 'public,community'; use '*' to disable) |
| 44 | +""", end='', file=file) |
| 45 | + |
| 46 | + |
| 47 | +def print_result(check_id, passed): |
| 48 | + print(check_id + ": " + ('FAIL', 'PASS')[bool(passed)]) |
| 49 | + |
| 50 | + |
| 51 | +def main(argv): |
| 52 | + # configure logging, disable verbose library logging |
| 53 | + logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) |
| 54 | + openstack.enable_logging(debug=False) |
| 55 | + warnings.filterwarnings("ignore", "search_floating_ips") |
| 56 | + |
| 57 | + try: |
| 58 | + opts, args = getopt.gnu_getopt(argv, "c:i:hdV:", ["os-cloud=", "images=", "help", "debug", "image-visibility="]) |
| 59 | + except getopt.GetoptError as exc: |
| 60 | + logger.critical(f"{exc}") |
| 61 | + print_usage() |
| 62 | + return 1 |
| 63 | + |
| 64 | + cloud = os.environ.get("OS_CLOUD") |
| 65 | + image_visibility = set() |
| 66 | + for opt in opts: |
| 67 | + if opt[0] == "-h" or opt[0] == "--help": |
| 68 | + print_usage() |
| 69 | + return 0 |
| 70 | + if opt[0] == "-i" or opt[0] == "--images": |
| 71 | + logger.info("ignoring obsolete option -i") |
| 72 | + if opt[0] == "-c" or opt[0] == "--os-cloud": |
| 73 | + cloud = opt[1] |
| 74 | + if opt[0] == "-d" or opt[0] == "--debug": |
| 75 | + logging.getLogger().setLevel(logging.DEBUG) |
| 76 | + if opt[0] == "-V" or opt[0] == "--image-visibility": |
| 77 | + image_visibility.update([v.strip() for v in opt[1].split(',')]) |
| 78 | + |
| 79 | + if not cloud: |
| 80 | + logger.critical("You need to have OS_CLOUD set or pass --os-cloud=CLOUD.") |
| 81 | + return 1 |
| 82 | + |
| 83 | + if not image_visibility: |
| 84 | + image_visibility.update(("public", "community")) |
| 85 | + |
| 86 | + try: |
| 87 | + logger.debug(f"Connecting to cloud '{cloud}'") |
| 88 | + with openstack.connect(cloud=cloud, timeout=32) as conn: |
| 89 | + all_images = conn.list_images() |
| 90 | + all_flavors = conn.list_flavors(get_extra=True) |
| 91 | + |
| 92 | + if '*' not in image_visibility: |
| 93 | + logger.debug(f"Images: filter for visibility {', '.join(sorted(image_visibility))}") |
| 94 | + all_images = [img for img in all_images if img.visibility in image_visibility] |
| 95 | + all_image_names = [f"{img.name} ({img.visibility})" for img in all_images] |
| 96 | + logger.debug(f"Images: {', '.join(all_image_names) or '(NONE)'}") |
| 97 | + |
| 98 | + if not all_images: |
| 99 | + logger.critical("Can't run this test without image") |
| 100 | + return 1 |
| 101 | + |
| 102 | + logger.debug("Checking images and flavors for recommended attributes") |
| 103 | + print_result('entropy-check-image-properties', entropy_check.compute_scs_0101_image_property(all_images)) |
| 104 | + print_result('entropy-check-flavor-properties', entropy_check.compute_scs_0101_flavor_property(all_flavors)) |
| 105 | + |
| 106 | + logger.debug("Checking dynamic instance properties") |
| 107 | + canonical_image = entropy_check.compute_canonical_image(all_images) |
| 108 | + collected_vm_output = entropy_check.compute_collected_vm_output(conn, all_flavors, canonical_image) |
| 109 | + print_result('entropy-check-rngd', entropy_check.compute_scs_0101_rngd(collected_vm_output, canonical_image.name)) |
| 110 | + scs_0101_entropy_avail_result = entropy_check.compute_scs_0101_entropy_avail(collected_vm_output, canonical_image.name) |
| 111 | + scs_0101_fips_test_result = entropy_check.compute_scs_0101_fips_test(collected_vm_output, canonical_image.name) |
| 112 | + entropy_check_result = entropy_check.compute_scs_0101_entropy_check( |
| 113 | + scs_0101_entropy_avail_result, |
| 114 | + scs_0101_fips_test_result, |
| 115 | + ) |
| 116 | + print_result('entropy-check-entropy-avail', scs_0101_entropy_avail_result) |
| 117 | + print_result('entropy-check-fips-test', scs_0101_fips_test_result) |
| 118 | + print_result('entropy-check', entropy_check_result) |
| 119 | + return not entropy_check_result |
| 120 | + except BaseException as e: |
| 121 | + logger.critical(f"{e!r}") |
| 122 | + logger.debug("Exception info", exc_info=True) |
| 123 | + return 1 |
| 124 | + |
| 125 | + |
| 126 | +if __name__ == "__main__": |
| 127 | + sys.exit(main(sys.argv[1:])) |
0 commit comments