Statistics
| Branch: | Tag: | Revision:

root / snf-deploy / snfdeploy / components.py @ d6d187ec

History | View | Annotate | Download (33.6 kB)

1
# Copyright (C) 2010, 2011, 2012, 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A. OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import datetime
35
from snfdeploy.utils import debug
36

    
37

    
38
class SynnefoComponent(object):
39

    
40
    REQUIRED_PACKAGES = []
41

    
42
    def debug(self, msg, info=""):
43
        debug(self.__class__.__name__, msg, info)
44

    
45
    def __init__(self, node_info, env, *args, **kwargs):
46
        """ Take a node_info and env as argument and initialize local vars """
47
        self.node_info = node_info
48
        self.env = env
49

    
50
    def check(self):
51
        """ Returns a list of bash commands that check prerequisites """
52
        return []
53

    
54
    def install(self):
55
        """ Returns a list of debian packages to install """
56
        return self.REQUIRED_PACKAGES
57

    
58
    def prepare(self):
59
        """ Returs a list of bash commands that prepares the component """
60
        return []
61

    
62
    def configure(self):
63
        """ Must return a list of tuples (tmpl_path, replace_dict, mode) """
64
        return []
65

    
66
    def initialize(self):
67
        """ Returs a list of bash commands that initialize the component """
68
        return []
69

    
70
    def test(self):
71
        """ Returs a list of bash commands that test existing installation """
72
        return []
73

    
74
    def restart(self):
75
        return []
76

    
77
    #TODO: add cleanup method for each component
78
    def clean(self):
79
        return []
80

    
81

    
82
class HW(SynnefoComponent):
83
    def test(self):
84
        return [
85
            "ping -c 1 %s" % self.node_info.ip,
86
            "ping -c 1 www.google.com",
87
            "apt-get update",
88
            ]
89

    
90

    
91
class SSH(SynnefoComponent):
92
    def prepare(self):
93
        return [
94
            "mkdir -p /root/.ssh",
95
            "for f in $(ls /root/.ssh/*); do cp $f $f.bak ; done",
96
            "echo StrictHostKeyChecking no >> /etc/ssh/ssh_config",
97
            ]
98

    
99
    def configure(self):
100
        files = [
101
            "authorized_keys", "id_dsa", "id_dsa.pub", "id_rsa", "id_rsa.pub"
102
            ]
103
        ssh = [("/root/.ssh/%s" % f, {}, {"mode": 0600}) for f in files]
104
        return ssh
105

    
106
    def initialize(self):
107
        f = "/root/.ssh/authorized_keys"
108
        return [
109
            "test -e {0}.bak && cat {0}.bak >> {0}".format(f)
110
            ]
111

    
112
    def test(self):
113
        return ["ssh %s date" % self.node_info.ip]
114

    
115

    
116
class DNS(SynnefoComponent):
117
    def prepare(self):
118
        return [
119
            "chattr -i /etc/resolv.conf",
120
            "sed -i 's/^127.*$/127.0.0.1 localhost/g' /etc/hosts",
121
            ]
122

    
123
    def configure(self):
124
        r1 = {
125
            "date": str(datetime.datetime.today()),
126
            "domain": self.env.env.domain,
127
            "ns_node_ip": self.env.env.ns.ip,
128
            }
129
        resolv = [
130
            ("/etc/resolv.conf", r1, {})
131
            ]
132
        return resolv
133

    
134
    def initialize(self):
135
        return ["chattr +i /etc/resolv.conf"]
136

    
137

    
138
class DDNS(SynnefoComponent):
139
    REQUIRED_PACKAGES = [
140
        "dnsutils",
141
        ]
142

    
143
    def prepare(self):
144
        return [
145
            "mkdir -p /root/ddns/"
146
            ]
147

    
148
    def configure(self):
149
        return [
150
            ("/root/ddns/" + k, {}, {}) for k in self.env.env.ddns_keys
151
            ]
152

    
153

    
154
class NS(SynnefoComponent):
155
    REQUIRED_PACKAGES = [
156
        "bind9",
157
        ]
158

    
159
    def nsupdate(self, cmd):
160
        ret = """
161
nsupdate -k {0} > /dev/null <<EOF || true
162
server {1}
163
{2}
164
send
165
EOF
166
""".format(self.env.env.ddns_private_key, self.node_info.ip, cmd)
167
        return ret
168

    
169
    def prepare(self):
170
        return [
171
            "mkdir -p /etc/bind/zones",
172
            "chmod g+w /etc/bind/zones",
173
            "mkdir -p /etc/bind/rev",
174
            "chmod g+w /etc/bind/rev",
175
            ]
176

    
177
    def configure(self):
178
        d = self.env.env.domain
179
        ip = self.node_info.ip
180
        return [
181
            ("/etc/bind/named.conf.local", {"domain": d}, {}),
182
            ("/etc/bind/zones/example.com",
183
             {"domain": d, "ns_node_ip": ip},
184
             {"remote": "/etc/bind/zones/%s" % d}),
185
            ("/etc/bind/zones/vm.example.com",
186
             {"domain": d, "ns_node_ip": ip},
187
             {"remote": "/etc/bind/zones/vm.%s" % d}),
188
            ("/etc/bind/rev/synnefo.in-addr.arpa.zone", {"domain": d}, {}),
189
            ("/etc/bind/rev/synnefo.ip6.arpa.zone", {"domain": d}, {}),
190
            ("/etc/bind/named.conf.options",
191
             {"node_ips": ";".join(self.env.env.ips)}, {}),
192
            ("/root/ddns/ddns.key", {}, {"remote": "/etc/bind/ddns.key"}),
193
            ]
194

    
195
    def update_cnamerecord(self, node_info):
196
        return self.nsupdate("update add %s" % node_info.cnamerecord)
197

    
198
    def update_arecord(self, node_info):
199
        return self.nsupdate("update add %s" % node_info.arecord)
200

    
201
    def update_ptrrecord(self, node_info):
202
        return self.nsupdate("update add %s" % node_info.ptrrecord)
203

    
204
    def update_ns_for_node(self, node_info):
205
        return [
206
            self.update_arecord(node_info),
207
            self.update_cnamerecord(node_info),
208
            self.update_ptrrecord(node_info)
209
            ]
210

    
211
    def initialize(self):
212
        a = [self.update_arecord(n)
213
             for n in self.env.env.nodes_info.values()]
214
        ptr = [self.update_ptrrecord(n)
215
               for n in self.env.env.nodes_info.values()]
216
        cnames = [self.update_cnamerecord(n)
217
                  for n in self.env.env.roles_info.values()]
218

    
219
        return a + ptr + cnames
220

    
221
    def restart(self):
222
        return ["/etc/init.d/bind9 restart"]
223

    
224
    def test(self):
225
        n = ["host %s localhost" % i.fqdn
226
             for i in self.env.env.nodes_info.values()]
227
        a = ["host %s localhost" % i.fqdn
228
             for i in self.env.env.roles_info.values()]
229
        return n + a
230

    
231

    
232
class APT(SynnefoComponent):
233
    """ Setup apt repos and check fqdns """
234
    REQUIRED_PACKAGES = ["curl"]
235

    
236
    def prepare(self):
237
        return [
238
            "echo 'APT::Install-Suggests \"false\";' >> /etc/apt/apt.conf",
239
            "curl -k https://dev.grnet.gr/files/apt-grnetdev.pub | \
240
                apt-key add -",
241
            ]
242

    
243
    def configure(self):
244
        return [
245
            ("/etc/apt/sources.list.d/synnefo.wheezy.list", {}, {})
246
            ]
247

    
248
    def initialize(self):
249
        return [
250
            "apt-get update",
251
            ]
252

    
253

    
254
class MQ(SynnefoComponent):
255
    REQUIRED_PACKAGES = ["rabbitmq-server"]
256

    
257
    def check(self):
258
        return ["ping -c 1 mq.%s" % self.env.env.domain]
259

    
260
    def initialize(self):
261
        u = self.env.env.synnefo_user
262
        p = self.env.env.synnefo_rabbitmq_passwd
263
        return [
264
            "rabbitmqctl add_user %s %s" % (u, p),
265
            "rabbitmqctl set_permissions %s \".*\" \".*\" \".*\"" % u,
266
            "rabbitmqctl delete_user guest",
267
            "rabbitmqctl set_user_tags %s administrator" % u,
268
            ]
269

    
270

    
271
class DB(SynnefoComponent):
272
    REQUIRED_PACKAGES = ["postgresql"]
273

    
274
    def check(self):
275
        return ["ping -c 1 db.%s" % self.env.env.domain]
276

    
277
    def get_user_info_from_db(self):
278
        cmd = """
279
cat > /tmp/psqlcmd <<EOF
280
select id, auth_token, uuid, email from auth_user, im_astakosuser \
281
where auth_user.id = im_astakosuser.user_ptr_id and auth_user.email = '{0}';
282
EOF
283

284
su - postgres -c  "psql -w -d snf_apps -f /tmp/psqlcmd"
285
""".format(self.env.env.user_email)
286

    
287
        return [cmd]
288

    
289
    def allow_access_in_db(self, node_info, user="all", method="md5"):
290
        f = "/etc/postgresql/*/main/pg_hba.conf"
291
        cmd1 = "echo host all %s %s/32 %s >> %s" % \
292
            (user, node_info.ip, method, f)
293
        cmd2 = "sed -i 's/\(host.*127.0.0.1.*\)md5/\\1trust/' %s" % f
294
        return [cmd1, cmd2] + self.restart()
295

    
296
    def configure(self):
297
        u = self.env.env.synnefo_user
298
        p = self.env.env.synnefo_db_passwd
299
        replace = {"synnefo_user": u, "synnefo_db_passwd": p}
300
        return [
301
            ("/tmp/db-init.psql", replace, {}),
302
            ]
303

    
304
    def make_db_fast(self):
305
        f = "/etc/postgresql/*/main/postgresql.conf"
306
        opts = "fsync=off\nsynchronous_commit=off\nfull_page_writes=off\n"
307
        return ["""echo -e "%s" >> %s""" % (opts, f)]
308

    
309
    def prepare(self):
310
        f = "/etc/postgresql/*/main/postgresql.conf"
311
        return [
312
            """echo "listen_addresses = '*'" >> %s""" % f,
313
            ]
314

    
315
    def initialize(self):
316
        script = "/tmp/db-init.psql"
317
        cmd = "su - postgres -c \"psql -w -f %s\" " % script
318
        return [cmd]
319

    
320
    def restart(self):
321
        return ["/etc/init.d/postgresql restart"]
322

    
323
    def destroy_db(self):
324
        return [
325
            """su - postgres -c ' psql -w -c "drop database snf_apps" '""",
326
            """su - postgres -c ' psql -w -c "drop database snf_pithos" '"""
327
            ]
328

    
329

    
330
class Ganeti(SynnefoComponent):
331

    
332
    REQUIRED_PACKAGES = [
333
        "qemu-kvm",
334
        "python-bitarray",
335
        "ganeti-htools",
336
        "ganeti-haskell",
337
        "snf-ganeti",
338
        "ganeti2",
339
        "bridge-utils",
340
        "lvm2",
341
        "drbd8-utils",
342
        ]
343

    
344
    def check(self):
345
        commands = [
346
            "getent hosts %s | grep -v ^127" % self.node_info.hostname,
347
            "hostname -f | grep %s" % self.node_info.fqdn,
348
            ]
349
        return commands
350

    
351
    def configure(self):
352
        return [
353
            ("/etc/ganeti/file-storage-paths", {}, {}),
354
            ]
355

    
356
    def prepare_lvm(self):
357
        return [
358
            "pvcreate %s" % self.env.env.extra_disk,
359
            "vgcreate %s %s" % (self.env.env.extra_disk, self.env.env.vg)
360
            ]
361

    
362
    def prepare_net_infra(self):
363
        br = self.env.env.common_bridge
364
        return [
365
            "brctl addbr {0}; ip link set {0} up".format(br)
366
            ]
367

    
368
    def prepare(self):
369
        return [
370
            "mkdir -p /srv/ganeti/file-storage/",
371
            "sed -i 's/^127.*$/127.0.0.1 localhost/g' /etc/hosts"
372
            ] + self.prepare_net_infra()
373

    
374
    def restart(self):
375
        return ["/etc/init.d/ganeti restart"]
376

    
377

    
378
class Master(SynnefoComponent):
379
    def add_rapi_user(self):
380
        user = self.env.env.synnefo_user
381
        passwd = self.env.env.synnefo_rapi_passwd
382
        x = "%s:Ganeti Remote API:%s" % (user, passwd)
383

    
384
        cmd = """
385
cat >> /var/lib/ganeti/rapi/users <<EOF
386
%s {HA1}$(echo -n %s | openssl md5 | sed 's/^.* //') write
387
EOF
388
""" % (self.env.env.synnefo_user, x)
389

    
390
        return [cmd] + self.restart()
391

    
392
    def add_node(self, node_info):
393
        commands = [
394
            "gnt-node add --no-ssh-key-check --master-capable=yes " +
395
            "--vm-capable=yes " + node_info.fqdn,
396
            ]
397
        return commands
398

    
399
    def try_use_vg(self):
400
        vg = self.env.env.vg
401
        return [
402
            "gnt-cluster modify --vg-name=%s || true" % vg,
403
            "gnt-cluster modify --disk-parameters=drbd:metavg=%s" % vg,
404
            "gnt-group modify --disk-parameters=drbd:metavg=%s default" % vg,
405
            ]
406

    
407
    def initialize(self):
408
        cmd = """
409
        gnt-cluster init --enabled-hypervisors=kvm \
410
            --no-lvm-storage --no-drbd-storage \
411
            --nic-parameters link={0},mode=bridged \
412
            --master-netdev {1} \
413
            --specs-nic-count min=0,max=8 \
414
            --default-iallocator hail \
415
            --hypervisor-parameters kvm:kernel_path=,vnc_bind_address=0.0.0.0 \
416
            --no-ssh-init --no-etc-hosts \
417
            --enabled-disk-templates file,plain,ext,drbd \
418
            {2}
419
        """.format(self.env.env.common_bridge,
420
                   self.env.env.cluster_netdev, self.env.env.cluster.fqdn)
421

    
422
        return [cmd] + self.try_use_vg() + self.add_rapi_user()
423

    
424
    def restart(self):
425
        return ["/etc/init.d/ganeti restart"]
426

    
427

    
428
class Image(SynnefoComponent):
429
    REQUIRED_PACKAGES = [
430
        "snf-image",
431
        ]
432

    
433
    def check(self):
434
        return ["mkdir -p %s" % self.env.env.image_dir]
435

    
436
    def configure(self):
437
        tmpl = "/etc/default/snf-image"
438
        replace = {
439
            "synnefo_user": self.env.env.synnefo_user,
440
            "synnefo_db_passwd": self.env.env.synnefo_db_passwd,
441
            "pithos_dir": self.env.env.pithos_dir,
442
            "db_node": self.env.env.db.ip,
443
            "image_dir": self.env.env.image_dir,
444
            }
445
        return [(tmpl, replace, {})]
446

    
447
    def initialize(self):
448
        return ["snf-image-update-helper -y"]
449

    
450

    
451
class GTools(SynnefoComponent):
452
    REQUIRED_PACKAGES = [
453
        "snf-cyclades-gtools",
454
        ]
455

    
456
    def check(self):
457
        return ["ping -c1 %s" % self.env.env.mq.ip]
458

    
459
    def configure(self):
460
        tmpl = "/etc/synnefo/gtools.conf"
461
        replace = {
462
            "synnefo_user": self.env.env.synnefo_user,
463
            "synnefo_rabbitmq_passwd": self.env.env.synnefo_rabbitmq_passwd,
464
            "mq_node": self.env.env.mq.ip,
465
            }
466
        return [(tmpl, replace, {})]
467

    
468
    def initialize(self):
469
        return [
470
            "sed -i 's/false/true/' /etc/default/snf-ganeti-eventd",
471
            "/etc/init.d/snf-ganeti-eventd start",
472
            ]
473

    
474
    def restart(self):
475
        return ["/etc/init.d/snf-ganeti-eventd restart"]
476

    
477

    
478
class Network(SynnefoComponent):
479
    REQUIRED_PACKAGES = [
480
        "python-nfqueue",
481
        "snf-network",
482
        "nfdhcpd",
483
        ]
484

    
485
    def configure(self):
486
        r1 = {
487
            "ns_node_ip": self.env.env.ns.ip
488
            }
489
        r2 = {
490
            "common_bridge": self.env.env.common_bridge,
491
            "public_iface": self.env.env.public_iface,
492
            "subnet": self.env.env.synnefo_public_network_subnet,
493
            "gateway": self.env.env.synnefo_public_network_gateway,
494
            "router_ip": self.env.env.router.ip,
495
            "node_ip": self.node_info.ip,
496
            }
497
        r3 = {
498
            "domain": self.env.env.domain,
499
            "server": self.env.env.ns.ip,
500
            "keyfile": self.env.env.ddns_private_key,
501
            }
502

    
503
        return [
504
            ("/etc/nfdhcpd/nfdhcpd.conf", r1, {}),
505
            ("/etc/rc.local", r2, {"mode": 0755}),
506
            ("/etc/default/snf-network", r3, {}),
507
            ]
508

    
509
    def initialize(self):
510
        return ["/etc/init.d/rc.local start"]
511

    
512
    def restart(self):
513
        return ["/etc/init.d/nfdhcpd restart"]
514

    
515

    
516
class Apache(SynnefoComponent):
517
    REQUIRED_PACKAGES = [
518
        "apache2",
519
        ]
520

    
521
    def prepare(self):
522
        return [
523
            "a2enmod ssl", "a2enmod rewrite", "a2dissite default",
524
            "a2enmod headers",
525
            "a2enmod proxy_http", "a2dismod autoindex",
526
            ]
527

    
528
    def configure(self):
529
        r1 = {"HOST": self.node_info.fqdn}
530
        return [
531
            ("/etc/apache2/sites-available/synnefo", r1, {}),
532
            ("/etc/apache2/sites-available/synnefo-ssl", r1, {}),
533
            ]
534

    
535
    def initialize(self):
536
        return [
537
            "a2ensite synnefo", "a2ensite synnefo-ssl",
538
            ]
539

    
540
    def restart(self):
541
        return [
542
            "/etc/init.d/apache2 restart",
543
            ]
544

    
545

    
546
class Gunicorn(SynnefoComponent):
547
    REQUIRED_PACKAGES = [
548
        "gunicorn",
549
        ]
550

    
551
    def prepare(self):
552
        return [
553
            "chown root.www-data /var/log/gunicorn",
554
            ]
555

    
556
    def configure(self):
557
        r1 = {"HOST": self.node_info.fqdn}
558
        return [
559
            ("/etc/gunicorn.d/synnefo", r1, {}),
560
            ]
561

    
562
    def restart(self):
563
        return [
564
            "/etc/init.d/gunicorn restart",
565
            ]
566

    
567

    
568
class Common(SynnefoComponent):
569
    REQUIRED_PACKAGES = [
570
        # snf-common
571
        "python-objpool",
572
        "snf-common",
573
        "python-astakosclient",
574
        "snf-django-lib",
575
        "snf-branding",
576
        ]
577

    
578
    def configure(self):
579
        r1 = {
580
            "EMAIL_SUBJECT_PREFIX": self.node_info.hostname,
581
            "domain": self.env.env.domain,
582
            "HOST": self.node_info.fqdn,
583
            "MAIL_DIR": self.env.env.mail_dir,
584
            }
585
        return [
586
            ("/etc/synnefo/common.conf", r1, {}),
587
            ]
588

    
589
    def initialize(self):
590
        return ["mkdir -p {0}; chmod 777 {0}".format(self.env.env.mail_dir)]
591

    
592
    def restart(self):
593
        return [
594
            "/etc/init.d/gunicorn restart",
595
            ]
596

    
597

    
598
class WEB(SynnefoComponent):
599
    REQUIRED_PACKAGES = [
600
        "snf-webproject",
601
        "python-psycopg2",
602
        "python-gevent",
603
        "python-django",
604
        ]
605

    
606
    def check(self):
607
        return ["ping -c1 %s" % self.env.env.db.fqdn]
608

    
609
    def configure(self):
610
        r1 = {
611
            "synnefo_user": self.env.env.synnefo_user,
612
            "synnefo_db_passwd": self.env.env.synnefo_db_passwd,
613
            "db_node": self.env.env.db.fqdn,
614
            "domain": self.env.env.domain,
615
            }
616
        return [
617
            ("/etc/synnefo/webproject.conf", r1, {}),
618
            ]
619

    
620
    def restart(self):
621
        return [
622
            "/etc/init.d/gunicorn restart",
623
            ]
624

    
625

    
626
class Astakos(SynnefoComponent):
627
    REQUIRED_PACKAGES = [
628
        "python-django-south",
629
        "snf-astakos-app",
630
        "kamaki",
631
        ]
632

    
633
    def export_service(self):
634
        f = self.env.jsonfile
635
        return [
636
            "snf-manage service-export-astakos > %s" % f
637
            ]
638

    
639
    def import_service(self):
640
        f = self.env.jsonfile
641
        return [
642
            "snf-manage service-import --json=%s" % f
643
            ]
644

    
645
    def set_default_quota(self):
646
        cmd = "snf-manage resource-modify --default-quota"
647
        return [
648
            "%s 40G pithos.diskspace" % cmd,
649
            "%s 2 astakos.pending_app" % cmd,
650
            "%s 4 cyclades.vm" % cmd,
651
            "%s 40G cyclades.disk" % cmd,
652
            "%s 16G cyclades.total_ram" % cmd,
653
            "%s 8G cyclades.ram" % cmd,
654
            "%s 32 cyclades.total_cpu" % cmd,
655
            "%s 16 cyclades.cpu" % cmd,
656
            "%s 4 cyclades.network.private" % cmd,
657
            "%s 4 cyclades.floating_ip" % cmd,
658
            ]
659

    
660
    def modify_all_quota(self):
661
        cmd = "snf-manage user-modify -f --all --base-quota"
662
        return [
663
            "%s pithos.diskspace 40G" % cmd,
664
            "%s astakos.pending_app 2" % cmd,
665
            "%s cyclades.vm 4" % cmd,
666
            "%s cyclades.disk 40G" % cmd,
667
            "%s cyclades.total_ram 16G" % cmd,
668
            "%s cyclades.ram 8G" % cmd,
669
            "%s cyclades.total_cpu 32" % cmd,
670
            "%s cyclades.cpu 16" % cmd,
671
            "%s cyclades.network.private 4" % cmd,
672
            "%s cyclades.floating_ip 4" % cmd,
673
            ]
674

    
675
    def get_services(self):
676
        return [
677
            "snf-manage component-list -o id,name,token"
678
            ]
679

    
680
    def configure(self):
681
        r1 = {
682
            "ACCOUNTS": self.env.env.accounts.fqdn,
683
            "domain": self.env.env.domain,
684
            "CYCLADES": self.env.env.cyclades.fqdn,
685
            "PITHOS": self.env.env.pithos.fqdn,
686
            }
687
        return [
688
            ("/etc/synnefo/astakos.conf", r1, {})
689
            ]
690

    
691
    def initialize(self):
692
        secret = self.env.env.oa2_secret
693
        view = "https://%s/pithos/ui/view" % self.env.env.pithos.fqdn
694
        oa2 = "snf-manage oauth2-client-add pithos-view \
695
                  --secret=%s --is-trusted --url %s" % (secret, view)
696

    
697
        return [
698
            "snf-manage syncdb --noinput",
699
            "snf-manage migrate im --delete-ghost-migrations",
700
            "snf-manage migrate quotaholder_app",
701
            "snf-manage migrate oa2",
702
            "snf-manage loaddata groups",
703
            oa2
704
            ] + self.astakos_register_components()
705

    
706
    def astakos_register_components(self):
707
        # base urls
708
        cbu = "https://%s/cyclades" % self.env.env.cyclades.fqdn
709
        pbu = "https://%s/pithos" % self.env.env.pithos.fqdn
710
        abu = "https://%s/astakos" % self.env.env.accounts.fqdn
711
        cmsurl = "https://%s/home" % self.env.env.cms.fqdn
712

    
713
        cmd = "snf-manage component-add"
714
        h = "%s home --base-url %s --ui-url %s" % (cmd, cmsurl, cmsurl)
715
        c = "%s cyclades --base-url %s --ui-url %s/ui" % (cmd, cbu, cbu)
716
        p = "%s pithos --base-url %s --ui-url %s/ui" % (cmd, pbu, pbu)
717
        a = "%s astakos --base-url %s --ui-url %s/ui" % (cmd, abu, abu)
718

    
719
        return [h, c, p, a]
720

    
721
    def add_user(self):
722
        info = (
723
            self.env.env.user_passwd,
724
            self.env.env.user_email,
725
            self.env.env.user_name,
726
            self.env.env.user_lastname,
727
            )
728
        cmd = "snf-manage user-add --password %s %s %s %s" % info
729
        return [cmd]
730

    
731
    def activate_user(self):
732
        user_id = self.env.user_id
733
        return [
734
            "snf-manage user-modify --verify %s" % user_id,
735
            "snf-manage user-modify --accept %s" % user_id,
736
            ]
737

    
738

    
739
class CMS(SynnefoComponent):
740
    REQUIRED_PACKAGES = [
741
        "snf-cloudcms"
742
        ]
743

    
744
    def configure(self):
745
        r1 = {
746
            "ACCOUNTS": self.env.env.accounts.fqdn
747
            }
748
        r2 = {
749
            "DOMAIN": self.env.env.domain
750
            }
751
        return [
752
            ("/etc/synnefo/cms.conf", r1, {}),
753
            ("/tmp/sites.json", r2, {}),
754
            ("/tmp/page.json", {}, {}),
755
            ]
756

    
757
    def initialize(self):
758
        return [
759
            "snf-manage syncdb",
760
            "snf-manage migrate --delete-ghost-migrations",
761
            "snf-manage loaddata /tmp/sites.json",
762
            "snf-manage loaddata /tmp/page.json",
763
            "snf-manage createsuperuser --username=admin \
764
                  --email=admin@%s --noinput" % self.env.env.domain,
765
            ]
766

    
767
    def restart(self):
768
        return ["/etc/init.d/gunicorn restart"]
769

    
770

    
771
class Mount(SynnefoComponent):
772
    REQUIRED_PACKAGES = [
773
        "nfs-common"
774
        ]
775

    
776
    def prepare(self):
777
        ret = []
778
        dirs = [self.env.env.pithos_dir, self.env.env.image_dir, "/srv/archip"]
779
        for d in dirs:
780
            ret.append("mkdir -p %s" % d)
781
            cmd = """
782
cat >> /etc/fstab <<EOF
783
{0}:{1} {1}  nfs defaults,rw,noatime,rsize=131072,wsize=131072 0 0
784
EOF
785
""".format(self.env.env.pithos.ip, d)
786
            ret.append(cmd)
787

    
788
        return ret
789

    
790
    def initialize(self):
791
        ret = []
792
        dirs = [self.env.env.pithos_dir, self.env.env.image_dir, "/srv/archip"]
793
        for d in dirs:
794
            ret.append("mount %s" % d)
795
        return ret
796

    
797

    
798
class NFS(SynnefoComponent):
799
    REQUIRED_PACKAGES = [
800
        "nfs-kernel-server"
801
        ]
802

    
803
    def prepare_image(self):
804
        url = self.env.env.debian_base_url
805
        d = self.env.env.image_dir
806
        image = "debian_base.diskdump"
807
        return ["wget %s -O %s/%s" % (url, d, image)]
808

    
809
    def prepare(self):
810
        p = self.env.env.pithos_dir
811
        return [
812
            "mkdir -p %s" % self.env.env.image_dir,
813
            "mkdir -p %s/data" % p,
814
            "mkdir -p /srv/archip/blocks",
815
            "mkdir -p /srv/archip/maps",
816
            "chown www-data.www-data %s/data" % p,
817
            "chmod g+ws %s/data" % p,
818
            ] + self.prepare_image()
819

    
820
    def update_exports(self, node_info):
821
        cmd = """
822
cat >> /etc/exports <<EOF
823
{0} {2}(rw,async,no_subtree_check,no_root_squash)
824
{1} {2}(rw,async,no_subtree_check,no_root_squash)
825
/srv/archip {2}(rw,async,no_subtree_check,no_root_squash)
826
EOF
827
""".format(self.env.env.pithos_dir, self.env.env.image_dir, node_info.ip)
828
        return [cmd] + self.restart()
829

    
830
    def restart(self):
831
        return ["exportfs -a"]
832

    
833

    
834
class Pithos(SynnefoComponent):
835
    REQUIRED_PACKAGES = [
836
        "kamaki",
837
        "python-svipc",
838
        "snf-pithos-app",
839
        "snf-pithos-webclient",
840
        ]
841

    
842
    def export_service(self):
843
        f = self.env.jsonfile
844
        return [
845
            "snf-manage service-export-pithos > %s" % f
846
            ]
847

    
848
    def configure(self):
849
        r1 = {
850
            "ACCOUNTS": self.env.env.accounts.fqdn,
851
            "PITHOS": self.env.env.pithos.fqdn,
852
            "db_node": self.env.env.db.ip,
853
            "synnefo_user": self.env.env.synnefo_user,
854
            "synnefo_db_passwd": self.env.env.synnefo_db_passwd,
855
            "pithos_dir": self.env.env.pithos_dir,
856
            "PITHOS_SERVICE_TOKEN": self.env.service_token,
857
            "oa2_secret": self.env.env.oa2_secret,
858
            }
859
        r2 = {
860
            "ACCOUNTS": self.env.env.accounts.fqdn,
861
            "PITHOS_UI_CLOUDBAR_ACTIVE_SERVICE": self.env.service_id,
862
            }
863

    
864
        return [
865
            ("/etc/synnefo/pithos.conf", r1, {}),
866
            ("/etc/synnefo/webclient.conf", r2, {}),
867
            ]
868

    
869
    def initialize(self):
870
        return ["pithos-migrate stamp head"]
871

    
872

    
873
class PithosBackend(SynnefoComponent):
874
    REQUIRED_PACKAGES = [
875
        "snf-pithos-backend",
876
        ]
877

    
878
    def configure(self):
879
        r1 = {
880
            "db_node": self.env.env.db.ip,
881
            "synnefo_user": self.env.env.synnefo_user,
882
            "synnefo_db_passwd": self.env.env.synnefo_db_passwd,
883
            "pithos_dir": self.env.env.pithos_dir,
884
            }
885

    
886
        return [
887
            ("/etc/synnefo/backend.conf", r1, {}),
888
            ]
889

    
890

    
891
class Cyclades(SynnefoComponent):
892
    REQUIRED_PACKAGES = [
893
        "memcached",
894
        "python-memcache",
895
        "kamaki",
896
        "snf-cyclades-app",
897
        "python-django-south",
898
        ]
899

    
900
    def add_network(self):
901
        subnet = self.env.env.synnefo_public_network_subnet
902
        gw = self.env.env.synnefo_public_network_gateway
903
        ntype = self.env.env.synnefo_public_network_type
904
        link = self.env.env.common_bridge
905

    
906
        cmd = """
907
snf-manage network-create --subnet={0} --gateway={1} --public \
908
  --dhcp=True --flavor={2} --mode=bridged --link={3} --name=Internet \
909
  --floating-ip-pool=True
910
""".format(subnet, gw, ntype, link)
911

    
912
        return [cmd]
913

    
914
    def add_network6(self):
915
        subnet = "babe::/64"
916
        gw = "babe::1"
917
        ntype = self.env.env.synnefo_public_network_type
918
        link = self.env.env.common_bridge
919

    
920
        cmd = """
921
snf-manage network-create --subnet6={0} \
922
      --gateway6={1} --public --dhcp=True --flavor={2} --mode=bridged \
923
       --link={3} --name=IPv6PublicNetwork
924
""".format(subnet, gw, ntype, link)
925

    
926
        return [cmd]
927

    
928
    def export_service(self):
929
        f = self.env.jsonfile
930
        return [
931
            "snf-manage service-export-cyclades > %s" % f
932
            ]
933

    
934
    def list_backends(self):
935
        return [
936
            "snf-manage backend-list"
937
            ]
938

    
939
    def add_backend(self):
940
        cluster = self.env.env.cluster
941
        user = self.env.env.synnefo_user
942
        passwd = self.env.env.synnefo_rapi_passwd
943
        return [
944
            "snf-manage backend-add --clustername=%s --user=%s --pass=%s" %
945
            (cluster.fqdn, user, passwd)
946
            ]
947

    
948
    def undrain_backend(self):
949
        backend_id = self.env.backend_id
950
        return [
951
            "snf-manage backend-modify --drained=False %s" % str(backend_id)
952
            ]
953

    
954
    def prepare(self):
955
        return ["sed -i 's/false/true/' /etc/default/snf-dispatcher"]
956

    
957
    def configure(self):
958
        r1 = {
959
            "ACCOUNTS": self.env.env.accounts.fqdn,
960
            "CYCLADES": self.env.env.cyclades.fqdn,
961
            "mq_node": self.env.env.mq.ip,
962
            "db_node": self.env.env.db.ip,
963
            "synnefo_user": self.env.env.synnefo_user,
964
            "synnefo_db_passwd": self.env.env.synnefo_db_passwd,
965
            "synnefo_rabbitmq_passwd": self.env.env.synnefo_rabbitmq_passwd,
966
            "pithos_dir": self.env.env.pithos_dir,
967
            "common_bridge": self.env.env.common_bridge,
968
            "HOST": self.env.env.cyclades.ip,
969
            "domain": self.env.env.domain,
970
            "CYCLADES_SERVICE_TOKEN": self.env.service_token,
971
            "STATS": self.env.env.stats.fqdn,
972
            "SYNNEFO_VNC_PASSWD": self.env.env.synnefo_vnc_passwd,
973
            "CYCLADES_NODE_IP": self.env.env.cyclades.ip
974
            }
975
        return [
976
            ("/etc/synnefo/cyclades.conf", r1, {})
977
            ]
978

    
979
    def initialize(self):
980
        cpu = self.env.env.flavor_cpu
981
        ram = self.env.env.flavor_ram
982
        disk = self.env.env.flavor_disk
983
        storage = self.env.env.flavor_storage
984
        return [
985
            "snf-manage syncdb",
986
            "snf-manage migrate --delete-ghost-migrations",
987
            "snf-manage pool-create --type=mac-prefix \
988
              --base=aa:00:0 --size=65536",
989
            "snf-manage pool-create --type=bridge --base=prv --size=20",
990
            "snf-manage flavor-create %s %s %s %s" % (cpu, ram, disk, storage),
991
            ]
992

    
993
    def restart(self):
994
        return [
995
            "/etc/init.d/gunicorn restart",
996
            "/etc/init.d/snf-dispatcher restart",
997
            ]
998

    
999

    
1000
class VNC(SynnefoComponent):
1001
    REQUIRED_PACKAGES = [
1002
        "snf-vncauthproxy"
1003
        ]
1004

    
1005
    def prepare(self):
1006
        return ["mkdir -p /var/lib/vncauthproxy"]
1007

    
1008
    def configure(self):
1009
        return [
1010
            ("/var/lib/vncauthproxy/users", {}, {})
1011
            ]
1012

    
1013
    def initialize(self):
1014
        user = self.env.env.synnefo_user
1015
        passwd = self.env.env.synnefo_vnc_passwd
1016
        #TODO: run vncauthproxy-passwd
1017
        return []
1018

    
1019
    def restart(self):
1020
        return [
1021
            "/etc/init.d/vncauthproxy restart"
1022
            ]
1023

    
1024

    
1025
class Kamaki(SynnefoComponent):
1026
    REQUIRED_PACKAGES = [
1027
        "python-progress",
1028
        "kamaki",
1029
        ]
1030

    
1031
    def initialize(self):
1032
        url = "https://%s/astakos/identity/v2.0" % self.env.env.accounts.fqdn
1033
        token = self.env.user_auth_token
1034
        return [
1035
            "kamaki config set cloud.default.url %s" % url,
1036
            "kamaki config set cloud.default.token %s" % token,
1037
            "kamaki container create images",
1038
            ]
1039

    
1040
    def fetch_image(self):
1041
        url = self.env.env.debian_base_url
1042
        image = "debian_base.diskdump"
1043
        return [
1044
            "wget %s -O /tmp/%s" % (url, image)
1045
            ]
1046

    
1047
    def upload_image(self):
1048
        image = "debian_base.diskdump"
1049
        return [
1050
            "kamaki file upload --container images /tmp/%s %s" % (image, image)
1051
            ]
1052

    
1053
    def register_image(self):
1054
        image = "debian_base.diskdump"
1055
        image_location = "/images/%s" % image
1056
        cmd = """
1057
        kamaki image register --name "Debian Base" --location {0} \
1058
              --public --disk-format=diskdump \
1059
              --property OSFAMILY=linux --property ROOT_PARTITION=1 \
1060
              --property description="Debian Squeeze Base System" \
1061
              --property size=450M --property kernel=2.6.32 \
1062
              --property GUI="No GUI" --property sortorder=1 \
1063
              --property USERS=root --property OS=debian
1064
        """.format(image_location)
1065
        return [
1066
            "sleep 5",
1067
            cmd
1068
            ]
1069

    
1070

    
1071
class Burnin(SynnefoComponent):
1072
    REQUIRED_PACKAGES = [
1073
        "kamaki",
1074
        "snf-tools",
1075
        ]
1076

    
1077

    
1078
class Collectd(SynnefoComponent):
1079
    REQUIRED_PACKAGES = [
1080
        "collectd",
1081
        ]
1082

    
1083
    def configure(self):
1084
        return [
1085
            ("/etc/collectd/collectd.conf", {}, {}),
1086
            ]
1087

    
1088
    def restart(self):
1089
        return [
1090
            "/etc/init.d/collectd restart",
1091
            ]
1092

    
1093

    
1094
class Stats(SynnefoComponent):
1095
    REQUIRED_PACKAGES = [
1096
        "snf-stats-app",
1097
        ]
1098

    
1099
    def prepare(self):
1100
        return [
1101
            "mkdir -p /var/cache/snf-stats-app/",
1102
            "chown www-data:www-data /var/cache/snf-stats-app/",
1103
            ]
1104

    
1105
    def configure(self):
1106
        r1 = {
1107
            "STATS": self.env.env.stats.fqdn,
1108
            }
1109
        return [
1110
            ("/etc/synnefo/stats.conf", r1, {}),
1111
            ("/etc/collectd/synnefo-stats.conf", r1, {}),
1112
            ]
1113

    
1114
    def restart(self):
1115
        return [
1116
            "/etc/init.d/gunicorn restart",
1117
            "/etc/init.d/apache2 restart",
1118
            ]
1119

    
1120

    
1121
class GanetiCollectd(SynnefoComponent):
1122
    def configure(self):
1123
        r1 = {
1124
            "STATS": self.env.env.stats.fqdn,
1125
            }
1126
        return [
1127
            ("/etc/collectd/passwd", {}, {}),
1128
            ("/etc/collectd/synnefo-ganeti.conf", r1, {}),
1129
            ]
1130

    
1131

    
1132
class Archip(SynnefoComponent):
1133
    REQUIRED_PACKAGES = [
1134
        "librados2",
1135
        "archipelago",
1136
        "archipelago-dbg",
1137
        "archipelago-modules-dkms",
1138
        "archipelago-modules-source",
1139
        "archipelago-rados",
1140
        "archipelago-rados-dbg",
1141
        "libxseg0",
1142
        "libxseg0-dbg",
1143
        "python-archipelago",
1144
        "python-xseg",
1145
        ]
1146

    
1147
    def prepare(self):
1148
        return ["mkdir -p /etc/archipelago"]
1149

    
1150
    def configure(self):
1151
        r1 = {"HOST": self.node_info.fqdn}
1152
        return [
1153
            ("/etc/gunicorn.d/synnefo-archip", r1,
1154
             {"remote": "/etc/gunicorn.d/synnefo"}),
1155
            ("/etc/archipelago/pithos.conf.py", {}, {}),
1156
            ("/etc/archipelago/archipelago.conf", {}, {})
1157
            ]
1158

    
1159
    def restart(self):
1160
        return [
1161
            "/etc/init.d/gunicorn restart",
1162
            "archipelago restart"
1163
            ]
1164

    
1165

    
1166
class ArchipGaneti(SynnefoComponent):
1167
    REQUIRED_PACKAGES = [
1168
        "archipelago-ganeti",
1169
        ]
1170

    
1171

    
1172
class ExtStorage(SynnefoComponent):
1173
    def prepare(self):
1174
        return ["mkdir -p /usr/local/lib/ganeti/"]
1175

    
1176
    def initialize(self):
1177
        url = "http://code.grnet.gr/git/extstorage"
1178
        extdir = "/usr/local/lib/ganeti/extstorage"
1179
        return [
1180
            "git clone %s %s" % (url, extdir)
1181
            ]