1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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)
|