Statistics
| Branch: | Tag: | Revision:

root / ci / utils.py @ 4a23280d

History | View | Annotate | Download (32.4 kB)

1 525f2979 Ilias Tsitsimpis
#!/usr/bin/env python
2 525f2979 Ilias Tsitsimpis
3 525f2979 Ilias Tsitsimpis
"""
4 525f2979 Ilias Tsitsimpis
Synnefo ci utils module
5 525f2979 Ilias Tsitsimpis
"""
6 525f2979 Ilias Tsitsimpis
7 525f2979 Ilias Tsitsimpis
import os
8 dae06cfc Ilias Tsitsimpis
import re
9 525f2979 Ilias Tsitsimpis
import sys
10 525f2979 Ilias Tsitsimpis
import time
11 525f2979 Ilias Tsitsimpis
import logging
12 525f2979 Ilias Tsitsimpis
import fabric.api as fabric
13 024faf05 Christos Stavrakakis
import subprocess
14 124c300e Christos Stavrakakis
import tempfile
15 525f2979 Ilias Tsitsimpis
from ConfigParser import ConfigParser, DuplicateSectionError
16 525f2979 Ilias Tsitsimpis
17 da593e0c Christos Stavrakakis
from kamaki.cli import config as kamaki_config
18 525f2979 Ilias Tsitsimpis
from kamaki.clients.astakos import AstakosClient
19 525f2979 Ilias Tsitsimpis
from kamaki.clients.cyclades import CycladesClient
20 525f2979 Ilias Tsitsimpis
from kamaki.clients.image import ImageClient
21 dae06cfc Ilias Tsitsimpis
from kamaki.clients.compute import ComputeClient
22 4b61ee63 Ilias Tsitsimpis
import filelocker
23 525f2979 Ilias Tsitsimpis
24 e2db4a57 Christos Stavrakakis
DEFAULT_CONFIG_FILE = "new_config"
25 60ddcdc3 Ilias Tsitsimpis
# UUID of owner of system images
26 60ddcdc3 Ilias Tsitsimpis
DEFAULT_SYSTEM_IMAGES_UUID = [
27 60ddcdc3 Ilias Tsitsimpis
    "25ecced9-bf53-4145-91ee-cf47377e9fb2",  # production (okeanos.grnet.gr)
28 60ddcdc3 Ilias Tsitsimpis
    "04cbe33f-29b7-4ef1-94fb-015929e5fc06",  # testing (okeanos.io)
29 ac1e7de3 Christos Stavrakakis
]
30 e2db4a57 Christos Stavrakakis
31 525f2979 Ilias Tsitsimpis
32 525f2979 Ilias Tsitsimpis
def _run(cmd, verbose):
33 525f2979 Ilias Tsitsimpis
    """Run fabric with verbose level"""
34 525f2979 Ilias Tsitsimpis
    if verbose:
35 525f2979 Ilias Tsitsimpis
        args = ('running',)
36 525f2979 Ilias Tsitsimpis
    else:
37 525f2979 Ilias Tsitsimpis
        args = ('running', 'stdout',)
38 33ad9a0d Ilias Tsitsimpis
    with fabric.hide(*args):  # Used * or ** magic. pylint: disable-msg=W0142
39 525f2979 Ilias Tsitsimpis
        return fabric.run(cmd)
40 525f2979 Ilias Tsitsimpis
41 525f2979 Ilias Tsitsimpis
42 4c818bb2 Ilias Tsitsimpis
def _put(local, remote):
43 4c818bb2 Ilias Tsitsimpis
    """Run fabric put command without output"""
44 4c818bb2 Ilias Tsitsimpis
    with fabric.quiet():
45 4c818bb2 Ilias Tsitsimpis
        fabric.put(local, remote)
46 4c818bb2 Ilias Tsitsimpis
47 4c818bb2 Ilias Tsitsimpis
48 525f2979 Ilias Tsitsimpis
def _red(msg):
49 525f2979 Ilias Tsitsimpis
    """Red color"""
50 525f2979 Ilias Tsitsimpis
    #return "\x1b[31m" + str(msg) + "\x1b[0m"
51 525f2979 Ilias Tsitsimpis
    return str(msg)
52 525f2979 Ilias Tsitsimpis
53 525f2979 Ilias Tsitsimpis
54 525f2979 Ilias Tsitsimpis
def _yellow(msg):
55 525f2979 Ilias Tsitsimpis
    """Yellow color"""
56 525f2979 Ilias Tsitsimpis
    #return "\x1b[33m" + str(msg) + "\x1b[0m"
57 525f2979 Ilias Tsitsimpis
    return str(msg)
58 525f2979 Ilias Tsitsimpis
59 525f2979 Ilias Tsitsimpis
60 525f2979 Ilias Tsitsimpis
def _green(msg):
61 525f2979 Ilias Tsitsimpis
    """Green color"""
62 525f2979 Ilias Tsitsimpis
    #return "\x1b[32m" + str(msg) + "\x1b[0m"
63 525f2979 Ilias Tsitsimpis
    return str(msg)
64 525f2979 Ilias Tsitsimpis
65 525f2979 Ilias Tsitsimpis
66 525f2979 Ilias Tsitsimpis
def _check_fabric(fun):
67 525f2979 Ilias Tsitsimpis
    """Check if fabric env has been set"""
68 947b6106 Christos Stavrakakis
    def wrapper(self, *args, **kwargs):
69 525f2979 Ilias Tsitsimpis
        """wrapper function"""
70 525f2979 Ilias Tsitsimpis
        if not self.fabric_installed:
71 525f2979 Ilias Tsitsimpis
            self.setup_fabric()
72 eaf0b161 Ilias Tsitsimpis
            self.fabric_installed = True
73 947b6106 Christos Stavrakakis
        return fun(self, *args, **kwargs)
74 525f2979 Ilias Tsitsimpis
    return wrapper
75 525f2979 Ilias Tsitsimpis
76 525f2979 Ilias Tsitsimpis
77 525f2979 Ilias Tsitsimpis
def _check_kamaki(fun):
78 525f2979 Ilias Tsitsimpis
    """Check if kamaki has been initialized"""
79 947b6106 Christos Stavrakakis
    def wrapper(self, *args, **kwargs):
80 525f2979 Ilias Tsitsimpis
        """wrapper function"""
81 525f2979 Ilias Tsitsimpis
        if not self.kamaki_installed:
82 525f2979 Ilias Tsitsimpis
            self.setup_kamaki()
83 eaf0b161 Ilias Tsitsimpis
            self.kamaki_installed = True
84 947b6106 Christos Stavrakakis
        return fun(self, *args, **kwargs)
85 525f2979 Ilias Tsitsimpis
    return wrapper
86 525f2979 Ilias Tsitsimpis
87 525f2979 Ilias Tsitsimpis
88 525f2979 Ilias Tsitsimpis
class _MyFormatter(logging.Formatter):
89 525f2979 Ilias Tsitsimpis
    """Logging Formatter"""
90 525f2979 Ilias Tsitsimpis
    def format(self, record):
91 525f2979 Ilias Tsitsimpis
        format_orig = self._fmt
92 525f2979 Ilias Tsitsimpis
        if record.levelno == logging.DEBUG:
93 525f2979 Ilias Tsitsimpis
            self._fmt = "  %(msg)s"
94 525f2979 Ilias Tsitsimpis
        elif record.levelno == logging.INFO:
95 525f2979 Ilias Tsitsimpis
            self._fmt = "%(msg)s"
96 525f2979 Ilias Tsitsimpis
        elif record.levelno == logging.WARNING:
97 525f2979 Ilias Tsitsimpis
            self._fmt = _yellow("[W] %(msg)s")
98 525f2979 Ilias Tsitsimpis
        elif record.levelno == logging.ERROR:
99 525f2979 Ilias Tsitsimpis
            self._fmt = _red("[E] %(msg)s")
100 525f2979 Ilias Tsitsimpis
        result = logging.Formatter.format(self, record)
101 525f2979 Ilias Tsitsimpis
        self._fmt = format_orig
102 525f2979 Ilias Tsitsimpis
        return result
103 525f2979 Ilias Tsitsimpis
104 525f2979 Ilias Tsitsimpis
105 d8363ea2 Ilias Tsitsimpis
# Too few public methods. pylint: disable-msg=R0903
106 d8363ea2 Ilias Tsitsimpis
class _InfoFilter(logging.Filter):
107 d8363ea2 Ilias Tsitsimpis
    """Logging Filter that allows DEBUG and INFO messages only"""
108 d8363ea2 Ilias Tsitsimpis
    def filter(self, rec):
109 d8363ea2 Ilias Tsitsimpis
        """The filter"""
110 d8363ea2 Ilias Tsitsimpis
        return rec.levelno in (logging.DEBUG, logging.INFO)
111 d8363ea2 Ilias Tsitsimpis
112 d8363ea2 Ilias Tsitsimpis
113 d8363ea2 Ilias Tsitsimpis
# Too many instance attributes. pylint: disable-msg=R0902
114 525f2979 Ilias Tsitsimpis
class SynnefoCI(object):
115 525f2979 Ilias Tsitsimpis
    """SynnefoCI python class"""
116 525f2979 Ilias Tsitsimpis
117 c441b6a7 Ilias Tsitsimpis
    def __init__(self, config_file=None, build_id=None, cloud=None):
118 525f2979 Ilias Tsitsimpis
        """ Initialize SynnefoCI python class
119 525f2979 Ilias Tsitsimpis

120 525f2979 Ilias Tsitsimpis
        Setup logger, local_dir, config and kamaki
121 525f2979 Ilias Tsitsimpis
        """
122 525f2979 Ilias Tsitsimpis
        # Setup logger
123 525f2979 Ilias Tsitsimpis
        self.logger = logging.getLogger('synnefo-ci')
124 525f2979 Ilias Tsitsimpis
        self.logger.setLevel(logging.DEBUG)
125 d8363ea2 Ilias Tsitsimpis
126 d8363ea2 Ilias Tsitsimpis
        handler1 = logging.StreamHandler(sys.stdout)
127 d8363ea2 Ilias Tsitsimpis
        handler1.setLevel(logging.DEBUG)
128 d8363ea2 Ilias Tsitsimpis
        handler1.addFilter(_InfoFilter())
129 d8363ea2 Ilias Tsitsimpis
        handler1.setFormatter(_MyFormatter())
130 d8363ea2 Ilias Tsitsimpis
        handler2 = logging.StreamHandler(sys.stderr)
131 d8363ea2 Ilias Tsitsimpis
        handler2.setLevel(logging.WARNING)
132 d8363ea2 Ilias Tsitsimpis
        handler2.setFormatter(_MyFormatter())
133 d8363ea2 Ilias Tsitsimpis
134 d8363ea2 Ilias Tsitsimpis
        self.logger.addHandler(handler1)
135 d8363ea2 Ilias Tsitsimpis
        self.logger.addHandler(handler2)
136 525f2979 Ilias Tsitsimpis
137 525f2979 Ilias Tsitsimpis
        # Get our local dir
138 525f2979 Ilias Tsitsimpis
        self.ci_dir = os.path.dirname(os.path.abspath(__file__))
139 525f2979 Ilias Tsitsimpis
        self.repo_dir = os.path.dirname(self.ci_dir)
140 525f2979 Ilias Tsitsimpis
141 525f2979 Ilias Tsitsimpis
        # Read config file
142 e2db4a57 Christos Stavrakakis
        if config_file is None:
143 e2db4a57 Christos Stavrakakis
            config_file = DEFAULT_CONFIG_FILE
144 e2db4a57 Christos Stavrakakis
        if not os.path.isabs(config_file):
145 e2db4a57 Christos Stavrakakis
            config_file = os.path.join(self.ci_dir, config_file)
146 525f2979 Ilias Tsitsimpis
        self.config = ConfigParser()
147 525f2979 Ilias Tsitsimpis
        self.config.optionxform = str
148 e2db4a57 Christos Stavrakakis
        self.config.read(config_file)
149 c441b6a7 Ilias Tsitsimpis
150 c441b6a7 Ilias Tsitsimpis
        # Read temporary_config file
151 4b61ee63 Ilias Tsitsimpis
        self.temp_config_file = \
152 4b61ee63 Ilias Tsitsimpis
            os.path.expanduser(self.config.get('Global', 'temporary_config'))
153 c441b6a7 Ilias Tsitsimpis
        self.temp_config = ConfigParser()
154 c441b6a7 Ilias Tsitsimpis
        self.temp_config.optionxform = str
155 4b61ee63 Ilias Tsitsimpis
        self.temp_config.read(self.temp_config_file)
156 ef695867 Ilias Tsitsimpis
        self.build_id = build_id
157 ef695867 Ilias Tsitsimpis
        self.logger.info("Will use \"%s\" as build id" % _green(self.build_id))
158 525f2979 Ilias Tsitsimpis
159 da593e0c Christos Stavrakakis
        # Set kamaki cloud
160 da593e0c Christos Stavrakakis
        if cloud is not None:
161 da593e0c Christos Stavrakakis
            self.kamaki_cloud = cloud
162 da593e0c Christos Stavrakakis
        elif self.config.has_option("Deployment", "kamaki_cloud"):
163 da593e0c Christos Stavrakakis
            kamaki_cloud = self.config.get("Deployment", "kamaki_cloud")
164 da593e0c Christos Stavrakakis
            if kamaki_cloud == "":
165 da593e0c Christos Stavrakakis
                self.kamaki_cloud = None
166 da593e0c Christos Stavrakakis
        else:
167 da593e0c Christos Stavrakakis
            self.kamaki_cloud = None
168 da593e0c Christos Stavrakakis
169 525f2979 Ilias Tsitsimpis
        # Initialize variables
170 525f2979 Ilias Tsitsimpis
        self.fabric_installed = False
171 525f2979 Ilias Tsitsimpis
        self.kamaki_installed = False
172 525f2979 Ilias Tsitsimpis
        self.cyclades_client = None
173 dae06cfc Ilias Tsitsimpis
        self.compute_client = None
174 525f2979 Ilias Tsitsimpis
        self.image_client = None
175 0373e3cf Ilias Tsitsimpis
        self.astakos_client = None
176 525f2979 Ilias Tsitsimpis
177 525f2979 Ilias Tsitsimpis
    def setup_kamaki(self):
178 525f2979 Ilias Tsitsimpis
        """Initialize kamaki
179 525f2979 Ilias Tsitsimpis

180 dae06cfc Ilias Tsitsimpis
        Setup cyclades_client, image_client and compute_client
181 525f2979 Ilias Tsitsimpis
        """
182 da593e0c Christos Stavrakakis
183 da593e0c Christos Stavrakakis
        config = kamaki_config.Config()
184 da593e0c Christos Stavrakakis
        if self.kamaki_cloud is None:
185 da593e0c Christos Stavrakakis
            self.kamaki_cloud = config.get_global("default_cloud")
186 da593e0c Christos Stavrakakis
187 da593e0c Christos Stavrakakis
        self.logger.info("Setup kamaki client, using cloud '%s'.." %
188 da593e0c Christos Stavrakakis
                         self.kamaki_cloud)
189 da593e0c Christos Stavrakakis
        auth_url = config.get_cloud(self.kamaki_cloud, "url")
190 525f2979 Ilias Tsitsimpis
        self.logger.debug("Authentication URL is %s" % _green(auth_url))
191 da593e0c Christos Stavrakakis
        token = config.get_cloud(self.kamaki_cloud, "token")
192 525f2979 Ilias Tsitsimpis
        #self.logger.debug("Token is %s" % _green(token))
193 525f2979 Ilias Tsitsimpis
194 ac1e7de3 Christos Stavrakakis
        self.astakos_client = AstakosClient(auth_url, token)
195 525f2979 Ilias Tsitsimpis
196 525f2979 Ilias Tsitsimpis
        cyclades_url = \
197 ac1e7de3 Christos Stavrakakis
            self.astakos_client.get_service_endpoints('compute')['publicURL']
198 525f2979 Ilias Tsitsimpis
        self.logger.debug("Cyclades API url is %s" % _green(cyclades_url))
199 525f2979 Ilias Tsitsimpis
        self.cyclades_client = CycladesClient(cyclades_url, token)
200 525f2979 Ilias Tsitsimpis
        self.cyclades_client.CONNECTION_RETRY_LIMIT = 2
201 525f2979 Ilias Tsitsimpis
202 525f2979 Ilias Tsitsimpis
        image_url = \
203 ac1e7de3 Christos Stavrakakis
            self.astakos_client.get_service_endpoints('image')['publicURL']
204 525f2979 Ilias Tsitsimpis
        self.logger.debug("Images API url is %s" % _green(image_url))
205 525f2979 Ilias Tsitsimpis
        self.image_client = ImageClient(cyclades_url, token)
206 525f2979 Ilias Tsitsimpis
        self.image_client.CONNECTION_RETRY_LIMIT = 2
207 525f2979 Ilias Tsitsimpis
208 dae06cfc Ilias Tsitsimpis
        compute_url = \
209 ac1e7de3 Christos Stavrakakis
            self.astakos_client.get_service_endpoints('compute')['publicURL']
210 dae06cfc Ilias Tsitsimpis
        self.logger.debug("Compute API url is %s" % _green(compute_url))
211 dae06cfc Ilias Tsitsimpis
        self.compute_client = ComputeClient(compute_url, token)
212 dae06cfc Ilias Tsitsimpis
        self.compute_client.CONNECTION_RETRY_LIMIT = 2
213 dae06cfc Ilias Tsitsimpis
214 525f2979 Ilias Tsitsimpis
    def _wait_transition(self, server_id, current_status, new_status):
215 525f2979 Ilias Tsitsimpis
        """Wait for server to go from current_status to new_status"""
216 525f2979 Ilias Tsitsimpis
        self.logger.debug("Waiting for server to become %s" % new_status)
217 525f2979 Ilias Tsitsimpis
        timeout = self.config.getint('Global', 'build_timeout')
218 525f2979 Ilias Tsitsimpis
        sleep_time = 5
219 525f2979 Ilias Tsitsimpis
        while True:
220 525f2979 Ilias Tsitsimpis
            server = self.cyclades_client.get_server_details(server_id)
221 525f2979 Ilias Tsitsimpis
            if server['status'] == new_status:
222 525f2979 Ilias Tsitsimpis
                return server
223 525f2979 Ilias Tsitsimpis
            elif timeout < 0:
224 525f2979 Ilias Tsitsimpis
                self.logger.error(
225 525f2979 Ilias Tsitsimpis
                    "Waiting for server to become %s timed out" % new_status)
226 525f2979 Ilias Tsitsimpis
                self.destroy_server(False)
227 525f2979 Ilias Tsitsimpis
                sys.exit(-1)
228 525f2979 Ilias Tsitsimpis
            elif server['status'] == current_status:
229 525f2979 Ilias Tsitsimpis
                # Sleep for #n secs and continue
230 525f2979 Ilias Tsitsimpis
                timeout = timeout - sleep_time
231 525f2979 Ilias Tsitsimpis
                time.sleep(sleep_time)
232 525f2979 Ilias Tsitsimpis
            else:
233 525f2979 Ilias Tsitsimpis
                self.logger.error(
234 525f2979 Ilias Tsitsimpis
                    "Server failed with status %s" % server['status'])
235 525f2979 Ilias Tsitsimpis
                self.destroy_server(False)
236 525f2979 Ilias Tsitsimpis
                sys.exit(-1)
237 525f2979 Ilias Tsitsimpis
238 525f2979 Ilias Tsitsimpis
    @_check_kamaki
239 525f2979 Ilias Tsitsimpis
    def destroy_server(self, wait=True):
240 525f2979 Ilias Tsitsimpis
        """Destroy slave server"""
241 ef695867 Ilias Tsitsimpis
        server_id = int(self.read_temp_config('server_id'))
242 525f2979 Ilias Tsitsimpis
        self.logger.info("Destoying server with id %s " % server_id)
243 525f2979 Ilias Tsitsimpis
        self.cyclades_client.delete_server(server_id)
244 525f2979 Ilias Tsitsimpis
        if wait:
245 525f2979 Ilias Tsitsimpis
            self._wait_transition(server_id, "ACTIVE", "DELETED")
246 525f2979 Ilias Tsitsimpis
247 525f2979 Ilias Tsitsimpis
    @_check_kamaki
248 6f8b2632 Ilias Tsitsimpis
    def create_server(self, image=None, flavor=None, ssh_keys=None):
249 525f2979 Ilias Tsitsimpis
        """Create slave server"""
250 525f2979 Ilias Tsitsimpis
        self.logger.info("Create a new server..")
251 ef695867 Ilias Tsitsimpis
252 ef695867 Ilias Tsitsimpis
        # Find a build_id to use
253 ef695867 Ilias Tsitsimpis
        if self.build_id is None:
254 ef695867 Ilias Tsitsimpis
            # If build_id is given use this, else ..
255 ef695867 Ilias Tsitsimpis
            # Find a uniq build_id to use
256 ef695867 Ilias Tsitsimpis
            ids = self.temp_config.sections()
257 ef695867 Ilias Tsitsimpis
            if ids:
258 ef695867 Ilias Tsitsimpis
                max_id = int(max(self.temp_config.sections(), key=int))
259 ef695867 Ilias Tsitsimpis
                self.build_id = max_id + 1
260 ef695867 Ilias Tsitsimpis
            else:
261 ef695867 Ilias Tsitsimpis
                self.build_id = 1
262 ef695867 Ilias Tsitsimpis
        self.logger.debug("New build id \"%s\" was created"
263 ef695867 Ilias Tsitsimpis
                          % _green(self.build_id))
264 ef695867 Ilias Tsitsimpis
265 ef695867 Ilias Tsitsimpis
        # Find an image to use
266 6f8b2632 Ilias Tsitsimpis
        image_id = self._find_image(image)
267 ef695867 Ilias Tsitsimpis
        # Find a flavor to use
268 6f8b2632 Ilias Tsitsimpis
        flavor_id = self._find_flavor(flavor)
269 6f8b2632 Ilias Tsitsimpis
270 6f8b2632 Ilias Tsitsimpis
        # Create Server
271 69a64e60 Christos Stavrakakis
        server_name = self.config.get("Deployment", "server_name")
272 525f2979 Ilias Tsitsimpis
        server = self.cyclades_client.create_server(
273 69a64e60 Christos Stavrakakis
            "%s(BID: %s)" % (server_name, self.build_id),
274 947b6106 Christos Stavrakakis
            flavor_id,
275 947b6106 Christos Stavrakakis
            image_id)
276 525f2979 Ilias Tsitsimpis
        server_id = server['id']
277 ef695867 Ilias Tsitsimpis
        self.write_temp_config('server_id', server_id)
278 525f2979 Ilias Tsitsimpis
        self.logger.debug("Server got id %s" % _green(server_id))
279 525f2979 Ilias Tsitsimpis
        server_user = server['metadata']['users']
280 ef695867 Ilias Tsitsimpis
        self.write_temp_config('server_user', server_user)
281 525f2979 Ilias Tsitsimpis
        self.logger.debug("Server's admin user is %s" % _green(server_user))
282 525f2979 Ilias Tsitsimpis
        server_passwd = server['adminPass']
283 ef695867 Ilias Tsitsimpis
        self.write_temp_config('server_passwd', server_passwd)
284 525f2979 Ilias Tsitsimpis
285 525f2979 Ilias Tsitsimpis
        server = self._wait_transition(server_id, "BUILD", "ACTIVE")
286 525f2979 Ilias Tsitsimpis
        self._get_server_ip_and_port(server)
287 4c818bb2 Ilias Tsitsimpis
        self._copy_ssh_keys(ssh_keys)
288 525f2979 Ilias Tsitsimpis
289 eaf0b161 Ilias Tsitsimpis
        # Setup Firewall
290 525f2979 Ilias Tsitsimpis
        self.setup_fabric()
291 525f2979 Ilias Tsitsimpis
        self.logger.info("Setup firewall")
292 4c818bb2 Ilias Tsitsimpis
        accept_ssh_from = self.config.get('Global', 'accept_ssh_from')
293 0082c787 Christos Stavrakakis
        if accept_ssh_from != "":
294 0082c787 Christos Stavrakakis
            self.logger.debug("Block ssh except from %s" % accept_ssh_from)
295 0082c787 Christos Stavrakakis
            cmd = """
296 0082c787 Christos Stavrakakis
            local_ip=$(/sbin/ifconfig eth0 | grep 'inet addr:' | \
297 0082c787 Christos Stavrakakis
                cut -d':' -f2 | cut -d' ' -f1)
298 0082c787 Christos Stavrakakis
            iptables -A INPUT -s localhost -j ACCEPT
299 0082c787 Christos Stavrakakis
            iptables -A INPUT -s $local_ip -j ACCEPT
300 0082c787 Christos Stavrakakis
            iptables -A INPUT -s {0} -p tcp --dport 22 -j ACCEPT
301 0082c787 Christos Stavrakakis
            iptables -A INPUT -p tcp --dport 22 -j DROP
302 0082c787 Christos Stavrakakis
            """.format(accept_ssh_from)
303 0082c787 Christos Stavrakakis
            _run(cmd, False)
304 525f2979 Ilias Tsitsimpis
305 eaf0b161 Ilias Tsitsimpis
        # Setup apt, download packages
306 eaf0b161 Ilias Tsitsimpis
        self.logger.debug("Setup apt. Install x2goserver and firefox")
307 eaf0b161 Ilias Tsitsimpis
        cmd = """
308 eaf0b161 Ilias Tsitsimpis
        echo 'APT::Install-Suggests "false";' >> /etc/apt/apt.conf
309 eaf0b161 Ilias Tsitsimpis
        apt-get update
310 769db39e Ilias Tsitsimpis
        apt-get install curl --yes --force-yes
311 eaf0b161 Ilias Tsitsimpis
        echo -e "\n\n{0}" >> /etc/apt/sources.list
312 eaf0b161 Ilias Tsitsimpis
        # Synnefo repo's key
313 eaf0b161 Ilias Tsitsimpis
        curl https://dev.grnet.gr/files/apt-grnetdev.pub | apt-key add -
314 fcfef550 Ilias Tsitsimpis

315 eaf0b161 Ilias Tsitsimpis
        # X2GO Key
316 eaf0b161 Ilias Tsitsimpis
        apt-key adv --recv-keys --keyserver keys.gnupg.net E1F958385BFE2B6E
317 769db39e Ilias Tsitsimpis
        apt-get install x2go-keyring --yes --force-yes
318 eaf0b161 Ilias Tsitsimpis
        apt-get update
319 769db39e Ilias Tsitsimpis
        apt-get install x2goserver x2goserver-xsession \
320 769db39e Ilias Tsitsimpis
                iceweasel --yes --force-yes
321 fcfef550 Ilias Tsitsimpis

322 fcfef550 Ilias Tsitsimpis
        # xterm published application
323 fcfef550 Ilias Tsitsimpis
        echo '[Desktop Entry]' > /usr/share/applications/xterm.desktop
324 fcfef550 Ilias Tsitsimpis
        echo 'Name=XTerm' >> /usr/share/applications/xterm.desktop
325 fcfef550 Ilias Tsitsimpis
        echo 'Comment=standard terminal emulator for the X window system' >> \
326 fcfef550 Ilias Tsitsimpis
            /usr/share/applications/xterm.desktop
327 fcfef550 Ilias Tsitsimpis
        echo 'Exec=xterm' >> /usr/share/applications/xterm.desktop
328 fcfef550 Ilias Tsitsimpis
        echo 'Terminal=false' >> /usr/share/applications/xterm.desktop
329 fcfef550 Ilias Tsitsimpis
        echo 'Type=Application' >> /usr/share/applications/xterm.desktop
330 fcfef550 Ilias Tsitsimpis
        echo 'Encoding=UTF-8' >> /usr/share/applications/xterm.desktop
331 fcfef550 Ilias Tsitsimpis
        echo 'Icon=xterm-color_48x48' >> /usr/share/applications/xterm.desktop
332 fcfef550 Ilias Tsitsimpis
        echo 'Categories=System;TerminalEmulator;' >> \
333 fcfef550 Ilias Tsitsimpis
                /usr/share/applications/xterm.desktop
334 eaf0b161 Ilias Tsitsimpis
        """.format(self.config.get('Global', 'apt_repo'))
335 eaf0b161 Ilias Tsitsimpis
        _run(cmd, False)
336 eaf0b161 Ilias Tsitsimpis
337 6f8b2632 Ilias Tsitsimpis
    def _find_flavor(self, flavor=None):
338 6f8b2632 Ilias Tsitsimpis
        """Find a suitable flavor to use
339 6f8b2632 Ilias Tsitsimpis

340 6f8b2632 Ilias Tsitsimpis
        Search by name (reg expression) or by id
341 6f8b2632 Ilias Tsitsimpis
        """
342 6f8b2632 Ilias Tsitsimpis
        # Get a list of flavors from config file
343 6f8b2632 Ilias Tsitsimpis
        flavors = self.config.get('Deployment', 'flavors').split(",")
344 6f8b2632 Ilias Tsitsimpis
        if flavor is not None:
345 c314fcd2 Ilias Tsitsimpis
            # If we have a flavor_name to use, add it to our list
346 6f8b2632 Ilias Tsitsimpis
            flavors.insert(0, flavor)
347 6f8b2632 Ilias Tsitsimpis
348 6f8b2632 Ilias Tsitsimpis
        list_flavors = self.compute_client.list_flavors()
349 6f8b2632 Ilias Tsitsimpis
        for flv in flavors:
350 ac1e7de3 Christos Stavrakakis
            flv_type, flv_value = parse_typed_option(option="flavor",
351 ac1e7de3 Christos Stavrakakis
                                                     value=flv)
352 6f8b2632 Ilias Tsitsimpis
            if flv_type == "name":
353 6f8b2632 Ilias Tsitsimpis
                # Filter flavors by name
354 6f8b2632 Ilias Tsitsimpis
                self.logger.debug(
355 6f8b2632 Ilias Tsitsimpis
                    "Trying to find a flavor with name \"%s\"" % flv_value)
356 6f8b2632 Ilias Tsitsimpis
                list_flvs = \
357 6f8b2632 Ilias Tsitsimpis
                    [f for f in list_flavors
358 ac1e7de3 Christos Stavrakakis
                     if re.search(flv_value, f['name'], flags=re.I)
359 ac1e7de3 Christos Stavrakakis
                     is not None]
360 6f8b2632 Ilias Tsitsimpis
            elif flv_type == "id":
361 6f8b2632 Ilias Tsitsimpis
                # Filter flavors by id
362 6f8b2632 Ilias Tsitsimpis
                self.logger.debug(
363 6f8b2632 Ilias Tsitsimpis
                    "Trying to find a flavor with id \"%s\"" % flv_value)
364 6f8b2632 Ilias Tsitsimpis
                list_flvs = \
365 6f8b2632 Ilias Tsitsimpis
                    [f for f in list_flavors
366 0373e3cf Ilias Tsitsimpis
                     if str(f['id']) == flv_value]
367 6f8b2632 Ilias Tsitsimpis
            else:
368 6f8b2632 Ilias Tsitsimpis
                self.logger.error("Unrecognized flavor type %s" % flv_type)
369 6f8b2632 Ilias Tsitsimpis
370 6f8b2632 Ilias Tsitsimpis
            # Check if we found one
371 6f8b2632 Ilias Tsitsimpis
            if list_flvs:
372 6f8b2632 Ilias Tsitsimpis
                self.logger.debug("Will use \"%s\" with id \"%s\""
373 6f8b2632 Ilias Tsitsimpis
                                  % (list_flvs[0]['name'], list_flvs[0]['id']))
374 6f8b2632 Ilias Tsitsimpis
                return list_flvs[0]['id']
375 c314fcd2 Ilias Tsitsimpis
376 c314fcd2 Ilias Tsitsimpis
        self.logger.error("No matching flavor found.. aborting")
377 c314fcd2 Ilias Tsitsimpis
        sys.exit(1)
378 dae06cfc Ilias Tsitsimpis
379 6f8b2632 Ilias Tsitsimpis
    def _find_image(self, image=None):
380 525f2979 Ilias Tsitsimpis
        """Find a suitable image to use
381 525f2979 Ilias Tsitsimpis

382 6f8b2632 Ilias Tsitsimpis
        In case of search by name, the image has to belong to one
383 6f8b2632 Ilias Tsitsimpis
        of the `DEFAULT_SYSTEM_IMAGES_UUID' users.
384 6f8b2632 Ilias Tsitsimpis
        In case of search by id it only has to exist.
385 525f2979 Ilias Tsitsimpis
        """
386 6f8b2632 Ilias Tsitsimpis
        # Get a list of images from config file
387 6f8b2632 Ilias Tsitsimpis
        images = self.config.get('Deployment', 'images').split(",")
388 6f8b2632 Ilias Tsitsimpis
        if image is not None:
389 6f8b2632 Ilias Tsitsimpis
            # If we have an image from command line, add it to our list
390 6f8b2632 Ilias Tsitsimpis
            images.insert(0, image)
391 6f8b2632 Ilias Tsitsimpis
392 ac1e7de3 Christos Stavrakakis
        auth = self.astakos_client.authenticate()
393 ac1e7de3 Christos Stavrakakis
        user_uuid = auth["access"]["token"]["tenant"]["id"]
394 6f8b2632 Ilias Tsitsimpis
        list_images = self.image_client.list_public(detail=True)['images']
395 6f8b2632 Ilias Tsitsimpis
        for img in images:
396 fe402df2 Christos Stavrakakis
            img_type, img_value = parse_typed_option(option="image", value=img)
397 6f8b2632 Ilias Tsitsimpis
            if img_type == "name":
398 6f8b2632 Ilias Tsitsimpis
                # Filter images by name
399 6f8b2632 Ilias Tsitsimpis
                self.logger.debug(
400 6f8b2632 Ilias Tsitsimpis
                    "Trying to find an image with name \"%s\"" % img_value)
401 ac1e7de3 Christos Stavrakakis
                accepted_uuids = DEFAULT_SYSTEM_IMAGES_UUID + [user_uuid]
402 6f8b2632 Ilias Tsitsimpis
                list_imgs = \
403 ac1e7de3 Christos Stavrakakis
                    [i for i in list_images if i['user_id'] in accepted_uuids
404 ac1e7de3 Christos Stavrakakis
                     and
405 ac1e7de3 Christos Stavrakakis
                     re.search(img_value, i['name'], flags=re.I) is not None]
406 6f8b2632 Ilias Tsitsimpis
            elif img_type == "id":
407 6f8b2632 Ilias Tsitsimpis
                # Filter images by id
408 6f8b2632 Ilias Tsitsimpis
                self.logger.debug(
409 6f8b2632 Ilias Tsitsimpis
                    "Trying to find an image with id \"%s\"" % img_value)
410 6f8b2632 Ilias Tsitsimpis
                list_imgs = \
411 6f8b2632 Ilias Tsitsimpis
                    [i for i in list_images
412 6f8b2632 Ilias Tsitsimpis
                     if i['id'].lower() == img_value.lower()]
413 6f8b2632 Ilias Tsitsimpis
            else:
414 6f8b2632 Ilias Tsitsimpis
                self.logger.error("Unrecognized image type %s" % img_type)
415 6f8b2632 Ilias Tsitsimpis
                sys.exit(1)
416 6f8b2632 Ilias Tsitsimpis
417 6f8b2632 Ilias Tsitsimpis
            # Check if we found one
418 6f8b2632 Ilias Tsitsimpis
            if list_imgs:
419 6f8b2632 Ilias Tsitsimpis
                self.logger.debug("Will use \"%s\" with id \"%s\""
420 6f8b2632 Ilias Tsitsimpis
                                  % (list_imgs[0]['name'], list_imgs[0]['id']))
421 6f8b2632 Ilias Tsitsimpis
                return list_imgs[0]['id']
422 6f8b2632 Ilias Tsitsimpis
423 6f8b2632 Ilias Tsitsimpis
        # We didn't found one
424 6f8b2632 Ilias Tsitsimpis
        self.logger.error("No matching image found.. aborting")
425 6f8b2632 Ilias Tsitsimpis
        sys.exit(1)
426 525f2979 Ilias Tsitsimpis
427 525f2979 Ilias Tsitsimpis
    def _get_server_ip_and_port(self, server):
428 525f2979 Ilias Tsitsimpis
        """Compute server's IPv4 and ssh port number"""
429 525f2979 Ilias Tsitsimpis
        self.logger.info("Get server connection details..")
430 525f2979 Ilias Tsitsimpis
        server_ip = server['attachments'][0]['ipv4']
431 c4c22bb4 Christos Stavrakakis
        if (".okeanos.io" in self.cyclades_client.base_url or
432 c4c22bb4 Christos Stavrakakis
           ".demo.synnefo.org" in self.cyclades_client.base_url):
433 525f2979 Ilias Tsitsimpis
            tmp1 = int(server_ip.split(".")[2])
434 525f2979 Ilias Tsitsimpis
            tmp2 = int(server_ip.split(".")[3])
435 525f2979 Ilias Tsitsimpis
            server_ip = "gate.okeanos.io"
436 525f2979 Ilias Tsitsimpis
            server_port = 10000 + tmp1 * 256 + tmp2
437 525f2979 Ilias Tsitsimpis
        else:
438 525f2979 Ilias Tsitsimpis
            server_port = 22
439 ef695867 Ilias Tsitsimpis
        self.write_temp_config('server_ip', server_ip)
440 525f2979 Ilias Tsitsimpis
        self.logger.debug("Server's IPv4 is %s" % _green(server_ip))
441 ef695867 Ilias Tsitsimpis
        self.write_temp_config('server_port', server_port)
442 525f2979 Ilias Tsitsimpis
        self.logger.debug("Server's ssh port is %s" % _green(server_port))
443 064d2f1b Ilias Tsitsimpis
        self.logger.debug("Access server using \"ssh -X -p %s %s@%s\"" %
444 ac1e7de3 Christos Stavrakakis
                          (server_port, server['metadata']['users'],
445 ac1e7de3 Christos Stavrakakis
                           server_ip))
446 525f2979 Ilias Tsitsimpis
447 5c3b5c9f Christos Stavrakakis
    @_check_fabric
448 4c818bb2 Ilias Tsitsimpis
    def _copy_ssh_keys(self, ssh_keys):
449 33ad9a0d Ilias Tsitsimpis
        """Upload/Install ssh keys to server"""
450 2fab294e Ilias Tsitsimpis
        self.logger.debug("Check for authentication keys to use")
451 4c818bb2 Ilias Tsitsimpis
        if ssh_keys is None:
452 4c818bb2 Ilias Tsitsimpis
            ssh_keys = self.config.get("Deployment", "ssh_keys")
453 4c818bb2 Ilias Tsitsimpis
454 2fab294e Ilias Tsitsimpis
        if ssh_keys != "":
455 8baa4ae7 Ilias Tsitsimpis
            ssh_keys = os.path.expanduser(ssh_keys)
456 ac1e7de3 Christos Stavrakakis
            self.logger.debug("Will use %s authentication keys file" %
457 ac1e7de3 Christos Stavrakakis
                              ssh_keys)
458 5c3b5c9f Christos Stavrakakis
            keyfile = '/tmp/%s.pub' % fabric.env.user
459 5c3b5c9f Christos Stavrakakis
            _run('mkdir -p ~/.ssh && chmod 700 ~/.ssh', False)
460 2fab294e Ilias Tsitsimpis
            if ssh_keys.startswith("http://") or \
461 2fab294e Ilias Tsitsimpis
                    ssh_keys.startswith("https://") or \
462 2fab294e Ilias Tsitsimpis
                    ssh_keys.startswith("ftp://"):
463 2fab294e Ilias Tsitsimpis
                cmd = """
464 2fab294e Ilias Tsitsimpis
                apt-get update
465 769db39e Ilias Tsitsimpis
                apt-get install wget --yes --force-yes
466 2fab294e Ilias Tsitsimpis
                wget {0} -O {1} --no-check-certificate
467 2fab294e Ilias Tsitsimpis
                """.format(ssh_keys, keyfile)
468 2fab294e Ilias Tsitsimpis
                _run(cmd, False)
469 2fab294e Ilias Tsitsimpis
            elif os.path.exists(ssh_keys):
470 2fab294e Ilias Tsitsimpis
                _put(ssh_keys, keyfile)
471 2fab294e Ilias Tsitsimpis
            else:
472 2fab294e Ilias Tsitsimpis
                self.logger.debug("No ssh keys found")
473 8baa4ae7 Ilias Tsitsimpis
                return
474 5c3b5c9f Christos Stavrakakis
            _run('cat %s >> ~/.ssh/authorized_keys' % keyfile, False)
475 5c3b5c9f Christos Stavrakakis
            _run('rm %s' % keyfile, False)
476 5c3b5c9f Christos Stavrakakis
            self.logger.debug("Uploaded ssh authorized keys")
477 5c3b5c9f Christos Stavrakakis
        else:
478 5c3b5c9f Christos Stavrakakis
            self.logger.debug("No ssh keys found")
479 5c3b5c9f Christos Stavrakakis
480 ef695867 Ilias Tsitsimpis
    def write_temp_config(self, option, value):
481 525f2979 Ilias Tsitsimpis
        """Write changes back to config file"""
482 4b61ee63 Ilias Tsitsimpis
        # Acquire the lock to write to temp_config_file
483 4b61ee63 Ilias Tsitsimpis
        with filelocker.lock("%s.lock" % self.temp_config_file,
484 4b61ee63 Ilias Tsitsimpis
                             filelocker.LOCK_EX):
485 4b61ee63 Ilias Tsitsimpis
486 4b61ee63 Ilias Tsitsimpis
            # Read temp_config again to get any new entries
487 4b61ee63 Ilias Tsitsimpis
            self.temp_config.read(self.temp_config_file)
488 4b61ee63 Ilias Tsitsimpis
489 4b61ee63 Ilias Tsitsimpis
            # If build_id section doesn't exist create a new one
490 4b61ee63 Ilias Tsitsimpis
            try:
491 4b61ee63 Ilias Tsitsimpis
                self.temp_config.add_section(str(self.build_id))
492 4b61ee63 Ilias Tsitsimpis
                creation_time = \
493 4b61ee63 Ilias Tsitsimpis
                    time.strftime("%a, %d %b %Y %X", time.localtime())
494 4b61ee63 Ilias Tsitsimpis
                self.temp_config.set(str(self.build_id),
495 4b61ee63 Ilias Tsitsimpis
                                     "created", str(creation_time))
496 4b61ee63 Ilias Tsitsimpis
            except DuplicateSectionError:
497 4b61ee63 Ilias Tsitsimpis
                pass
498 4b61ee63 Ilias Tsitsimpis
            self.temp_config.set(str(self.build_id), option, str(value))
499 4b61ee63 Ilias Tsitsimpis
            curr_time = time.strftime("%a, %d %b %Y %X", time.localtime())
500 4b61ee63 Ilias Tsitsimpis
            self.temp_config.set(str(self.build_id), "modified", curr_time)
501 4b61ee63 Ilias Tsitsimpis
            with open(self.temp_config_file, 'wb') as tcf:
502 4b61ee63 Ilias Tsitsimpis
                self.temp_config.write(tcf)
503 525f2979 Ilias Tsitsimpis
504 ef695867 Ilias Tsitsimpis
    def read_temp_config(self, option):
505 ef695867 Ilias Tsitsimpis
        """Read from temporary_config file"""
506 ef695867 Ilias Tsitsimpis
        # If build_id is None use the latest one
507 ef695867 Ilias Tsitsimpis
        if self.build_id is None:
508 ef695867 Ilias Tsitsimpis
            ids = self.temp_config.sections()
509 ef695867 Ilias Tsitsimpis
            if ids:
510 ef695867 Ilias Tsitsimpis
                self.build_id = int(ids[-1])
511 ef695867 Ilias Tsitsimpis
            else:
512 ef695867 Ilias Tsitsimpis
                self.logger.error("No sections in temporary config file")
513 ef695867 Ilias Tsitsimpis
                sys.exit(1)
514 ef695867 Ilias Tsitsimpis
            self.logger.debug("Will use \"%s\" as build id"
515 ef695867 Ilias Tsitsimpis
                              % _green(self.build_id))
516 ef695867 Ilias Tsitsimpis
        # Read specified option
517 ef695867 Ilias Tsitsimpis
        return self.temp_config.get(str(self.build_id), option)
518 ef695867 Ilias Tsitsimpis
519 525f2979 Ilias Tsitsimpis
    def setup_fabric(self):
520 525f2979 Ilias Tsitsimpis
        """Setup fabric environment"""
521 525f2979 Ilias Tsitsimpis
        self.logger.info("Setup fabric parameters..")
522 ef695867 Ilias Tsitsimpis
        fabric.env.user = self.read_temp_config('server_user')
523 ef695867 Ilias Tsitsimpis
        fabric.env.host_string = self.read_temp_config('server_ip')
524 ef695867 Ilias Tsitsimpis
        fabric.env.port = int(self.read_temp_config('server_port'))
525 ef695867 Ilias Tsitsimpis
        fabric.env.password = self.read_temp_config('server_passwd')
526 525f2979 Ilias Tsitsimpis
        fabric.env.connection_attempts = 10
527 525f2979 Ilias Tsitsimpis
        fabric.env.shell = "/bin/bash -c"
528 525f2979 Ilias Tsitsimpis
        fabric.env.disable_known_hosts = True
529 525f2979 Ilias Tsitsimpis
        fabric.env.output_prefix = None
530 525f2979 Ilias Tsitsimpis
531 525f2979 Ilias Tsitsimpis
    def _check_hash_sum(self, localfile, remotefile):
532 525f2979 Ilias Tsitsimpis
        """Check hash sums of two files"""
533 525f2979 Ilias Tsitsimpis
        self.logger.debug("Check hash sum for local file %s" % localfile)
534 525f2979 Ilias Tsitsimpis
        hash1 = os.popen("sha256sum %s" % localfile).read().split(' ')[0]
535 525f2979 Ilias Tsitsimpis
        self.logger.debug("Local file has sha256 hash %s" % hash1)
536 525f2979 Ilias Tsitsimpis
        self.logger.debug("Check hash sum for remote file %s" % remotefile)
537 525f2979 Ilias Tsitsimpis
        hash2 = _run("sha256sum %s" % remotefile, False)
538 525f2979 Ilias Tsitsimpis
        hash2 = hash2.split(' ')[0]
539 525f2979 Ilias Tsitsimpis
        self.logger.debug("Remote file has sha256 hash %s" % hash2)
540 525f2979 Ilias Tsitsimpis
        if hash1 != hash2:
541 525f2979 Ilias Tsitsimpis
            self.logger.error("Hashes differ.. aborting")
542 525f2979 Ilias Tsitsimpis
            sys.exit(-1)
543 525f2979 Ilias Tsitsimpis
544 525f2979 Ilias Tsitsimpis
    @_check_fabric
545 1d9f2031 Ilias Tsitsimpis
    def clone_repo(self, local_repo=False):
546 525f2979 Ilias Tsitsimpis
        """Clone Synnefo repo from slave server"""
547 525f2979 Ilias Tsitsimpis
        self.logger.info("Configure repositories on remote server..")
548 eaf0b161 Ilias Tsitsimpis
        self.logger.debug("Install/Setup git")
549 525f2979 Ilias Tsitsimpis
        cmd = """
550 769db39e Ilias Tsitsimpis
        apt-get install git --yes --force-yes
551 eaf0b161 Ilias Tsitsimpis
        git config --global user.name {0}
552 eaf0b161 Ilias Tsitsimpis
        git config --global user.email {1}
553 eaf0b161 Ilias Tsitsimpis
        """.format(self.config.get('Global', 'git_config_name'),
554 525f2979 Ilias Tsitsimpis
                   self.config.get('Global', 'git_config_mail'))
555 525f2979 Ilias Tsitsimpis
        _run(cmd, False)
556 525f2979 Ilias Tsitsimpis
557 1d9f2031 Ilias Tsitsimpis
        # Find synnefo_repo and synnefo_branch to use
558 525f2979 Ilias Tsitsimpis
        synnefo_repo = self.config.get('Global', 'synnefo_repo')
559 024faf05 Christos Stavrakakis
        synnefo_branch = self.config.get("Global", "synnefo_branch")
560 024faf05 Christos Stavrakakis
        if synnefo_branch == "":
561 33ad9a0d Ilias Tsitsimpis
            synnefo_branch = \
562 33ad9a0d Ilias Tsitsimpis
                subprocess.Popen(
563 33ad9a0d Ilias Tsitsimpis
                    ["git", "rev-parse", "--abbrev-ref", "HEAD"],
564 024faf05 Christos Stavrakakis
                    stdout=subprocess.PIPE).communicate()[0].strip()
565 024faf05 Christos Stavrakakis
            if synnefo_branch == "HEAD":
566 024faf05 Christos Stavrakakis
                synnefo_branch = \
567 33ad9a0d Ilias Tsitsimpis
                    subprocess.Popen(
568 33ad9a0d Ilias Tsitsimpis
                        ["git", "rev-parse", "--short", "HEAD"],
569 024faf05 Christos Stavrakakis
                        stdout=subprocess.PIPE).communicate()[0].strip()
570 024faf05 Christos Stavrakakis
        self.logger.info("Will use branch %s" % synnefo_branch)
571 1d9f2031 Ilias Tsitsimpis
572 1d9f2031 Ilias Tsitsimpis
        if local_repo or synnefo_branch == "":
573 1d9f2031 Ilias Tsitsimpis
            # Use local_repo
574 1d9f2031 Ilias Tsitsimpis
            self.logger.debug("Push local repo to server")
575 1d9f2031 Ilias Tsitsimpis
            # Firstly create the remote repo
576 1d9f2031 Ilias Tsitsimpis
            _run("git init synnefo", False)
577 1d9f2031 Ilias Tsitsimpis
            # Then push our local repo over ssh
578 1d9f2031 Ilias Tsitsimpis
            # We have to pass some arguments to ssh command
579 1d9f2031 Ilias Tsitsimpis
            # namely to disable host checking.
580 1d9f2031 Ilias Tsitsimpis
            (temp_ssh_file_handle, temp_ssh_file) = tempfile.mkstemp()
581 1d9f2031 Ilias Tsitsimpis
            os.close(temp_ssh_file_handle)
582 4b61ee63 Ilias Tsitsimpis
            # XXX: git push doesn't read the password
583 1d9f2031 Ilias Tsitsimpis
            cmd = """
584 1d9f2031 Ilias Tsitsimpis
            echo 'exec ssh -o "StrictHostKeyChecking no" \
585 1d9f2031 Ilias Tsitsimpis
                           -o "UserKnownHostsFile /dev/null" \
586 1d9f2031 Ilias Tsitsimpis
                           -q "$@"' > {4}
587 1d9f2031 Ilias Tsitsimpis
            chmod u+x {4}
588 1d9f2031 Ilias Tsitsimpis
            export GIT_SSH="{4}"
589 1d9f2031 Ilias Tsitsimpis
            echo "{0}" | git push --mirror ssh://{1}@{2}:{3}/~/synnefo
590 1d9f2031 Ilias Tsitsimpis
            rm -f {4}
591 1d9f2031 Ilias Tsitsimpis
            """.format(fabric.env.password,
592 1d9f2031 Ilias Tsitsimpis
                       fabric.env.user,
593 1d9f2031 Ilias Tsitsimpis
                       fabric.env.host_string,
594 1d9f2031 Ilias Tsitsimpis
                       fabric.env.port,
595 1d9f2031 Ilias Tsitsimpis
                       temp_ssh_file)
596 1d9f2031 Ilias Tsitsimpis
            os.system(cmd)
597 1d9f2031 Ilias Tsitsimpis
        else:
598 1d9f2031 Ilias Tsitsimpis
            # Clone Synnefo from remote repo
599 1d9f2031 Ilias Tsitsimpis
            # Currently clonning synnefo can fail unexpectedly
600 1d9f2031 Ilias Tsitsimpis
            cloned = False
601 1d9f2031 Ilias Tsitsimpis
            for i in range(10):
602 1d9f2031 Ilias Tsitsimpis
                self.logger.debug("Clone synnefo from %s" % synnefo_repo)
603 1d9f2031 Ilias Tsitsimpis
                try:
604 1d9f2031 Ilias Tsitsimpis
                    _run("git clone %s synnefo" % synnefo_repo, False)
605 1d9f2031 Ilias Tsitsimpis
                    cloned = True
606 1d9f2031 Ilias Tsitsimpis
                    break
607 1d9f2031 Ilias Tsitsimpis
                except BaseException:
608 1d9f2031 Ilias Tsitsimpis
                    self.logger.warning(
609 1d9f2031 Ilias Tsitsimpis
                        "Clonning synnefo failed.. retrying %s" % i)
610 1d9f2031 Ilias Tsitsimpis
            if not cloned:
611 1d9f2031 Ilias Tsitsimpis
                self.logger.error("Can not clone Synnefo repo.")
612 1d9f2031 Ilias Tsitsimpis
                sys.exit(-1)
613 1d9f2031 Ilias Tsitsimpis
614 1d9f2031 Ilias Tsitsimpis
        # Checkout the desired synnefo_branch
615 064d2f1b Ilias Tsitsimpis
        self.logger.debug("Checkout \"%s\" branch/commit" % synnefo_branch)
616 33ad9a0d Ilias Tsitsimpis
        cmd = """
617 024faf05 Christos Stavrakakis
        cd synnefo
618 33ad9a0d Ilias Tsitsimpis
        for branch in `git branch -a | grep remotes | \
619 33ad9a0d Ilias Tsitsimpis
                       grep -v HEAD | grep -v master`; do
620 024faf05 Christos Stavrakakis
            git branch --track ${branch##*/} $branch
621 024faf05 Christos Stavrakakis
        done
622 024faf05 Christos Stavrakakis
        git checkout %s
623 024faf05 Christos Stavrakakis
        """ % (synnefo_branch)
624 024faf05 Christos Stavrakakis
        _run(cmd, False)
625 024faf05 Christos Stavrakakis
626 525f2979 Ilias Tsitsimpis
    @_check_fabric
627 525f2979 Ilias Tsitsimpis
    def build_synnefo(self):
628 525f2979 Ilias Tsitsimpis
        """Build Synnefo packages"""
629 525f2979 Ilias Tsitsimpis
        self.logger.info("Build Synnefo packages..")
630 525f2979 Ilias Tsitsimpis
        self.logger.debug("Install development packages")
631 525f2979 Ilias Tsitsimpis
        cmd = """
632 525f2979 Ilias Tsitsimpis
        apt-get update
633 525f2979 Ilias Tsitsimpis
        apt-get install zlib1g-dev dpkg-dev debhelper git-buildpackage \
634 769db39e Ilias Tsitsimpis
                python-dev python-all python-pip --yes --force-yes
635 525f2979 Ilias Tsitsimpis
        pip install devflow
636 525f2979 Ilias Tsitsimpis
        """
637 525f2979 Ilias Tsitsimpis
        _run(cmd, False)
638 525f2979 Ilias Tsitsimpis
639 133e5a5b Christos Stavrakakis
        if self.config.get('Global', 'patch_pydist') == "True":
640 525f2979 Ilias Tsitsimpis
            self.logger.debug("Patch pydist.py module")
641 525f2979 Ilias Tsitsimpis
            cmd = r"""
642 525f2979 Ilias Tsitsimpis
            sed -r -i 's/(\(\?P<name>\[A-Za-z\]\[A-Za-z0-9_\.)/\1\\\-/' \
643 525f2979 Ilias Tsitsimpis
                /usr/share/python/debpython/pydist.py
644 525f2979 Ilias Tsitsimpis
            """
645 525f2979 Ilias Tsitsimpis
            _run(cmd, False)
646 525f2979 Ilias Tsitsimpis

647 24ccf5d3 Ilias Tsitsimpis
        # Build synnefo packages
648 24ccf5d3 Ilias Tsitsimpis
        self.logger.debug("Build synnefo packages")
649 525f2979 Ilias Tsitsimpis
        cmd = """
650 24ccf5d3 Ilias Tsitsimpis
        devflow-autopkg snapshot -b ~/synnefo_build-area --no-sign
651 525f2979 Ilias Tsitsimpis
        """
652 24ccf5d3 Ilias Tsitsimpis
        with fabric.cd("synnefo"):
653 525f2979 Ilias Tsitsimpis
            _run(cmd, True)
654 525f2979 Ilias Tsitsimpis

655 24ccf5d3 Ilias Tsitsimpis
        # Install snf-deploy package
656 525f2979 Ilias Tsitsimpis
        self.logger.debug("Install snf-deploy package")
657 525f2979 Ilias Tsitsimpis
        cmd = """
658 525f2979 Ilias Tsitsimpis
        dpkg -i snf-deploy*.deb
659 769db39e Ilias Tsitsimpis
        apt-get -f install --yes --force-yes
660 525f2979 Ilias Tsitsimpis
        """
661 24ccf5d3 Ilias Tsitsimpis
        with fabric.cd("synnefo_build-area"):
662 525f2979 Ilias Tsitsimpis
            with fabric.settings(warn_only=True):
663 525f2979 Ilias Tsitsimpis
                _run(cmd, True)
664 525f2979 Ilias Tsitsimpis

665 24ccf5d3 Ilias Tsitsimpis
        # Setup synnefo packages for snf-deploy
666 525f2979 Ilias Tsitsimpis
        self.logger.debug("Copy synnefo debs to snf-deploy packages dir")
667 525f2979 Ilias Tsitsimpis
        cmd = """
668 525f2979 Ilias Tsitsimpis
        cp ~/synnefo_build-area/*.deb /var/lib/snf-deploy/packages/
669 525f2979 Ilias Tsitsimpis
        """
670 525f2979 Ilias Tsitsimpis
        _run(cmd, False)
671 525f2979 Ilias Tsitsimpis

672 124c300e Christos Stavrakakis
    @_check_fabric
673 124c300e Christos Stavrakakis
    def build_documentation(self):
674 33ad9a0d Ilias Tsitsimpis
        """Build Synnefo documentation"""
675 124c300e Christos Stavrakakis
        self.logger.info("Build Synnefo documentation..")
676 124c300e Christos Stavrakakis
        _run("pip install -U Sphinx", False)
677 124c300e Christos Stavrakakis
        with fabric.cd("synnefo"):
678 33ad9a0d Ilias Tsitsimpis
            _run("devflow-update-version; "
679 33ad9a0d Ilias Tsitsimpis
                 "./ci/make_docs.sh synnefo_documentation", False)
680 124c300e Christos Stavrakakis

681 124c300e Christos Stavrakakis
    def fetch_documentation(self, dest=None):
682 33ad9a0d Ilias Tsitsimpis
        """Fetch Synnefo documentation"""
683 33ad9a0d Ilias Tsitsimpis
        self.logger.info("Fetch Synnefo documentation..")
684 124c300e Christos Stavrakakis
        if dest is None:
685 124c300e Christos Stavrakakis
            dest = "synnefo_documentation"
686 124c300e Christos Stavrakakis
        dest = os.path.abspath(dest)
687 124c300e Christos Stavrakakis
        if not os.path.exists(dest):
688 124c300e Christos Stavrakakis
            os.makedirs(dest)
689 124c300e Christos Stavrakakis
        self.fetch_compressed("synnefo/synnefo_documentation", dest)
690 124c300e Christos Stavrakakis
        self.logger.info("Downloaded documentation to %s" %
691 124c300e Christos Stavrakakis
                         _green(dest))
692 124c300e Christos Stavrakakis

693 525f2979 Ilias Tsitsimpis
    @_check_fabric
694 46a07468 Christos Stavrakakis
    def deploy_synnefo(self, schema=None):
695 525f2979 Ilias Tsitsimpis
        """Deploy Synnefo using snf-deploy"""
696 525f2979 Ilias Tsitsimpis
        self.logger.info("Deploy Synnefo..")
697 46a07468 Christos Stavrakakis
        if schema is None:
698 46a07468 Christos Stavrakakis
            schema = self.config.get('Global', 'schema')
699 6f8b2632 Ilias Tsitsimpis
        self.logger.debug("Will use \"%s\" schema" % schema)
700 525f2979 Ilias Tsitsimpis

701 46a07468 Christos Stavrakakis
        schema_dir = os.path.join(self.ci_dir, "schemas/%s" % schema)
702 46a07468 Christos Stavrakakis
        if not (os.path.exists(schema_dir) and os.path.isdir(schema_dir)):
703 46a07468 Christos Stavrakakis
            raise ValueError("Unknown schema: %s" % schema)
704 46a07468 Christos Stavrakakis

705 525f2979 Ilias Tsitsimpis
        self.logger.debug("Upload schema files to server")
706 4c818bb2 Ilias Tsitsimpis
        _put(os.path.join(schema_dir, "*"), "/etc/snf-deploy/")
707 525f2979 Ilias Tsitsimpis

708 525f2979 Ilias Tsitsimpis
        self.logger.debug("Change password in nodes.conf file")
709 525f2979 Ilias Tsitsimpis
        cmd = """
710 525f2979 Ilias Tsitsimpis
        sed -i 's/^password =.*/password = {0}/' /etc/snf-deploy/nodes.conf
711 525f2979 Ilias Tsitsimpis
        """.format(fabric.env.password)
712 525f2979 Ilias Tsitsimpis
        _run(cmd, False)
713 525f2979 Ilias Tsitsimpis

714 525f2979 Ilias Tsitsimpis
        self.logger.debug("Run snf-deploy")
715 525f2979 Ilias Tsitsimpis
        cmd = """
716 87e2667b Ilias Tsitsimpis
        snf-deploy keygen --force
717 39dd21c2 Christos Stavrakakis
        snf-deploy --disable-colors --autoconf all
718 525f2979 Ilias Tsitsimpis
        """
719 525f2979 Ilias Tsitsimpis
        _run(cmd, True)
720 525f2979 Ilias Tsitsimpis

721 525f2979 Ilias Tsitsimpis
    @_check_fabric
722 525f2979 Ilias Tsitsimpis
    def unit_test(self):
723 525f2979 Ilias Tsitsimpis
        """Run Synnefo unit test suite"""
724 525f2979 Ilias Tsitsimpis
        self.logger.info("Run Synnefo unit test suite")
725 525f2979 Ilias Tsitsimpis
        component = self.config.get('Unit Tests', 'component')
726 525f2979 Ilias Tsitsimpis

727 525f2979 Ilias Tsitsimpis
        self.logger.debug("Install needed packages")
728 525f2979 Ilias Tsitsimpis
        cmd = """
729 525f2979 Ilias Tsitsimpis
        pip install mock
730 525f2979 Ilias Tsitsimpis
        pip install factory_boy
731 525f2979 Ilias Tsitsimpis
        """
732 525f2979 Ilias Tsitsimpis
        _run(cmd, False)
733 525f2979 Ilias Tsitsimpis

734 2255812d Ilias Tsitsimpis
        self.logger.debug("Upload tests.sh file")
735 2255812d Ilias Tsitsimpis
        unit_tests_file = os.path.join(self.ci_dir, "tests.sh")
736 4c818bb2 Ilias Tsitsimpis
        _put(unit_tests_file, ".")
737 525f2979 Ilias Tsitsimpis

738 525f2979 Ilias Tsitsimpis
        self.logger.debug("Run unit tests")
739 525f2979 Ilias Tsitsimpis
        cmd = """
740 2255812d Ilias Tsitsimpis
        bash tests.sh {0}
741 525f2979 Ilias Tsitsimpis
        """.format(component)
742 525f2979 Ilias Tsitsimpis
        _run(cmd, True)
743 525f2979 Ilias Tsitsimpis

744 525f2979 Ilias Tsitsimpis
    @_check_fabric
745 525f2979 Ilias Tsitsimpis
    def run_burnin(self):
746 525f2979 Ilias Tsitsimpis
        """Run burnin functional test suite"""
747 525f2979 Ilias Tsitsimpis
        self.logger.info("Run Burnin functional test suite")
748 525f2979 Ilias Tsitsimpis
        cmd = """
749 525f2979 Ilias Tsitsimpis
        auth_url=$(grep -e '^url =' .kamakirc | cut -d' ' -f3)
750 525f2979 Ilias Tsitsimpis
        token=$(grep -e '^token =' .kamakirc | cut -d' ' -f3)
751 525f2979 Ilias Tsitsimpis
        images_user=$(kamaki image list -l | grep owner | \
752 525f2979 Ilias Tsitsimpis
                      cut -d':' -f2 | tr -d ' ')
753 525f2979 Ilias Tsitsimpis
        snf-burnin --auth-url=$auth_url --token=$token \
754 525f2979 Ilias Tsitsimpis
            --force-flavor=2 --image-id=all \
755 525f2979 Ilias Tsitsimpis
            --system-images-user=$images_user \
756 525f2979 Ilias Tsitsimpis
            {0}
757 f8607bde Ilias Tsitsimpis
        BurninExitStatus=$?
758 525f2979 Ilias Tsitsimpis
        log_folder=$(ls -1d /var/log/burnin/* | tail -n1)
759 525f2979 Ilias Tsitsimpis
        for i in $(ls $log_folder/*/details*); do
760 525f2979 Ilias Tsitsimpis
            echo -e "\\n\\n"
761 525f2979 Ilias Tsitsimpis
            echo -e "***** $i\\n"
762 525f2979 Ilias Tsitsimpis
            cat $i
763 525f2979 Ilias Tsitsimpis
        done
764 f8607bde Ilias Tsitsimpis
        exit $BurninExitStatus
765 525f2979 Ilias Tsitsimpis
        """.format(self.config.get('Burnin', 'cmd_options'))
766 525f2979 Ilias Tsitsimpis
        _run(cmd, True)
767 525f2979 Ilias Tsitsimpis

768 525f2979 Ilias Tsitsimpis
    @_check_fabric
769 124c300e Christos Stavrakakis
    def fetch_compressed(self, src, dest=None):
770 33ad9a0d Ilias Tsitsimpis
        """Create a tarball and fetch it locally"""
771 124c300e Christos Stavrakakis
        self.logger.debug("Creating tarball of %s" % src)
772 124c300e Christos Stavrakakis
        basename = os.path.basename(src)
773 124c300e Christos Stavrakakis
        tar_file = basename + ".tgz"
774 124c300e Christos Stavrakakis
        cmd = "tar czf %s %s" % (tar_file, src)
775 525f2979 Ilias Tsitsimpis
        _run(cmd, False)
776 124c300e Christos Stavrakakis
        if not os.path.exists(dest):
777 124c300e Christos Stavrakakis
            os.makedirs(dest)
778 525f2979 Ilias Tsitsimpis

779 124c300e Christos Stavrakakis
        tmp_dir = tempfile.mkdtemp()
780 124c300e Christos Stavrakakis
        fabric.get(tar_file, tmp_dir)
781 525f2979 Ilias Tsitsimpis

782 124c300e Christos Stavrakakis
        dest_file = os.path.join(tmp_dir, tar_file)
783 124c300e Christos Stavrakakis
        self._check_hash_sum(dest_file, tar_file)
784 124c300e Christos Stavrakakis
        self.logger.debug("Untar packages file %s" % dest_file)
785 ee2419c5 Christos Stavrakakis
        cmd = """
786 ee2419c5 Christos Stavrakakis
        cd %s
787 124c300e Christos Stavrakakis
        tar xzf %s
788 124c300e Christos Stavrakakis
        cp -r %s/* %s
789 124c300e Christos Stavrakakis
        rm -r %s
790 124c300e Christos Stavrakakis
        """ % (tmp_dir, tar_file, src, dest, tmp_dir)
791 ee2419c5 Christos Stavrakakis
        os.system(cmd)
792 124c300e Christos Stavrakakis
        self.logger.info("Downloaded %s to %s" %
793 124c300e Christos Stavrakakis
                         (src, _green(dest)))
794 124c300e Christos Stavrakakis

795 124c300e Christos Stavrakakis
    @_check_fabric
796 124c300e Christos Stavrakakis
    def fetch_packages(self, dest=None):
797 33ad9a0d Ilias Tsitsimpis
        """Fetch Synnefo packages"""
798 124c300e Christos Stavrakakis
        if dest is None:
799 124c300e Christos Stavrakakis
            dest = self.config.get('Global', 'pkgs_dir')
800 8baa4ae7 Ilias Tsitsimpis
        dest = os.path.abspath(os.path.expanduser(dest))
801 124c300e Christos Stavrakakis
        if not os.path.exists(dest):
802 124c300e Christos Stavrakakis
            os.makedirs(dest)
803 124c300e Christos Stavrakakis
        self.fetch_compressed("synnefo_build-area", dest)
804 85bcc8da Christos Stavrakakis
        self.logger.info("Downloaded debian packages to %s" %
805 124c300e Christos Stavrakakis
                         _green(dest))
806 fe402df2 Christos Stavrakakis

807 e213881d Ilias Tsitsimpis
    def x2go_plugin(self, dest=None):
808 e213881d Ilias Tsitsimpis
        """Produce an html page which will use the x2goplugin
809 e213881d Ilias Tsitsimpis
810 e213881d Ilias Tsitsimpis
        Arguments:
811 e213881d Ilias Tsitsimpis
          dest  -- The file where to save the page (String)
812 e213881d Ilias Tsitsimpis
813 e213881d Ilias Tsitsimpis
        """
814 e213881d Ilias Tsitsimpis
        output_str = """
815 e213881d Ilias Tsitsimpis
        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
816 e213881d Ilias Tsitsimpis
        <html>
817 e213881d Ilias Tsitsimpis
        <head>
818 e213881d Ilias Tsitsimpis
        <title>X2Go SynnefoCI Service</title>
819 e213881d Ilias Tsitsimpis
        </head>
820 e213881d Ilias Tsitsimpis
        <body onload="checkPlugin()">
821 e213881d Ilias Tsitsimpis
        <div id="x2goplugin">
822 e213881d Ilias Tsitsimpis
            <object
823 e213881d Ilias Tsitsimpis
                src="location"
824 e213881d Ilias Tsitsimpis
                type="application/x2go"
825 e213881d Ilias Tsitsimpis
                name="x2goplugin"
826 e213881d Ilias Tsitsimpis
                palette="background"
827 e213881d Ilias Tsitsimpis
                height="100%"
828 e213881d Ilias Tsitsimpis
                hspace="0"
829 e213881d Ilias Tsitsimpis
                vspace="0"
830 e213881d Ilias Tsitsimpis
                width="100%"
831 e213881d Ilias Tsitsimpis
                x2goconfig="
832 e213881d Ilias Tsitsimpis
                    session=X2Go-SynnefoCI-Session
833 e213881d Ilias Tsitsimpis
                    server={0}
834 e213881d Ilias Tsitsimpis
                    user={1}
835 e213881d Ilias Tsitsimpis
                    sshport={2}
836 e213881d Ilias Tsitsimpis
                    published=true
837 e213881d Ilias Tsitsimpis
                    autologin=true
838 e213881d Ilias Tsitsimpis
                ">
839 e213881d Ilias Tsitsimpis
            </object>
840 e213881d Ilias Tsitsimpis
        </div>
841 e213881d Ilias Tsitsimpis
        </body>
842 e213881d Ilias Tsitsimpis
        </html>
843 e213881d Ilias Tsitsimpis
        """.format(self.read_temp_config('server_ip'),
844 e213881d Ilias Tsitsimpis
                   self.read_temp_config('server_user'),
845 e213881d Ilias Tsitsimpis
                   self.read_temp_config('server_port'))
846 e213881d Ilias Tsitsimpis
        if dest is None:
847 e213881d Ilias Tsitsimpis
            dest = self.config.get('Global', 'x2go_plugin_file')
848 e213881d Ilias Tsitsimpis

849 e213881d Ilias Tsitsimpis
        self.logger.info("Writting x2go plugin html file to %s" % dest)
850 e213881d Ilias Tsitsimpis
        fid = open(dest, 'w')
851 e213881d Ilias Tsitsimpis
        fid.write(output_str)
852 e213881d Ilias Tsitsimpis
        fid.close()
853 e213881d Ilias Tsitsimpis

854 fe402df2 Christos Stavrakakis

855 fe402df2 Christos Stavrakakis
def parse_typed_option(option, value):
856 0373e3cf Ilias Tsitsimpis
    """Parsed typed options (flavors and images)"""
857 fe402df2 Christos Stavrakakis
    try:
858 fe402df2 Christos Stavrakakis
        [type_, val] = value.strip().split(':')
859 fe402df2 Christos Stavrakakis
        if type_ not in ["id", "name"]:
860 fe402df2 Christos Stavrakakis
            raise ValueError
861 fe402df2 Christos Stavrakakis
        return type_, val
862 fe402df2 Christos Stavrakakis
    except ValueError:
863 fe402df2 Christos Stavrakakis
        msg = "Invalid %s format. Must be [id|name]:.+" % option
864 fe402df2 Christos Stavrakakis
        raise ValueError(msg)