Statistics
| Branch: | Tag: | Revision:

root / snf-deploy / snfdeploy / __init__.py @ 1bc6d467

History | View | Annotate | Download (17.4 kB)

1
import json
2
import time
3
import ipaddr
4
import os
5
import signal
6
import time
7
import ConfigParser
8
import argparse
9
import sys
10
import re
11
import random
12
import subprocess
13
import imp
14
import ast
15
from snfdeploy.lib import *
16

    
17
def print_available_actions(command):
18

    
19
  if command == "vcluster":
20
    print """
21
Usage: snf-deploy vcluster
22

23
  Run the following actions concerning the local virtual cluster:
24

25
    - Download base image and create additional disk (if --create-extra-disk is passed)
26
    - Does all the network related actions (bridge, iptables, NAT)
27
    - Launches dnsmasq for dhcp server on bridge
28
    - Creates the virtual cluster (with kvm)
29

30
  """
31

    
32
  if command == "prepare":
33
    print """
34
Usage: snf-deploy prepare
35

36
  Run the following actions concerning deployment preparation:
37

38
    - Setup an internal Domain Name Server
39
    - Tweak hosts and add ssh keys
40
    - Check network setup
41
    - Setup apt repository and apt-get update
42
    - Setup the nfs server and clients among all nodes
43

44
  """
45

    
46
  if command == "backend":
47
    print """
48
Usage: snf-deploy backend [update]
49

50
  Run the following actions concerning a ganeti backend:
51

52
    - Create and add a backend to cyclades
53
    - Does all the net-infra specific actions in backend nodes
54
      (create/connect bridges, iptables..)
55
    - Does all the storage-infra specific actions in backend nodes
56
      depending on the --extra-disk option (create VG, enable lvm/drbd storage..)
57

58
    or
59

60
    - Update packages in an already registered backend in cyclades.
61

62
  """
63

    
64
  if command == "run":
65
    print """
66
Usage: snf-deploy run <action> [<action>...]
67

68
  Run any of the following fabric commands:
69

70

71
    Setup commands:        Init commands:                Admin commands:
72
      setup_apache           add_pools                     activate_user
73
      setup_apt              add_rapi_user                 add_backend
74
      setup_astakos          add_nodes                     add_image_locally
75
      setup_cms              astakos_loaddata              add_network
76
      setup_common           astakos_register_services     add_ns
77
      setup_cyclades         cms_loaddata                  add_user
78
      setup_db               cyclades_loaddata             connect_bridges
79
      setup_ganeti           enable_drbd                   create_bridges
80
      setup_gtools           init_cluster                  create_vlans
81
      setup_gunicorn         setup_nfs_clients             destroy_db
82
      setup_hosts            setup_nfs_server              get_auth_token_from_db
83
      setup_image_helper     update_ns_for_ganeti          get_service_details
84
      setup_image_host                                     gnt_instance_add
85
      setup_iptables                                       gnt_network_add
86
      setup_kamaki         Test commands:                  register_image
87
      setup_lvm              test                          restart_services
88
      setup_mq                                             setup_drbd_dparams
89
      setup_net_infra
90
      setup_network
91
      setup_ns
92
      setup_pithos
93
      setup_pithos_dir
94
      setup_router
95
      setup_vncauthproxy
96
      setup_webproject
97

98
  """
99

    
100
  sys.exit(1)
101

    
102

    
103
def create_dnsmasq_files(args, env):
104

    
105
  print("Customize dnsmasq..")
106
  out = env.dns
107

    
108
  hostsfile = open(out + "/dhcp-hostsfile", "w")
109
  optsfile = open(out + "/dhcp-optsfile", "w")
110
  conffile = open(out + "/conf-file", "w")
111

    
112
  for node, info in env.nodes_info.iteritems():
113
    # serve ip and hostname to nodes
114
    hostsfile.write("%s,%s,%s,2m\n" % (info.mac, info.ip, info.hostname))
115

    
116
  hostsfile.write("52:54:56:*:*:*,ignore\n")
117

    
118
  # Netmask
119
  optsfile.write("1,%s\n" % env.net.netmask)
120
  # Gateway
121
  optsfile.write("3,%s\n" % env.gateway)
122
  # Namesevers
123
  optsfile.write("6,%s\n" % "8.8.8.8")
124

    
125
  dnsconf = """
126
user=dnsmasq
127
bogus-priv
128
no-poll
129
no-negcache
130
leasefile-ro
131
bind-interfaces
132
except-interface=lo
133
dhcp-fqdn
134
no-resolv
135
# disable DNS
136
port=0
137
""".format(env.ns.ip)
138

    
139
  dnsconf += """
140
# serve domain and search domain for resolv.conf
141
domain={5}
142
interface={0}
143
dhcp-hostsfile={1}
144
dhcp-optsfile={2}
145
dhcp-range={0},{4},static,2m
146
""".format(env.bridge, hostsfile.name, optsfile.name,
147
           env.domain, env.net.network, env.domain)
148

    
149
  conffile.write(dnsconf)
150

    
151
  hostsfile.close()
152
  optsfile.close()
153
  conffile.close()
154

    
155

    
156
def cleanup(args, env):
157
  print("Cleaning up bridge, NAT, resolv.conf...")
158

    
159
  for f in os.listdir(env.run):
160
    if re.search(".pid$", f):
161
      check_pidfile(os.path.join(env.run, f))
162

    
163
  create_dir(env.run, True)
164
  # create_dir(env.cmd, True)
165
  cmd = """
166
  iptables -t nat -D POSTROUTING -s {0} -o {1} -j MASQUERADE
167
  echo 0 > /proc/sys/net/ipv4/ip_forward
168
  iptables -D INPUT -i {2} -j ACCEPT
169
  iptables -D FORWARD -i {2} -j ACCEPT
170
  iptables -D OUTPUT -o {2} -j ACCEPT
171
  """.format(env.subnet, get_default_route()[1], env.bridge)
172
  os.system(cmd)
173

    
174
  cmd = """
175
  ip link show {0} && ip addr del {1}/{2} dev {0}
176
  sleep 1
177
  ip link set {0} down
178
  sleep 1
179
  brctl delbr {0}
180
  """.format(env.bridge, env.gateway, env.net.prefixlen)
181
  os.system(cmd)
182

    
183

    
184
def network(args, env):
185
  print("Create bridge..Add gateway IP..Activate NAT..Append NS options to resolv.conf")
186

    
187
  cmd = """
188
  ! ip link show {0} && brctl addbr {0} && ip link set {0} up
189
  sleep 1
190
  ip link set promisc on dev {0}
191
  ip addr add {1}/{2} dev {0}
192
  """.format(env.bridge, env.gateway, env.net.prefixlen)
193
  os.system(cmd)
194

    
195
  cmd = """
196
  iptables -t nat -A POSTROUTING -s {0} -o {1} -j MASQUERADE
197
  echo 1 > /proc/sys/net/ipv4/ip_forward
198
  iptables -I INPUT 1 -i {2} -j ACCEPT
199
  iptables -I FORWARD 1 -i {2} -j ACCEPT
200
  iptables -I OUTPUT 1 -o {2} -j ACCEPT
201
  """.format(env.subnet, get_default_route()[1], env.bridge)
202
  os.system(cmd)
203

    
204

    
205
def image(args, env):
206
  if env.os == "ubuntu":
207
    url = env.ubuntu_image_url
208
  else:
209
    url = env.squeeze_image_url
210

    
211
  disk0 = "{0}/{1}.disk0".format(env.images, env.os)
212
  disk1 = "{0}/{1}.disk1".format(env.images, env.os)
213

    
214
  if url and not os.path.exists(disk0):
215
    cmd = "wget {0} -O {1}".format(url, disk0)
216
    os.system(cmd)
217

    
218
  if ast.literal_eval(env.create_extra_disk) and not os.path.exists(disk1):
219
    if env.lvg:
220
      cmd = "lvcreate -L30G -n{0}.disk1 {1}".format(env.os, env.lvg)
221
      os.system(cmd)
222
      cmd = "ln -s /dev/{0}/{1}.disk1 {2}".format(env.lvg, env.os, disk1)
223
      os.system(cmd)
224
    else:
225
      cmd = "dd if=/dev/zero of={0} bs=10M count=3000".format(disk1)
226
      os.system(cmd)
227

    
228

    
229
def fabcommand(args, env, actions, nodes=[]):
230
  levels = ["status", "aborts", "warnings", "running",
231
            "stdout", "stderr", "user", "debug"]
232

    
233
  level_aliases = {
234
    "output": ["stdout", "stderr"],
235
    "everything": ["warnings", "running", "user", "output"]
236
    }
237

    
238
  hide = ",".join(level_aliases["everything"])
239
  show = None
240

    
241
  if args.verbose == 1:
242
    show = ",".join(levels[:3])
243
    hide = ",".join(levels[3:])
244
  elif args.verbose == 2:
245
    show = ",".join(levels[:4])
246
    hide = ",".join(levels[4:])
247
  elif args.verbose >= 3 or args.debug:
248
    show = ",".join(levels)
249
    hide = None
250

    
251
  if args.ssh_key:
252
    fabcmd = "fab -i %s " % args.ssh_key
253
  else:
254
    fabcmd = "fab "
255

    
256
  fabcmd += " --fabfile {4}/fabfile.py \
257
setup_env:confdir={0},packages={1},templates={2},cluster_name={3},\
258
autoconf={5}, disable_colors={6} \
259
".format(args.confdir, env.packages, env.templates, args.cluster_name,
260
         env.lib, args.autoconf, args.disable_colors)
261

    
262
  if nodes:
263
    hosts = [env.nodes_info[n].hostname for n in nodes]
264
    actions = [a + ':hosts="%s"' % ";".join(hosts) for a in actions]
265

    
266
  extra = " ".join(actions)
267

    
268
  fabcmd += extra
269

    
270
  if show:
271
    fabcmd += " --show %s " % show
272
  if hide:
273
    fabcmd += " --hide %s " % hide
274

    
275
  # print("snf-deploy run " + " ".join(actions) + " -vvv")
276
  print(fabcmd)
277

    
278
  if not args.dry_run:
279
    os.system(fabcmd)
280

    
281

    
282
def cluster(args, env):
283
  for hostname, mac in env.node2mac.iteritems():
284
    launch_vm(args, env, hostname, mac)
285

    
286
  time.sleep(30)
287
  os.system("reset")
288

    
289

    
290
def launch_vm(args, env, hostname, mac):
291
  check_pidfile("%s/%s.pid" % (env.run, hostname))
292

    
293
  print("Launching cluster node {0}..".format(hostname))
294
  os.environ["BRIDGE"] = env.bridge
295
  if args.vnc:
296
    graphics = "-vnc :{0}".format(random.randint(1, 1000))
297
  else:
298
    graphics = "-nographic"
299

    
300
  disks = """ \
301
-drive file={0}/{1}.disk0,format=raw,if=none,id=drive0,snapshot=on \
302
-device virtio-blk-pci,drive=drive0,id=virtio-blk-pci.0 \
303
  """.format(env.images, env.os)
304

    
305
  if ast.literal_eval(env.create_extra_disk):
306
    disks += """ \
307
-drive file={0}/{1}.disk1,format=raw,if=none,id=drive1,snapshot=on \
308
-device virtio-blk-pci,drive=drive1,id=virtio-blk-pci.1 \
309
  """.format(env.images, env.os)
310

    
311

    
312
  ifup = env.lib + "/ifup"
313
  nics = """ \
314
-netdev tap,id=netdev0,script={0},downscript=no \
315
-device virtio-net-pci,mac={1},netdev=netdev0,id=virtio-net-pci.0 \
316
-netdev tap,id=netdev1,script={0},downscript=no \
317
-device virtio-net-pci,mac={2},netdev=netdev1,id=virtio-net-pci.1 \
318
-netdev tap,id=netdev2,script={0},downscript=no \
319
-device virtio-net-pci,mac={3},netdev=netdev2,id=virtio-net-pci.2 \
320
  """.format(ifup, mac, randomMAC(), randomMAC())
321

    
322
  cmd = """
323
/usr/bin/kvm -name {0} -pidfile {1}/{0}.pid -balloon virtio -daemonize \
324
-monitor unix:{1}/{0}.monitor,server,nowait -usbdevice tablet -boot c \
325
{2} \
326
{3} \
327
-m {4} -smp {5} {6} \
328
  """.format(hostname, env.run, disks, nics, args.mem, args.smp, graphics)
329
  print cmd
330
  os.system(cmd)
331

    
332

    
333
def dnsmasq(args, env):
334
  check_pidfile(env.run + "/dnsmasq.pid")
335
  cmd = "dnsmasq --pid-file={0}/dnsmasq.pid --conf-file={1}/conf-file".format(env.run, env.dns)
336
  os.system(cmd)
337

    
338

    
339
def get_packages(args, env):
340
  if env.package_url:
341
    os.system("rm {0}/*.deb".format(env.packages))
342
    os.system("wget -r --level=1 -nH --no-parent --cut-dirs=4 {0} -P {1}".format(env.package_url, env.packages))
343

    
344

    
345
def parse_options():
346
  parser = argparse.ArgumentParser()
347

    
348
  # Directories to load/store config
349
  parser.add_argument("-c", dest="confdir",
350
                      default="/etc/snf-deploy",
351
                      help="Directory to find default configuration")
352
  parser.add_argument("--dry-run", dest="dry_run",
353
                      default=False, action="store_true",
354
                      help="Do not execute or write anything.")
355
  parser.add_argument("-v", dest="verbose",
356
                      default=0, action="count",
357
                      help="Increase verbosity.")
358
  parser.add_argument("-d", dest="debug",
359
                      default=False, action="store_true",
360
                      help="Debug mode")
361
  parser.add_argument("--autoconf", dest="autoconf",
362
                      default=False, action="store_true",
363
                      help="In case of all in one auto conf setup")
364

    
365
  # virtual cluster related options
366
  parser.add_argument("--mem", dest="mem",
367
                      default=2024,
368
                      help="Memory for every virutal node")
369
  parser.add_argument("--smp", dest="smp",
370
                      default=1,
371
                      help="Virtual CPUs for every virtual node")
372
  parser.add_argument("--vnc", dest="vnc",
373
                      default=False, action="store_true",
374
                      help="Wheter virtual nodes will have a vnc console or not")
375
  parser.add_argument("-k", "--keygen", dest="keygen",
376
                      default=False, action="store_true",
377
                      help="Whether to create new ssh key pairs")
378

    
379
  parser.add_argument("-i", "--ssh-key", dest="ssh_key",
380
                      default=None,
381
                      help="Path of an existing ssh key to use")
382

    
383
  # backend related options
384
  parser.add_argument("--cluster-name", dest="cluster_name",
385
                      default="ganeti1",
386
                      help="The cluster name in ganeti.conf")
387

    
388
  # backend related options
389
  parser.add_argument("--cluster-node", dest="cluster_node",
390
                      default=None,
391
                      help="The node to add to the existing cluster")
392

    
393
  # available commands
394
  parser.add_argument("command", type=str,
395
                      choices=["packages", "vcluster", "prepare",
396
                               "synnefo", "backend", "ganeti",
397
                               "run", "cleanup", "test",
398
                               "all", "add"],
399
                      help="Run on of the supported deployment commands")
400

    
401
  # available actions for the run command
402
  parser.add_argument("actions", type=str, nargs="*",
403
                      help="Run one or more of the supported subcommands")
404

    
405
  # disable colors in terminal
406
  parser.add_argument("--disable-colors", dest="disable_colors", default=False,
407
                      action="store_true", help="Disable colors in terminal")
408

    
409
  return parser.parse_args()
410

    
411

    
412
def get_actions(*args):
413
    actions = {
414
      # prepare actions
415
      "ns":  ["setup_ns", "setup_resolv_conf"],
416
      "hosts": ["setup_hosts", "add_keys"],
417
      "check": ["check_dhcp", "check_dns", "check_connectivity", "check_ssh"],
418
      "apt": ["apt_get_update", "setup_apt"],
419
      "nfs": ["setup_nfs_server", "setup_nfs_clients"],
420
      "prepare":  [
421
        "setup_hosts", "add_keys",
422
        "setup_ns", "setup_resolv_conf",
423
        "check_dhcp", "check_dns", "check_connectivity", "check_ssh",
424
        "apt_get_update", "setup_apt",
425
        "setup_nfs_server", "setup_nfs_clients"
426
        ],
427
      # synnefo actions
428
      "synnefo": [
429
        "setup_mq", "setup_db",
430
        "setup_astakos",
431
        #TODO: astakos-quota fails if no user is added.
432
        #      add_user fails if no groups found
433
        "astakos_loaddata", "add_user", "activate_user", "astakos_register_services",
434
        "setup_cms", "cms_loaddata",
435
        "setup_pithos",
436
        "setup_cyclades", "cyclades_loaddata", "add_pools", "setup_vncauthproxy",
437
        "setup_kamaki", "upload_image", "register_image",
438
        "setup_burnin"
439
        ],
440
      "supdate": [
441
        "apt_get_update", "setup_astakos",
442
        "setup_cms", "setup_pithos", "setup_cyclades"
443
        ],
444
      # backend actions
445
      "backend": [
446
        "update_ns_for_ganeti",
447
        "setup_ganeti", "init_cluster",
448
        "add_rapi_user", "add_nodes",
449
        "setup_image_host", "setup_image_helper",
450
        "setup_network",
451
        "setup_gtools", "add_backend", "add_network",
452
        "setup_lvm", "enable_lvm",
453
        "enable_drbd", "setup_drbd_dparams",
454
        "setup_net_infra", "setup_iptables", "setup_router",
455
        ],
456
      "bstorage": [
457
        "setup_lvm", "enable_lvm",
458
        "enable_drbd", "setup_drbd_dparams"
459
        ],
460
      "bnetwork": ["setup_net_infra", "setup_iptables", "setup_router"],
461
      "bupdate": [
462
        "apt_get_update", "setup_ganeti", "setup_image_host", "setup_image_helper",
463
        "setup_network", "setup_gtools"
464
        ],
465
      # ganeti actions
466
      "ganeti": [
467
        "update_ns_for_ganeti",
468
        "setup_ganeti", "init_cluster", "add_nodes",
469
        "setup_image_host", "setup_image_helper", "add_image_locally",
470
        "debootstrap", "setup_net_infra",
471
        "setup_lvm", "enable_lvm", "enable_drbd", "setup_drbd_dparams",
472
        ],
473
      "gupdate": ["setup_apt", "setup_ganeti"],
474
      "gdestroy": ["destroy_cluster"],
475
      }
476

    
477
    ret = []
478
    for x in args:
479
      ret += actions[x]
480

    
481
    return ret
482

    
483

    
484
def create_keys(args, env):
485
  d = os.path.join(env.templates, "root/.ssh")
486
  a = os.path.join(d, "authorized_keys")
487
  for t in ("dsa", "rsa"):
488
    f = os.path.join(d, "id_" + t)
489
    cmd = 'ssh-keygen -q -t {0} -f {1} -N ""'.format(t, f)
490
    os.system(cmd)
491
    cmd = 'cat {0}.pub >> {1}'.format(f, a)
492
    os.system(cmd)
493

    
494
def add_node(args, env):
495
    actions = [
496
      "update_ns_for_node:" + args.cluster_node,
497
      ]
498
    fabcommand(args, env, actions)
499
    actions = [
500
      "setup_resolv_conf",
501
      "apt_get_update",
502
      "setup_apt",
503
      "setup_hosts",
504
      "add_keys",
505
      ]
506
    fabcommand(args, env, actions, [args.cluster_node])
507

    
508
    actions = get_actions("check")
509
    fabcommand(args, env, actions)
510

    
511
    actions = [
512
      "setup_nfs_clients",
513
      "setup_ganeti",
514
      "setup_image_host", "setup_image_helper", "setup_network", "setup_gtools",
515
      ]
516
    fabcommand(args, env, actions, [args.cluster_node])
517

    
518
    actions = [
519
      "add_node:" + args.cluster_node,
520
      ]
521
    fabcommand(args, env, actions)
522

    
523
    actions = [
524
      "setup_lvm", "enable_drbd",
525
      "setup_net_infra", "setup_iptables",
526
      ]
527
    fabcommand(args, env, actions, [args.cluster_node])
528

    
529
def main():
530
  args = parse_options()
531

    
532
  conf = Conf.configure(args.confdir, args.cluster_name, args, args.autoconf)
533
  env = Env(conf)
534

    
535
  create_dir(env.run, False)
536
  create_dir(env.dns, False)
537

    
538
  if args.command == "test":
539
    conf.print_config()
540

    
541
  if args.command == "cleanup":
542
    cleanup(args, env)
543

    
544
  if args.keygen:
545
    create_keys(args, env)
546

    
547
  if args.command == "packages":
548
    create_dir(env.packages, True)
549
    get_packages(args, env)
550

    
551
  if args.command == "vcluster":
552
    image(args, env)
553
    network(args, env)
554
    create_dnsmasq_files(args, env)
555
    dnsmasq(args, env)
556
    cluster(args, env)
557

    
558
  if args.command == "prepare":
559
    actions = get_actions("prepare")
560
    fabcommand(args, env, actions)
561

    
562
  if args.command == "synnefo":
563
    actions = get_actions("synnefo")
564
    fabcommand(args, env, actions)
565

    
566
  if args.command == "backend":
567
    actions = get_actions("backend")
568
    fabcommand(args, env, actions)
569

    
570
  if args.command == "ganeti":
571
    actions += get_actions("ganeti")
572
    fabcommand(args, env, actions)
573

    
574

    
575

    
576

    
577
  if args.command == "all":
578
    actions = get_actions("prepare", "synnefo", "backend")
579
    fabcommand(args, env, actions)
580

    
581
  if args.command == "add":
582
    if args.cluster_node:
583
      add_node(args, env)
584
    else:
585
      actions = get_actions("backend")
586
      fabcommand(args, env, actions)
587

    
588

    
589
  if args.command == "run":
590
    if not args.actions:
591
      print_available_actions(args.command)
592
    else:
593
      fabcommand(args, env, args.actions)
594

    
595

    
596
if __name__ == "__main__":
597
  sys.exit(main())