aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2024-07-30 15:40:11 +0100
committerMichael Brown <mcb30@ipxe.org>2024-07-30 16:11:28 +0100
commit0dc8933f6769e375a645ed4e9053855191cf8170 (patch)
treee2c7b8aca17edf6295a8d4672ec61fbd02362b45 /contrib
parentd2d194bc60f012569fa95ed54693cb6663beb5ce (diff)
downloadipxe-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-xcontrib/cloud/gce-int13con146
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)