-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlaunch_gcloud_instance.py
executable file
·135 lines (111 loc) · 5.37 KB
/
launch_gcloud_instance.py
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
#!/usr/bin/env python3
"""
Launches a GCP instance.
Many of the steps of this script invoke the gcloud CLI directly, rather than using the python client
library. This is because the python client library does not provide as much detail in the error
messages, which makes it harder to debug issues. Besides, the gcloud CLI is identical across
platforms (such as Windows, Mac, and Linux), so doing it this way is more portable. Finally, the
CLI is much simpler and easier to use than the python client library.
"""
from gcloud_common import Defaults, get_gcloud_zone
from setup_common import get_env_json, update_env_json
import argparse
from dataclasses import dataclass, fields
import shlex
import subprocess
from typing import Optional
@dataclass
class Params:
image_name: str = '' # default: latest from image family
image_family: str = Defaults.image_family
name: str = 'compute-instance'
zone: str = get_gcloud_zone()
machine_type: str = Defaults.machine_type
ssd_disk_count: int = 1
gpu_type: Optional[str] = Defaults.gpu_type
gpu_count: int = 1
disk_name: str = get_env_json().get('GCP_PERSISTENT_DISK', None)
disk_mode: str = 'rw'
preemptible: bool = False
@staticmethod
def create(args) -> 'Params':
kwargs = {f.name: getattr(args, f.name) for f in fields(Params)}
params = Params(**kwargs)
if None in (params.disk_name, params.zone):
raise ValueError('Please run gcp_setup_wizard.py')
return params
@staticmethod
def add_args(parser):
group = parser.add_argument_group('launch_gcloud_instance.py options')
defaults = Params()
group.add_argument('-f', '--image-family', default=defaults.image_family,
help='Family of the image to use (default: %(default)s)')
group.add_argument('-i', '--image-name', default=defaults.image_name,
help='Name of the image to use (if specified, overrides -f/--image-family)')
group.add_argument('-n', '--name', default=defaults.name,
help='Name of the instance to create (default: %(default)s)')
group.add_argument('-z', '--zone', default=defaults.zone,
help='Zone to create the instance in (default: %(default)s)')
group.add_argument('-m', '--machine-type', default=defaults.machine_type,
help='Machine type to create the instance with (default: %(default)s)')
group.add_argument('-g', '--gpu-type', default=defaults.gpu_type,
help='GPU type to create the instance with (default: %(default)s). '
'Empty-string means omit gpu.')
group.add_argument('-c', '--gpu-count', default=defaults.gpu_count, type=int,
help='Number of GPUs to create the instance with (default: %(default)s).'
' Only used if -g/--gpu-type is not empty-string.')
group.add_argument('-s', '--ssd-disk-count', default=defaults.ssd_disk_count, type=int,
help='Number of SSD disks to create the instance with (default: %(default)s)')
group.add_argument('-d', '--disk-name', default=defaults.disk_name,
help='Name of the disk to attach (default: %(default)s)')
group.add_argument('-D', '--disk-mode', default=defaults.disk_mode,
help='Disk mode to attach (default: %(default)s)')
group.add_argument('-p', '--preemptible', action='store_true',
help='Create a preemptible instance')
def load_args() -> Params:
parser = argparse.ArgumentParser()
Params.add_args(parser)
return Params.create(parser.parse_args())
def main():
params = load_args()
cmd = [
'gcloud', 'compute', 'instances', 'create', params.name,
f'--zone={params.zone}',
f'--machine-type={params.machine_type}',
f'--disk=name={params.disk_name},mode={params.disk_mode},boot=no,auto-delete=no',
'--maintenance-policy=TERMINATE',
'--metadata-from-file=startup-script=gcloud/instance_startup.sh',
f'--image-project={Defaults.a0a_project}',
]
for _ in range(params.ssd_disk_count):
cmd.append('--local-ssd=interface=nvme')
if params.gpu_type:
cmd.append(f'--accelerator=type={params.gpu_type},count={params.gpu_count}')
if params.preemptible:
cmd.append('--preemptible')
if params.image_name:
cmd.append(f'--image={params.image_name}')
else:
cmd.append(f'--image-family={params.image_family}')
cmd_str = " ".join(shlex.quote(arg) for arg in cmd)
print(f'About to run command: {cmd_str}')
print('')
print('TODO: provide an estimate of the hourly cost of this instance.')
print('TODO: warn user to delete the instance when done.')
print('')
print('Press enter to continue...')
input()
print('Launching. This may take a few minutes...')
subprocess.run(cmd, check=True)
print('')
print('✅ Successfully launched gcloud instance!')
print('')
print('To connect to this instance, please run:')
print('')
print('./ssh_to_gcloud_instance.py')
print('')
print('To monitor, please visit: https://console.cloud.google.com/compute/instances')
print('')
update_env_json({'GCP_INSTANCE': params.name})
if __name__ == "__main__":
main()