Statistics
| Branch: | Tag: | Revision:

root / ci / utils.py @ f041c796

History | View | Annotate | Download (20.9 kB)

1 c29ac11d Ilias Tsitsimpis
#!/usr/bin/env python
2 c29ac11d Ilias Tsitsimpis
3 c29ac11d Ilias Tsitsimpis
"""
4 c29ac11d Ilias Tsitsimpis
Synnefo ci utils module
5 c29ac11d Ilias Tsitsimpis
"""
6 c29ac11d Ilias Tsitsimpis
7 c29ac11d Ilias Tsitsimpis
import os
8 c29ac11d Ilias Tsitsimpis
import sys
9 c29ac11d Ilias Tsitsimpis
import time
10 c29ac11d Ilias Tsitsimpis
import logging
11 c29ac11d Ilias Tsitsimpis
import fabric.api as fabric
12 b69b55ca Christos Stavrakakis
import subprocess
13 88e6558b Christos Stavrakakis
import tempfile
14 c29ac11d Ilias Tsitsimpis
from ConfigParser import ConfigParser, DuplicateSectionError
15 c29ac11d Ilias Tsitsimpis
16 c1b1d444 Christos Stavrakakis
from kamaki.cli import config as kamaki_config
17 c29ac11d Ilias Tsitsimpis
from kamaki.clients.astakos import AstakosClient
18 c29ac11d Ilias Tsitsimpis
from kamaki.clients.cyclades import CycladesClient
19 c29ac11d Ilias Tsitsimpis
from kamaki.clients.image import ImageClient
20 c29ac11d Ilias Tsitsimpis
21 6ca8f81a Christos Stavrakakis
DEFAULT_CONFIG_FILE = "new_config"
22 6ca8f81a Christos Stavrakakis
23 c29ac11d Ilias Tsitsimpis
24 c29ac11d Ilias Tsitsimpis
def _run(cmd, verbose):
25 c29ac11d Ilias Tsitsimpis
    """Run fabric with verbose level"""
26 c29ac11d Ilias Tsitsimpis
    if verbose:
27 c29ac11d Ilias Tsitsimpis
        args = ('running',)
28 c29ac11d Ilias Tsitsimpis
    else:
29 c29ac11d Ilias Tsitsimpis
        args = ('running', 'stdout',)
30 c29ac11d Ilias Tsitsimpis
    with fabric.hide(*args):
31 c29ac11d Ilias Tsitsimpis
        return fabric.run(cmd)
32 c29ac11d Ilias Tsitsimpis
33 c29ac11d Ilias Tsitsimpis
34 c29ac11d Ilias Tsitsimpis
def _red(msg):
35 c29ac11d Ilias Tsitsimpis
    """Red color"""
36 c29ac11d Ilias Tsitsimpis
    #return "\x1b[31m" + str(msg) + "\x1b[0m"
37 c29ac11d Ilias Tsitsimpis
    return str(msg)
38 c29ac11d Ilias Tsitsimpis
39 c29ac11d Ilias Tsitsimpis
40 c29ac11d Ilias Tsitsimpis
def _yellow(msg):
41 c29ac11d Ilias Tsitsimpis
    """Yellow color"""
42 c29ac11d Ilias Tsitsimpis
    #return "\x1b[33m" + str(msg) + "\x1b[0m"
43 c29ac11d Ilias Tsitsimpis
    return str(msg)
44 c29ac11d Ilias Tsitsimpis
45 c29ac11d Ilias Tsitsimpis
46 c29ac11d Ilias Tsitsimpis
def _green(msg):
47 c29ac11d Ilias Tsitsimpis
    """Green color"""
48 c29ac11d Ilias Tsitsimpis
    #return "\x1b[32m" + str(msg) + "\x1b[0m"
49 c29ac11d Ilias Tsitsimpis
    return str(msg)
50 c29ac11d Ilias Tsitsimpis
51 c29ac11d Ilias Tsitsimpis
52 c29ac11d Ilias Tsitsimpis
def _check_fabric(fun):
53 c29ac11d Ilias Tsitsimpis
    """Check if fabric env has been set"""
54 43a22402 Christos Stavrakakis
    def wrapper(self, *args, **kwargs):
55 c29ac11d Ilias Tsitsimpis
        """wrapper function"""
56 c29ac11d Ilias Tsitsimpis
        if not self.fabric_installed:
57 c29ac11d Ilias Tsitsimpis
            self.setup_fabric()
58 43a22402 Christos Stavrakakis
        return fun(self, *args, **kwargs)
59 c29ac11d Ilias Tsitsimpis
    return wrapper
60 c29ac11d Ilias Tsitsimpis
61 c29ac11d Ilias Tsitsimpis
62 c29ac11d Ilias Tsitsimpis
def _check_kamaki(fun):
63 c29ac11d Ilias Tsitsimpis
    """Check if kamaki has been initialized"""
64 43a22402 Christos Stavrakakis
    def wrapper(self, *args, **kwargs):
65 c29ac11d Ilias Tsitsimpis
        """wrapper function"""
66 c29ac11d Ilias Tsitsimpis
        if not self.kamaki_installed:
67 c29ac11d Ilias Tsitsimpis
            self.setup_kamaki()
68 43a22402 Christos Stavrakakis
        return fun(self, *args, **kwargs)
69 c29ac11d Ilias Tsitsimpis
    return wrapper
70 c29ac11d Ilias Tsitsimpis
71 c29ac11d Ilias Tsitsimpis
72 c29ac11d Ilias Tsitsimpis
class _MyFormatter(logging.Formatter):
73 c29ac11d Ilias Tsitsimpis
    """Logging Formatter"""
74 c29ac11d Ilias Tsitsimpis
    def format(self, record):
75 c29ac11d Ilias Tsitsimpis
        format_orig = self._fmt
76 c29ac11d Ilias Tsitsimpis
        if record.levelno == logging.DEBUG:
77 c29ac11d Ilias Tsitsimpis
            self._fmt = "  %(msg)s"
78 c29ac11d Ilias Tsitsimpis
        elif record.levelno == logging.INFO:
79 c29ac11d Ilias Tsitsimpis
            self._fmt = "%(msg)s"
80 c29ac11d Ilias Tsitsimpis
        elif record.levelno == logging.WARNING:
81 c29ac11d Ilias Tsitsimpis
            self._fmt = _yellow("[W] %(msg)s")
82 c29ac11d Ilias Tsitsimpis
        elif record.levelno == logging.ERROR:
83 c29ac11d Ilias Tsitsimpis
            self._fmt = _red("[E] %(msg)s")
84 c29ac11d Ilias Tsitsimpis
        result = logging.Formatter.format(self, record)
85 c29ac11d Ilias Tsitsimpis
        self._fmt = format_orig
86 c29ac11d Ilias Tsitsimpis
        return result
87 c29ac11d Ilias Tsitsimpis
88 c29ac11d Ilias Tsitsimpis
89 c29ac11d Ilias Tsitsimpis
class SynnefoCI(object):
90 c29ac11d Ilias Tsitsimpis
    """SynnefoCI python class"""
91 c29ac11d Ilias Tsitsimpis
92 6ca8f81a Christos Stavrakakis
    def __init__(self, config_file=None, cleanup_config=False, cloud=None):
93 c29ac11d Ilias Tsitsimpis
        """ Initialize SynnefoCI python class
94 c29ac11d Ilias Tsitsimpis

95 c29ac11d Ilias Tsitsimpis
        Setup logger, local_dir, config and kamaki
96 c29ac11d Ilias Tsitsimpis
        """
97 c29ac11d Ilias Tsitsimpis
        # Setup logger
98 c29ac11d Ilias Tsitsimpis
        self.logger = logging.getLogger('synnefo-ci')
99 c29ac11d Ilias Tsitsimpis
        self.logger.setLevel(logging.DEBUG)
100 c29ac11d Ilias Tsitsimpis
        handler = logging.StreamHandler()
101 c29ac11d Ilias Tsitsimpis
        handler.setFormatter(_MyFormatter())
102 c29ac11d Ilias Tsitsimpis
        self.logger.addHandler(handler)
103 c29ac11d Ilias Tsitsimpis
104 c29ac11d Ilias Tsitsimpis
        # Get our local dir
105 c29ac11d Ilias Tsitsimpis
        self.ci_dir = os.path.dirname(os.path.abspath(__file__))
106 c29ac11d Ilias Tsitsimpis
        self.repo_dir = os.path.dirname(self.ci_dir)
107 c29ac11d Ilias Tsitsimpis
108 c29ac11d Ilias Tsitsimpis
        # Read config file
109 6ca8f81a Christos Stavrakakis
        if config_file is None:
110 6ca8f81a Christos Stavrakakis
            config_file = DEFAULT_CONFIG_FILE
111 6ca8f81a Christos Stavrakakis
        if not os.path.isabs(config_file):
112 6ca8f81a Christos Stavrakakis
            config_file = os.path.join(self.ci_dir, config_file)
113 e908a902 Christos Stavrakakis
114 c29ac11d Ilias Tsitsimpis
        self.config = ConfigParser()
115 c29ac11d Ilias Tsitsimpis
        self.config.optionxform = str
116 6ca8f81a Christos Stavrakakis
        self.config.read(config_file)
117 c29ac11d Ilias Tsitsimpis
        temp_config = self.config.get('Global', 'temporary_config')
118 c29ac11d Ilias Tsitsimpis
        if cleanup_config:
119 c29ac11d Ilias Tsitsimpis
            try:
120 c29ac11d Ilias Tsitsimpis
                os.remove(temp_config)
121 c29ac11d Ilias Tsitsimpis
            except:
122 c29ac11d Ilias Tsitsimpis
                pass
123 c29ac11d Ilias Tsitsimpis
        else:
124 c29ac11d Ilias Tsitsimpis
            self.config.read(self.config.get('Global', 'temporary_config'))
125 c29ac11d Ilias Tsitsimpis
126 c1b1d444 Christos Stavrakakis
        # Set kamaki cloud
127 c1b1d444 Christos Stavrakakis
        if cloud is not None:
128 c1b1d444 Christos Stavrakakis
            self.kamaki_cloud = cloud
129 c1b1d444 Christos Stavrakakis
        elif self.config.has_option("Deployment", "kamaki_cloud"):
130 c1b1d444 Christos Stavrakakis
            kamaki_cloud = self.config.get("Deployment", "kamaki_cloud")
131 c1b1d444 Christos Stavrakakis
            if kamaki_cloud == "":
132 c1b1d444 Christos Stavrakakis
                self.kamaki_cloud = None
133 c1b1d444 Christos Stavrakakis
        else:
134 c1b1d444 Christos Stavrakakis
            self.kamaki_cloud = None
135 c1b1d444 Christos Stavrakakis
136 c29ac11d Ilias Tsitsimpis
        # Initialize variables
137 c29ac11d Ilias Tsitsimpis
        self.fabric_installed = False
138 c29ac11d Ilias Tsitsimpis
        self.kamaki_installed = False
139 c29ac11d Ilias Tsitsimpis
        self.cyclades_client = None
140 c29ac11d Ilias Tsitsimpis
        self.image_client = None
141 c29ac11d Ilias Tsitsimpis
142 c29ac11d Ilias Tsitsimpis
    def setup_kamaki(self):
143 c29ac11d Ilias Tsitsimpis
        """Initialize kamaki
144 c29ac11d Ilias Tsitsimpis

145 c29ac11d Ilias Tsitsimpis
        Setup cyclades_client and image_client
146 c29ac11d Ilias Tsitsimpis
        """
147 c1b1d444 Christos Stavrakakis
148 c1b1d444 Christos Stavrakakis
        config = kamaki_config.Config()
149 c1b1d444 Christos Stavrakakis
        if self.kamaki_cloud is None:
150 c1b1d444 Christos Stavrakakis
            self.kamaki_cloud = config.get_global("default_cloud")
151 c1b1d444 Christos Stavrakakis
152 c1b1d444 Christos Stavrakakis
        self.logger.info("Setup kamaki client, using cloud '%s'.." %
153 c1b1d444 Christos Stavrakakis
                         self.kamaki_cloud)
154 c1b1d444 Christos Stavrakakis
        auth_url = config.get_cloud(self.kamaki_cloud, "url")
155 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Authentication URL is %s" % _green(auth_url))
156 c1b1d444 Christos Stavrakakis
        token = config.get_cloud(self.kamaki_cloud, "token")
157 c29ac11d Ilias Tsitsimpis
        #self.logger.debug("Token is %s" % _green(token))
158 c29ac11d Ilias Tsitsimpis
159 c29ac11d Ilias Tsitsimpis
        astakos_client = AstakosClient(auth_url, token)
160 c29ac11d Ilias Tsitsimpis
161 c29ac11d Ilias Tsitsimpis
        cyclades_url = \
162 c29ac11d Ilias Tsitsimpis
            astakos_client.get_service_endpoints('compute')['publicURL']
163 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Cyclades API url is %s" % _green(cyclades_url))
164 c29ac11d Ilias Tsitsimpis
        self.cyclades_client = CycladesClient(cyclades_url, token)
165 c29ac11d Ilias Tsitsimpis
        self.cyclades_client.CONNECTION_RETRY_LIMIT = 2
166 c29ac11d Ilias Tsitsimpis
167 c29ac11d Ilias Tsitsimpis
        image_url = \
168 c29ac11d Ilias Tsitsimpis
            astakos_client.get_service_endpoints('image')['publicURL']
169 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Images API url is %s" % _green(image_url))
170 c29ac11d Ilias Tsitsimpis
        self.image_client = ImageClient(cyclades_url, token)
171 c29ac11d Ilias Tsitsimpis
        self.image_client.CONNECTION_RETRY_LIMIT = 2
172 c29ac11d Ilias Tsitsimpis
173 c29ac11d Ilias Tsitsimpis
    def _wait_transition(self, server_id, current_status, new_status):
174 c29ac11d Ilias Tsitsimpis
        """Wait for server to go from current_status to new_status"""
175 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Waiting for server to become %s" % new_status)
176 c29ac11d Ilias Tsitsimpis
        timeout = self.config.getint('Global', 'build_timeout')
177 c29ac11d Ilias Tsitsimpis
        sleep_time = 5
178 c29ac11d Ilias Tsitsimpis
        while True:
179 c29ac11d Ilias Tsitsimpis
            server = self.cyclades_client.get_server_details(server_id)
180 c29ac11d Ilias Tsitsimpis
            if server['status'] == new_status:
181 c29ac11d Ilias Tsitsimpis
                return server
182 c29ac11d Ilias Tsitsimpis
            elif timeout < 0:
183 c29ac11d Ilias Tsitsimpis
                self.logger.error(
184 c29ac11d Ilias Tsitsimpis
                    "Waiting for server to become %s timed out" % new_status)
185 c29ac11d Ilias Tsitsimpis
                self.destroy_server(False)
186 c29ac11d Ilias Tsitsimpis
                sys.exit(-1)
187 c29ac11d Ilias Tsitsimpis
            elif server['status'] == current_status:
188 c29ac11d Ilias Tsitsimpis
                # Sleep for #n secs and continue
189 c29ac11d Ilias Tsitsimpis
                timeout = timeout - sleep_time
190 c29ac11d Ilias Tsitsimpis
                time.sleep(sleep_time)
191 c29ac11d Ilias Tsitsimpis
            else:
192 c29ac11d Ilias Tsitsimpis
                self.logger.error(
193 c29ac11d Ilias Tsitsimpis
                    "Server failed with status %s" % server['status'])
194 c29ac11d Ilias Tsitsimpis
                self.destroy_server(False)
195 c29ac11d Ilias Tsitsimpis
                sys.exit(-1)
196 c29ac11d Ilias Tsitsimpis
197 c29ac11d Ilias Tsitsimpis
    @_check_kamaki
198 c29ac11d Ilias Tsitsimpis
    def destroy_server(self, wait=True):
199 c29ac11d Ilias Tsitsimpis
        """Destroy slave server"""
200 c29ac11d Ilias Tsitsimpis
        server_id = self.config.getint('Temporary Options', 'server_id')
201 c29ac11d Ilias Tsitsimpis
        self.logger.info("Destoying server with id %s " % server_id)
202 c29ac11d Ilias Tsitsimpis
        self.cyclades_client.delete_server(server_id)
203 c29ac11d Ilias Tsitsimpis
        if wait:
204 c29ac11d Ilias Tsitsimpis
            self._wait_transition(server_id, "ACTIVE", "DELETED")
205 c29ac11d Ilias Tsitsimpis
206 c29ac11d Ilias Tsitsimpis
    @_check_kamaki
207 43a22402 Christos Stavrakakis
    def create_server(self, image_id=None, flavor_id=None):
208 c29ac11d Ilias Tsitsimpis
        """Create slave server"""
209 c29ac11d Ilias Tsitsimpis
        self.logger.info("Create a new server..")
210 43a22402 Christos Stavrakakis
        if image_id is None:
211 43a22402 Christos Stavrakakis
            image = self._find_image()
212 43a22402 Christos Stavrakakis
            self.logger.debug("Will use image \"%s\"" % _green(image['name']))
213 43a22402 Christos Stavrakakis
            image_id = image["id"]
214 43a22402 Christos Stavrakakis
        self.logger.debug("Image has id %s" % _green(image_id))
215 43a22402 Christos Stavrakakis
        if flavor_id is None:
216 43a22402 Christos Stavrakakis
            flavor_id = self.config.getint("Deployment", "flavor_id")
217 c29ac11d Ilias Tsitsimpis
        server = self.cyclades_client.create_server(
218 c29ac11d Ilias Tsitsimpis
            self.config.get('Deployment', 'server_name'),
219 43a22402 Christos Stavrakakis
            flavor_id,
220 43a22402 Christos Stavrakakis
            image_id)
221 c29ac11d Ilias Tsitsimpis
        server_id = server['id']
222 c29ac11d Ilias Tsitsimpis
        self.write_config('server_id', server_id)
223 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Server got id %s" % _green(server_id))
224 c29ac11d Ilias Tsitsimpis
        server_user = server['metadata']['users']
225 c29ac11d Ilias Tsitsimpis
        self.write_config('server_user', server_user)
226 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Server's admin user is %s" % _green(server_user))
227 c29ac11d Ilias Tsitsimpis
        server_passwd = server['adminPass']
228 c29ac11d Ilias Tsitsimpis
        self.write_config('server_passwd', server_passwd)
229 c29ac11d Ilias Tsitsimpis
230 c29ac11d Ilias Tsitsimpis
        server = self._wait_transition(server_id, "BUILD", "ACTIVE")
231 c29ac11d Ilias Tsitsimpis
        self._get_server_ip_and_port(server)
232 e480f0ce Christos Stavrakakis
        self._copy_ssh_keys()
233 c29ac11d Ilias Tsitsimpis
234 c29ac11d Ilias Tsitsimpis
        self.setup_fabric()
235 c29ac11d Ilias Tsitsimpis
        self.logger.info("Setup firewall")
236 c29ac11d Ilias Tsitsimpis
        accept_ssh_from = self.config.get('Global', 'filter_access_network')
237 1c75c4dd Christos Stavrakakis
        if accept_ssh_from != "":
238 1c75c4dd Christos Stavrakakis
            self.logger.debug("Block ssh except from %s" % accept_ssh_from)
239 1c75c4dd Christos Stavrakakis
            cmd = """
240 1c75c4dd Christos Stavrakakis
            local_ip=$(/sbin/ifconfig eth0 | grep 'inet addr:' | \
241 1c75c4dd Christos Stavrakakis
                cut -d':' -f2 | cut -d' ' -f1)
242 1c75c4dd Christos Stavrakakis
            iptables -A INPUT -s localhost -j ACCEPT
243 1c75c4dd Christos Stavrakakis
            iptables -A INPUT -s $local_ip -j ACCEPT
244 1c75c4dd Christos Stavrakakis
            iptables -A INPUT -s {0} -p tcp --dport 22 -j ACCEPT
245 1c75c4dd Christos Stavrakakis
            iptables -A INPUT -p tcp --dport 22 -j DROP
246 1c75c4dd Christos Stavrakakis
            """.format(accept_ssh_from)
247 1c75c4dd Christos Stavrakakis
            _run(cmd, False)
248 c29ac11d Ilias Tsitsimpis
249 c29ac11d Ilias Tsitsimpis
    def _find_image(self):
250 c29ac11d Ilias Tsitsimpis
        """Find a suitable image to use
251 c29ac11d Ilias Tsitsimpis

252 c29ac11d Ilias Tsitsimpis
        It has to belong to the `system_uuid' user and
253 c29ac11d Ilias Tsitsimpis
        contain the word `image_name'
254 c29ac11d Ilias Tsitsimpis
        """
255 c29ac11d Ilias Tsitsimpis
        system_uuid = self.config.get('Deployment', 'system_uuid')
256 c29ac11d Ilias Tsitsimpis
        image_name = self.config.get('Deployment', 'image_name').lower()
257 c29ac11d Ilias Tsitsimpis
        images = self.image_client.list_public(detail=True)['images']
258 c29ac11d Ilias Tsitsimpis
        # Select images by `system_uuid' user
259 c29ac11d Ilias Tsitsimpis
        images = [x for x in images if x['user_id'] == system_uuid]
260 c29ac11d Ilias Tsitsimpis
        # Select images with `image_name' in their names
261 c29ac11d Ilias Tsitsimpis
        images = \
262 c29ac11d Ilias Tsitsimpis
            [x for x in images if x['name'].lower().find(image_name) != -1]
263 c29ac11d Ilias Tsitsimpis
        # Let's select the first one
264 c29ac11d Ilias Tsitsimpis
        return images[0]
265 c29ac11d Ilias Tsitsimpis
266 c29ac11d Ilias Tsitsimpis
    def _get_server_ip_and_port(self, server):
267 c29ac11d Ilias Tsitsimpis
        """Compute server's IPv4 and ssh port number"""
268 c29ac11d Ilias Tsitsimpis
        self.logger.info("Get server connection details..")
269 c29ac11d Ilias Tsitsimpis
        server_ip = server['attachments'][0]['ipv4']
270 3b26ae00 Christos Stavrakakis
        if ".okeanos.io" in self.cyclades_client.base_url:
271 c29ac11d Ilias Tsitsimpis
            tmp1 = int(server_ip.split(".")[2])
272 c29ac11d Ilias Tsitsimpis
            tmp2 = int(server_ip.split(".")[3])
273 c29ac11d Ilias Tsitsimpis
            server_ip = "gate.okeanos.io"
274 c29ac11d Ilias Tsitsimpis
            server_port = 10000 + tmp1 * 256 + tmp2
275 c29ac11d Ilias Tsitsimpis
        else:
276 c29ac11d Ilias Tsitsimpis
            server_port = 22
277 c29ac11d Ilias Tsitsimpis
        self.write_config('server_ip', server_ip)
278 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Server's IPv4 is %s" % _green(server_ip))
279 c29ac11d Ilias Tsitsimpis
        self.write_config('server_port', server_port)
280 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Server's ssh port is %s" % _green(server_port))
281 c29ac11d Ilias Tsitsimpis
282 e480f0ce Christos Stavrakakis
    @_check_fabric
283 e480f0ce Christos Stavrakakis
    def _copy_ssh_keys(self):
284 1c75c4dd Christos Stavrakakis
        if not self.config.has_option("Deployment", "ssh_keys"):
285 1c75c4dd Christos Stavrakakis
            return
286 e480f0ce Christos Stavrakakis
        authorized_keys = self.config.get("Deployment",
287 e480f0ce Christos Stavrakakis
                                          "ssh_keys")
288 1c75c4dd Christos Stavrakakis
        if authorized_keys != "" and os.path.exists(authorized_keys):
289 e480f0ce Christos Stavrakakis
            keyfile = '/tmp/%s.pub' % fabric.env.user
290 e480f0ce Christos Stavrakakis
            _run('mkdir -p ~/.ssh && chmod 700 ~/.ssh', False)
291 e480f0ce Christos Stavrakakis
            fabric.put(authorized_keys, keyfile)
292 e480f0ce Christos Stavrakakis
            _run('cat %s >> ~/.ssh/authorized_keys' % keyfile, False)
293 e480f0ce Christos Stavrakakis
            _run('rm %s' % keyfile, False)
294 e480f0ce Christos Stavrakakis
            self.logger.debug("Uploaded ssh authorized keys")
295 e480f0ce Christos Stavrakakis
        else:
296 e480f0ce Christos Stavrakakis
            self.logger.debug("No ssh keys found")
297 e480f0ce Christos Stavrakakis
298 c29ac11d Ilias Tsitsimpis
    def write_config(self, option, value, section="Temporary Options"):
299 c29ac11d Ilias Tsitsimpis
        """Write changes back to config file"""
300 c29ac11d Ilias Tsitsimpis
        try:
301 c29ac11d Ilias Tsitsimpis
            self.config.add_section(section)
302 c29ac11d Ilias Tsitsimpis
        except DuplicateSectionError:
303 c29ac11d Ilias Tsitsimpis
            pass
304 c29ac11d Ilias Tsitsimpis
        self.config.set(section, option, str(value))
305 c29ac11d Ilias Tsitsimpis
        temp_conf_file = self.config.get('Global', 'temporary_config')
306 c29ac11d Ilias Tsitsimpis
        with open(temp_conf_file, 'wb') as tcf:
307 c29ac11d Ilias Tsitsimpis
            self.config.write(tcf)
308 c29ac11d Ilias Tsitsimpis
309 c29ac11d Ilias Tsitsimpis
    def setup_fabric(self):
310 c29ac11d Ilias Tsitsimpis
        """Setup fabric environment"""
311 c29ac11d Ilias Tsitsimpis
        self.logger.info("Setup fabric parameters..")
312 c29ac11d Ilias Tsitsimpis
        fabric.env.user = self.config.get('Temporary Options', 'server_user')
313 c29ac11d Ilias Tsitsimpis
        fabric.env.host_string = \
314 c29ac11d Ilias Tsitsimpis
            self.config.get('Temporary Options', 'server_ip')
315 e908a902 Christos Stavrakakis
        fabric.env.port = self.config.getint('Temporary Options',
316 e908a902 Christos Stavrakakis
                                             'server_port')
317 e908a902 Christos Stavrakakis
        fabric.env.password = self.config.get('Temporary Options',
318 e908a902 Christos Stavrakakis
                                              'server_passwd')
319 c29ac11d Ilias Tsitsimpis
        fabric.env.connection_attempts = 10
320 c29ac11d Ilias Tsitsimpis
        fabric.env.shell = "/bin/bash -c"
321 c29ac11d Ilias Tsitsimpis
        fabric.env.disable_known_hosts = True
322 c29ac11d Ilias Tsitsimpis
        fabric.env.output_prefix = None
323 c29ac11d Ilias Tsitsimpis
324 c29ac11d Ilias Tsitsimpis
    def _check_hash_sum(self, localfile, remotefile):
325 c29ac11d Ilias Tsitsimpis
        """Check hash sums of two files"""
326 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Check hash sum for local file %s" % localfile)
327 c29ac11d Ilias Tsitsimpis
        hash1 = os.popen("sha256sum %s" % localfile).read().split(' ')[0]
328 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Local file has sha256 hash %s" % hash1)
329 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Check hash sum for remote file %s" % remotefile)
330 c29ac11d Ilias Tsitsimpis
        hash2 = _run("sha256sum %s" % remotefile, False)
331 c29ac11d Ilias Tsitsimpis
        hash2 = hash2.split(' ')[0]
332 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Remote file has sha256 hash %s" % hash2)
333 c29ac11d Ilias Tsitsimpis
        if hash1 != hash2:
334 c29ac11d Ilias Tsitsimpis
            self.logger.error("Hashes differ.. aborting")
335 c29ac11d Ilias Tsitsimpis
            sys.exit(-1)
336 c29ac11d Ilias Tsitsimpis
337 c29ac11d Ilias Tsitsimpis
    @_check_fabric
338 c29ac11d Ilias Tsitsimpis
    def clone_repo(self):
339 c29ac11d Ilias Tsitsimpis
        """Clone Synnefo repo from slave server"""
340 c29ac11d Ilias Tsitsimpis
        self.logger.info("Configure repositories on remote server..")
341 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Setup apt, install curl and git")
342 c29ac11d Ilias Tsitsimpis
        cmd = """
343 c29ac11d Ilias Tsitsimpis
        echo 'APT::Install-Suggests "false";' >> /etc/apt/apt.conf
344 c29ac11d Ilias Tsitsimpis
        apt-get update
345 c29ac11d Ilias Tsitsimpis
        apt-get install curl git --yes
346 c29ac11d Ilias Tsitsimpis
        echo -e "\n\ndeb {0}" >> /etc/apt/sources.list
347 c29ac11d Ilias Tsitsimpis
        curl https://dev.grnet.gr/files/apt-grnetdev.pub | apt-key add -
348 c29ac11d Ilias Tsitsimpis
        apt-get update
349 c29ac11d Ilias Tsitsimpis
        git config --global user.name {1}
350 c29ac11d Ilias Tsitsimpis
        git config --global user.mail {2}
351 c29ac11d Ilias Tsitsimpis
        """.format(self.config.get('Global', 'apt_repo'),
352 c29ac11d Ilias Tsitsimpis
                   self.config.get('Global', 'git_config_name'),
353 c29ac11d Ilias Tsitsimpis
                   self.config.get('Global', 'git_config_mail'))
354 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
355 c29ac11d Ilias Tsitsimpis
356 c29ac11d Ilias Tsitsimpis
        synnefo_repo = self.config.get('Global', 'synnefo_repo')
357 b69b55ca Christos Stavrakakis
        synnefo_branch = self.config.get("Global", "synnefo_branch")
358 b69b55ca Christos Stavrakakis
        if synnefo_branch == "":
359 b69b55ca Christos Stavrakakis
            synnefo_branch =\
360 b69b55ca Christos Stavrakakis
                subprocess.Popen(["git", "rev-parse", "--abbrev-ref", "HEAD"],
361 b69b55ca Christos Stavrakakis
                    stdout=subprocess.PIPE).communicate()[0].strip()
362 b69b55ca Christos Stavrakakis
            if synnefo_branch == "HEAD":
363 b69b55ca Christos Stavrakakis
                synnefo_branch = \
364 b69b55ca Christos Stavrakakis
                    subprocess.Popen(["git", "rev-parse","--short", "HEAD"],
365 b69b55ca Christos Stavrakakis
                        stdout=subprocess.PIPE).communicate()[0].strip()
366 b69b55ca Christos Stavrakakis
        self.logger.info("Will use branch %s" % synnefo_branch)
367 c29ac11d Ilias Tsitsimpis
        # Currently clonning synnefo can fail unexpectedly
368 e908a902 Christos Stavrakakis
        cloned = False
369 b69b55ca Christos Stavrakakis
        for i in range(10):
370 c29ac11d Ilias Tsitsimpis
            self.logger.debug("Clone synnefo from %s" % synnefo_repo)
371 c29ac11d Ilias Tsitsimpis
            try:
372 b69b55ca Christos Stavrakakis
                _run("git clone %s synnefo" % synnefo_repo, False)
373 e908a902 Christos Stavrakakis
                cloned = True
374 c29ac11d Ilias Tsitsimpis
                break
375 c29ac11d Ilias Tsitsimpis
            except:
376 e908a902 Christos Stavrakakis
                self.logger.warning("Clonning synnefo failed.. retrying %s"
377 e908a902 Christos Stavrakakis
                                    % i)
378 b69b55ca Christos Stavrakakis
        cmd ="""
379 b69b55ca Christos Stavrakakis
        cd synnefo
380 b69b55ca Christos Stavrakakis
        for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master`; do
381 b69b55ca Christos Stavrakakis
            git branch --track ${branch##*/} $branch
382 b69b55ca Christos Stavrakakis
        done
383 b69b55ca Christos Stavrakakis
        git checkout %s
384 b69b55ca Christos Stavrakakis
        """ % (synnefo_branch)
385 b69b55ca Christos Stavrakakis
        _run(cmd, False)
386 b69b55ca Christos Stavrakakis
387 e908a902 Christos Stavrakakis
        if not cloned:
388 e908a902 Christos Stavrakakis
            self.logger.error("Can not clone Synnefo repo.")
389 e908a902 Christos Stavrakakis
            sys.exit(-1)
390 c29ac11d Ilias Tsitsimpis
391 c29ac11d Ilias Tsitsimpis
        deploy_repo = self.config.get('Global', 'deploy_repo')
392 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Clone snf-deploy from %s" % deploy_repo)
393 e908a902 Christos Stavrakakis
        _run("git clone --depth 1 %s" % deploy_repo, False)
394 c29ac11d Ilias Tsitsimpis
395 c29ac11d Ilias Tsitsimpis
    @_check_fabric
396 c29ac11d Ilias Tsitsimpis
    def build_synnefo(self):
397 c29ac11d Ilias Tsitsimpis
        """Build Synnefo packages"""
398 c29ac11d Ilias Tsitsimpis
        self.logger.info("Build Synnefo packages..")
399 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Install development packages")
400 c29ac11d Ilias Tsitsimpis
        cmd = """
401 c29ac11d Ilias Tsitsimpis
        apt-get update
402 c29ac11d Ilias Tsitsimpis
        apt-get install zlib1g-dev dpkg-dev debhelper git-buildpackage \
403 c29ac11d Ilias Tsitsimpis
                python-dev python-all python-pip --yes
404 c29ac11d Ilias Tsitsimpis
        pip install devflow
405 c29ac11d Ilias Tsitsimpis
        """
406 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
407 c29ac11d Ilias Tsitsimpis
408 e908a902 Christos Stavrakakis
        if self.config.get('Global', 'patch_pydist') == "True":
409 c29ac11d Ilias Tsitsimpis
            self.logger.debug("Patch pydist.py module")
410 c29ac11d Ilias Tsitsimpis
            cmd = r"""
411 c29ac11d Ilias Tsitsimpis
            sed -r -i 's/(\(\?P<name>\[A-Za-z\]\[A-Za-z0-9_\.)/\1\\\-/' \
412 c29ac11d Ilias Tsitsimpis
                /usr/share/python/debpython/pydist.py
413 c29ac11d Ilias Tsitsimpis
            """
414 c29ac11d Ilias Tsitsimpis
            _run(cmd, False)
415 c29ac11d Ilias Tsitsimpis

416 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Build snf-deploy package")
417 c29ac11d Ilias Tsitsimpis
        cmd = """
418 c29ac11d Ilias Tsitsimpis
        git checkout -t origin/debian
419 c29ac11d Ilias Tsitsimpis
        git-buildpackage --git-upstream-branch=master \
420 c29ac11d Ilias Tsitsimpis
                --git-debian-branch=debian \
421 c29ac11d Ilias Tsitsimpis
                --git-export-dir=../snf-deploy_build-area \
422 c29ac11d Ilias Tsitsimpis
                -uc -us
423 c29ac11d Ilias Tsitsimpis
        """
424 c29ac11d Ilias Tsitsimpis
        with fabric.cd("snf-deploy"):
425 c29ac11d Ilias Tsitsimpis
            _run(cmd, True)
426 c29ac11d Ilias Tsitsimpis

427 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Install snf-deploy package")
428 c29ac11d Ilias Tsitsimpis
        cmd = """
429 c29ac11d Ilias Tsitsimpis
        dpkg -i snf-deploy*.deb
430 c29ac11d Ilias Tsitsimpis
        apt-get -f install --yes
431 c29ac11d Ilias Tsitsimpis
        """
432 c29ac11d Ilias Tsitsimpis
        with fabric.cd("snf-deploy_build-area"):
433 c29ac11d Ilias Tsitsimpis
            with fabric.settings(warn_only=True):
434 c29ac11d Ilias Tsitsimpis
                _run(cmd, True)
435 c29ac11d Ilias Tsitsimpis

436 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Build synnefo packages")
437 c29ac11d Ilias Tsitsimpis
        cmd = """
438 c29ac11d Ilias Tsitsimpis
        devflow-autopkg snapshot -b ~/synnefo_build-area --no-sign
439 c29ac11d Ilias Tsitsimpis
        """
440 c29ac11d Ilias Tsitsimpis
        with fabric.cd("synnefo"):
441 c29ac11d Ilias Tsitsimpis
            _run(cmd, True)
442 c29ac11d Ilias Tsitsimpis

443 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Copy synnefo debs to snf-deploy packages dir")
444 c29ac11d Ilias Tsitsimpis
        cmd = """
445 c29ac11d Ilias Tsitsimpis
        cp ~/synnefo_build-area/*.deb /var/lib/snf-deploy/packages/
446 c29ac11d Ilias Tsitsimpis
        """
447 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
448 c29ac11d Ilias Tsitsimpis

449 88e6558b Christos Stavrakakis

450 88e6558b Christos Stavrakakis
    @_check_fabric
451 88e6558b Christos Stavrakakis
    def build_documentation(self):
452 88e6558b Christos Stavrakakis
        self.logger.info("Build Synnefo documentation..")
453 88e6558b Christos Stavrakakis
        _run("pip install -U Sphinx", False)
454 88e6558b Christos Stavrakakis
        with fabric.cd("synnefo"):
455 f041c796 Christos Stavrakakis
            _run("devflow-update-version; ./ci/make_docs.sh synnefo_documentation", False)
456 88e6558b Christos Stavrakakis

457 88e6558b Christos Stavrakakis
    def fetch_documentation(self, dest=None):
458 88e6558b Christos Stavrakakis
        if dest is None:
459 88e6558b Christos Stavrakakis
            dest = "synnefo_documentation"
460 88e6558b Christos Stavrakakis
        dest = os.path.abspath(dest)
461 88e6558b Christos Stavrakakis
        if not os.path.exists(dest):
462 88e6558b Christos Stavrakakis
            os.makedirs(dest)
463 88e6558b Christos Stavrakakis
        self.fetch_compressed("synnefo/synnefo_documentation", dest)
464 88e6558b Christos Stavrakakis
        self.logger.info("Downloaded documentation to %s" %
465 88e6558b Christos Stavrakakis
                         _green(dest))
466 88e6558b Christos Stavrakakis

467 c29ac11d Ilias Tsitsimpis
    @_check_fabric
468 ec8bc030 Christos Stavrakakis
    def deploy_synnefo(self, schema=None):
469 c29ac11d Ilias Tsitsimpis
        """Deploy Synnefo using snf-deploy"""
470 c29ac11d Ilias Tsitsimpis
        self.logger.info("Deploy Synnefo..")
471 ec8bc030 Christos Stavrakakis
        if schema is None:
472 ec8bc030 Christos Stavrakakis
            schema = self.config.get('Global', 'schema')
473 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Will use %s schema" % schema)
474 c29ac11d Ilias Tsitsimpis

475 ec8bc030 Christos Stavrakakis
        schema_dir = os.path.join(self.ci_dir, "schemas/%s" % schema)
476 ec8bc030 Christos Stavrakakis
        if not (os.path.exists(schema_dir) and os.path.isdir(schema_dir)):
477 ec8bc030 Christos Stavrakakis
            raise ValueError("Unknown schema: %s" % schema)
478 ec8bc030 Christos Stavrakakis

479 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Upload schema files to server")
480 c29ac11d Ilias Tsitsimpis
        with fabric.quiet():
481 ec8bc030 Christos Stavrakakis
            fabric.put(os.path.join(schema_dir, "*"), "/etc/snf-deploy/")
482 c29ac11d Ilias Tsitsimpis

483 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Change password in nodes.conf file")
484 c29ac11d Ilias Tsitsimpis
        cmd = """
485 c29ac11d Ilias Tsitsimpis
        sed -i 's/^password =.*/password = {0}/' /etc/snf-deploy/nodes.conf
486 c29ac11d Ilias Tsitsimpis
        """.format(fabric.env.password)
487 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
488 c29ac11d Ilias Tsitsimpis

489 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Run snf-deploy")
490 c29ac11d Ilias Tsitsimpis
        cmd = """
491 c29ac11d Ilias Tsitsimpis
        snf-deploy all --autoconf
492 c29ac11d Ilias Tsitsimpis
        """
493 c29ac11d Ilias Tsitsimpis
        _run(cmd, True)
494 c29ac11d Ilias Tsitsimpis

495 c29ac11d Ilias Tsitsimpis
    @_check_fabric
496 c29ac11d Ilias Tsitsimpis
    def unit_test(self):
497 c29ac11d Ilias Tsitsimpis
        """Run Synnefo unit test suite"""
498 c29ac11d Ilias Tsitsimpis
        self.logger.info("Run Synnefo unit test suite")
499 c29ac11d Ilias Tsitsimpis
        component = self.config.get('Unit Tests', 'component')
500 c29ac11d Ilias Tsitsimpis

501 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Install needed packages")
502 c29ac11d Ilias Tsitsimpis
        cmd = """
503 c29ac11d Ilias Tsitsimpis
        pip install mock
504 c29ac11d Ilias Tsitsimpis
        pip install factory_boy
505 c29ac11d Ilias Tsitsimpis
        """
506 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
507 c29ac11d Ilias Tsitsimpis

508 861b437e Ilias Tsitsimpis
        self.logger.debug("Upload tests.sh file")
509 861b437e Ilias Tsitsimpis
        unit_tests_file = os.path.join(self.ci_dir, "tests.sh")
510 c29ac11d Ilias Tsitsimpis
        with fabric.quiet():
511 c29ac11d Ilias Tsitsimpis
            fabric.put(unit_tests_file, ".")
512 c29ac11d Ilias Tsitsimpis

513 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Run unit tests")
514 c29ac11d Ilias Tsitsimpis
        cmd = """
515 861b437e Ilias Tsitsimpis
        bash tests.sh {0}
516 c29ac11d Ilias Tsitsimpis
        """.format(component)
517 c29ac11d Ilias Tsitsimpis
        _run(cmd, True)
518 c29ac11d Ilias Tsitsimpis

519 c29ac11d Ilias Tsitsimpis
    @_check_fabric
520 c29ac11d Ilias Tsitsimpis
    def run_burnin(self):
521 c29ac11d Ilias Tsitsimpis
        """Run burnin functional test suite"""
522 c29ac11d Ilias Tsitsimpis
        self.logger.info("Run Burnin functional test suite")
523 c29ac11d Ilias Tsitsimpis
        cmd = """
524 c29ac11d Ilias Tsitsimpis
        auth_url=$(grep -e '^url =' .kamakirc | cut -d' ' -f3)
525 c29ac11d Ilias Tsitsimpis
        token=$(grep -e '^token =' .kamakirc | cut -d' ' -f3)
526 c29ac11d Ilias Tsitsimpis
        images_user=$(kamaki image list -l | grep owner | \
527 c29ac11d Ilias Tsitsimpis
                      cut -d':' -f2 | tr -d ' ')
528 c29ac11d Ilias Tsitsimpis
        snf-burnin --auth-url=$auth_url --token=$token \
529 c29ac11d Ilias Tsitsimpis
            --force-flavor=2 --image-id=all \
530 c29ac11d Ilias Tsitsimpis
            --system-images-user=$images_user \
531 c29ac11d Ilias Tsitsimpis
            {0}
532 c29ac11d Ilias Tsitsimpis
        log_folder=$(ls -1d /var/log/burnin/* | tail -n1)
533 c29ac11d Ilias Tsitsimpis
        for i in $(ls $log_folder/*/details*); do
534 c29ac11d Ilias Tsitsimpis
            echo -e "\\n\\n"
535 c29ac11d Ilias Tsitsimpis
            echo -e "***** $i\\n"
536 c29ac11d Ilias Tsitsimpis
            cat $i
537 c29ac11d Ilias Tsitsimpis
        done
538 c29ac11d Ilias Tsitsimpis
        """.format(self.config.get('Burnin', 'cmd_options'))
539 c29ac11d Ilias Tsitsimpis
        _run(cmd, True)
540 c29ac11d Ilias Tsitsimpis

541 c29ac11d Ilias Tsitsimpis
    @_check_fabric
542 88e6558b Christos Stavrakakis
    def fetch_compressed(self, src, dest=None):
543 88e6558b Christos Stavrakakis
        self.logger.debug("Creating tarball of %s" % src)
544 88e6558b Christos Stavrakakis
        basename = os.path.basename(src)
545 88e6558b Christos Stavrakakis
        tar_file = basename + ".tgz"
546 88e6558b Christos Stavrakakis
        cmd = "tar czf %s %s" % (tar_file, src)
547 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
548 88e6558b Christos Stavrakakis
        if not os.path.exists(dest):
549 88e6558b Christos Stavrakakis
            os.makedirs(dest)
550 c29ac11d Ilias Tsitsimpis

551 88e6558b Christos Stavrakakis
        tmp_dir = tempfile.mkdtemp()
552 88e6558b Christos Stavrakakis
        fabric.get(tar_file, tmp_dir)
553 c29ac11d Ilias Tsitsimpis

554 88e6558b Christos Stavrakakis
        dest_file = os.path.join(tmp_dir, tar_file)
555 88e6558b Christos Stavrakakis
        self._check_hash_sum(dest_file, tar_file)
556 88e6558b Christos Stavrakakis
        self.logger.debug("Untar packages file %s" % dest_file)
557 e2dc37b2 Christos Stavrakakis
        cmd = """
558 e2dc37b2 Christos Stavrakakis
        cd %s
559 88e6558b Christos Stavrakakis
        tar xzf %s
560 88e6558b Christos Stavrakakis
        cp -r %s/* %s
561 88e6558b Christos Stavrakakis
        rm -r %s
562 88e6558b Christos Stavrakakis
        """ % (tmp_dir, tar_file, src, dest, tmp_dir)
563 e2dc37b2 Christos Stavrakakis
        os.system(cmd)
564 88e6558b Christos Stavrakakis
        self.logger.info("Downloaded %s to %s" %
565 88e6558b Christos Stavrakakis
                         (src, _green(dest)))
566 88e6558b Christos Stavrakakis

567 88e6558b Christos Stavrakakis
    @_check_fabric
568 88e6558b Christos Stavrakakis
    def fetch_packages(self, dest=None):
569 88e6558b Christos Stavrakakis
        if dest is None:
570 88e6558b Christos Stavrakakis
            dest = self.config.get('Global', 'pkgs_dir')
571 88e6558b Christos Stavrakakis
        dest = os.path.abspath(dest)
572 88e6558b Christos Stavrakakis
        if not os.path.exists(dest):
573 88e6558b Christos Stavrakakis
            os.makedirs(dest)
574 88e6558b Christos Stavrakakis
        self.fetch_compressed("synnefo_build-area", dest)
575 464e58e9 Christos Stavrakakis
        self.logger.info("Downloaded debian packages to %s" %
576 88e6558b Christos Stavrakakis
                         _green(dest))