Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
| Download
Views: 39598
1
#!/usr/bin/env python
2
3
"""
4
5
Development setup:
6
7
gce.py create_smc_server --machine_type g1-small --disk_size=0 0-devel
8
gce.py create_compute_server --machine_type g1-small 0-devel
9
10
"""
11
12
import math, os, sys, argparse, json, time
13
from pprint import pprint
14
15
TIMESTAMP_FORMAT = "%Y-%m-%d-%H%M%S"
16
17
import locale
18
def money(s):
19
locale.setlocale( locale.LC_ALL, 'en_US.UTF-8' )
20
return locale.currency(s)
21
22
from pricing import PRICING
23
24
if 'SALVUS_ROOT' not in os.environ:
25
# this is a hack because I'm in a hurry and want to run this script from cron
26
os.environ['SALVUS_ROOT'] = '%s/salvus/salvus'%os.environ['HOME']
27
os.environ['PATH'] = '%s/google-cloud-sdk/bin/:%s'%(os.environ['HOME'], os.environ['PATH'])
28
29
sys.path.append(os.path.join(os.environ['SALVUS_ROOT'], 'scripts'))
30
from smc_firewall import log, cmd
31
32
33
class GCE(object):
34
def __init__(self):
35
self.project = os.environ.get("SMC_PROJECT", "sage-math-inc")
36
37
def instance_name(self, node, prefix, zone, devel=False):
38
# the zone names have got annoyingly non-canonical...
39
if prefix.startswith('smc'):
40
zone = "-"+self.expand_zone(zone)
41
elif prefix.startswith('compute'):
42
zone = "-"+self.short_zone(zone)
43
else:
44
zone = ''
45
return '%s%s%s%s'%(prefix, node, '-devel' if devel else '', zone)
46
47
def snapshots(self, prefix, devel=False):
48
w = []
49
usage = 0
50
if devel:
51
p = 'devel-%s'%prefix
52
else:
53
p = prefix
54
for x in cmd(['gcloud', 'compute', 'snapshots', 'list'], verbose=0).splitlines()[1:]:
55
v = x.split()
56
if len(v) > 0:
57
if v[0].startswith(p):
58
w.append(v[0])
59
usage += int(v[1])
60
w.sort()
61
return w
62
63
def newest_snapshot(self, prefix=''):
64
return self.snapshots(prefix)[-1]
65
66
def short_zone(self, zone):
67
return zone.split('-')[0]
68
69
def expand_zone(self, zone):
70
# See https://cloud.google.com/compute/docs/zones
71
# Haswell processors are much better than Ivy Bridge and Sandy Bridge.
72
if zone == 'us':
73
return 'us-central1-c'
74
elif zone == 'eu' or zone == 'europe':
75
return 'europe-west1-d'
76
elif zone == 'asia':
77
return 'asia-east1-c' # not Haswell
78
else:
79
return zone
80
81
def _create_compute_server(self, node, zone='us-central1-c',
82
machine_type='n1-highmem-4', network='default',
83
projects_ssd=False, base_ssd=False,
84
projects_size=150,
85
devel=False,
86
address=None,
87
preemptible=False):
88
zone = self.expand_zone(zone)
89
name = self.instance_name(node=node, prefix='compute', zone=zone, devel=devel)
90
91
log("creating root filesystem image")
92
try:
93
opts = ['gcloud', 'compute', '--project', self.project, 'disks', 'create', name,
94
'--zone', zone, '--source-snapshot', self.newest_snapshot('compute')]
95
if base_ssd:
96
opts.extend(['--type', 'pd-ssd'])
97
cmd(opts)
98
except Exception, mesg:
99
if 'already exists' not in str(mesg):
100
raise
101
log("%s already exists", name)
102
103
log("creating /dev/sdb persistent disk")
104
disk_name = "%s-projects"%name
105
try:
106
opts = ['gcloud', 'compute', '--project', self.project, 'disks', 'create', disk_name,
107
'--size', projects_size, '--zone', zone]
108
if projects_ssd:
109
opts.extend(['--type', 'pd-ssd'])
110
cmd(opts)
111
except Exception, mesg:
112
if 'already exists' not in str(mesg):
113
raise
114
115
log("creating and starting compute instance")
116
opts =['gcloud', 'compute', '--project', self.project, 'instances', 'create', name,
117
'--zone', zone, '--machine-type', machine_type, '--network', network]
118
if address:
119
opts.extend(["--address", address])
120
if preemptible:
121
opts.append('--preemptible')
122
else:
123
opts.extend(['--maintenance-policy', 'MIGRATE'])
124
opts.extend(['--scopes', 'https://www.googleapis.com/auth/logging.write',
125
'--disk', 'name=%s,device-name=%s,mode=rw,boot=yes'%(name, name)])
126
opts.extend(['--disk', 'name=%s'%disk_name, 'device-name=%s'%disk_name, 'mode=rw'])
127
opts.extend(['--tags', 'compute'])
128
#if local_ssd:
129
# opts.append('--local-ssd')
130
#else:
131
cmd(opts, system=True)
132
133
if devel:
134
self.set_boot_auto_delete(name=name, zone=zone)
135
136
def create_compute_server0(self, node, zone='us-central1-c', machine_type='n1-highmem-4', preemptible=False, address=None):
137
self._create_compute_server(node=node, zone=zone,
138
machine_type=machine_type, projects_ssd=True,
139
projects_size=150,
140
base_ssd=True,
141
network='default',
142
address=address,
143
preemptible=preemptible)
144
145
def create_compute_server(self, node, zone='us-central1-c', machine_type='n1-highmem-4',
146
projects_size=500, preemptible=False, address=None):
147
self._create_compute_server(node=node, zone=zone,
148
machine_type=machine_type, projects_ssd=False,
149
projects_size=projects_size,
150
base_ssd=False, network='default',
151
preemptible=preemptible,
152
address=address)
153
154
def create_devel_compute_server(self, node, zone='us-central1-c', machine_type='g1-small', preemptible=True):
155
self._create_compute_server(node=node, zone=zone,
156
machine_type = machine_type,
157
projects_ssd = False,
158
projects_size = 10,
159
base_ssd = False,
160
network = 'devel',
161
devel = True,
162
preemptible = preemptible)
163
164
def create_boot_snapshot(self, node, prefix, zone='us-central1-c', devel=False):
165
"""
166
Snapshot the boot disk on the give machine. Typically used for
167
replicating configuration.
168
"""
169
zone = self.expand_zone(zone)
170
instance_name = self.instance_name(node, prefix, zone, devel=devel)
171
snapshot_name = "%s%s-%s"%(prefix, node, time.strftime(TIMESTAMP_FORMAT))
172
cmd(['gcloud', 'compute', 'disks', 'snapshot', '--project', self.project,
173
instance_name,
174
'--snapshot-names', snapshot_name,
175
'--zone', zone], system=True)
176
177
def create_all_boot_snapshots(self):
178
v = []
179
180
v.append(('kubectl', ''))
181
182
v.append(('postgres', 0))
183
184
#for i in [0,1,2,3,4,5]:
185
# v.append(('db', i))
186
187
#for name in self.dev_instances():
188
# node = name.split('v')[1]
189
# v.append(('dev', node))
190
191
#for i in [0,1,2]:
192
# v.append(('web',i))
193
v.append(('admin',0))
194
log("snapshotting storage machine boot images")
195
for i in [0,1,2,3,4,5]:
196
v.append(('storage', i))
197
log("snapshotting compute machine boot images")
198
for i in [0,1,2,3,4,5,6,7,8]:
199
v.append(('compute', i))
200
201
errors = []
202
log("snapshotting boot images: %s"%v)
203
for prefix, node in v:
204
try:
205
log('snapshotting %s%s'%(prefix, node))
206
self.create_boot_snapshot(node=node, prefix=prefix, zone='us-central1-c', devel=False)
207
except Exception, mesg:
208
errors.append(mesg)
209
log("WARNING: issue making snapshot -- %s", mesg)
210
if len(errors) > 0:
211
raise Exception("Errors %s"%errors)
212
213
def create_data_snapshot(self, node, prefix, zone='us-central1-c', devel=False):
214
"""
215
Snapshot the data disk on the given machine. Typically used for
216
backing up very important data.
217
"""
218
zone = self.expand_zone(zone)
219
instance_name = self.instance_name(node, prefix, zone, devel=devel)
220
info = json.loads(cmd(['gcloud', 'compute', 'instances', 'describe',
221
instance_name, '--zone', zone, '--format=json'], verbose=0))
222
223
errors = []
224
for disk in info['disks']:
225
# ignore boot disks (would be True)
226
if disk.get('boot', False):
227
continue
228
# ignore read-only disks (like for globally mounted data volumes)
229
# they would be snapshotted manually
230
if disk.get('mode', 'READ_WRITE') == 'READ_ONLY':
231
continue
232
233
src = disk['source'].split('/')[-1]
234
if 'swap' in src: continue
235
if 'tmp' in src: continue
236
target = 'data-%s-%s'%(src, time.strftime(TIMESTAMP_FORMAT))
237
log("%s --> %s", src, target)
238
try:
239
cmd(['gcloud', 'compute', 'disks', 'snapshot',
240
'--project', self.project,
241
src,
242
'--snapshot-names', target,
243
'--zone', zone], system=True)
244
except Exception, mesg:
245
log("WARNING: issue making snapshot %s -- %s", target, mesg)
246
errors.append(mesg)
247
if len(errors) > 0:
248
raise Exception("Errors %s"%errors)
249
250
def compute_nodes(self, zone='us-central1-c'):
251
# names of the compute nodes in the given zone, with the zone postfix and compute prefix removed.
252
n = len("compute")
253
def f(name):
254
return name[n:name.rfind('-')]
255
info = json.loads(cmd(['gcloud', 'compute', 'instances', 'list', '-r', '^compute.*', '--format=json'], verbose=0))
256
return [f(x['name']) for x in info if x['zone'] == zone and f(x['name'])]
257
258
def create_all_data_snapshots(self, zone='us-central1-c'):
259
# database backup disk
260
#self.create_data_snapshot(node='-backup', prefix='db', zone=zone, devel=False)
261
262
log("snapshotting postgres0 data")
263
self.create_data_snapshot(node=0, prefix='postgres', zone=zone, devel=False)
264
265
for i in range(6):
266
log("snapshotting storage%s storage data"%i)
267
self.create_data_snapshot(node=i, prefix='storage', zone=zone, devel=False)
268
269
log("snapshotting live user data")
270
for n in self.compute_nodes(zone):
271
self.create_data_snapshot(node=n, prefix='compute', zone=zone, devel=False)
272
273
def _create_smc_server(self, node, zone='us-central1-c', machine_type='n1-highmem-2',
274
disk_size=100, network='default', devel=False):
275
zone = self.expand_zone(zone)
276
name = self.instance_name(node=node, prefix='smc', zone=zone, devel=devel)
277
disk_name = "%s-cassandra"%name
278
279
log("creating hard disk root filesystem image")
280
try:
281
cmd(['gcloud', 'compute', '--project', self.project, 'disks', 'create', name,
282
'--zone', zone, '--source-snapshot', self.newest_snapshot('smc'),
283
'--type', 'pd-standard'])
284
except Exception, mesg:
285
if 'already exists' not in str(mesg):
286
raise
287
288
if disk_size:
289
log("creating persistent SSD disk on which to store Cassandra's files")
290
try:
291
cmd(['gcloud', 'compute', '--project', self.project, 'disks', 'create', disk_name,
292
'--size', disk_size, '--zone', zone, '--type', 'pd-ssd'])
293
except Exception, mesg:
294
if 'already exists' not in str(mesg):
295
raise
296
297
log("create and starting smc compute instance")
298
opts = ['gcloud', 'compute', '--project', self.project, 'instances', 'create', name,
299
'--zone', zone, '--machine-type', machine_type, '--network', network,
300
'--maintenance-policy', 'MIGRATE', '--scopes',
301
'https://www.googleapis.com/auth/logging.write',
302
'--tags', 'http-server,https-server,hub',
303
'--disk', 'name=%s'%name, 'device-name=%s'%name, 'mode=rw', 'boot=yes',
304
]
305
if disk_size:
306
opts.extend(['--disk', 'name=%s'%disk_name, 'device-name=%s'%disk_name, 'mode=rw'])
307
cmd(opts, system=True)
308
309
if devel:
310
self.set_boot_auto_delete(name=name, zone=zone)
311
312
def set_boot_auto_delete(self, name, zone):
313
log("set boot disk of %s to auto-delete"%name)
314
cmd(['gcloud', 'compute', '--project', self.project, 'instances',
315
'set-disk-auto-delete', name,
316
'--zone', zone, '--disk', name, '--auto-delete'])
317
318
def create_smc_server(self, node, zone='us-central1-c', machine_type='n1-highmem-2'):
319
self._create_smc_server(node=node, zone=zone, machine_type=machine_type,
320
disk_size=100, network='default', devel=False)
321
322
def create_devel_smc_server(self, node, zone='us-central1-c'):
323
self._create_smc_server(node=node, zone=zone, machine_type='g1-small',
324
disk_size=0, network='devel', devel=True)
325
326
def _create_storage_server(self, node, zone, machine_type,
327
disk_size, network, devel):
328
zone = self.expand_zone(zone)
329
name = self.instance_name(node=node, prefix='storage', zone=zone, devel=devel)
330
disk_name = "%s-projects"%name
331
332
log("creating hard disk root filesystem image")
333
try:
334
cmd(['gcloud', 'compute', '--project', self.project, 'disks', 'create', name,
335
'--zone', zone, '--source-snapshot', self.newest_snapshot('storage'),
336
'--type', 'pd-standard'])
337
except Exception, mesg:
338
if 'already exists' not in str(mesg):
339
raise
340
341
if disk_size:
342
log("creating persistent disk on which to store projects")
343
try:
344
cmd(['gcloud', 'compute', '--project', self.project, 'disks', 'create', disk_name,
345
'--size', disk_size, '--zone', zone, '--type', 'pd-standard'])
346
except Exception, mesg:
347
if 'already exists' not in str(mesg):
348
raise
349
350
log("create storage compute instance")
351
opts = (['gcloud', 'compute', '--project', self.project, 'instances', 'create', name,
352
'--zone', zone,
353
'--tags', 'storage',
354
'--machine-type', machine_type, '--network', network,
355
'--maintenance-policy', 'MIGRATE', '--scopes'] +
356
([] if devel else ['https://www.googleapis.com/auth/devstorage.full_control']) +
357
['https://www.googleapis.com/auth/logging.write',
358
'--disk=name=%s,device-name=%s,mode=rw,boot=yes'%(name, name)] +
359
([] if devel else ['--no-boot-disk-auto-delete'])
360
)
361
if disk_size:
362
opts.extend(['--disk=name=%s,device-name=%s,mode=rw'%(disk_name, disk_name)])
363
try:
364
cmd(opts)
365
except Exception, mesg:
366
if 'already exists' not in str(mesg):
367
raise
368
369
if devel:
370
self.set_boot_auto_delete(name=name, zone=zone)
371
372
def create_storage_server(self, node, zone='us-central1-c', machine_type='n1-standard-1'):
373
# not tested!
374
self._create_storage_server(node=node, zone=zone, machine_type=machine_type,
375
disk_size=2000, network='default', devel=False)
376
377
def create_devel_storage_server(self, node, zone='us-central1-c', machine_type='f1-micro'):
378
self._create_storage_server(node=node, zone=zone, machine_type=machine_type,
379
disk_size=10, network='devel', devel=True)
380
381
def stop_devel_instances(self):
382
for x in cmd(['gcloud', 'compute', 'instances', 'list']).splitlines()[1:]:
383
v = x.split()
384
name = v[0]
385
if '-devel-' in name:
386
zone = v[1]
387
status = v[-1]
388
if status == "RUNNING":
389
log("stopping %s"%name)
390
cmd(['gcloud', 'compute', 'instances', 'stop', '--zone', zone, name])
391
392
def delete_devel_instances(self):
393
for x in cmd(['gcloud', 'compute', 'instances', 'list'], verbose=0).splitlines()[1:]:
394
v = x.split()
395
name = v[0]
396
if '-devel-' in name:
397
zone = v[1]
398
status = v[-1]
399
log("deleting devel instance: %s"%name)
400
cmd(['gcloud', 'compute', 'instances', 'delete', '--zone', zone, name], system=True)
401
402
def devel_etc_hosts(self):
403
hosts = []
404
for x in cmd(['gcloud', 'compute', 'instances', 'list'], verbose=0).splitlines()[1:]:
405
v = x.split()
406
name = v[0]
407
if '-devel-' in name:
408
i = name.find('-devel')
409
hosts.append("%s %s %s"%(v[4], v[0], v[0][:i+6]))
410
if hosts:
411
print "\n".join(hosts)
412
x = open("/etc/hosts").readlines()
413
y = [a.strip() for a in x if '-devel-' not in a]
414
open('/tmp/hosts','w').write('\n'.join(y+hosts))
415
cmd("sudo cp -v /etc/hosts /etc/hosts.0 && sudo cp -v /tmp/hosts /etc/hosts", system=True)
416
417
def start_devel_instances(self):
418
for x in cmd(['gcloud', 'compute', 'instances', 'list']).splitlines()[1:]:
419
v = x.split()
420
name = v[0]
421
if '-devel-' in name:
422
zone = v[1]
423
status = v[-1]
424
if status == "TERMINATED":
425
log("starting %s"%name)
426
cmd(['gcloud', 'compute', 'instances', 'start', '--zone', zone, name])
427
428
def dev_instances(self):
429
a = []
430
for x in cmd(['gcloud', 'compute', 'instances', 'list']).splitlines()[1:]:
431
name = x.split()[0]
432
if name.startswith('dev'):
433
a.append(name)
434
return a
435
436
def create_dev(self, node, zone='us-central1-c', machine_type='n1-standard-1', size=30, preemptible=True, address=''):
437
zone = self.expand_zone(zone)
438
name = self.instance_name(node=node, prefix='dev', zone=zone)
439
440
log("creating %sGB hard disk root filesystem image based on last smc snapshot", size)
441
try:
442
cmd(['gcloud', 'compute', '--project', self.project, 'disks', 'create', name,
443
'--zone', zone, '--source-snapshot', self.newest_snapshot('smc'),
444
'--size', size, '--type', 'pd-standard'])
445
except Exception, mesg:
446
if 'already exists' not in str(mesg):
447
raise
448
449
log("create and starting dev compute instance")
450
opts = ['gcloud', 'compute', '--project', self.project,
451
'instances', 'create', name,
452
'--zone', zone, '--machine-type', machine_type] + \
453
(['--preemptible'] if preemptible else []) + \
454
['--tags', 'http-server,https-server,dev',
455
'--disk', 'name=%s,device-name=%s,mode=rw,boot=yes'%(name, name)]
456
if address:
457
opts.extend(["--address", address])
458
459
cmd(opts, system=True)
460
461
def set_metadata(self, prefix=''):
462
if not prefix:
463
for p in ['smc', 'compute', 'admin', 'storage']:
464
self.set_metadata(p)
465
return
466
names = []
467
for x in cmd(['gcloud', 'compute', 'instances', 'list']).splitlines()[1:]:
468
v = x.split()
469
if v[-1] != 'RUNNING':
470
continue
471
name = v[0]
472
if name.startswith(prefix) and 'devel' not in name: #TODO
473
names.append(name)
474
if prefix == 'smc' and name.startswith('web'): # also allow web servers as "smc servers"
475
names.append(name)
476
names = ' '.join(names)
477
cmd(['gcloud', 'compute', 'project-info', 'add-metadata', '--metadata', '%s-servers=%s'%(prefix, names)])
478
479
def delete_all_old_snapshots(self, max_age_days=7, quiet=False):
480
snapshots = [x.split()[0] for x in cmd(['gcloud', 'compute', 'snapshots', 'list']).splitlines()[1:]]
481
log("snapshots=%s", snapshots)
482
# restrict to snapshots that end with a timestamp
483
# and for each restructure by base
484
w = {}
485
n = len('2015-05-03-081013')
486
for s in snapshots:
487
try:
488
time.strptime(s[-n:], TIMESTAMP_FORMAT)
489
base = s[:-n]
490
if base in w:
491
w[base].append(s[-n:])
492
else:
493
w[base] = [s[-n:]]
494
except: pass
495
print w
496
497
# now decide what to delete
498
to_delete = []
499
cutoff = time.strftime(TIMESTAMP_FORMAT, time.gmtime(time.time()-60*60*24*max_age_days))
500
for base in w:
501
v = w[base]
502
v.sort()
503
if len(v) <= 1 or v[0] >= cutoff:
504
# definitely don't delete last one or if all are new
505
continue
506
for x in v:
507
if x < cutoff:
508
to_delete.append(base + x)
509
510
if len(to_delete) == 0:
511
log("no old snapshots to delete")
512
else:
513
log("deleting these snapshots: %s", to_delete)
514
a = ['gcloud', 'compute', 'snapshots', 'delete']
515
if quiet:
516
a.append("--quiet")
517
cmd(a + to_delete, system=True)
518
519
def snapshot_usage(self): # in gigabytes
520
usage = 0
521
for s in json.loads(cmd(['gcloud', 'compute', 'snapshots', 'list', '--format', 'json'], verbose=0)):
522
# storageBytes need not be set, e.g., while snapshot is being made.
523
usage += float(s.get("storageBytes",0))/1000/1000/1000.
524
return int(math.ceil(usage))
525
526
def snapshot_costs(self):
527
usage = self.snapshot_usage()
528
cost = usage*PRICING['snapshot']
529
log("SNAPSHOT : %8s/month -- snapshot storage of %sGB", money(cost), usage)
530
return cost
531
532
def disk_costs(self):
533
cost = 0
534
usage_standard = 0
535
usage_ssd = 0
536
for x in cmd(['gcloud', 'compute', 'disks', 'list'], verbose=0).splitlines()[1:]:
537
v = x.split()
538
size = int(v[2])
539
typ = v[3]
540
if typ == 'pd-ssd':
541
usage_ssd += size
542
elif typ == 'pd-standard':
543
usage_standard += size
544
cost += size * PRICING[typ]
545
log("DISK : %8s/month -- storage (standard=%sGB, ssd=%sGB)",
546
money(cost), usage_standard, usage_ssd)
547
return cost
548
549
def instance_costs(self):
550
cost_lower = cost_upper = 0
551
n_compute = 0
552
n_web = 0
553
n_db = 0
554
n_other = 0
555
n_dev =0
556
n_admin =0
557
n_storage =0
558
n_preempt = 0
559
for x in cmd(['gcloud', 'compute', 'instances', 'list'], verbose=0).splitlines()[1:]:
560
v = x.split()
561
zone = v[1]
562
machine_type = v[2]
563
status = v[-1]
564
if status != 'RUNNING':
565
continue
566
if len(v) == 7:
567
preempt = (v[3] == 'true')
568
n_preempt += 1
569
else:
570
preempt = False
571
if v[0].startswith('compute'):
572
n_compute += 1
573
elif v[0].startswith('web'):
574
n_web += 1
575
elif v[0].startswith('db'):
576
n_db += 1
577
elif v[0].startswith('dev'):
578
n_dev += 1
579
elif v[0].startswith('admin'):
580
n_admin += 1
581
elif v[0].startswith('storage'):
582
n_storage += 1
583
else:
584
n_other += 1
585
t = machine_type.split('-')
586
if len(t) == 3:
587
b = '-'.join(t[:2])
588
cpus = int(t[2])
589
else:
590
b = machine_type
591
cpus = 1
592
if b == 'custom':
593
print("warning -custom machine types not supported; skipping ", x)
594
continue
595
if preempt:
596
pricing_hour = PRICING[b+'-hour-pre']
597
pricing_month = pricing_hour*24*30.5
598
else:
599
pricing_hour = PRICING[b+'-hour']
600
pricing_month = PRICING[b+'-month']
601
cost_lower += pricing_month * cpus * PRICING[zone.split('-')[0]]
602
cost_upper += pricing_hour *30.5*24* cpus * PRICING[zone.split('-')[0]]
603
log("INSTANCES : %8s/month -- (or %8s/month without sustained!); compute=%s, web=%s, db=%s, dev=%s, admin=%s, storage=%s, other=%s (preempt=%s)",
604
money(cost_lower), money(cost_upper), n_compute, n_web, n_db, n_dev, n_admin, n_storage, n_other, n_preempt)
605
return {'lower':cost_lower, 'upper':cost_upper}
606
607
def network_costs(self):
608
# These are estimates based on usage during March and April. May be lower in future
609
# do to moving everything to GCE. Not sure.
610
us = 700; aus = 150; china = 20
611
costs = 700 * PRICING['egress'] + 150*PRICING['egress-australia'] + 20*PRICING['egress-china']
612
log("NETWORK : %8s/month -- approx. %sGB Americas, %sGB EUR, %sGB CHINA", money(costs), us, aus, china)
613
return costs
614
615
def gcs_costs(self):
616
# usage based on running "time gsutil du -sch" every once in a while, since it takes
617
# quite a while to run.
618
smc_db_backup = 50
619
smc_projects_backup = 1300
620
# This takes about 15 minutes and gives the usage above
621
# time gsutil du -sch gs://smc-projects-bup
622
# time gsutil du -sch gs://smc-db-backup
623
usage = (smc_db_backup + smc_projects_backup)
624
costs = usage * PRICING['gcs-nearline']
625
log("CLOUD STORAGE: %8s/month -- approx. %sGB nearline", money(costs), usage)
626
return costs
627
628
def costs(self):
629
costs = {}
630
total_lower = 0
631
total_upper = 0
632
for t in ['snapshot', 'disk', 'instance', 'network', 'gcs']:
633
costs[t] = getattr(self, '%s_costs'%t)()
634
if isinstance(costs[t], dict):
635
total_lower += costs[t]['lower']
636
total_upper += costs[t]['upper']
637
else:
638
total_lower += costs[t]
639
total_upper += costs[t]
640
log("SILVER SUPPORT : %8s/month ", money(150))
641
total_lower += 150
642
total_upper += 150
643
log("SALES TAX : %8s/month -- 10.1%% WA+Seattle sales tax", money(total_lower*0.101))
644
total_lower *= 1.101
645
total_upper *= 1.101
646
log("TOTAL : %8s/month -- up to as worse as %8s/month without sustained", money(total_lower), money(total_upper))
647
#return costs
648
649
def autostart(self, instance):
650
"""
651
Ensure that each instance in the input is running.
652
"""
653
for x in cmd(['gcloud', 'compute', 'instances', 'list'], verbose=0).splitlines()[1:]:
654
v = x.split()
655
if len(v) > 2 and v[-1] != 'RUNNING':
656
name = v[0]; zone = v[1]
657
for x in instance:
658
if name.startswith(x):
659
log("Starting %s... at %s", name, time.asctime())
660
cmd(' '.join(['gcloud', 'compute', 'instances', 'start', '--zone', zone, name]) + '&', system=True)
661
break
662
663
664
if __name__ == "__main__":
665
666
parser = argparse.ArgumentParser(description="Create VM instances on Google Compute Engine")
667
subparsers = parser.add_subparsers(help='sub-command help')
668
669
def f(subparser):
670
function = subparser.prog.split()[-1]
671
def g(args):
672
special = [k for k in args.__dict__.keys() if k not in ['func']]
673
out = []
674
errors = False
675
kwds = dict([(k,getattr(args, k)) for k in special])
676
try:
677
result = getattr(GCE(), function)(**kwds)
678
except Exception, mesg:
679
raise #-- for debugging
680
errors = True
681
result = {'error':str(mesg)}
682
print json.dumps(result)
683
if errors:
684
sys.exit(1)
685
subparser.set_defaults(func=g)
686
687
parser_create_compute_server = subparsers.add_parser('create_compute_server', help='')
688
parser_create_compute_server.add_argument('node', help="", type=str)
689
parser_create_compute_server.add_argument('--zone', help="", type=str, default="us-central1-c")
690
parser_create_compute_server.add_argument('--projects_size', help="", type=int, default=500)
691
parser_create_compute_server.add_argument('--machine_type', help="", type=str, default="n1-highmem-4")
692
parser_create_compute_server.add_argument('--address', help="an IP address or the name or URI of an address", type=str, default="")
693
parser_create_compute_server.add_argument('--preemptible', default=False, action="store_const", const=True)
694
f(parser_create_compute_server)
695
696
parser_create_devel_compute_server = subparsers.add_parser('create_devel_compute_server', help='')
697
parser_create_devel_compute_server.add_argument('node', help="", type=str)
698
parser_create_devel_compute_server.add_argument('--zone', help="", type=str, default="us-central1-c")
699
f(parser_create_devel_compute_server)
700
701
parser_create_smc_server = subparsers.add_parser('create_smc_server', help='')
702
parser_create_smc_server.add_argument('node', help="", type=str)
703
parser_create_smc_server.add_argument('--zone', help="", type=str, default="us-central1-c")
704
parser_create_smc_server.add_argument('--machine_type', help="", type=str, default="n1-highmem-2")
705
f(parser_create_smc_server)
706
707
parser_create_devel_smc_server = subparsers.add_parser('create_devel_smc_server', help='')
708
parser_create_devel_smc_server.add_argument('node', help="", type=str)
709
parser_create_devel_smc_server.add_argument('--zone', help="", type=str, default="us-central1-c")
710
f(parser_create_devel_smc_server)
711
712
parser_create_storage_server = subparsers.add_parser('create_storage_server', help='')
713
parser_create_storage_server.add_argument('node', help="", type=str)
714
parser_create_storage_server.add_argument('--zone', help="", type=str, default="us-central1-c")
715
parser_create_storage_server.add_argument('--machine_type', help="", type=str, default="n1-highcpu-2")
716
f(parser_create_storage_server)
717
718
parser_create_devel_storage_server = subparsers.add_parser('create_devel_storage_server', help='')
719
parser_create_devel_storage_server.add_argument('node', help="", type=str)
720
parser_create_devel_storage_server.add_argument('--zone', help="", type=str, default="us-central1-c")
721
f(parser_create_devel_storage_server)
722
723
parser_create_dev = subparsers.add_parser('create_dev', help='create a complete self contained development instance')
724
parser_create_dev.add_argument('node', help="", type=str)
725
parser_create_dev.add_argument('--zone', help="default=(us-central1-c)", type=str, default="us-central1-c")
726
parser_create_dev.add_argument('--machine_type', help="GCE instance type (default=n1-standard-1)", type=str, default="n1-standard-1")
727
parser_create_dev.add_argument('--size', help="base image size (should be at least 20GB)", type=int, default=20)
728
parser_create_dev.add_argument('--preemptible', default=False, action="store_const", const=True)
729
parser_create_dev.add_argument('--address', help="an IP address or the name or URI of an address", type=str, default="")
730
f(parser_create_dev)
731
732
733
parser_compute_nodes = subparsers.add_parser('compute_nodes', help='node names of all compute nodes in the given zeon')
734
parser_compute_nodes.add_argument('--zone', help="default=(us-central1-c)", type=str, default="us-central1-c")
735
f(parser_compute_nodes)
736
737
f(subparsers.add_parser("stop_devel_instances", help='stop all the *devel* instances'))
738
f(subparsers.add_parser("start_devel_instances", help='start all the *devel* instances running'))
739
f(subparsers.add_parser("delete_devel_instances", help='completely delete all the *devel* instances'))
740
f(subparsers.add_parser("devel_etc_hosts", help='add external devel instance ips to /etc/hosts'))
741
742
parser_create_boot_snapshot = subparsers.add_parser('create_boot_snapshot', help='')
743
parser_create_boot_snapshot.add_argument('node', help="", type=str)
744
parser_create_boot_snapshot.add_argument('prefix', help="", type=str)
745
parser_create_boot_snapshot.add_argument('--zone', help="", type=str, default="us-central1-c")
746
parser_create_boot_snapshot.add_argument("--devel", default=False, action="store_const", const=True)
747
f(parser_create_boot_snapshot)
748
749
f(subparsers.add_parser('create_all_boot_snapshots', help='snapshot all boot images of production machines'))
750
751
parser_create_data_snapshot = subparsers.add_parser('create_data_snapshot', help='')
752
parser_create_data_snapshot.add_argument('node', help="", type=str)
753
parser_create_data_snapshot.add_argument('prefix', help="", type=str)
754
parser_create_data_snapshot.add_argument('--zone', help="", type=str, default="us-central1-c")
755
parser_create_data_snapshot.add_argument("--devel", default=False, action="store_const", const=True)
756
f(parser_create_data_snapshot)
757
758
f(subparsers.add_parser('create_all_data_snapshots', help='snapshot all data images of production machines'))
759
760
parser_delete_all_old_snapshots = subparsers.add_parser('delete_all_old_snapshots',
761
help='delete every snapshot foo-[date] such that there is a newer foo-[data_newer] *and* foo-[date] is older than max_age_days')
762
parser_delete_all_old_snapshots.add_argument('--max_age_days', help="", type=int, default=15)
763
parser_delete_all_old_snapshots.add_argument("--quiet",
764
help="Disable all interactive prompts when running gcloud commands. If input is required, defaults will be used.",
765
default=False, action="store_const", const=True)
766
f(parser_delete_all_old_snapshots)
767
768
for cost in ['snapshot_', 'disk_', 'instance_', 'network_', 'gcs_', '']:
769
f(subparsers.add_parser('%scosts'%cost))
770
771
parser_set_metadata = subparsers.add_parser('set_metadata', help='')
772
parser_set_metadata.add_argument('--prefix', help="", type=str, default="")
773
f(parser_set_metadata)
774
775
parser_autostart = subparsers.add_parser('autostart', help='start any listed instances if they are TERMINATED; use from a crontab in order to ensure that pre-empt instances stay running')
776
parser_autostart.add_argument("instance", help="name of instance", type=str, nargs="+")
777
f(parser_autostart)
778
779
args = parser.parse_args()
780
args.func(args)
781
782