Statistics
| Branch: | Tag: | Revision:

root / snf-deploy / fabfile.py @ df1d01d4

History | View | Annotate | Download (37.5 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, True)
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, True)
210

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

    
214
@roles("nodes")
215
def check_connectivity():
216
    debug(env.host, "Checking internet connectivity..")
217
    try_run("ping -c 1 www.google.com", True)
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", True)
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, abort=False):
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
      if abort:
289
        raise
290

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

    
299

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

    
307

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

    
314

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

    
326

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

    
336
    return custom
337

    
338

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

    
354

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

    
361

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

    
371

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

    
398

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

    
412

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

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

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

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

    
443

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

    
449

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

    
472

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

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

    
500

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

    
530

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

    
549

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

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

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

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

    
594
    try_run("/etc/init.d/gunicorn restart")
595

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

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

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

    
621

    
622
@roles("db")
623
def get_auth_token_from_db(user_email=None):
624
    if not user_email:
625
        user_email=env.env.user_email
626
    debug(env.host, " * Getting authentication token and uuid for user %s..." % user_email)
627
    cmd = """
628
    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
629
    su - postgres -c  "psql -w -d snf_apps -f /tmp/psqlcmd"
630
    """.format(user_email)
631

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

    
638
    return (uid, user_auth_token, user_uuid)
639

    
640

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

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

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

    
664

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

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

    
686

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

    
693

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

    
706

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

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

    
724

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

    
740

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

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

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

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

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

    
788

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

    
796

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

    
801

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

    
830

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

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

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

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

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

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

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

    
901

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

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

    
927

    
928
@roles("ganeti")
929
def debootstrap():
930
    install_package("ganeti-instance-debootstrap")
931

    
932

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

    
949

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

    
958

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

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

    
981

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

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

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

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

    
1014

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

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

    
1038

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

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

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

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

    
1085
    try_run("snf-manage syncdb")
1086
    try_run("snf-manage migrate --delete-ghost-migrations")
1087

    
1088

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

    
1094

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

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

1115
BACKEND_PER_USER = {
1116
  '%s': %s,
1117
}
1118

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

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

    
1130

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

    
1143

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

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

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

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

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

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

    
1188
    pithos_url = "pithos://{0}/images/{1}".format(user_uuid, image)
1189
    cmd = """
1190
    sleep 5
1191
    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
1192
    """.format(pithos_url)
1193
    try_run(cmd)
1194

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

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

    
1207

    
1208
@roles("master")
1209
def gnt_instance_add(name="test"):
1210
    debug(env.host, " * Adding test instance to Ganeti...")
1211
    osp="""img_passwd=gamwtosecurity,img_format=diskdump,img_id=debian_base,img_properties='{"OSFAMILY":"linux"\,"ROOT_PARTITION":"1"}'"""
1212
    cmd = """
1213
    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}
1214
    """.format(osp, name)
1215
    try_run(cmd)
1216

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

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