Statistics
| Branch: | Tag: | Revision:

root / snf-deploy / fabfile.py @ 34e315ac

History | View | Annotate | Download (37.4 kB)

1
from __future__ import with_statement
2
from fabric.api import *
3
from fabric.contrib.console import confirm
4
from random import choice
5
from fabric.operations import run, put
6
import re
7
import shutil, os
8
from functools import wraps
9
import imp
10
import ConfigParser
11
import sys
12
import tempfile
13
import ast
14
from snfdeploy.lib import *
15
from snfdeploy import massedit
16

    
17

    
18
def setup_env(confdir="conf", packages="packages",
19
              templates="files", cluster_name="ganeti1", autoconf=False, disable_colors=False):
20
    print("Loading configuration for synnefo...")
21
    print(" * Using config files under %s..." % confdir)
22
    print(" * Using %s and %s for packages and templates accordingly..." % (packages, templates))
23

    
24
    autoconf = ast.literal_eval(autoconf)
25
    disable_colors = ast.literal_eval(disable_colors)
26
    conf = Conf.configure(confdir=confdir, cluster_name=cluster_name, autoconf=autoconf)
27
    env.env = Env(conf)
28

    
29
    env.local = autoconf
30
    env.password = env.env.password
31
    env.user = env.env.user
32
    env.shell = "/bin/bash -c"
33

    
34
    if disable_colors:
35
        disable_color()
36

    
37
    if env.env.cms.hostname in [env.env.accounts.hostname, env.env.cyclades.hostname, env.env.pithos.hostname]:
38
      env.cms_pass = True
39
    else:
40
      env.cms_pass = False
41

    
42
    if env.env.accounts.hostname in [env.env.cyclades.hostname, env.env.pithos.hostname]:
43
      env.csrf_disable = True
44
    else:
45
      env.csrf_disable = False
46

    
47

    
48
    env.roledefs = {
49
        "nodes": env.env.ips,
50
        "ips": env.env.ips,
51
        "accounts": [env.env.accounts.ip],
52
        "cyclades": [env.env.cyclades.ip],
53
        "pithos": [env.env.pithos.ip],
54
        "cms": [env.env.cms.ip],
55
        "mq": [env.env.mq.ip],
56
        "db": [env.env.db.ip],
57
        "ns": [env.env.ns.ip],
58
        "client": [env.env.client.ip],
59
        "router": [env.env.router.ip],
60
    }
61

    
62
    env.enable_lvm = False
63
    env.enable_drbd = False
64
    if ast.literal_eval(env.env.create_extra_disk) and env.env.extra_disk:
65
        env.enable_lvm = True
66
        env.enable_drbd = True
67

    
68
    env.roledefs.update({
69
        "ganeti": env.env.cluster_ips,
70
        "master": [env.env.master.ip],
71
    })
72

    
73

    
74
def install_package(package):
75
    debug(env.host, " * Installing package %s..." % package)
76
    APT_GET = "export DEBIAN_FRONTEND=noninteractive ;apt-get install -y --force-yes "
77

    
78
    if ast.literal_eval(env.env.use_local_packages):
79
        with settings(warn_only=True):
80
            deb = local("ls %s/%s*deb" % (env.env.packages, package))
81
            if deb:
82
                debug(env.host, " * Package %s found in %s..." % (package, env.env.packages))
83
                put(deb, "/tmp/")
84
                try_run("dpkg -i /tmp/%s*deb || " % package + APT_GET + "-f")
85
                try_run("rm /tmp/%s*deb" % package)
86
                return
87

    
88
    info = getattr(env.env, package)
89
    if info in ["stable", "squeeze-backports", "testing", "unstable"]:
90
        APT_GET += " -t %s %s " % (info, package)
91
    elif info:
92
        APT_GET += " %s=%s " % (package, info)
93
    else:
94
        APT_GET += package
95

    
96
    try_run(APT_GET)
97

    
98
    return
99

    
100

    
101
@roles("ns")
102
def update_ns_for_ganeti():
103
    debug(env.host, "Updating name server entries for backend %s..." % env.env.cluster.fqdn)
104
    update_arecord(env.env.cluster)
105
    update_ptrrecord(env.env.cluster)
106
    try_run("/etc/init.d/bind9 restart")
107

    
108

    
109
@roles("ns")
110
def update_ns_for_node(node):
111
    info = env.env.nodes_info.get(node)
112
    update_arecord(info)
113
    update_ptrrecord(info)
114
    try_run("/etc/init.d/bind9 restart")
115

    
116

    
117
@roles("ns")
118
def update_arecord(host):
119
    filename = "/etc/bind/zones/" + env.env.domain
120
    cmd = """
121
    echo '{0}' >> {1}
122
    """.format(host.arecord, filename)
123
    try_run(cmd)
124

    
125

    
126
@roles("ns")
127
def update_cnamerecord(host):
128
    filename = "/etc/bind/zones/" + env.env.domain
129
    cmd = """
130
    echo '{0}' >> {1}
131
    """.format(host.cnamerecord, filename)
132
    try_run(cmd)
133

    
134

    
135
@roles("ns")
136
def update_ptrrecord(host):
137
    filename = "/etc/bind/rev/synnefo.in-addr.arpa.zone"
138
    cmd = """
139
    echo '{0}' >> {1}
140
    """.format(host.ptrrecord, filename)
141
    try_run(cmd)
142

    
143
@roles("nodes")
144
def apt_get_update():
145
    debug(env.host, "apt-get update....")
146
    try_run("apt-get update")
147

    
148
@roles("ns")
149
def setup_ns():
150
    debug(env.host, "Setting up name server..")
151
    #WARNING: this should be remove after we are done
152
    # because gevent does pick randomly nameservers and google does
153
    # not know our setup!!!!!
154
    apt_get_update()
155
    install_package("bind9")
156
    tmpl = "/etc/bind/named.conf.local"
157
    replace = {
158
      "domain": env.env.domain,
159
      }
160
    custom = customize_settings_from_tmpl(tmpl, replace)
161
    put(custom, tmpl)
162

    
163
    try_run("mkdir -p /etc/bind/zones")
164
    tmpl = "/etc/bind/zones/example.com"
165
    replace = {
166
      "domain": env.env.domain,
167
      "ns_node_ip": env.env.ns.ip,
168
      }
169
    custom = customize_settings_from_tmpl(tmpl, replace)
170
    remote = "/etc/bind/zones/" + env.env.domain
171
    put(custom, remote)
172

    
173
    try_run("mkdir -p /etc/bind/rev")
174
    tmpl = "/etc/bind/rev/synnefo.in-addr.arpa.zone"
175
    replace = {
176
      "domain": env.env.domain,
177
      }
178
    custom = customize_settings_from_tmpl(tmpl, replace)
179
    put(custom, tmpl)
180

    
181
    tmpl = "/etc/bind/named.conf.options"
182
    replace = {
183
      "NODE_IPS": ";".join(env.env.ips),
184
      }
185
    custom = customize_settings_from_tmpl(tmpl, replace)
186
    put(custom, tmpl, mode=0644)
187

    
188
    for role, info in env.env.roles.iteritems():
189
        if role == "ns":
190
            continue
191
        update_cnamerecord(info)
192
    for node, info in env.env.nodes_info.iteritems():
193
        update_arecord(info)
194
        update_ptrrecord(info)
195

    
196
    try_run("/etc/init.d/bind9 restart")
197

    
198

    
199
@roles("nodes")
200
def check_dhcp():
201
    debug(env.host, "Checking IPs for synnefo..")
202
    for n, info in env.env.nodes_info.iteritems():
203
        try_run("ping -c 1 " + info.ip)
204

    
205
@roles("nodes")
206
def check_dns():
207
    debug(env.host, "Checking fqdns for synnefo..")
208
    for n, info in env.env.nodes_info.iteritems():
209
        try_run("ping -c 1 " + info.fqdn)
210

    
211
    for n, info in env.env.roles.iteritems():
212
        try_run("ping -c 1 " + info.fqdn)
213

    
214
@roles("nodes")
215
def check_connectivity():
216
    debug(env.host, "Checking internet connectivity..")
217
    try_run("ping -c 1 www.google.com")
218

    
219

    
220
@roles("nodes")
221
def check_ssh():
222
    debug(env.host, "Checking password-less ssh..")
223
    for n, info in env.env.nodes_info.iteritems():
224
        try_run("ssh " + info.fqdn + "  date")
225

    
226

    
227
@roles("ips")
228
def add_keys():
229
    debug(env.host, "Adding rsa/dsa keys..")
230
    try_run("mkdir -p /root/.ssh")
231
    cmd = """
232
for f in $(ls /root/.ssh/*); do
233
  cp $f $f.bak
234
done
235
    """
236
    try_run(cmd)
237
    files = ["authorized_keys", "id_dsa", "id_dsa.pub",
238
             "id_rsa", "id_rsa.pub"]
239
    for f in files:
240
      tmpl = "/root/.ssh/" + f
241
      replace = {}
242
      custom = customize_settings_from_tmpl(tmpl, replace)
243
      put(custom, tmpl)
244

    
245
    cmd = """
246
if [ -e /root/.ssh/authorized_keys.bak ]; then
247
  cat /root/.ssh/authorized_keys.bak >> /root/.ssh/authorized_keys
248
fi
249
    """
250
    debug(env.host, "Updating exising authorized keys..")
251
    try_run(cmd)
252

    
253
@roles("ips")
254
def setup_resolv_conf():
255
    debug(env.host, "Tweak /etc/resolv.conf...")
256
    try_run("/etc/init.d/network-manager stop")
257
    tmpl = "/etc/dhcp/dhclient-enter-hooks.d/nodnsupdate"
258
    replace = {}
259
    custom = customize_settings_from_tmpl(tmpl, replace)
260
    put(custom, tmpl, mode=0644)
261
    try_run("cp /etc/resolv.conf /etc/resolv.conf.bak")
262
    tmpl = "/etc/resolv.conf"
263
    replace = {
264
      "domain": env.env.domain,
265
      "ns_node_ip": env.env.ns.ip,
266
      }
267
    custom = customize_settings_from_tmpl(tmpl, replace)
268
    put(custom, tmpl)
269
    try_run("chattr +i /etc/resolv.conf")
270

    
271

    
272
@roles("ips")
273
def setup_hosts():
274
    debug(env.host, "Tweaking /etc/hosts and ssh_config files...")
275
    try_run("echo StrictHostKeyChecking no >> /etc/ssh/ssh_config")
276
    cmd = " sed -i 's/^127.*/127.0.0.1 localhost/g' /etc/hosts "
277
    try_run(cmd)
278

    
279

    
280
def try_run(cmd):
281
    try:
282
      if env.local:
283
        return local(cmd, capture=True)
284
      else:
285
        return run(cmd)
286
    except:
287
      debug(env.host, "WARNING: command failed. Continuing anyway...")
288

    
289
def create_bridges():
290
    debug(env.host, " * Creating bridges...")
291
    install_package("bridge-utils")
292
    cmd = """
293
    brctl addbr {0} ; ip link set {0} up
294
    """.format(env.env.common_bridge)
295
    try_run(cmd)
296

    
297

    
298
def connect_bridges():
299
    debug(env.host, " * Connecting bridges...")
300
    cmd = """
301
    brctl addif {0} {1}
302
    """.format(env.env.common_bridge, env.env.public_iface)
303
    #try_run(cmd)
304

    
305

    
306
@roles("ganeti")
307
def setup_net_infra():
308
    debug(env.host, "Setup networking infrastracture..")
309
    create_bridges()
310
    connect_bridges()
311

    
312

    
313
@roles("ganeti")
314
def setup_lvm():
315
    debug(env.host, "create volume group %s for ganeti.." % env.env.vg)
316
    if env.enable_lvm:
317
        install_package("lvm2")
318
        cmd = """
319
        pvcreate {0}
320
        vgcreate {1} {0}
321
        """.format(env.env.extra_disk, env.env.vg)
322
        try_run(cmd)
323

    
324

    
325
def customize_settings_from_tmpl(tmpl, replace):
326
    debug(env.host, " * Customizing template %s..." % tmpl)
327
    local = env.env.templates + tmpl
328
    _, custom = tempfile.mkstemp()
329
    shutil.copyfile(local, custom)
330
    for k, v in replace.iteritems():
331
        regex = "re.sub('%{0}%', '{1}', line)".format(k.upper(), v)
332
        massedit.edit_files([custom], [regex], dry_run=False)
333

    
334
    return custom
335

    
336

    
337
@roles("nodes")
338
def setup_apt():
339
    debug(env.host, "Setting up apt sources...")
340
    install_package("curl")
341
    cmd = """
342
    echo 'APT::Install-Suggests "false";' >> /etc/apt/apt.conf
343
    curl -k https://dev.grnet.gr/files/apt-grnetdev.pub | apt-key add -
344
    """
345
    try_run(cmd)
346
    tmpl = "/etc/apt/sources.list.d/okeanos.list"
347
    replace = {}
348
    custom = customize_settings_from_tmpl(tmpl, replace)
349
    put(custom, tmpl)
350
    apt_get_update()
351

    
352

    
353
@roles("cyclades", "cms", "pithos", "accounts")
354
def restart_services():
355
    debug(env.host, " * Restarting apache2 and gunicorn...")
356
    try_run("/etc/init.d/gunicorn restart")
357
    try_run("/etc/init.d/apache2 restart")
358

    
359

    
360
def setup_gunicorn():
361
    debug(env.host, " * Setting up gunicorn...")
362
    install_package("gunicorn")
363
    tmpl = "/etc/gunicorn.d/synnefo"
364
    replace = {}
365
    custom = customize_settings_from_tmpl(tmpl, replace)
366
    put(custom, tmpl, mode=0644)
367
    try_run("/etc/init.d/gunicorn restart")
368

    
369

    
370
def setup_apache():
371
    debug(env.host, " * Setting up apache2...")
372
    host_info = env.env.ips_info[env.host]
373
    install_package("apache2")
374
    tmpl = "/etc/apache2/sites-available/synnefo"
375
    replace = {
376
        "HOST": host_info.fqdn,
377
    }
378
    custom = customize_settings_from_tmpl(tmpl, replace)
379
    put(custom, tmpl)
380
    tmpl = "/etc/apache2/sites-available/synnefo-ssl"
381
    custom = customize_settings_from_tmpl(tmpl, replace)
382
    put(custom, tmpl)
383
    cmd = """
384
    a2enmod ssl
385
    a2enmod rewrite
386
    a2dissite default
387
    a2ensite synnefo
388
    a2ensite synnefo-ssl
389
    a2enmod headers
390
    a2enmod proxy_http
391
    a2dismod autoindex
392
    """
393
    try_run(cmd)
394
    try_run("/etc/init.d/apache2 restart")
395

    
396

    
397
@roles("mq")
398
def setup_mq():
399
    debug(env.host, "Setting up RabbitMQ...")
400
    install_package("rabbitmq-server")
401
    cmd = """
402
    rabbitmqctl add_user {0} {1}
403
    rabbitmqctl set_permissions {0} ".*" ".*" ".*"
404
    rabbitmqctl delete_user guest
405
    rabbitmqctl set_user_tags {0} administrator
406
    """.format(env.env.synnefo_user, env.env.synnefo_rabbitmq_passwd)
407
    try_run(cmd)
408
    try_run("/etc/init.d/rabbitmq-server restart")
409

    
410

    
411
@roles("db")
412
def allow_access_in_db(ip, user="all", trust=""):
413
    cmd = """
414
    echo host all {0} {1}/32 md5 {2} >> /etc/postgresql/8.4/main/pg_hba.conf
415
    """.format(user, ip, trust)
416
    try_run(cmd)
417
    try_run("/etc/init.d/postgresql restart")
418

    
419
@roles("db")
420
def setup_db():
421
    debug(env.host, "Setting up DataBase server...")
422
    install_package("postgresql")
423

    
424
    tmpl = "/tmp/db-init.psql"
425
    replace = {
426
        "synnefo_user": env.env.synnefo_user,
427
        "synnefo_db_passwd": env.env.synnefo_db_passwd,
428
        }
429
    custom = customize_settings_from_tmpl(tmpl, replace)
430
    put(custom, tmpl)
431
    cmd = 'su - postgres -c "psql -w -f %s" ' % tmpl
432
    try_run(cmd)
433
    cmd = """
434
    echo "listen_addresses = '*'" >> /etc/postgresql/8.4/main/postgresql.conf
435
    """
436
    try_run(cmd)
437

    
438
    try_run("/etc/init.d/postgresql restart")
439
    allow_access_in_db("127.0.0.1", "postgres", "trust")
440

    
441

    
442
@roles("db")
443
def destroy_db():
444
    try_run("""su - postgres -c ' psql -w -c "drop database snf_apps" '""")
445
    try_run("""su - postgres -c ' psql -w -c "drop database snf_pithos" '""")
446

    
447

    
448
def setup_webproject():
449
    debug(env.host, " * Setting up snf-webproject...")
450
    with settings(hide("everything")):
451
        try_run("ping -c1 " + env.env.db.ip)
452
    setup_common()
453
    install_package("snf-webproject")
454
    install_package("python-psycopg2")
455
    install_package("python-gevent")
456
    tmpl = "/etc/synnefo/webproject.conf"
457
    replace = {
458
        "synnefo_user": env.env.synnefo_user,
459
        "synnefo_db_passwd": env.env.synnefo_db_passwd,
460
        "db_node": env.env.db.ip,
461
        "domain": env.env.domain,
462
    }
463
    custom = customize_settings_from_tmpl(tmpl, replace)
464
    put(custom, tmpl, mode=0644)
465
    with settings(host_string=env.env.db.ip):
466
        host_info = env.env.ips_info[env.host]
467
        allow_access_in_db(host_info.ip)
468
    try_run("/etc/init.d/gunicorn restart")
469

    
470

    
471
def setup_common():
472
    debug(env.host, " * Setting up snf-common...")
473
    host_info = env.env.ips_info[env.host]
474
    install_package("python-objpool")
475
    install_package("snf-common")
476
    install_package("python-astakosclient")
477
    install_package("snf-django-lib")
478
    install_package("snf-branding")
479
    tmpl = "/etc/synnefo/common.conf"
480
    replace = {
481
        #FIXME:
482
        "EMAIL_SUBJECT_PREFIX": env.host,
483
        "domain": env.env.domain,
484
        "HOST": host_info.fqdn,
485
    }
486
    custom = customize_settings_from_tmpl(tmpl, replace)
487
    put(custom, tmpl, mode=0644)
488
    try_run("/etc/init.d/gunicorn restart")
489

    
490
@roles("accounts")
491
def astakos_loaddata():
492
    debug(env.host, " * Loading initial data to astakos...")
493
    cmd = """
494
    snf-manage loaddata groups
495
    """
496
    try_run(cmd)
497

    
498

    
499
@roles("accounts")
500
def astakos_register_services():
501
    debug(env.host, " * Register services in astakos...")
502
    cmd = """
503
    snf-manage component-add "home" https://{0} home-icon.png
504
    snf-manage component-add "cyclades" https://{1}/cyclades/ui/
505
    snf-manage component-add "pithos" https://{2}/pithos/ui/
506
    snf-manage component-add "astakos" https://{3}/astakos/ui/
507
    """.format(env.env.cms.fqdn, env.env.cyclades.fqdn, env.env.pithos.fqdn, env.env.accounts.fqdn)
508
    try_run(cmd)
509
    import_service("astakos")
510
    import_service("pithos")
511
    import_service("cyclades")
512
    tmpl = "/tmp/resources.json"
513
    replace = {}
514
    custom = customize_settings_from_tmpl(tmpl, replace)
515
    put(custom, tmpl)
516
    try_run("snf-manage resource-import --json %s" % tmpl)
517
    cmd = """
518
    snf-manage resource-modify --limit 40G pithos.diskspace
519
    snf-manage resource-modify --limit 2 astakos.pending_app
520
    snf-manage resource-modify --limit 4 cyclades.vm
521
    snf-manage resource-modify --limit 40G cyclades.disk
522
    snf-manage resource-modify --limit 8G cyclades.ram
523
    snf-manage resource-modify --limit 16 cyclades.cpu
524
    snf-manage resource-modify --limit 4 cyclades.network.private
525
    """
526
    try_run(cmd)
527

    
528

    
529
@roles("accounts")
530
def add_user():
531
    debug(env.host, " * adding user %s to astakos..." % env.env.user_email)
532
    email=env.env.user_email
533
    name=env.env.user_name
534
    lastname=env.env.user_lastname
535
    passwd=env.env.user_passwd
536
    cmd = """
537
    snf-manage user-add {0} {1} {2}
538
    """.format(email, name, lastname)
539
    try_run(cmd)
540
    with settings(host_string=env.env.db.ip):
541
        uid, user_auth_token, user_uuid = get_auth_token_from_db(email)
542
    cmd = """
543
    snf-manage user-modify --password {0} {1}
544
    """.format(passwd, uid)
545
    try_run(cmd)
546

    
547

    
548
@roles("accounts")
549
def activate_user(user_email=None):
550
    if not user_email:
551
      user_email = env.env.user_email
552
    debug(env.host, " * Activate user %s..." % user_email)
553
    with settings(host_string=env.env.db.ip):
554
        uid, user_auth_token, user_uuid = get_auth_token_from_db(user_email)
555

    
556
    cmd = """
557
    snf-manage user-modify --verify {0}
558
    snf-manage user-modify --accept {0}
559
    """.format(uid)
560
    try_run(cmd)
561

    
562
@roles("accounts")
563
def setup_astakos():
564
    debug(env.host, "Setting up snf-astakos-app...")
565
    setup_gunicorn()
566
    setup_apache()
567
    setup_webproject()
568
    install_package("python-django-south")
569
    install_package("snf-astakos-app")
570
    install_package("kamaki")
571

    
572
    tmpl = "/etc/synnefo/astakos.conf"
573
    replace = {
574
      "ACCOUNTS": env.env.accounts.fqdn,
575
      "domain": env.env.domain,
576
      "CYCLADES": env.env.cyclades.fqdn,
577
      "PITHOS": env.env.pithos.fqdn,
578
    }
579
    custom = customize_settings_from_tmpl(tmpl, replace)
580
    put(custom, tmpl, mode=0644)
581
    if env.csrf_disable:
582
      cmd = """
583
cat <<EOF >> /etc/synnefo/astakos.conf
584
try:
585
  MIDDLEWARE_CLASSES.remove('django.middleware.csrf.CsrfViewMiddleware')
586
except:
587
  pass
588
EOF
589
"""
590
      try_run(cmd)
591

    
592
    try_run("/etc/init.d/gunicorn restart")
593

    
594
    cmd = """
595
    snf-manage syncdb --noinput
596
    snf-manage migrate im --delete-ghost-migrations
597
    snf-manage migrate quotaholder_app
598
    """
599
    try_run(cmd)
600

    
601
def import_service(service):
602
    tmpl = "/tmp/%s.json" % service
603
    replace = {
604
      "DOMAIN": env.env.domain,
605
      }
606
    custom = customize_settings_from_tmpl(tmpl, replace)
607
    put(custom, tmpl)
608
    try_run("snf-manage service-import --json %s" % tmpl)
609

    
610
@roles("accounts")
611
def get_service_details(service="pithos"):
612
    debug(env.host, " * Getting registered details for %s service..." % service)
613
    result = try_run("snf-manage component-list")
614
    r = re.compile(r".*%s.*" % service, re.M)
615
    service_id, _, _, service_token = r.search(result).group().split()
616
    # print("%s: %s %s" % (service, service_id, service_token))
617
    return (service_id, service_token)
618

    
619

    
620
@roles("db")
621
def get_auth_token_from_db(user_email=None):
622
    if not user_email:
623
        user_email=env.env.user_email
624
    debug(env.host, " * Getting authentication token and uuid for user %s..." % user_email)
625
    cmd = """
626
    echo "select id, auth_token, uuid, email from auth_user, im_astakosuser where auth_user.id = im_astakosuser.user_ptr_id and auth_user.email = '{0}';" > /tmp/psqlcmd
627
    su - postgres -c  "psql -w -d snf_apps -f /tmp/psqlcmd"
628
    """.format(user_email)
629

    
630
    result = try_run(cmd)
631
    r = re.compile(r"(\d+)[ |]*(\S+)[ |]*(\S+)[ |]*" + user_email, re.M)
632
    match = r.search(result)
633
    uid, user_auth_token, user_uuid = match.groups()
634
    # print("%s: %s %s %s" % ( user_email, uid, user_auth_token, user_uuid))
635

    
636
    return (uid, user_auth_token, user_uuid)
637

    
638

    
639
@roles("cms")
640
def cms_loaddata():
641
    debug(env.host, " * Loading cms initial data...")
642
    if env.cms_pass:
643
      debug(env.host, "Aborting. Prerequisites not met.")
644
      return
645
    tmpl = "/tmp/sites.json"
646
    replace = {}
647
    custom = customize_settings_from_tmpl(tmpl, replace)
648
    put(custom, tmpl)
649

    
650
    tmpl = "/tmp/page.json"
651
    replace = {}
652
    custom = customize_settings_from_tmpl(tmpl, replace)
653
    put(custom, tmpl)
654

    
655
    cmd = """
656
    snf-manage loaddata /tmp/sites.json
657
    snf-manage loaddata /tmp/page.json
658
    snf-manage createsuperuser --username=admin --email=admin@{0} --noinput
659
    """.format(env.env.domain)
660
    try_run(cmd)
661

    
662

    
663
@roles("cms")
664
def setup_cms():
665
    debug(env.host, "Setting up cms...")
666
    if env.cms_pass:
667
      debug(env.host, "Aborting. Prerequisites not met.")
668
      return
669
    with settings(hide("everything")):
670
        try_run("ping -c1 accounts." + env.env.domain)
671
    setup_gunicorn()
672
    setup_apache()
673
    setup_webproject()
674
    install_package("snf-cloudcms")
675

    
676
    tmpl = "/etc/synnefo/cms.conf"
677
    replace = {
678
        "ACCOUNTS": env.env.accounts.fqdn,
679
        }
680
    custom = customize_settings_from_tmpl(tmpl, replace)
681
    put(custom, tmpl, mode=0644)
682
    try_run("/etc/init.d/gunicorn restart")
683

    
684

    
685
    cmd = """
686
    snf-manage syncdb
687
    snf-manage migrate --delete-ghost-migrations
688
    """.format(env.env.domain)
689
    try_run(cmd)
690

    
691

    
692
def setup_nfs_dirs():
693
    debug(env.host, " * Creating NFS mount point for pithos and ganeti...")
694
    cmd = """
695
    mkdir -p {0}
696
    cd {0}
697
    mkdir -p data
698
    chown www-data:www-data data
699
    chmod g+ws data
700
    mkdir -p /srv/okeanos
701
    """.format(env.env.pithos_dir)
702
    try_run(cmd)
703

    
704

    
705
@roles("nodes")
706
def setup_nfs_clients():
707
    if env.host == env.env.pithos.ip:
708
      return
709

    
710
    debug(env.host, " * Mounting pithos NFS mount point...")
711
    with settings(hide("everything")):
712
        try_run("ping -c1 " + env.env.pithos.hostname)
713
    install_package("nfs-common")
714
    for d in [env.env.pithos_dir, "/srv/okeanos"]:
715
      try_run("mkdir -p " + d)
716
      cmd = """
717
      echo "{0}:/{1} {2}  nfs4 defaults,rw,noatime,nodiratime,intr,rsize=1048576,wsize=1048576,noacl" >> /etc/fstab
718
      """.format(env.env.pithos.hostname, os.path.basename(d), d)
719
      try_run(cmd)
720
      try_run("mount " + d)
721

    
722

    
723
@roles("pithos")
724
def setup_nfs_server():
725
    debug(env.host, " * Setting up NFS server for pithos...")
726
    setup_nfs_dirs()
727
    install_package("nfs-kernel-server")
728
    tmpl = "/etc/exports"
729
    replace = {
730
      "pithos_dir": env.env.pithos_dir,
731
      "srv": os.path.dirname(env.env.pithos_dir),
732
      "subnet": env.env.subnet
733
      }
734
    custom = customize_settings_from_tmpl(tmpl, replace)
735
    put(custom, tmpl)
736
    try_run("/etc/init.d/nfs-kernel-server restart")
737

    
738

    
739
@roles("pithos")
740
def setup_pithos():
741
    debug(env.host, "Setting up snf-pithos-app...")
742
    with settings(hide("everything")):
743
        try_run("ping -c1 accounts." + env.env.domain)
744
        try_run("ping -c1 " + env.env.db.ip)
745
    setup_gunicorn()
746
    setup_apache()
747
    setup_webproject()
748

    
749
    with settings(host_string=env.env.accounts.ip):
750
        service_id, service_token = get_service_details("pithos")
751

    
752
    install_package("kamaki")
753
    install_package("snf-pithos-backend")
754
    install_package("snf-pithos-app")
755
    tmpl = "/etc/synnefo/pithos.conf"
756
    replace = {
757
        "ACCOUNTS": env.env.accounts.fqdn,
758
        "PITHOS": env.env.pithos.fqdn,
759
        "db_node": env.env.db.ip,
760
        "synnefo_user": env.env.synnefo_user,
761
        "synnefo_db_passwd": env.env.synnefo_db_passwd,
762
        "pithos_dir": env.env.pithos_dir,
763
        "PITHOS_SERVICE_TOKEN": service_token,
764
        "proxy": env.env.pithos.hostname == env.env.accounts.hostname
765
        }
766
    custom = customize_settings_from_tmpl(tmpl, replace)
767
    put(custom, tmpl, mode=0644)
768
    try_run("/etc/init.d/gunicorn restart")
769

    
770
    install_package("snf-pithos-webclient")
771
    tmpl = "/etc/synnefo/webclient.conf"
772
    replace = {
773
        "ACCOUNTS": env.env.accounts.fqdn,
774
        "PITHOS_UI_CLOUDBAR_ACTIVE_SERVICE": service_id,
775
        }
776
    custom = customize_settings_from_tmpl(tmpl, replace)
777
    put(custom, tmpl, mode=0644)
778

    
779
    try_run("/etc/init.d/gunicorn restart")
780
    #TOFIX: the previous command lets pithos-backend create blocks and maps
781
    #       with root owner
782
    try_run("chown -R www-data:www-data %s/data " % env.env.pithos_dir)
783
    #try_run("pithos-migrate stamp 4c8ccdc58192")
784
    #try_run("pithos-migrate upgrade head")
785

    
786

    
787
def add_wheezy():
788
    tmpl = "/etc/apt/sources.list.d/wheezy.list"
789
    replace = {}
790
    custom = customize_settings_from_tmpl(tmpl, replace)
791
    put(custom, tmpl)
792
    apt_get_update()
793

    
794

    
795
def remove_wheezy():
796
    try_run("rm -f /etc/apt/sources.list.d/wheezy.list")
797
    apt_get_update()
798

    
799

    
800
@roles("ganeti")
801
def setup_ganeti():
802
    debug(env.host, "Setting up snf-ganeti...")
803
    node_info = env.env.ips_info[env.host]
804
    with settings(hide("everything")):
805
        #if env.enable_lvm:
806
        #    try_run("vgs " + env.env.vg)
807
        try_run("getent hosts " + env.env.cluster.fqdn)
808
        try_run("getent hosts %s | grep -v ^127" % env.host)
809
        try_run("hostname -f | grep " + node_info.fqdn)
810
        #try_run("ip link show " + env.env.common_bridge)
811
        #try_run("ip link show " + env.env.common_bridge)
812
        #try_run("apt-get update")
813
    install_package("qemu-kvm")
814
    install_package("python-bitarray")
815
    add_wheezy()
816
    install_package("ganeti-htools")
817
    remove_wheezy()
818
    install_package("snf-ganeti")
819
    try_run("mkdir -p /srv/ganeti/file-storage/")
820
    cmd = """
821
cat <<EOF > /etc/ganeti/file-storage-paths
822
/srv/ganeti/file-storage
823
/srv/ganeti/shared-file-storage
824
EOF
825
"""
826
    try_run(cmd)
827

    
828

    
829
@roles("master")
830
def add_rapi_user():
831
    debug(env.host, " * Adding RAPI user to Ganeti backend...")
832
    cmd = """
833
    echo -n "{0}:Ganeti Remote API:{1}" | openssl md5
834
    """.format(env.env.synnefo_user, env.env.synnefo_rapi_passwd)
835
    result = try_run(cmd)
836
    cmd = """
837
    echo "{0} {1}{2} write" >> /var/lib/ganeti/rapi/users
838
    """.format(env.env.synnefo_user, '{ha1}',result)
839
    try_run(cmd)
840
    try_run("/etc/init.d/ganeti restart")
841

    
842
@roles("master")
843
def add_nodes():
844
    nodes = env.env.cluster_nodes.split(",")
845
    nodes.remove(env.env.master_node)
846
    debug(env.host, " * Adding nodes to Ganeti backend...")
847
    for n in nodes:
848
        add_node(n)
849

    
850
@roles("master")
851
def add_node(node):
852
    node_info = env.env.nodes_info[node]
853
    debug(env.host, " * Adding node %s to Ganeti backend..." % node_info.fqdn)
854
    cmd = "gnt-node add --no-ssh-key-check --master-capable=yes --vm-capable=yes " + node_info.fqdn
855
    try_run(cmd)
856

    
857
@roles("ganeti")
858
def enable_drbd():
859
    if env.enable_drbd:
860
        debug(env.host, " * Enabling DRBD...")
861
        try_run("modprobe drbd minor_count=255 usermode_helper=/bin/true")
862
        try_run("echo drbd minor_count=255 usermode_helper=/bin/true >> /etc/modules")
863

    
864
@roles("master")
865
def setup_drbd_dparams():
866
    if env.enable_drbd:
867
        debug(env.host, " * Twicking drbd related disk parameters in Ganeti...")
868
        cmd = """
869
        gnt-cluster modify --disk-parameters=drbd:metavg={0}
870
        gnt-group modify --disk-parameters=drbd:metavg={0} default
871
        """.format(env.env.vg)
872
        try_run(cmd)
873

    
874
@roles("master")
875
def enable_lvm():
876
    if env.enable_lvm:
877
        debug(env.host, " * Enabling LVM...")
878
        cmd = """
879
        gnt-cluster modify --vg-name={0}
880
        """.format(env.env.vg)
881
        try_run(cmd)
882
    else:
883
        debug(env.host, " * Disabling LVM...")
884
        try_run("gnt-cluster modify --no-lvm-storage")
885

    
886
@roles("master")
887
def destroy_cluster():
888
    debug(env.host, " * Destroying Ganeti cluster...")
889
    #TODO: remove instances first
890
    allnodes = env.env.cluster_hostnames[:]
891
    allnodes.remove(env.host)
892
    for n in allnodes:
893
      host_info = env.env.ips_info[host]
894
      debug(env.host, " * Removing node %s..." % n)
895
      cmd = "gnt-node remove  " + host_info.fqdn
896
      try_run(cmd)
897
    try_run("gnt-cluster destroy --yes-do-it")
898

    
899

    
900
@roles("master")
901
def init_cluster():
902
    debug(env.host, " * Initializing Ganeti backend...")
903
    # extra = ""
904
    # if env.enable_lvm:
905
    #     extra += " --vg-name={0} ".format(env.env.vg)
906
    # else:
907
    #     extra += " --no-lvm-storage "
908
    # if not env.enable_drbd:
909
    #     extra += " --no-drbd-storage "
910
    extra = " --no-lvm-storage --no-drbd-storage "
911
    cmd = """
912
    gnt-cluster init --enabled-hypervisors=kvm \
913
                     {0} \
914
                     --nic-parameters link={1},mode=bridged \
915
                     --master-netdev {2} \
916
                     --default-iallocator hail \
917
                     --hypervisor-parameters kvm:kernel_path=,vnc_bind_address=0.0.0.0 \
918
                     --no-ssh-init --no-etc-hosts \
919
                    {3}
920

921
    """.format(extra, env.env.common_bridge,
922
               env.env.cluster_netdev, env.env.cluster.fqdn)
923
    try_run(cmd)
924

    
925

    
926
@roles("ganeti")
927
def debootstrap():
928
    install_package("ganeti-instance-debootstrap")
929

    
930

    
931
@roles("ganeti")
932
def setup_image_host():
933
    debug(env.host, "Setting up snf-image...")
934
    install_package("snf-pithos-backend")
935
    install_package("snf-image")
936
    try_run("mkdir -p /srv/okeanos")
937
    tmpl = "/etc/default/snf-image"
938
    replace = {
939
        "synnefo_user": env.env.synnefo_user,
940
        "synnefo_db_passwd": env.env.synnefo_db_passwd,
941
        "pithos_dir": env.env.pithos_dir,
942
        "db_node": env.env.db.ip,
943
    }
944
    custom = customize_settings_from_tmpl(tmpl, replace)
945
    put(custom, tmpl)
946

    
947

    
948
@roles("ganeti")
949
def setup_image_helper():
950
    debug(env.host, " * Updating helper image...")
951
    cmd = """
952
    snf-image-update-helper -y
953
    """
954
    try_run(cmd)
955

    
956

    
957
@roles("ganeti")
958
def setup_gtools():
959
    debug(env.host, " * Setting up snf-cyclades-gtools...")
960
    with settings(hide("everything")):
961
        try_run("ping -c1 " + env.env.mq.ip)
962
    setup_common()
963
    install_package("snf-cyclades-gtools")
964
    tmpl = "/etc/synnefo/gtools.conf"
965
    replace = {
966
        "synnefo_user": env.env.synnefo_user,
967
        "synnefo_rabbitmq_passwd": env.env.synnefo_rabbitmq_passwd,
968
        "mq_node": env.env.mq.ip,
969
    }
970
    custom = customize_settings_from_tmpl(tmpl, replace)
971
    put(custom, tmpl)
972

    
973
    cmd = """
974
    sed -i 's/false/true/' /etc/default/snf-ganeti-eventd
975
    /etc/init.d/snf-ganeti-eventd start
976
    """
977
    try_run(cmd)
978

    
979

    
980
@roles("ganeti")
981
def setup_iptables():
982
    debug(env.host, " * Setting up iptables to mangle DHCP requests...")
983
    cmd = """
984
    iptables -t mangle -A PREROUTING -i br+ -p udp -m udp --dport 67 -j NFQUEUE --queue-num 42
985
    iptables -t mangle -A PREROUTING -i tap+ -p udp -m udp --dport 67 -j NFQUEUE --queue-num 42
986
    iptables -t mangle -A PREROUTING -i prv+ -p udp -m udp --dport 67 -j NFQUEUE --queue-num 42
987

988
    ip6tables -t mangle -A PREROUTING -i br+ -p ipv6-icmp -m icmp6 --icmpv6-type 133 -j NFQUEUE --queue-num 43
989
    ip6tables -t mangle -A PREROUTING -i br+ -p ipv6-icmp -m icmp6 --icmpv6-type 135 -j NFQUEUE --queue-num 44
990
    """
991
    try_run(cmd)
992

    
993
@roles("ganeti")
994
def setup_network():
995
    debug(env.host, "Setting up networking for Ganeti instances (nfdhcpd, etc.)...")
996
    install_package("nfqueue-bindings-python")
997
    install_package("nfdhcpd")
998
    tmpl = "/etc/nfdhcpd/nfdhcpd.conf"
999
    replace = {
1000
      "ns_node_ip": env.env.ns.ip
1001
      }
1002
    custom = customize_settings_from_tmpl(tmpl, replace)
1003
    put(custom, tmpl)
1004
    try_run("/etc/init.d/nfdhcpd restart")
1005

    
1006
    install_package("snf-network")
1007
    cmd = """
1008
    sed -i 's/MAC_MASK.*/MAC_MASK = ff:ff:f0:00:00:00/' /etc/default/snf-network
1009
    """
1010
    try_run(cmd)
1011

    
1012

    
1013
@roles("router")
1014
def setup_router():
1015
    debug(env.host, " * Setting up internal router for NAT...")
1016
    cmd = """
1017
    echo 1 > /proc/sys/net/ipv4/ip_forward
1018
    iptables -t nat -A POSTROUTING -s {0} -o {3} -j MASQUERADE
1019
    ip addr add {1} dev {2}
1020
    ip route add {0} dev {2} src {1}
1021
    """.format(env.env.synnefo_public_network_subnet,
1022
               env.env.synnefo_public_network_gateway,
1023
               env.env.common_bridge, env.env.public_iface)
1024
    try_run(cmd)
1025

    
1026
@roles("cyclades")
1027
def cyclades_loaddata():
1028
    debug(env.host, " * Loading initial data for cyclades...")
1029
    tmpl = "/tmp/flavor.json"
1030
    replace = {}
1031
    custom = customize_settings_from_tmpl(tmpl, replace)
1032
    put(custom, tmpl)
1033
    try_run("snf-manage loaddata " + tmpl)
1034
    #run("snf-manage loaddata flavors")
1035

    
1036

    
1037
@roles("cyclades")
1038
def setup_cyclades():
1039
    debug(env.host, "Setting up snf-cyclades-app...")
1040
    with settings(hide("everything")):
1041
        try_run("ping -c1 accounts." + env.env.domain)
1042
        try_run("ping -c1 " + env.env.db.ip)
1043
        try_run("ping -c1 " + env.env.mq.ip)
1044
    setup_gunicorn()
1045
    setup_apache()
1046
    setup_webproject()
1047
    install_package("memcached")
1048
    install_package("python-memcache")
1049
    install_package("snf-pithos-backend")
1050
    install_package("kamaki")
1051
    install_package("snf-cyclades-app")
1052
    install_package("python-django-south")
1053
    tmpl = "/etc/synnefo/cyclades.conf"
1054

    
1055
    with settings(host_string=env.env.accounts.ip):
1056
        service_id, service_token = get_service_details("cyclades")
1057

    
1058
    replace = {
1059
        "ACCOUNTS": env.env.accounts.fqdn,
1060
        "CYCLADES": env.env.cyclades.fqdn,
1061
        "mq_node": env.env.mq.ip,
1062
        "db_node": env.env.db.ip,
1063
        "synnefo_user": env.env.synnefo_user,
1064
        "synnefo_db_passwd": env.env.synnefo_db_passwd,
1065
        "synnefo_rabbitmq_passwd": env.env.synnefo_rabbitmq_passwd,
1066
        "pithos_dir": env.env.pithos_dir,
1067
        "common_bridge": env.env.common_bridge,
1068
        "HOST": env.env.cyclades.ip,
1069
        "domain": env.env.domain,
1070
        "CYCLADES_SERVICE_TOKEN": service_token,
1071
        "proxy": env.env.cyclades.hostname == env.env.accounts.hostname
1072
        }
1073
    custom = customize_settings_from_tmpl(tmpl, replace)
1074
    put(custom, tmpl, mode=0644)
1075
    try_run("/etc/init.d/gunicorn restart")
1076

    
1077
    cmd = """
1078
    sed -i 's/false/true/' /etc/default/snf-dispatcher
1079
    /etc/init.d/snf-dispatcher start
1080
    """
1081
    try_run(cmd)
1082

    
1083
    try_run("snf-manage syncdb")
1084
    try_run("snf-manage migrate --delete-ghost-migrations")
1085

    
1086

    
1087
@roles("cyclades")
1088
def get_backend_id(cluster_name="ganeti1.synnefo.deploy.local"):
1089
    backend_id = try_run("snf-manage backend-list 2>/dev/null | grep %s | awk '{print $1}'" % cluster_name)
1090
    return backend_id
1091

    
1092

    
1093
@roles("cyclades")
1094
def add_backend():
1095
    debug(env.host, "adding %s ganeti backend to cyclades..." % env.env.cluster.fqdn)
1096
    with settings(hide("everything")):
1097
        try_run("ping -c1 " + env.env.cluster.fqdn)
1098
    cmd = """
1099
    snf-manage backend-add --clustername={0} --user={1} --pass={2}
1100
    """.format(env.env.cluster.fqdn, env.env.synnefo_user,
1101
               env.env.synnefo_rapi_passwd)
1102
    try_run(cmd)
1103
    backend_id = get_backend_id(env.env.cluster.fqdn)
1104
    try_run("snf-manage backend-modify --drained=False " + backend_id)
1105

    
1106
@roles("cyclades")
1107
def pin_user_to_backend(user_email):
1108
    backend_id = get_backend_id(env.env.cluster.fqdn)
1109
    # pin user to backend
1110
    cmd = """
1111
cat <<EOF >> /etc/synnefo/cyclades.conf
1112

1113
BACKEND_PER_USER = {
1114
  '%s': %s,
1115
}
1116

1117
EOF
1118
/etc/init.d/gunicorn restart
1119
    """  % (user_email, backend_id)
1120
    try_run(cmd)
1121

    
1122
@roles("cyclades")
1123
def add_pools():
1124
    debug(env.host, " * Creating pools of resources (brigdes, mac prefixes) in cyclades...")
1125
    try_run("snf-manage pool-create --type=mac-prefix --base=aa:00:0 --size=65536")
1126
    try_run("snf-manage pool-create --type=bridge --base=prv --size=20")
1127

    
1128

    
1129
@roles("cyclades")
1130
def add_network():
1131
    debug(env.host, " * Adding public network in cyclades...")
1132
    backend_id = get_backend_id(env.env.cluster.fqdn)
1133
    cmd = """
1134
    snf-manage network-create --subnet={0} --gateway={1} --public --dhcp --flavor={2} --mode=bridged --link={3} --name=Internet --backend-id={4}
1135
    """.format(env.env.synnefo_public_network_subnet,
1136
               env.env.synnefo_public_network_gateway,
1137
               env.env.synnefo_public_network_type,
1138
               env.env.common_bridge, backend_id)
1139
    try_run(cmd)
1140

    
1141

    
1142
@roles("cyclades")
1143
def setup_vncauthproxy():
1144
    debug(env.host, " * Setting up vncauthproxy...")
1145
    install_package("snf-vncauthproxy")
1146
    cmd = """
1147
    echo CHUID="www-data:nogroup" >> /etc/default/vncauthproxy
1148
    rm /var/log/vncauthproxy/vncauthproxy.log
1149
    """
1150
    try_run(cmd)
1151
    try_run("/etc/init.d/vncauthproxy restart")
1152

    
1153
@roles("client")
1154
def setup_kamaki():
1155
    debug(env.host, "Setting up kamaki client...")
1156
    with settings(hide("everything")):
1157
        try_run("ping -c1 accounts." + env.env.domain)
1158
        try_run("ping -c1 cyclades." + env.env.domain)
1159
        try_run("ping -c1 pithos." + env.env.domain)
1160

    
1161
    with settings(host_string=env.env.db.ip):
1162
        uid, user_auth_token, user_uuid = get_auth_token_from_db(env.env.user_email)
1163

    
1164
    install_package("python-progress")
1165
    install_package("kamaki")
1166
    cmd = """
1167
    kamaki config set cloud.default.url "https://{0}/astakos/identity/v2.0/"
1168
    kamaki config set cloud.default.token {1}
1169
    """.format(env.env.accounts.fqdn, user_auth_token)
1170
    try_run(cmd)
1171
    try_run("kamaki file create images")
1172

    
1173
@roles("client")
1174
def upload_image(image="debian_base.diskdump"):
1175
    debug(env.host, " * Uploading initial image to pithos...")
1176
    image = "debian_base.diskdump"
1177
    try_run("wget {0} -O /tmp/{1}".format(env.env.debian_base_url, image))
1178
    try_run("kamaki file upload --container images /tmp/{0} {0}".format(image))
1179

    
1180
@roles("client")
1181
def register_image(image="debian_base.diskdump"):
1182
    debug(env.host, " * Register image to plankton...")
1183
    with settings(host_string=env.env.db.ip):
1184
        uid, user_auth_token, user_uuid = get_auth_token_from_db(env.env.user_email)
1185

    
1186
    pithos_url = "pithos://{0}/images/{1}".format(user_uuid, image)
1187
    cmd = """
1188
    sleep 5
1189
    kamaki image register "Debian Base" {0} --public --disk-format=diskdump --property OSFAMILY=linux --property ROOT_PARTITION=1 --property description="Debian Squeeze Base System" --property size=450M --property kernel=2.6.32 --property GUI="No GUI" --property sortorder=1 --property USERS=root --property OS=debian
1190
    """.format(pithos_url)
1191
    try_run(cmd)
1192

    
1193
@roles("client")
1194
def setup_burnin():
1195
    debug(env.host, "Setting up burnin testing tool...")
1196
    install_package("kamaki")
1197
    install_package("snf-tools")
1198

    
1199
@roles("pithos")
1200
def add_image_locally():
1201
    debug(env.host, " * Getting image locally in order snf-image to use it directly..")
1202
    image = "debian_base.diskdump"
1203
    try_run("wget {0} -O /srv/okeanos/{1}".format(env.env.debian_base_url, image))
1204

    
1205

    
1206
@roles("master")
1207
def gnt_instance_add(name="test"):
1208
    debug(env.host, " * Adding test instance to Ganeti...")
1209
    osp="""img_passwd=gamwtosecurity,img_format=diskdump,img_id=debian_base,img_properties='{"OSFAMILY":"linux"\,"ROOT_PARTITION":"1"}'"""
1210
    cmd = """
1211
    gnt-instance add  -o snf-image+default --os-parameters {0} -t plain --disk 0:size=1G --no-name-check --no-ip-check --net 0:ip=pool,network=test --no-install --hypervisor-parameters kvm:machine_version=pc-1.0 {1}
1212
    """.format(osp, name)
1213
    try_run(cmd)
1214

    
1215
@roles("master")
1216
def gnt_network_add(name="test", subnet="10.0.0.0/26", gw="10.0.0.1", mode="bridged", link="br0"):
1217
    debug(env.host, " * Adding test network to Ganeti...")
1218
    cmd = """
1219
    gnt-network add --network={1} --gateway={2} {0}
1220
    gnt-network connect {0} {3} {4}
1221
    """.format(name, subnet, gw, mode, link)
1222
    try_run(cmd)
1223

    
1224
@roles("ips")
1225
def test():
1226
    debug(env.host, "Testing...")
1227
    try_run("hostname && date")