Statistics
| Branch: | Tag: | Revision:

root / ci / utils.py @ 329705c8

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

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

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

304 6c3cc77e Ilias Tsitsimpis
        # X2GO Key
305 6c3cc77e Ilias Tsitsimpis
        apt-key adv --recv-keys --keyserver keys.gnupg.net E1F958385BFE2B6E
306 71f2649f Ilias Tsitsimpis
        apt-get install x2go-keyring --yes --force-yes
307 6c3cc77e Ilias Tsitsimpis
        apt-get update
308 71f2649f Ilias Tsitsimpis
        apt-get install x2goserver x2goserver-xsession \
309 71f2649f Ilias Tsitsimpis
                iceweasel --yes --force-yes
310 8ddb541b Ilias Tsitsimpis

311 8ddb541b Ilias Tsitsimpis
        # xterm published application
312 8ddb541b Ilias Tsitsimpis
        echo '[Desktop Entry]' > /usr/share/applications/xterm.desktop
313 8ddb541b Ilias Tsitsimpis
        echo 'Name=XTerm' >> /usr/share/applications/xterm.desktop
314 8ddb541b Ilias Tsitsimpis
        echo 'Comment=standard terminal emulator for the X window system' >> \
315 8ddb541b Ilias Tsitsimpis
            /usr/share/applications/xterm.desktop
316 8ddb541b Ilias Tsitsimpis
        echo 'Exec=xterm' >> /usr/share/applications/xterm.desktop
317 8ddb541b Ilias Tsitsimpis
        echo 'Terminal=false' >> /usr/share/applications/xterm.desktop
318 8ddb541b Ilias Tsitsimpis
        echo 'Type=Application' >> /usr/share/applications/xterm.desktop
319 8ddb541b Ilias Tsitsimpis
        echo 'Encoding=UTF-8' >> /usr/share/applications/xterm.desktop
320 8ddb541b Ilias Tsitsimpis
        echo 'Icon=xterm-color_48x48' >> /usr/share/applications/xterm.desktop
321 8ddb541b Ilias Tsitsimpis
        echo 'Categories=System;TerminalEmulator;' >> \
322 8ddb541b Ilias Tsitsimpis
                /usr/share/applications/xterm.desktop
323 6c3cc77e Ilias Tsitsimpis
        """.format(self.config.get('Global', 'apt_repo'))
324 6c3cc77e Ilias Tsitsimpis
        _run(cmd, False)
325 6c3cc77e Ilias Tsitsimpis
326 358a19bc Ilias Tsitsimpis
    def _find_flavor(self, flavor=None):
327 358a19bc Ilias Tsitsimpis
        """Find a suitable flavor to use
328 358a19bc Ilias Tsitsimpis

329 358a19bc Ilias Tsitsimpis
        Search by name (reg expression) or by id
330 358a19bc Ilias Tsitsimpis
        """
331 358a19bc Ilias Tsitsimpis
        # Get a list of flavors from config file
332 358a19bc Ilias Tsitsimpis
        flavors = self.config.get('Deployment', 'flavors').split(",")
333 358a19bc Ilias Tsitsimpis
        if flavor is not None:
334 a8bfeffc Ilias Tsitsimpis
            # If we have a flavor_name to use, add it to our list
335 358a19bc Ilias Tsitsimpis
            flavors.insert(0, flavor)
336 358a19bc Ilias Tsitsimpis
337 358a19bc Ilias Tsitsimpis
        list_flavors = self.compute_client.list_flavors()
338 358a19bc Ilias Tsitsimpis
        for flv in flavors:
339 905bb10b Christos Stavrakakis
            flv_type, flv_value = parse_typed_option(option="flavor",
340 905bb10b Christos Stavrakakis
                                                     value=flv)
341 358a19bc Ilias Tsitsimpis
            if flv_type == "name":
342 358a19bc Ilias Tsitsimpis
                # Filter flavors by name
343 358a19bc Ilias Tsitsimpis
                self.logger.debug(
344 358a19bc Ilias Tsitsimpis
                    "Trying to find a flavor with name \"%s\"" % flv_value)
345 358a19bc Ilias Tsitsimpis
                list_flvs = \
346 358a19bc Ilias Tsitsimpis
                    [f for f in list_flavors
347 905bb10b Christos Stavrakakis
                     if re.search(flv_value, f['name'], flags=re.I)
348 905bb10b Christos Stavrakakis
                     is not None]
349 358a19bc Ilias Tsitsimpis
            elif flv_type == "id":
350 358a19bc Ilias Tsitsimpis
                # Filter flavors by id
351 358a19bc Ilias Tsitsimpis
                self.logger.debug(
352 358a19bc Ilias Tsitsimpis
                    "Trying to find a flavor with id \"%s\"" % flv_value)
353 358a19bc Ilias Tsitsimpis
                list_flvs = \
354 358a19bc Ilias Tsitsimpis
                    [f for f in list_flavors
355 694c79bb Ilias Tsitsimpis
                     if str(f['id']) == flv_value]
356 358a19bc Ilias Tsitsimpis
            else:
357 358a19bc Ilias Tsitsimpis
                self.logger.error("Unrecognized flavor type %s" % flv_type)
358 358a19bc Ilias Tsitsimpis
359 358a19bc Ilias Tsitsimpis
            # Check if we found one
360 358a19bc Ilias Tsitsimpis
            if list_flvs:
361 358a19bc Ilias Tsitsimpis
                self.logger.debug("Will use \"%s\" with id \"%s\""
362 358a19bc Ilias Tsitsimpis
                                  % (list_flvs[0]['name'], list_flvs[0]['id']))
363 358a19bc Ilias Tsitsimpis
                return list_flvs[0]['id']
364 a8bfeffc Ilias Tsitsimpis
365 a8bfeffc Ilias Tsitsimpis
        self.logger.error("No matching flavor found.. aborting")
366 a8bfeffc Ilias Tsitsimpis
        sys.exit(1)
367 ad21ffa9 Ilias Tsitsimpis
368 358a19bc Ilias Tsitsimpis
    def _find_image(self, image=None):
369 c29ac11d Ilias Tsitsimpis
        """Find a suitable image to use
370 c29ac11d Ilias Tsitsimpis

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

665 1c230fd8 Ilias Tsitsimpis
        # Build synnefo packages
666 1c230fd8 Ilias Tsitsimpis
        self.logger.debug("Build synnefo packages")
667 c29ac11d Ilias Tsitsimpis
        cmd = """
668 1c230fd8 Ilias Tsitsimpis
        devflow-autopkg snapshot -b ~/synnefo_build-area --no-sign
669 c29ac11d Ilias Tsitsimpis
        """
670 1c230fd8 Ilias Tsitsimpis
        with fabric.cd("synnefo"):
671 c29ac11d Ilias Tsitsimpis
            _run(cmd, True)
672 c29ac11d Ilias Tsitsimpis

673 1c230fd8 Ilias Tsitsimpis
        # Install snf-deploy package
674 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Install snf-deploy package")
675 c29ac11d Ilias Tsitsimpis
        cmd = """
676 c29ac11d Ilias Tsitsimpis
        dpkg -i snf-deploy*.deb
677 71f2649f Ilias Tsitsimpis
        apt-get -f install --yes --force-yes
678 c29ac11d Ilias Tsitsimpis
        """
679 1c230fd8 Ilias Tsitsimpis
        with fabric.cd("synnefo_build-area"):
680 c29ac11d Ilias Tsitsimpis
            with fabric.settings(warn_only=True):
681 c29ac11d Ilias Tsitsimpis
                _run(cmd, True)
682 c29ac11d Ilias Tsitsimpis

683 1c230fd8 Ilias Tsitsimpis
        # Setup synnefo packages for snf-deploy
684 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Copy synnefo debs to snf-deploy packages dir")
685 c29ac11d Ilias Tsitsimpis
        cmd = """
686 c29ac11d Ilias Tsitsimpis
        cp ~/synnefo_build-area/*.deb /var/lib/snf-deploy/packages/
687 c29ac11d Ilias Tsitsimpis
        """
688 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
689 c29ac11d Ilias Tsitsimpis

690 88e6558b Christos Stavrakakis
    @_check_fabric
691 88e6558b Christos Stavrakakis
    def build_documentation(self):
692 e2a0abb8 Ilias Tsitsimpis
        """Build Synnefo documentation"""
693 88e6558b Christos Stavrakakis
        self.logger.info("Build Synnefo documentation..")
694 88e6558b Christos Stavrakakis
        _run("pip install -U Sphinx", False)
695 88e6558b Christos Stavrakakis
        with fabric.cd("synnefo"):
696 e2a0abb8 Ilias Tsitsimpis
            _run("devflow-update-version; "
697 e2a0abb8 Ilias Tsitsimpis
                 "./ci/make_docs.sh synnefo_documentation", False)
698 88e6558b Christos Stavrakakis

699 88e6558b Christos Stavrakakis
    def fetch_documentation(self, dest=None):
700 e2a0abb8 Ilias Tsitsimpis
        """Fetch Synnefo documentation"""
701 e2a0abb8 Ilias Tsitsimpis
        self.logger.info("Fetch Synnefo documentation..")
702 88e6558b Christos Stavrakakis
        if dest is None:
703 88e6558b Christos Stavrakakis
            dest = "synnefo_documentation"
704 88e6558b Christos Stavrakakis
        dest = os.path.abspath(dest)
705 88e6558b Christos Stavrakakis
        if not os.path.exists(dest):
706 88e6558b Christos Stavrakakis
            os.makedirs(dest)
707 88e6558b Christos Stavrakakis
        self.fetch_compressed("synnefo/synnefo_documentation", dest)
708 88e6558b Christos Stavrakakis
        self.logger.info("Downloaded documentation to %s" %
709 88e6558b Christos Stavrakakis
                         _green(dest))
710 88e6558b Christos Stavrakakis

711 c29ac11d Ilias Tsitsimpis
    @_check_fabric
712 ec8bc030 Christos Stavrakakis
    def deploy_synnefo(self, schema=None):
713 c29ac11d Ilias Tsitsimpis
        """Deploy Synnefo using snf-deploy"""
714 c29ac11d Ilias Tsitsimpis
        self.logger.info("Deploy Synnefo..")
715 ec8bc030 Christos Stavrakakis
        if schema is None:
716 ec8bc030 Christos Stavrakakis
            schema = self.config.get('Global', 'schema')
717 358a19bc Ilias Tsitsimpis
        self.logger.debug("Will use \"%s\" schema" % schema)
718 c29ac11d Ilias Tsitsimpis

719 ec8bc030 Christos Stavrakakis
        schema_dir = os.path.join(self.ci_dir, "schemas/%s" % schema)
720 ec8bc030 Christos Stavrakakis
        if not (os.path.exists(schema_dir) and os.path.isdir(schema_dir)):
721 ec8bc030 Christos Stavrakakis
            raise ValueError("Unknown schema: %s" % schema)
722 ec8bc030 Christos Stavrakakis

723 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Upload schema files to server")
724 6868804a Ilias Tsitsimpis
        _put(os.path.join(schema_dir, "*"), "/etc/snf-deploy/")
725 c29ac11d Ilias Tsitsimpis

726 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Change password in nodes.conf file")
727 c29ac11d Ilias Tsitsimpis
        cmd = """
728 c29ac11d Ilias Tsitsimpis
        sed -i 's/^password =.*/password = {0}/' /etc/snf-deploy/nodes.conf
729 c29ac11d Ilias Tsitsimpis
        """.format(fabric.env.password)
730 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
731 c29ac11d Ilias Tsitsimpis

732 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Run snf-deploy")
733 c29ac11d Ilias Tsitsimpis
        cmd = """
734 985442f5 Ilias Tsitsimpis
        snf-deploy keygen --force
735 f82ce32a Christos Stavrakakis
        snf-deploy --disable-colors --autoconf all
736 c29ac11d Ilias Tsitsimpis
        """
737 c29ac11d Ilias Tsitsimpis
        _run(cmd, True)
738 c29ac11d Ilias Tsitsimpis

739 c29ac11d Ilias Tsitsimpis
    @_check_fabric
740 c29ac11d Ilias Tsitsimpis
    def unit_test(self):
741 c29ac11d Ilias Tsitsimpis
        """Run Synnefo unit test suite"""
742 c29ac11d Ilias Tsitsimpis
        self.logger.info("Run Synnefo unit test suite")
743 c29ac11d Ilias Tsitsimpis
        component = self.config.get('Unit Tests', 'component')
744 c29ac11d Ilias Tsitsimpis

745 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Install needed packages")
746 c29ac11d Ilias Tsitsimpis
        cmd = """
747 8d2c08e4 Christos Stavrakakis
        pip install -U mock
748 8d2c08e4 Christos Stavrakakis
        pip install -U factory_boy
749 c29ac11d Ilias Tsitsimpis
        """
750 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
751 c29ac11d Ilias Tsitsimpis

752 861b437e Ilias Tsitsimpis
        self.logger.debug("Upload tests.sh file")
753 861b437e Ilias Tsitsimpis
        unit_tests_file = os.path.join(self.ci_dir, "tests.sh")
754 6868804a Ilias Tsitsimpis
        _put(unit_tests_file, ".")
755 c29ac11d Ilias Tsitsimpis

756 c29ac11d Ilias Tsitsimpis
        self.logger.debug("Run unit tests")
757 c29ac11d Ilias Tsitsimpis
        cmd = """
758 861b437e Ilias Tsitsimpis
        bash tests.sh {0}
759 c29ac11d Ilias Tsitsimpis
        """.format(component)
760 c29ac11d Ilias Tsitsimpis
        _run(cmd, True)
761 c29ac11d Ilias Tsitsimpis

762 c29ac11d Ilias Tsitsimpis
    @_check_fabric
763 c29ac11d Ilias Tsitsimpis
    def run_burnin(self):
764 c29ac11d Ilias Tsitsimpis
        """Run burnin functional test suite"""
765 c29ac11d Ilias Tsitsimpis
        self.logger.info("Run Burnin functional test suite")
766 c29ac11d Ilias Tsitsimpis
        cmd = """
767 c29ac11d Ilias Tsitsimpis
        auth_url=$(grep -e '^url =' .kamakirc | cut -d' ' -f3)
768 c29ac11d Ilias Tsitsimpis
        token=$(grep -e '^token =' .kamakirc | cut -d' ' -f3)
769 c29ac11d Ilias Tsitsimpis
        images_user=$(kamaki image list -l | grep owner | \
770 c29ac11d Ilias Tsitsimpis
                      cut -d':' -f2 | tr -d ' ')
771 c29ac11d Ilias Tsitsimpis
        snf-burnin --auth-url=$auth_url --token=$token \
772 c29ac11d Ilias Tsitsimpis
            --force-flavor=2 --image-id=all \
773 c29ac11d Ilias Tsitsimpis
            --system-images-user=$images_user \
774 c29ac11d Ilias Tsitsimpis
            {0}
775 281c7634 Ilias Tsitsimpis
        BurninExitStatus=$?
776 c29ac11d Ilias Tsitsimpis
        log_folder=$(ls -1d /var/log/burnin/* | tail -n1)
777 c29ac11d Ilias Tsitsimpis
        for i in $(ls $log_folder/*/details*); do
778 c29ac11d Ilias Tsitsimpis
            echo -e "\\n\\n"
779 c29ac11d Ilias Tsitsimpis
            echo -e "***** $i\\n"
780 c29ac11d Ilias Tsitsimpis
            cat $i
781 c29ac11d Ilias Tsitsimpis
        done
782 281c7634 Ilias Tsitsimpis
        exit $BurninExitStatus
783 c29ac11d Ilias Tsitsimpis
        """.format(self.config.get('Burnin', 'cmd_options'))
784 c29ac11d Ilias Tsitsimpis
        _run(cmd, True)
785 c29ac11d Ilias Tsitsimpis

786 c29ac11d Ilias Tsitsimpis
    @_check_fabric
787 88e6558b Christos Stavrakakis
    def fetch_compressed(self, src, dest=None):
788 e2a0abb8 Ilias Tsitsimpis
        """Create a tarball and fetch it locally"""
789 88e6558b Christos Stavrakakis
        self.logger.debug("Creating tarball of %s" % src)
790 88e6558b Christos Stavrakakis
        basename = os.path.basename(src)
791 88e6558b Christos Stavrakakis
        tar_file = basename + ".tgz"
792 88e6558b Christos Stavrakakis
        cmd = "tar czf %s %s" % (tar_file, src)
793 c29ac11d Ilias Tsitsimpis
        _run(cmd, False)
794 88e6558b Christos Stavrakakis
        if not os.path.exists(dest):
795 88e6558b Christos Stavrakakis
            os.makedirs(dest)
796 c29ac11d Ilias Tsitsimpis

797 88e6558b Christos Stavrakakis
        tmp_dir = tempfile.mkdtemp()
798 88e6558b Christos Stavrakakis
        fabric.get(tar_file, tmp_dir)
799 c29ac11d Ilias Tsitsimpis

800 88e6558b Christos Stavrakakis
        dest_file = os.path.join(tmp_dir, tar_file)
801 88e6558b Christos Stavrakakis
        self._check_hash_sum(dest_file, tar_file)
802 88e6558b Christos Stavrakakis
        self.logger.debug("Untar packages file %s" % dest_file)
803 e2dc37b2 Christos Stavrakakis
        cmd = """
804 e2dc37b2 Christos Stavrakakis
        cd %s
805 88e6558b Christos Stavrakakis
        tar xzf %s
806 88e6558b Christos Stavrakakis
        cp -r %s/* %s
807 88e6558b Christos Stavrakakis
        rm -r %s
808 88e6558b Christos Stavrakakis
        """ % (tmp_dir, tar_file, src, dest, tmp_dir)
809 e2dc37b2 Christos Stavrakakis
        os.system(cmd)
810 88e6558b Christos Stavrakakis
        self.logger.info("Downloaded %s to %s" %
811 88e6558b Christos Stavrakakis
                         (src, _green(dest)))
812 88e6558b Christos Stavrakakis

813 88e6558b Christos Stavrakakis
    @_check_fabric
814 88e6558b Christos Stavrakakis
    def fetch_packages(self, dest=None):
815 e2a0abb8 Ilias Tsitsimpis
        """Fetch Synnefo packages"""
816 88e6558b Christos Stavrakakis
        if dest is None:
817 88e6558b Christos Stavrakakis
            dest = self.config.get('Global', 'pkgs_dir')
818 6058ae72 Ilias Tsitsimpis
        dest = os.path.abspath(os.path.expanduser(dest))
819 88e6558b Christos Stavrakakis
        if not os.path.exists(dest):
820 88e6558b Christos Stavrakakis
            os.makedirs(dest)
821 88e6558b Christos Stavrakakis
        self.fetch_compressed("synnefo_build-area", dest)
822 464e58e9 Christos Stavrakakis
        self.logger.info("Downloaded debian packages to %s" %
823 88e6558b Christos Stavrakakis
                         _green(dest))
824 6958ffc6 Christos Stavrakakis

825 cf99ca80 Ilias Tsitsimpis
    def x2go_plugin(self, dest=None):
826 cf99ca80 Ilias Tsitsimpis
        """Produce an html page which will use the x2goplugin
827 cf99ca80 Ilias Tsitsimpis
828 cf99ca80 Ilias Tsitsimpis
        Arguments:
829 cf99ca80 Ilias Tsitsimpis
          dest  -- The file where to save the page (String)
830 cf99ca80 Ilias Tsitsimpis
831 cf99ca80 Ilias Tsitsimpis
        """
832 cf99ca80 Ilias Tsitsimpis
        output_str = """
833 cf99ca80 Ilias Tsitsimpis
        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
834 cf99ca80 Ilias Tsitsimpis
        <html>
835 cf99ca80 Ilias Tsitsimpis
        <head>
836 cf99ca80 Ilias Tsitsimpis
        <title>X2Go SynnefoCI Service</title>
837 cf99ca80 Ilias Tsitsimpis
        </head>
838 cf99ca80 Ilias Tsitsimpis
        <body onload="checkPlugin()">
839 cf99ca80 Ilias Tsitsimpis
        <div id="x2goplugin">
840 cf99ca80 Ilias Tsitsimpis
            <object
841 cf99ca80 Ilias Tsitsimpis
                src="location"
842 cf99ca80 Ilias Tsitsimpis
                type="application/x2go"
843 cf99ca80 Ilias Tsitsimpis
                name="x2goplugin"
844 cf99ca80 Ilias Tsitsimpis
                palette="background"
845 cf99ca80 Ilias Tsitsimpis
                height="100%"
846 cf99ca80 Ilias Tsitsimpis
                hspace="0"
847 cf99ca80 Ilias Tsitsimpis
                vspace="0"
848 cf99ca80 Ilias Tsitsimpis
                width="100%"
849 cf99ca80 Ilias Tsitsimpis
                x2goconfig="
850 cf99ca80 Ilias Tsitsimpis
                    session=X2Go-SynnefoCI-Session
851 cf99ca80 Ilias Tsitsimpis
                    server={0}
852 cf99ca80 Ilias Tsitsimpis
                    user={1}
853 cf99ca80 Ilias Tsitsimpis
                    sshport={2}
854 cf99ca80 Ilias Tsitsimpis
                    published=true
855 cf99ca80 Ilias Tsitsimpis
                    autologin=true
856 cf99ca80 Ilias Tsitsimpis
                ">
857 cf99ca80 Ilias Tsitsimpis
            </object>
858 cf99ca80 Ilias Tsitsimpis
        </div>
859 cf99ca80 Ilias Tsitsimpis
        </body>
860 cf99ca80 Ilias Tsitsimpis
        </html>
861 cf99ca80 Ilias Tsitsimpis
        """.format(self.read_temp_config('server_ip'),
862 cf99ca80 Ilias Tsitsimpis
                   self.read_temp_config('server_user'),
863 cf99ca80 Ilias Tsitsimpis
                   self.read_temp_config('server_port'))
864 cf99ca80 Ilias Tsitsimpis
        if dest is None:
865 cf99ca80 Ilias Tsitsimpis
            dest = self.config.get('Global', 'x2go_plugin_file')
866 cf99ca80 Ilias Tsitsimpis

867 cf99ca80 Ilias Tsitsimpis
        self.logger.info("Writting x2go plugin html file to %s" % dest)
868 cf99ca80 Ilias Tsitsimpis
        fid = open(dest, 'w')
869 cf99ca80 Ilias Tsitsimpis
        fid.write(output_str)
870 cf99ca80 Ilias Tsitsimpis
        fid.close()
871 cf99ca80 Ilias Tsitsimpis

872 6958ffc6 Christos Stavrakakis

873 6958ffc6 Christos Stavrakakis
def parse_typed_option(option, value):
874 694c79bb Ilias Tsitsimpis
    """Parsed typed options (flavors and images)"""
875 6958ffc6 Christos Stavrakakis
    try:
876 6958ffc6 Christos Stavrakakis
        [type_, val] = value.strip().split(':')
877 6958ffc6 Christos Stavrakakis
        if type_ not in ["id", "name"]:
878 6958ffc6 Christos Stavrakakis
            raise ValueError
879 6958ffc6 Christos Stavrakakis
        return type_, val
880 6958ffc6 Christos Stavrakakis
    except ValueError:
881 6958ffc6 Christos Stavrakakis
        msg = "Invalid %s format. Must be [id|name]:.+" % option
882 6958ffc6 Christos Stavrakakis
        raise ValueError(msg)