Statistics
| Branch: | Tag: | Revision:

root / snf-deploy / snfdeploy / lib.py @ 01cf073b

History | View | Annotate | Download (10.6 kB)

1
#!/usr/bin/python
2

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

    
36
import time
37
import ipaddr
38
import os
39
import signal
40
import ConfigParser
41
import sys
42
import re
43
import random
44
import subprocess
45
import imp
46

    
47

    
48
HEADER = '\033[95m'
49
OKBLUE = '\033[94m'
50
OKGREEN = '\033[92m'
51
WARNING = '\033[93m'
52
FAIL = '\033[91m'
53
ENDC = '\033[0m'
54

    
55

    
56
def disable_color():
57
    global HEADER
58
    global OKBLUE
59
    global OKGREEN
60
    global WARNING
61
    global FAIL
62
    global ENDC
63

    
64
    HEADER = ''
65
    OKBLUE = ''
66
    OKGREEN = ''
67
    WARNING = ''
68
    FAIL = ''
69
    ENDC = ''
70

    
71

    
72
if not sys.stdout.isatty():
73
    disable_color()
74

    
75

    
76
class Host(object):
77
    def __init__(self, hostname, ip, mac, domain, os, passwd):
78
        self.hostname = hostname
79
        self.ip = ip
80
        self.mac = mac
81
        self.domain = domain
82
        self.os = os
83
        self.passwd = passwd
84

    
85
    @property
86
    def fqdn(self):
87
        return self.hostname + "." + self.domain
88

    
89
    @property
90
    def arecord(self):
91
        return self.fqdn + " 300 A " + self.ip
92

    
93
    @property
94
    def ptrrecord(self):
95
        return ".".join(raddr(self.ip)) + ".in-addr.arpa 300 PTR " + self.fqdn
96

    
97
    @property
98
    def cnamerecord(self):
99
        return ""
100

    
101

    
102
class Alias(Host):
103
    def __init__(self, host, alias):
104
        super(Alias, self).__init__(host.hostname, host.ip, host.mac,
105
                                    host.domain, host.os, host.passwd)
106
        self.alias = alias
107

    
108
    @property
109
    def cnamerecord(self):
110
        return self.fqdn + " 300 CNAME " + self.hostname + "." + self.domain
111

    
112
    @property
113
    def ptrrecord(self):
114
        return ""
115

    
116
    @property
117
    def arecord(self):
118
        return ""
119

    
120
    @property
121
    def fqdn(self):
122
        return self.alias + "." + self.domain
123

    
124

    
125
class Env(object):
126

    
127
    def update_packages(self, os):
128
        for section in self.conf.files[os]:
129
            self.evaluate(os, section)
130

    
131
    def evaluate(self, filename, section):
132
        for k, v in self.conf.get_section(filename, section):
133
            setattr(self, k, v)
134

    
135
    def __init__(self, conf):
136
        self.conf = conf
137
        for f, sections in conf.files.iteritems():
138
            for s in sections:
139
                self.evaluate(f, s)
140

    
141
        self.node2hostname = dict(conf.get_section("nodes", "hostnames"))
142
        self.node2ip = dict(conf.get_section("nodes", "ips"))
143
        self.node2mac = dict(conf.get_section("nodes", "macs"))
144
        self.node2os = dict(conf.get_section("nodes", "os"))
145
        self.node2passwd = dict(conf.get_section("nodes", "passwords"))
146

    
147
        self.hostnames = [self.node2hostname[n]
148
                          for n in self.nodes.split(",")]
149

    
150
        self.ips = [self.node2ip[n]
151
                    for n in self.nodes.split(",")]
152

    
153
        self.net = ipaddr.IPNetwork(self.subnet)
154

    
155
        self.nodes_info = {}
156
        self.hosts_info = {}
157
        self.ips_info = {}
158
        for node in self.nodes.split(","):
159
            host = Host(self.node2hostname[node],
160
                        self.node2ip[node],
161
                        self.node2mac[node], self.domain, self.node2os[node],
162
                        self.node2passwd[node])
163

    
164
            self.nodes_info[node] = host
165
            self.hosts_info[host.hostname] = host
166
            self.ips_info[host.ip] = host
167

    
168
        self.cluster = Host(self.cluster_name, self.cluster_ip, None,
169
                            self.domain, None, None)
170

    
171
        # This is needed because "".split(",") -> ['']
172
        if self.cluster_nodes:
173
            self.cluster_nodes = self.cluster_nodes.split(",")
174
        else:
175
            self.cluster_nodes = []
176

    
177
        self.cluster_hostnames = [self.node2hostname[n]
178
                                  for n in self.cluster_nodes]
179

    
180
        self.cluster_ips = [self.node2ip[n]
181
                            for n in self.cluster_nodes]
182

    
183
        self.master = self.nodes_info[self.master_node]
184

    
185
        self.roles_info = {}
186
        for role, node in conf.get_section("synnefo", "roles"):
187
            self.roles_info[role] = Alias(self.nodes_info[node], role)
188
            setattr(self, role, self.roles_info[role])
189

    
190
        self.astakos = self.accounts
191
        # This is the nodes that get nfs mount points
192
        self.mount = self.ips[:]
193
        self.mount.remove(self.pithos.ip)
194

    
195

    
196
class Status(object):
197
    STATUSES = [
198
        "check",
199
        "prepare",
200
        "install",
201
        "restart",
202
        "initialize",
203
        "test",
204
        "ok",
205
        ]
206

    
207
    def create_section(self, ip):
208
        try:
209
            section = self.config.items(ip, True)
210
        except ConfigParser.NoSectionError:
211
            self.config.add_section(ip)
212

    
213
    def __init__(self, args):
214
        self.config = ConfigParser.ConfigParser()
215
        self.config.optionxform = str
216
        self.statusfile = os.path.join(args.confdir, "status.conf")
217
        self.config.read(self.statusfile)
218

    
219
    def check_status(self, ip, component_class):
220
        try:
221
            return self.config.get(ip, component_class.__name__, True)
222
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
223
            return None
224

    
225
    def update_status(self, ip, component_class, status):
226
        self.create_section(ip)
227
        self.config.set(ip, component_class.__name__, status)
228

    
229
    def write_status(self):
230
        with open(self.statusfile, 'wb') as configfile:
231
            self.config.write(configfile)
232

    
233

    
234
class Conf(object):
235

    
236
    files = {
237
        "nodes": ["network", "info"],
238
        "deploy": ["dirs", "packages", "keys", "options"],
239
        "vcluster": ["cluster", "image", "network"],
240
        "synnefo": ["cred", "synnefo", "roles"],
241
        "wheezy": ["debian", "ganeti", "synnefo", "other", "archip"],
242
    }
243
    confdir = "/etc/snf-deploy"
244

    
245
    def get_ganeti(self, cluster_name):
246
        self.files["ganeti"] = [cluster_name]
247

    
248
    def __init__(self, args):
249
        self.confdir = args.confdir
250
        self.get_ganeti(args.cluster_name)
251
        for f in self.files.keys():
252
            setattr(self, f, self.read_config(f))
253
        for f, sections in self.files.iteritems():
254
            for s in sections:
255
                for k, v in self.get_section(f, s):
256
                    if getattr(args, k, None):
257
                        self.set(f, s, k, getattr(args, k))
258
        if args.autoconf:
259
            self.autoconf()
260

    
261
    def autoconf(self):
262
        #domain = get_domain()
263
        #if domain:
264
        #    self.nodes.set("network", "domain", get_domain())
265
        # self.nodes.set("network", "subnet", "/".join(get_netinfo()))
266
        # self.nodes.set("network", "gateway", get_default_route()[0])
267
        self.nodes.set("hostnames", "node1", get_hostname())
268
        self.nodes.set("ips", "node1", get_netinfo()[0])
269
        self.nodes.set("info", "nodes", "node1")
270
        self.nodes.set("info", "public_iface", get_default_route()[1])
271

    
272
    def read_config(self, f):
273
        config = ConfigParser.ConfigParser()
274
        config.optionxform = str
275
        filename = os.path.join(self.confdir, f) + ".conf"
276
        config.read(filename)
277
        return config
278

    
279
    def set(self, conf, section, option, value):
280
        c = getattr(self, conf)
281
        c.set(section, option, value)
282

    
283
    def get(self, conf, section, option):
284
        c = getattr(self, conf)
285
        return c.get(section, option, True)
286

    
287
    def get_section(self, conf, section):
288
        c = getattr(self, conf)
289
        return c.items(section, True)
290

    
291
    def print_config(self):
292
        for f in self.files.keys():
293
            getattr(self, f).write(sys.stdout)
294

    
295

    
296
def debug(host, msg, info=""):
297

    
298
    print " ".join([HEADER, host, OKBLUE, msg, OKGREEN, info, ENDC])
299

    
300

    
301
def check_pidfile(pidfile):
302
    print("Checking pidfile " + pidfile)
303
    try:
304
        f = open(pidfile, "r")
305
        pid = f.readline()
306
        os.kill(int(pid), signal.SIGKILL)
307
        f.close()
308
        os.remove(pidfile)
309
        time.sleep(5)
310
    except:
311
        pass
312

    
313

    
314
def random_mac():
315
    mac = [0x52, 0x54, 0x56,
316
           random.randint(0x00, 0xff),
317
           random.randint(0x00, 0xff),
318
           random.randint(0x00, 0xff)]
319
    return ':'.join(map(lambda x: "%02x" % x, mac))
320

    
321

    
322
def create_dir(d, clean=False):
323
    os.system("mkdir -p " + d)
324
    if clean:
325
        try:
326
            os.system("rm -f %s/*" % d)
327
        except:
328
            pass
329

    
330

    
331
def get_netinfo():
332
    _, pdev = get_default_route()
333
    r = re.compile(".*inet (\S+)/(\S+).*")
334
    s = subprocess.Popen(['ip', 'addr', 'show', 'dev', pdev],
335
                         stdout=subprocess.PIPE)
336

    
337
    for line in s.stdout.readlines():
338
        match = r.search(line)
339
        if match:
340
            ip, size = match.groups()
341
            break
342

    
343
    return ip, size
344

    
345

    
346
def get_hostname():
347
    s = subprocess.Popen(['hostname', '-s'], stdout=subprocess.PIPE)
348
    return s.stdout.readline().strip()
349

    
350

    
351
def get_domain():
352
    s = subprocess.Popen(['hostname', '-d'], stdout=subprocess.PIPE)
353
    return s.stdout.readline().strip()
354

    
355

    
356
def get_default_route():
357
    r = re.compile("default via (\S+) dev (\S+)")
358
    s = subprocess.Popen(['ip', 'route'], stdout=subprocess.PIPE)
359
    for line in s.stdout.readlines():
360
        match = r.search(line)
361
        if match:
362
            gw, dev = match.groups()
363
            break
364

    
365
    return (gw, dev)
366

    
367

    
368
def import_conf_file(filename, directory):
369
    return imp.load_module(filename, *imp.find_module(filename, [directory]))
370

    
371

    
372
def raddr(addr):
373
    return list(reversed(addr.replace("/", "-").split(".")))