Statistics
| Branch: | Tag: | Revision:

root / snf-deploy / snfdeploy / utils.py @ d4f28d8f

History | View | Annotate | Download (7.8 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
from __future__ import with_statement
35
from fabric.api import hide, env, settings, local, roles
36
from fabric.operations import run, put, get
37
import fabric
38
import re
39
import os
40
import shutil
41
import tempfile
42
import ast
43
from snfdeploy.lib import debug, Conf, Env, disable_color
44
from snfdeploy import massedit
45
from snfdeploy.components import *
46
from snfdeploy.roles import ROLES, CONFLICTS
47

    
48

    
49
def abort(action):
50
    def inner(*args, **kwargs):
51
        try:
52
            return action(*args, **kwargs)
53
        except BaseException as e:
54
            abort = kwargs.get("abort", True)
55
            force = env.force
56
            if not abort or force:
57
                debug(env.host,
58
                      "WARNING: command failed. Continuing anyway...")
59
            else:
60
                fabric.utils.abort(e)
61
    return inner
62

    
63

    
64
@abort
65
def try_get(remote_path, local_path=None, **kwargs):
66
    if env.dry_run:
67
        debug(env.host, " * Fetching file localy... ", remote_path)
68
        return
69
    get(remote_path, local_path=local_path, **kwargs)
70

    
71

    
72
@abort
73
def try_put(local_path=None, remote_path=None, mode=0644, **kwargs):
74
    if env.dry_run:
75
        debug(env.host, " * Upload file... ", remote_path)
76
        return
77
    put(local_path=local_path, remote_path=remote_path, mode=mode)
78

    
79

    
80
@abort
81
def try_run(cmd, **kwargs):
82
    if env.dry_run:
83
        debug(env.host, cmd)
84
        return ""
85
    elif env.local:
86
        return local(cmd, capture=True, shell="/bin/bash")
87
    else:
88
        return run(cmd)
89

    
90

    
91
def install_package(package):
92
    debug(env.host, " * Installing package... ", package)
93
    apt_get = "export DEBIAN_FRONTEND=noninteractive ;" + \
94
              "apt-get install -y --force-yes "
95

    
96
    host_info = env.env.ips_info[env.host]
97
    env.env.update_packages(host_info.os)
98
    if ast.literal_eval(env.env.use_local_packages):
99
        with settings(warn_only=True):
100
            deb = local("ls %s/%s*%s_*.deb"
101
                        % (env.env.packages, package, host_info.os),
102
                        capture=True)
103
            if deb:
104
                debug(env.host,
105
                      " * Package %s found in %s..."
106
                      % (package, env.env.packages))
107
                try_put(deb, "/tmp/")
108
                try_run("dpkg -i /tmp/%s || "
109
                        % os.path.basename(deb) + apt_get + "-f")
110
                try_run("rm /tmp/%s" % os.path.basename(deb))
111
                return
112

    
113
    info = getattr(env.env, package)
114
    if info in \
115
            ["squeeze-backports", "squeeze", "stable",
116
             "testing", "unstable", "wheezy"]:
117
        apt_get += " -t %s %s " % (info, package)
118
    elif info:
119
        apt_get += " %s=%s " % (package, info)
120
    else:
121
        apt_get += package
122

    
123
    try_run(apt_get)
124

    
125
    return
126

    
127

    
128
def customize_settings_from_tmpl(tmpl, replace):
129
    debug(env.host, " * Customizing template %s..." % tmpl)
130
    local = env.env.templates + tmpl
131
    _, custom = tempfile.mkstemp()
132
    shutil.copyfile(local, custom)
133
    for k, v in replace.iteritems():
134
        regex = "re.sub('%{0}%', '{1}', line)".format(k.upper(), v)
135
        massedit.edit_files([custom], [regex], dry_run=False)
136

    
137
    return custom
138

    
139

    
140
def get_node_info(ident):
141
    if ident in env.env.ips_info:
142
        return env.env.ips_info[ident]
143
    elif ident in env.env.hosts_info:
144
        return env.env.hosts_info[ident]
145
    elif ident in env.env.nodes_info:
146
        return env.env.nodes_info[ident]
147

    
148

    
149
def GetFromComponent(component, remote, local):
150
    c = GetSynnefoComponent(component)
151
    c.debug(" * Downloading: ", remote)
152
    with settings(host_string=c.node_info.ip):
153
        try_get(remote, local)
154

    
155

    
156
def PutToComponent(component, local, remote):
157
    c = GetSynnefoComponent(component)
158
    c.debug(" * Uploading: ", remote)
159
    with settings(host_string=c.node_info.ip):
160
        try_put(local, remote)
161

    
162

    
163
def RunComponentMethod(component, method, *args, **kwargs):
164
    c = GetSynnefoComponent(component)
165
    c.debug(" * Running method: ", method)
166
    with settings(host_string=c.node_info.ip):
167
        fn = getattr(c, method)
168
        ret = ""
169
        for cmd in fn(*args, **kwargs):
170
            ret += try_run(cmd)
171
        return ret
172

    
173

    
174
def GetSynnefoComponent(component):
175
    node_info = get_node_info(env.host)
176
    env.password = node_info.passwd
177
    return component(node_info, env)
178

    
179

    
180
def conflicting_exists(component):
181
    conflict = CONFLICTS.get(component, [])
182
    for c in conflict:
183
        cs = env.env.status.check_status(env.host, c)
184
        if cs:
185
            debug(env.host, "Conflicting component already exists", c.__name__)
186
            return True
187

    
188
    return False
189

    
190

    
191
def SetupSynnefoRole(role):
192
    debug("Setting up base configuration for: ", role)
193
    try:
194
        components = ROLES.get(role)
195
    except KeyError:
196
        debug(env.host, "Please give a valid role")
197
        return
198
    for c in components:
199
        if conflicting_exists(c):
200
            continue
201
        status = env.env.status.check_status(env.host, c)
202
        if status:
203
            debug(env.host, "Base configuration already exists", c.__name__)
204
        else:
205
            AddSynnefoComponent(c)
206
            env.env.status.update_status(env.host, c, "ok")
207
            env.env.status.write_status()
208

    
209

    
210
class AddSynnefoComponent(object):
211

    
212
    def _run(self, commands):
213
        for c in commands:
214
            try_run(c)
215

    
216
    def _install(self, packages):
217
        for p in packages:
218
            install_package(p)
219

    
220
    def _configure(self, templates):
221
        for tmpl, replace, opts in templates:
222
            mode = opts.get("mode", 0644)
223
            remote = opts.get("remote", tmpl)
224
            custom = customize_settings_from_tmpl(tmpl, replace)
225
            try_put(custom, remote, mode)
226
            os.remove(custom)
227

    
228
    def __init__(self, component):
229
        self.c = GetSynnefoComponent(component)
230
        self.c.debug("Adding component..")
231

    
232
        self.c.debug(" * Checking prerequisites..")
233
        self._run(self.c.check())
234

    
235
        self.c.debug(" * Installing packages..")
236
        self._install(self.c.install())
237

    
238
        self.c.debug(" * Preparing configuration..")
239
        self._run(self.c.prepare())
240

    
241
        self.c.debug(" * Setting up configuration files..")
242
        self._configure(self.c.configure())
243

    
244
        self.c.debug(" * Restarting services..")
245
        self._run(self.c.restart())
246

    
247
        self.c.debug(" * Initializing setup..")
248
        self._run(self.c.initialize())
249

    
250
        self.c.debug(" * Testing setup..")
251
        self._run(self.c.test())