diff options
author | Michael Brown <mcb30@ipxe.org> | 2024-07-30 15:40:11 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2024-07-30 16:11:28 +0100 |
commit | 0dc8933f6769e375a645ed4e9053855191cf8170 (patch) | |
tree | e2c7b8aca17edf6295a8d4672ec61fbd02362b45 /contrib | |
parent | d2d194bc60f012569fa95ed54693cb6663beb5ce (diff) | |
download | ipxe-0dc8933f6769e375a645ed4e9053855191cf8170.zip ipxe-0dc8933f6769e375a645ed4e9053855191cf8170.tar.gz ipxe-0dc8933f6769e375a645ed4e9053855191cf8170.tar.bz2 |
[cloud] Add utility to read INT13CON partition in Google Compute Engine
Following the example of aws-int13con, add a utility that can be used
to read the INT13 console log from a used iPXE boot disk in Google
Compute Engine.
There seems to be no easy way to directly read the contents of either
a disk image or a snapshot in Google Cloud. Work around this
limitation by creating a snapshot and attaching this snapshot as a
data disk to a temporary Linux instance, which is then used to echo
the INT13 console log to the serial port.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'contrib')
-rwxr-xr-x | contrib/cloud/gce-int13con | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/contrib/cloud/gce-int13con b/contrib/cloud/gce-int13con new file mode 100755 index 0000000..3b909a4 --- /dev/null +++ b/contrib/cloud/gce-int13con @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 + +import argparse +import textwrap +import time +from uuid import uuid4 + +from google.cloud import compute + +IPXE_LOG_PREFIX = 'ipxe-log-temp-' +IPXE_LOG_MAGIC = 'iPXE LOG' +IPXE_LOG_END = '----- END OF iPXE LOG -----' + +def get_log_disk(instances, project, zone, name): + """Get log disk source URL""" + instance = instances.get(project=project, zone=zone, instance=name) + disk = next(x for x in instance.disks if x.boot) + return disk.source + +def delete_temp_snapshot(snapshots, project, name): + """Delete temporary snapshot""" + assert name.startswith(IPXE_LOG_PREFIX) + snapshots.delete(project=project, snapshot=name) + +def delete_temp_snapshots(snapshots, project): + """Delete all old temporary snapshots""" + filter = "name eq %s.+" % IPXE_LOG_PREFIX + request = compute.ListSnapshotsRequest(project=project, filter=filter) + for snapshot in snapshots.list(request=request): + delete_temp_snapshot(snapshots, project, snapshot.name) + +def create_temp_snapshot(snapshots, project, source): + """Create temporary snapshot""" + name = '%s%s' % (IPXE_LOG_PREFIX, uuid4()) + snapshot = compute.Snapshot(name=name, source_disk=source) + snapshots.insert(project=project, snapshot_resource=snapshot).result() + return name + +def delete_temp_instance(instances, project, zone, name): + """Delete log dumper temporary instance""" + assert name.startswith(IPXE_LOG_PREFIX) + instances.delete(project=project, zone=zone, instance=name) + +def delete_temp_instances(instances, project, zone): + """Delete all old log dumper temporary instances""" + filter = "name eq %s.+" % IPXE_LOG_PREFIX + request = compute.ListInstancesRequest(project=project, zone=zone, + filter=filter) + for instance in instances.list(request=request): + delete_temp_instance(instances, project, zone, instance.name) + +def create_temp_instance(instances, project, zone, family, image, machine, + snapshot): + """Create log dumper temporary instance""" + image = "projects/%s/global/images/family/%s" % (family, image) + machine_type = "zones/%s/machineTypes/%s" % (zone, machine) + logsource = "global/snapshots/%s" % snapshot + bootparams = compute.AttachedDiskInitializeParams(source_image=image) + bootdisk = compute.AttachedDisk(boot=True, auto_delete=True, + initialize_params=bootparams) + logparams = compute.AttachedDiskInitializeParams(source_snapshot=logsource) + logdisk = compute.AttachedDisk(boot=False, auto_delete=True, + initialize_params=logparams, + device_name="ipxelog") + nic = compute.NetworkInterface() + name = '%s%s' % (IPXE_LOG_PREFIX, uuid4()) + script = textwrap.dedent(f""" + #!/bin/sh + tr -d '\\000' < /dev/disk/by-id/google-ipxelog-part3 > /dev/ttyS3 + echo "{IPXE_LOG_END}" > /dev/ttyS3 + """).strip() + items = compute.Items(key="startup-script", value=script) + metadata = compute.Metadata(items=[items]) + instance = compute.Instance(name=name, machine_type=machine_type, + network_interfaces=[nic], metadata=metadata, + disks=[bootdisk, logdisk]) + instances.insert(project=project, zone=zone, + instance_resource=instance).result() + return name + +def get_log_output(instances, project, zone, name): + """Get iPXE log output""" + request = compute.GetSerialPortOutputInstanceRequest(project=project, + zone=zone, port=4, + instance=name) + while True: + log = instances.get_serial_port_output(request=request).contents.strip() + if log.endswith(IPXE_LOG_END): + if log.startswith(IPXE_LOG_MAGIC): + return log[len(IPXE_LOG_MAGIC):-len(IPXE_LOG_END)] + else: + return log[:-len(IPXE_LOG_END)] + time.sleep(1) + +# Parse command-line arguments +# +parser = argparse.ArgumentParser(description="Import Google Cloud image") +parser.add_argument('--project', '-j', default="ipxe-images", + help="Google Cloud project") +parser.add_argument('--zone', '-z', required=True, + help="Google Cloud zone") +parser.add_argument('--family', '-f', default="debian-cloud", + help="Helper OS image family") +parser.add_argument('--image', '-i', default="debian-12", + help="Helper OS image") +parser.add_argument('--machine', '-m', default="e2-micro", + help="Helper machine type") +parser.add_argument('instance', help="Instance name") +args = parser.parse_args() + +# Construct client objects +# +instances = compute.InstancesClient() +snapshots = compute.SnapshotsClient() + +# Clean up old temporary objects +# +delete_temp_instances(instances, project=args.project, zone=args.zone) +delete_temp_snapshots(snapshots, project=args.project) + +# Create log disk snapshot +# +logdisk = get_log_disk(instances, project=args.project, zone=args.zone, + name=args.instance) +logsnap = create_temp_snapshot(snapshots, project=args.project, source=logdisk) + +# Create log dumper instance +# +dumper = create_temp_instance(instances, project=args.project, zone=args.zone, + family=args.family, image=args.image, + machine=args.machine, snapshot=logsnap) + +# Wait for log output +# +output = get_log_output(instances, project=args.project, zone=args.zone, + name=dumper) + +# Print log output +# +print(output) + +# Clean up +# +delete_temp_instance(instances, project=args.project, zone=args.zone, + name=dumper) +delete_temp_snapshot(snapshots, project=args.project, name=logsnap) |