Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli.py @ c4922a05

History | View | Annotate | Download (31.5 kB)

1 5d1d131b Giorgos Verigakis
#!/usr/bin/env python
2 5d1d131b Giorgos Verigakis
3 43ca98ee Giorgos Verigakis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
4 5d1d131b Giorgos Verigakis
#
5 5d1d131b Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
6 5d1d131b Giorgos Verigakis
# without modification, are permitted provided that the following
7 5d1d131b Giorgos Verigakis
# conditions are met:
8 5d1d131b Giorgos Verigakis
#
9 5d1d131b Giorgos Verigakis
#   1. Redistributions of source code must retain the above
10 5d1d131b Giorgos Verigakis
#      copyright notice, this list of conditions and the following
11 5d1d131b Giorgos Verigakis
#      disclaimer.
12 5d1d131b Giorgos Verigakis
#
13 5d1d131b Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
14 5d1d131b Giorgos Verigakis
#      copyright notice, this list of conditions and the following
15 5d1d131b Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
16 5d1d131b Giorgos Verigakis
#      provided with the distribution.
17 5d1d131b Giorgos Verigakis
#
18 5d1d131b Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 5d1d131b Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 5d1d131b Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 5d1d131b Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 5d1d131b Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 5d1d131b Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 5d1d131b Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 5d1d131b Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 5d1d131b Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 5d1d131b Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 5d1d131b Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 5d1d131b Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
30 5d1d131b Giorgos Verigakis
#
31 5d1d131b Giorgos Verigakis
# The views and conclusions contained in the software and
32 5d1d131b Giorgos Verigakis
# documentation are those of the authors and should not be
33 5d1d131b Giorgos Verigakis
# interpreted as representing official policies, either expressed
34 5d1d131b Giorgos Verigakis
# or implied, of GRNET S.A.
35 5d1d131b Giorgos Verigakis
36 eb3ca8ca Giorgos Verigakis
"""
37 eb3ca8ca Giorgos Verigakis
To add a command create a new class and add a 'command' decorator. The class
38 eb3ca8ca Giorgos Verigakis
must have a 'main' method which will contain the code to be executed.
39 eb3ca8ca Giorgos Verigakis
Optionally a command can implement an 'update_parser' class method in order
40 eb3ca8ca Giorgos Verigakis
to add command line arguments, or modify the OptionParser in any way.
41 eb3ca8ca Giorgos Verigakis

42 eb3ca8ca Giorgos Verigakis
The name of the class is important and it will determine the name and grouping
43 eb3ca8ca Giorgos Verigakis
of the command. This behavior can be overriden with the 'group' and 'name'
44 eb3ca8ca Giorgos Verigakis
decorator arguments:
45 eb3ca8ca Giorgos Verigakis

46 a1c50326 Giorgos Verigakis
    @command(api='compute')
47 eb3ca8ca Giorgos Verigakis
    class server_list(object):
48 eb3ca8ca Giorgos Verigakis
        # This command will be named 'list' under group 'server'
49 eb3ca8ca Giorgos Verigakis
        ...
50 eb3ca8ca Giorgos Verigakis

51 a1c50326 Giorgos Verigakis
    @command(api='compute', name='ls')
52 eb3ca8ca Giorgos Verigakis
    class server_list(object):
53 eb3ca8ca Giorgos Verigakis
        # This command will be named 'ls' under group 'server'
54 eb3ca8ca Giorgos Verigakis
        ...
55 eb3ca8ca Giorgos Verigakis

56 eb3ca8ca Giorgos Verigakis
The docstring of a command class will be used as the command description in
57 eb3ca8ca Giorgos Verigakis
help messages, unless overriden with the 'description' decorator argument.
58 eb3ca8ca Giorgos Verigakis

59 eb3ca8ca Giorgos Verigakis
The syntax of a command will be generated dynamically based on the signature
60 eb3ca8ca Giorgos Verigakis
of the 'main' method, unless overriden with the 'syntax' decorator argument:
61 eb3ca8ca Giorgos Verigakis

62 eb3ca8ca Giorgos Verigakis
    def main(self, server_id, network=None):
63 eb3ca8ca Giorgos Verigakis
        # This syntax of this command will be: '<server id> [network]'
64 eb3ca8ca Giorgos Verigakis
        ...
65 eb3ca8ca Giorgos Verigakis

66 eb3ca8ca Giorgos Verigakis
The order of commands is important, it will be preserved in the help output.
67 eb3ca8ca Giorgos Verigakis
"""
68 eb3ca8ca Giorgos Verigakis
69 5d1d131b Giorgos Verigakis
import inspect
70 5d1d131b Giorgos Verigakis
import logging
71 5d1d131b Giorgos Verigakis
import os
72 5d1d131b Giorgos Verigakis
73 57b8dd5a Giorgos Verigakis
from base64 import b64encode
74 57b8dd5a Giorgos Verigakis
from grp import getgrgid
75 5d1d131b Giorgos Verigakis
from optparse import OptionParser
76 f3ddb705 Giorgos Verigakis
from os.path import abspath, basename, exists, expanduser
77 57b8dd5a Giorgos Verigakis
from pwd import getpwuid
78 176894c1 Giorgos Verigakis
from sys import argv, exit, stdout
79 5d1d131b Giorgos Verigakis
80 6a0b1658 Giorgos Verigakis
from clint import args
81 6a0b1658 Giorgos Verigakis
from clint.textui import puts, puts_err, indent, progress
82 6a0b1658 Giorgos Verigakis
from clint.textui.colored import magenta, red, yellow
83 f3ddb705 Giorgos Verigakis
from clint.textui.cols import columns
84 f3ddb705 Giorgos Verigakis
85 6a0b1658 Giorgos Verigakis
from requests.exceptions import ConnectionError
86 6a0b1658 Giorgos Verigakis
87 a1c50326 Giorgos Verigakis
from kamaki import clients
88 f3ddb705 Giorgos Verigakis
from kamaki.config import Config
89 614b80ce Giorgos Verigakis
from kamaki.utils import OrderedDict, print_addresses, print_dict, print_items
90 5d1d131b Giorgos Verigakis
91 5d1d131b Giorgos Verigakis
92 f3ddb705 Giorgos Verigakis
# Path to the file that stores the configuration
93 f3ddb705 Giorgos Verigakis
CONFIG_PATH = expanduser('~/.kamakirc')
94 f3ddb705 Giorgos Verigakis
95 f3ddb705 Giorgos Verigakis
# Name of a shell variable to bypass the CONFIG_PATH value
96 f3ddb705 Giorgos Verigakis
CONFIG_ENV = 'KAMAKI_CONFIG'
97 f3ddb705 Giorgos Verigakis
98 653b0597 Giorgos Verigakis
99 eb3ca8ca Giorgos Verigakis
_commands = OrderedDict()
100 eb3ca8ca Giorgos Verigakis
101 653b0597 Giorgos Verigakis
102 f3ddb705 Giorgos Verigakis
GROUPS = {
103 f3ddb705 Giorgos Verigakis
    'config': "Configuration commands",
104 f3ddb705 Giorgos Verigakis
    'server': "Compute API server commands",
105 f3ddb705 Giorgos Verigakis
    'flavor': "Compute API flavor commands",
106 cb813272 Giorgos Verigakis
    'image': "Compute or Glance API image commands",
107 f3ddb705 Giorgos Verigakis
    'network': "Compute API network commands (Cyclades extension)",
108 43ca98ee Giorgos Verigakis
    'store': "Storage API commands",
109 43ca98ee Giorgos Verigakis
    'astakos': "Astakos API commands"}
110 f3ddb705 Giorgos Verigakis
111 f3ddb705 Giorgos Verigakis
112 f3ddb705 Giorgos Verigakis
def command(api=None, group=None, name=None, syntax=None):
113 eb3ca8ca Giorgos Verigakis
    """Class decorator that registers a class as a CLI command."""
114 eb3ca8ca Giorgos Verigakis
    
115 eb3ca8ca Giorgos Verigakis
    def decorator(cls):
116 eb3ca8ca Giorgos Verigakis
        grp, sep, cmd = cls.__name__.partition('_')
117 eb3ca8ca Giorgos Verigakis
        if not sep:
118 eb3ca8ca Giorgos Verigakis
            grp, cmd = None, cls.__name__
119 5d1d131b Giorgos Verigakis
        
120 eb3ca8ca Giorgos Verigakis
        cls.api = api
121 eb3ca8ca Giorgos Verigakis
        cls.group = group or grp
122 eb3ca8ca Giorgos Verigakis
        cls.name = name or cmd
123 5d1d131b Giorgos Verigakis
        
124 f3ddb705 Giorgos Verigakis
        short_description, sep, long_description = cls.__doc__.partition('\n')
125 f3ddb705 Giorgos Verigakis
        cls.description = short_description
126 f3ddb705 Giorgos Verigakis
        cls.long_description = long_description or short_description
127 f3ddb705 Giorgos Verigakis
        
128 f3ddb705 Giorgos Verigakis
        cls.syntax = syntax
129 eb3ca8ca Giorgos Verigakis
        if cls.syntax is None:
130 eb3ca8ca Giorgos Verigakis
            # Generate a syntax string based on main's arguments
131 eb3ca8ca Giorgos Verigakis
            spec = inspect.getargspec(cls.main.im_func)
132 eb3ca8ca Giorgos Verigakis
            args = spec.args[1:]
133 eb3ca8ca Giorgos Verigakis
            n = len(args) - len(spec.defaults or ())
134 eb3ca8ca Giorgos Verigakis
            required = ' '.join('<%s>' % x.replace('_', ' ') for x in args[:n])
135 eb3ca8ca Giorgos Verigakis
            optional = ' '.join('[%s]' % x.replace('_', ' ') for x in args[n:])
136 eb3ca8ca Giorgos Verigakis
            cls.syntax = ' '.join(x for x in [required, optional] if x)
137 8ab2c986 Giorgos Verigakis
            if spec.varargs:
138 8ab2c986 Giorgos Verigakis
                cls.syntax += ' <%s ...>' % spec.varargs
139 5d1d131b Giorgos Verigakis
        
140 eb3ca8ca Giorgos Verigakis
        if cls.group not in _commands:
141 eb3ca8ca Giorgos Verigakis
            _commands[cls.group] = OrderedDict()
142 eb3ca8ca Giorgos Verigakis
        _commands[cls.group][cls.name] = cls
143 eb3ca8ca Giorgos Verigakis
        return cls
144 eb3ca8ca Giorgos Verigakis
    return decorator
145 5d1d131b Giorgos Verigakis
146 5d1d131b Giorgos Verigakis
147 f3ddb705 Giorgos Verigakis
@command(api='config')
148 eb3ca8ca Giorgos Verigakis
class config_list(object):
149 f3ddb705 Giorgos Verigakis
    """List configuration options"""
150 eb3ca8ca Giorgos Verigakis
    
151 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
152 d2cea1e2 Giorgos Verigakis
        parser.add_option('-a', dest='all', action='store_true',
153 f3ddb705 Giorgos Verigakis
                          default=False, help='include default values')
154 d2cea1e2 Giorgos Verigakis
    
155 eb3ca8ca Giorgos Verigakis
    def main(self):
156 f3ddb705 Giorgos Verigakis
        include_defaults = self.options.all
157 f3ddb705 Giorgos Verigakis
        for section in sorted(self.config.sections()):
158 f3ddb705 Giorgos Verigakis
            items = self.config.items(section, include_defaults)
159 f3ddb705 Giorgos Verigakis
            for key, val in sorted(items):
160 f3ddb705 Giorgos Verigakis
                puts('%s.%s = %s' % (section, key, val))
161 5d1d131b Giorgos Verigakis
162 5d1d131b Giorgos Verigakis
163 f3ddb705 Giorgos Verigakis
@command(api='config')
164 eb3ca8ca Giorgos Verigakis
class config_get(object):
165 f3ddb705 Giorgos Verigakis
    """Show a configuration option"""
166 5d1d131b Giorgos Verigakis
    
167 f3ddb705 Giorgos Verigakis
    def main(self, option):
168 f3ddb705 Giorgos Verigakis
        section, sep, key = option.rpartition('.')
169 f3ddb705 Giorgos Verigakis
        section = section or 'global'
170 f3ddb705 Giorgos Verigakis
        value = self.config.get(section, key)
171 f3ddb705 Giorgos Verigakis
        if value is not None:
172 f3ddb705 Giorgos Verigakis
            print value
173 eb3ca8ca Giorgos Verigakis
174 eb3ca8ca Giorgos Verigakis
175 f3ddb705 Giorgos Verigakis
@command(api='config')
176 eb3ca8ca Giorgos Verigakis
class config_set(object):
177 f3ddb705 Giorgos Verigakis
    """Set a configuration option"""
178 eb3ca8ca Giorgos Verigakis
    
179 f3ddb705 Giorgos Verigakis
    def main(self, option, value):
180 f3ddb705 Giorgos Verigakis
        section, sep, key = option.rpartition('.')
181 f3ddb705 Giorgos Verigakis
        section = section or 'global'
182 f3ddb705 Giorgos Verigakis
        self.config.set(section, key, value)
183 f3ddb705 Giorgos Verigakis
        self.config.write()
184 eb3ca8ca Giorgos Verigakis
185 eb3ca8ca Giorgos Verigakis
186 f3ddb705 Giorgos Verigakis
@command(api='config')
187 f3ddb705 Giorgos Verigakis
class config_delete(object):
188 f3ddb705 Giorgos Verigakis
    """Delete a configuration option (and use the default value)"""
189 eb3ca8ca Giorgos Verigakis
    
190 f3ddb705 Giorgos Verigakis
    def main(self, option):
191 f3ddb705 Giorgos Verigakis
        section, sep, key = option.rpartition('.')
192 f3ddb705 Giorgos Verigakis
        section = section or 'global'
193 f3ddb705 Giorgos Verigakis
        self.config.remove_option(section, key)
194 f3ddb705 Giorgos Verigakis
        self.config.write()
195 eb3ca8ca Giorgos Verigakis
196 eb3ca8ca Giorgos Verigakis
197 a1c50326 Giorgos Verigakis
@command(api='compute')
198 eb3ca8ca Giorgos Verigakis
class server_list(object):
199 6a0b1658 Giorgos Verigakis
    """List servers"""
200 eb3ca8ca Giorgos Verigakis
    
201 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
202 eb3ca8ca Giorgos Verigakis
        parser.add_option('-l', dest='detail', action='store_true',
203 eb3ca8ca Giorgos Verigakis
                default=False, help='show detailed output')
204 5d1d131b Giorgos Verigakis
    
205 5d1d131b Giorgos Verigakis
    def main(self):
206 eb3ca8ca Giorgos Verigakis
        servers = self.client.list_servers(self.options.detail)
207 a6757cbc Giorgos Verigakis
        print_items(servers)
208 5d1d131b Giorgos Verigakis
209 5d1d131b Giorgos Verigakis
210 a1c50326 Giorgos Verigakis
@command(api='compute')
211 eb3ca8ca Giorgos Verigakis
class server_info(object):
212 6a0b1658 Giorgos Verigakis
    """Get server details"""
213 5d1d131b Giorgos Verigakis
    
214 5d1d131b Giorgos Verigakis
    def main(self, server_id):
215 5d1d131b Giorgos Verigakis
        server = self.client.get_server_details(int(server_id))
216 5d1d131b Giorgos Verigakis
        print_dict(server)
217 5d1d131b Giorgos Verigakis
218 5d1d131b Giorgos Verigakis
219 a1c50326 Giorgos Verigakis
@command(api='compute')
220 eb3ca8ca Giorgos Verigakis
class server_create(object):
221 6a0b1658 Giorgos Verigakis
    """Create a server"""
222 76f01c50 Giorgos Verigakis
    
223 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
224 eb3ca8ca Giorgos Verigakis
        parser.add_option('--personality', dest='personalities',
225 6a0b1658 Giorgos Verigakis
                          action='append', default=[],
226 6a0b1658 Giorgos Verigakis
                          metavar='PATH[,SERVER PATH[,OWNER[,GROUP,[MODE]]]]',
227 6a0b1658 Giorgos Verigakis
                          help='add a personality file')
228 eb3ca8ca Giorgos Verigakis
        parser.epilog = "If missing, optional personality values will be " \
229 6a0b1658 Giorgos Verigakis
                        "filled based on the file at PATH."
230 eb3ca8ca Giorgos Verigakis
    
231 eb3ca8ca Giorgos Verigakis
    def main(self, name, flavor_id, image_id):
232 57b8dd5a Giorgos Verigakis
        personalities = []
233 eb3ca8ca Giorgos Verigakis
        for personality in self.options.personalities:
234 57b8dd5a Giorgos Verigakis
            p = personality.split(',')
235 57b8dd5a Giorgos Verigakis
            p.extend([None] * (5 - len(p)))     # Fill missing fields with None
236 57b8dd5a Giorgos Verigakis
            
237 57b8dd5a Giorgos Verigakis
            path = p[0]
238 57b8dd5a Giorgos Verigakis
            
239 57b8dd5a Giorgos Verigakis
            if not path:
240 57b8dd5a Giorgos Verigakis
                log.error("Invalid personality argument '%s'", p)
241 eb3ca8ca Giorgos Verigakis
                return 1
242 a1c50326 Giorgos Verigakis
            if not exists(path):
243 57b8dd5a Giorgos Verigakis
                log.error("File %s does not exist", path)
244 eb3ca8ca Giorgos Verigakis
                return 1
245 57b8dd5a Giorgos Verigakis
            
246 57b8dd5a Giorgos Verigakis
            with open(path) as f:
247 57b8dd5a Giorgos Verigakis
                contents = b64encode(f.read())
248 57b8dd5a Giorgos Verigakis
            
249 57b8dd5a Giorgos Verigakis
            st = os.stat(path)
250 57b8dd5a Giorgos Verigakis
            personalities.append({
251 a1c50326 Giorgos Verigakis
                'path': p[1] or abspath(path),
252 57b8dd5a Giorgos Verigakis
                'owner': p[2] or getpwuid(st.st_uid).pw_name,
253 57b8dd5a Giorgos Verigakis
                'group': p[3] or getgrgid(st.st_gid).gr_name,
254 57b8dd5a Giorgos Verigakis
                'mode': int(p[4]) if p[4] else 0x7777 & st.st_mode,
255 57b8dd5a Giorgos Verigakis
                'contents': contents})
256 76f01c50 Giorgos Verigakis
        
257 eb3ca8ca Giorgos Verigakis
        reply = self.client.create_server(name, int(flavor_id), image_id,
258 eb3ca8ca Giorgos Verigakis
                personalities)
259 5d1d131b Giorgos Verigakis
        print_dict(reply)
260 5d1d131b Giorgos Verigakis
261 5d1d131b Giorgos Verigakis
262 a1c50326 Giorgos Verigakis
@command(api='compute')
263 eb3ca8ca Giorgos Verigakis
class server_rename(object):
264 6a0b1658 Giorgos Verigakis
    """Update a server's name"""
265 5d1d131b Giorgos Verigakis
    
266 5d1d131b Giorgos Verigakis
    def main(self, server_id, new_name):
267 5d1d131b Giorgos Verigakis
        self.client.update_server_name(int(server_id), new_name)
268 5d1d131b Giorgos Verigakis
269 5d1d131b Giorgos Verigakis
270 a1c50326 Giorgos Verigakis
@command(api='compute')
271 eb3ca8ca Giorgos Verigakis
class server_delete(object):
272 6a0b1658 Giorgos Verigakis
    """Delete a server"""
273 5d1d131b Giorgos Verigakis
    
274 5d1d131b Giorgos Verigakis
    def main(self, server_id):
275 5d1d131b Giorgos Verigakis
        self.client.delete_server(int(server_id))
276 5d1d131b Giorgos Verigakis
277 5d1d131b Giorgos Verigakis
278 a1c50326 Giorgos Verigakis
@command(api='compute')
279 eb3ca8ca Giorgos Verigakis
class server_reboot(object):
280 6a0b1658 Giorgos Verigakis
    """Reboot a server"""
281 5d1d131b Giorgos Verigakis
    
282 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
283 eb3ca8ca Giorgos Verigakis
        parser.add_option('-f', dest='hard', action='store_true',
284 eb3ca8ca Giorgos Verigakis
                default=False, help='perform a hard reboot')
285 5d1d131b Giorgos Verigakis
    
286 5d1d131b Giorgos Verigakis
    def main(self, server_id):
287 eb3ca8ca Giorgos Verigakis
        self.client.reboot_server(int(server_id), self.options.hard)
288 5d1d131b Giorgos Verigakis
289 5d1d131b Giorgos Verigakis
290 b3b32add Giorgos Verigakis
@command(api='cyclades')
291 eb3ca8ca Giorgos Verigakis
class server_start(object):
292 6a0b1658 Giorgos Verigakis
    """Start a server"""
293 5d1d131b Giorgos Verigakis
    
294 5d1d131b Giorgos Verigakis
    def main(self, server_id):
295 5d1d131b Giorgos Verigakis
        self.client.start_server(int(server_id))
296 5d1d131b Giorgos Verigakis
297 5d1d131b Giorgos Verigakis
298 b3b32add Giorgos Verigakis
@command(api='cyclades')
299 eb3ca8ca Giorgos Verigakis
class server_shutdown(object):
300 6a0b1658 Giorgos Verigakis
    """Shutdown a server"""
301 5d1d131b Giorgos Verigakis
    
302 5d1d131b Giorgos Verigakis
    def main(self, server_id):
303 5d1d131b Giorgos Verigakis
        self.client.shutdown_server(int(server_id))
304 5d1d131b Giorgos Verigakis
305 5d1d131b Giorgos Verigakis
306 b3b32add Giorgos Verigakis
@command(api='cyclades')
307 eb3ca8ca Giorgos Verigakis
class server_console(object):
308 6a0b1658 Giorgos Verigakis
    """Get a VNC console"""
309 eb3ca8ca Giorgos Verigakis
    
310 5d1d131b Giorgos Verigakis
    def main(self, server_id):
311 5d1d131b Giorgos Verigakis
        reply = self.client.get_server_console(int(server_id))
312 5d1d131b Giorgos Verigakis
        print_dict(reply)
313 5d1d131b Giorgos Verigakis
314 5d1d131b Giorgos Verigakis
315 b3b32add Giorgos Verigakis
@command(api='cyclades')
316 eb3ca8ca Giorgos Verigakis
class server_firewall(object):
317 6a0b1658 Giorgos Verigakis
    """Set the server's firewall profile"""
318 5d1d131b Giorgos Verigakis
    
319 5d1d131b Giorgos Verigakis
    def main(self, server_id, profile):
320 5d1d131b Giorgos Verigakis
        self.client.set_firewall_profile(int(server_id), profile)
321 5d1d131b Giorgos Verigakis
322 5d1d131b Giorgos Verigakis
323 b3b32add Giorgos Verigakis
@command(api='cyclades')
324 eb3ca8ca Giorgos Verigakis
class server_addr(object):
325 6a0b1658 Giorgos Verigakis
    """List a server's addresses"""
326 5d1d131b Giorgos Verigakis
    
327 5d1d131b Giorgos Verigakis
    def main(self, server_id, network=None):
328 5d1d131b Giorgos Verigakis
        reply = self.client.list_server_addresses(int(server_id), network)
329 5d1d131b Giorgos Verigakis
        margin = max(len(x['name']) for x in reply)
330 5d1d131b Giorgos Verigakis
        print_addresses(reply, margin)
331 5d1d131b Giorgos Verigakis
332 5d1d131b Giorgos Verigakis
333 a1c50326 Giorgos Verigakis
@command(api='compute')
334 eb3ca8ca Giorgos Verigakis
class server_meta(object):
335 6a0b1658 Giorgos Verigakis
    """Get a server's metadata"""
336 5d1d131b Giorgos Verigakis
    
337 5d1d131b Giorgos Verigakis
    def main(self, server_id, key=None):
338 5d1d131b Giorgos Verigakis
        reply = self.client.get_server_metadata(int(server_id), key)
339 5d1d131b Giorgos Verigakis
        print_dict(reply)
340 5d1d131b Giorgos Verigakis
341 5d1d131b Giorgos Verigakis
342 a1c50326 Giorgos Verigakis
@command(api='compute')
343 eb3ca8ca Giorgos Verigakis
class server_addmeta(object):
344 6a0b1658 Giorgos Verigakis
    """Add server metadata"""
345 5d1d131b Giorgos Verigakis
    
346 5d1d131b Giorgos Verigakis
    def main(self, server_id, key, val):
347 5d1d131b Giorgos Verigakis
        reply = self.client.create_server_metadata(int(server_id), key, val)
348 5d1d131b Giorgos Verigakis
        print_dict(reply)
349 5d1d131b Giorgos Verigakis
350 5d1d131b Giorgos Verigakis
351 a1c50326 Giorgos Verigakis
@command(api='compute')
352 eb3ca8ca Giorgos Verigakis
class server_setmeta(object):
353 6a0b1658 Giorgos Verigakis
    """Update server's metadata"""
354 5d1d131b Giorgos Verigakis
    
355 5d1d131b Giorgos Verigakis
    def main(self, server_id, key, val):
356 ec52784d Giorgos Verigakis
        metadata = {key: val}
357 ec52784d Giorgos Verigakis
        reply = self.client.update_server_metadata(int(server_id), **metadata)
358 5d1d131b Giorgos Verigakis
        print_dict(reply)
359 5d1d131b Giorgos Verigakis
360 5d1d131b Giorgos Verigakis
361 a1c50326 Giorgos Verigakis
@command(api='compute')
362 eb3ca8ca Giorgos Verigakis
class server_delmeta(object):
363 6a0b1658 Giorgos Verigakis
    """Delete server metadata"""
364 5d1d131b Giorgos Verigakis
    
365 5d1d131b Giorgos Verigakis
    def main(self, server_id, key):
366 5d1d131b Giorgos Verigakis
        self.client.delete_server_metadata(int(server_id), key)
367 5d1d131b Giorgos Verigakis
368 5d1d131b Giorgos Verigakis
369 b3b32add Giorgos Verigakis
@command(api='cyclades')
370 eb3ca8ca Giorgos Verigakis
class server_stats(object):
371 6a0b1658 Giorgos Verigakis
    """Get server statistics"""
372 5d1d131b Giorgos Verigakis
    
373 5d1d131b Giorgos Verigakis
    def main(self, server_id):
374 5d1d131b Giorgos Verigakis
        reply = self.client.get_server_stats(int(server_id))
375 5d1d131b Giorgos Verigakis
        print_dict(reply, exclude=('serverRef',))
376 5d1d131b Giorgos Verigakis
377 5d1d131b Giorgos Verigakis
378 a1c50326 Giorgos Verigakis
@command(api='compute')
379 eb3ca8ca Giorgos Verigakis
class flavor_list(object):
380 6a0b1658 Giorgos Verigakis
    """List flavors"""
381 eb3ca8ca Giorgos Verigakis
    
382 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
383 eb3ca8ca Giorgos Verigakis
        parser.add_option('-l', dest='detail', action='store_true',
384 eb3ca8ca Giorgos Verigakis
                default=False, help='show detailed output')
385 5d1d131b Giorgos Verigakis
    
386 5d1d131b Giorgos Verigakis
    def main(self):
387 eb3ca8ca Giorgos Verigakis
        flavors = self.client.list_flavors(self.options.detail)
388 a6757cbc Giorgos Verigakis
        print_items(flavors)
389 5d1d131b Giorgos Verigakis
390 5d1d131b Giorgos Verigakis
391 a1c50326 Giorgos Verigakis
@command(api='compute')
392 eb3ca8ca Giorgos Verigakis
class flavor_info(object):
393 6a0b1658 Giorgos Verigakis
    """Get flavor details"""
394 5d1d131b Giorgos Verigakis
    
395 5d1d131b Giorgos Verigakis
    def main(self, flavor_id):
396 5d1d131b Giorgos Verigakis
        flavor = self.client.get_flavor_details(int(flavor_id))
397 5d1d131b Giorgos Verigakis
        print_dict(flavor)
398 5d1d131b Giorgos Verigakis
399 5d1d131b Giorgos Verigakis
400 a1c50326 Giorgos Verigakis
@command(api='compute')
401 eb3ca8ca Giorgos Verigakis
class image_list(object):
402 6a0b1658 Giorgos Verigakis
    """List images"""
403 eb3ca8ca Giorgos Verigakis
    
404 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
405 eb3ca8ca Giorgos Verigakis
        parser.add_option('-l', dest='detail', action='store_true',
406 eb3ca8ca Giorgos Verigakis
                default=False, help='show detailed output')
407 eb3ca8ca Giorgos Verigakis
    
408 5d1d131b Giorgos Verigakis
    def main(self):
409 eb3ca8ca Giorgos Verigakis
        images = self.client.list_images(self.options.detail)
410 a6757cbc Giorgos Verigakis
        print_items(images)
411 5d1d131b Giorgos Verigakis
412 5d1d131b Giorgos Verigakis
413 a1c50326 Giorgos Verigakis
@command(api='compute')
414 eb3ca8ca Giorgos Verigakis
class image_info(object):
415 6a0b1658 Giorgos Verigakis
    """Get image details"""
416 5d1d131b Giorgos Verigakis
    
417 5d1d131b Giorgos Verigakis
    def main(self, image_id):
418 eb3ca8ca Giorgos Verigakis
        image = self.client.get_image_details(image_id)
419 5d1d131b Giorgos Verigakis
        print_dict(image)
420 5d1d131b Giorgos Verigakis
421 5d1d131b Giorgos Verigakis
422 a1c50326 Giorgos Verigakis
@command(api='compute')
423 eb3ca8ca Giorgos Verigakis
class image_delete(object):
424 6a0b1658 Giorgos Verigakis
    """Delete image"""
425 5d1d131b Giorgos Verigakis
    
426 5d1d131b Giorgos Verigakis
    def main(self, image_id):
427 eb3ca8ca Giorgos Verigakis
        self.client.delete_image(image_id)
428 5d1d131b Giorgos Verigakis
429 5d1d131b Giorgos Verigakis
430 a1c50326 Giorgos Verigakis
@command(api='compute')
431 cb813272 Giorgos Verigakis
class image_properties(object):
432 cb813272 Giorgos Verigakis
    """Get image properties"""
433 5d1d131b Giorgos Verigakis
    
434 5d1d131b Giorgos Verigakis
    def main(self, image_id, key=None):
435 eb3ca8ca Giorgos Verigakis
        reply = self.client.get_image_metadata(image_id, key)
436 5d1d131b Giorgos Verigakis
        print_dict(reply)
437 5d1d131b Giorgos Verigakis
438 5d1d131b Giorgos Verigakis
439 a1c50326 Giorgos Verigakis
@command(api='compute')
440 cb813272 Giorgos Verigakis
class image_addproperty(object):
441 cb813272 Giorgos Verigakis
    """Add an image property"""
442 5d1d131b Giorgos Verigakis
    
443 5d1d131b Giorgos Verigakis
    def main(self, image_id, key, val):
444 eb3ca8ca Giorgos Verigakis
        reply = self.client.create_image_metadata(image_id, key, val)
445 5d1d131b Giorgos Verigakis
        print_dict(reply)
446 5d1d131b Giorgos Verigakis
447 5d1d131b Giorgos Verigakis
448 a1c50326 Giorgos Verigakis
@command(api='compute')
449 cb813272 Giorgos Verigakis
class image_setproperty(object):
450 cb813272 Giorgos Verigakis
    """Update an image property"""
451 5d1d131b Giorgos Verigakis
    
452 5d1d131b Giorgos Verigakis
    def main(self, image_id, key, val):
453 ec52784d Giorgos Verigakis
        metadata = {key: val}
454 eb3ca8ca Giorgos Verigakis
        reply = self.client.update_image_metadata(image_id, **metadata)
455 5d1d131b Giorgos Verigakis
        print_dict(reply)
456 5d1d131b Giorgos Verigakis
457 5d1d131b Giorgos Verigakis
458 a1c50326 Giorgos Verigakis
@command(api='compute')
459 cb813272 Giorgos Verigakis
class image_delproperty(object):
460 cb813272 Giorgos Verigakis
    """Delete an image property"""
461 5d1d131b Giorgos Verigakis
    
462 5d1d131b Giorgos Verigakis
    def main(self, image_id, key):
463 eb3ca8ca Giorgos Verigakis
        self.client.delete_image_metadata(image_id, key)
464 5d1d131b Giorgos Verigakis
465 5d1d131b Giorgos Verigakis
466 b3b32add Giorgos Verigakis
@command(api='cyclades')
467 eb3ca8ca Giorgos Verigakis
class network_list(object):
468 6a0b1658 Giorgos Verigakis
    """List networks"""
469 5d1d131b Giorgos Verigakis
    
470 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
471 eb3ca8ca Giorgos Verigakis
        parser.add_option('-l', dest='detail', action='store_true',
472 eb3ca8ca Giorgos Verigakis
                default=False, help='show detailed output')
473 5d1d131b Giorgos Verigakis
    
474 5d1d131b Giorgos Verigakis
    def main(self):
475 eb3ca8ca Giorgos Verigakis
        networks = self.client.list_networks(self.options.detail)
476 a6757cbc Giorgos Verigakis
        print_items(networks)
477 5d1d131b Giorgos Verigakis
478 5d1d131b Giorgos Verigakis
479 b3b32add Giorgos Verigakis
@command(api='cyclades')
480 eb3ca8ca Giorgos Verigakis
class network_create(object):
481 6a0b1658 Giorgos Verigakis
    """Create a network"""
482 5d1d131b Giorgos Verigakis
    
483 5d1d131b Giorgos Verigakis
    def main(self, name):
484 5d1d131b Giorgos Verigakis
        reply = self.client.create_network(name)
485 5d1d131b Giorgos Verigakis
        print_dict(reply)
486 5d1d131b Giorgos Verigakis
487 5d1d131b Giorgos Verigakis
488 b3b32add Giorgos Verigakis
@command(api='cyclades')
489 eb3ca8ca Giorgos Verigakis
class network_info(object):
490 6a0b1658 Giorgos Verigakis
    """Get network details"""
491 eb3ca8ca Giorgos Verigakis
    
492 5d1d131b Giorgos Verigakis
    def main(self, network_id):
493 5d1d131b Giorgos Verigakis
        network = self.client.get_network_details(network_id)
494 5d1d131b Giorgos Verigakis
        print_dict(network)
495 5d1d131b Giorgos Verigakis
496 5d1d131b Giorgos Verigakis
497 b3b32add Giorgos Verigakis
@command(api='cyclades')
498 eb3ca8ca Giorgos Verigakis
class network_rename(object):
499 6a0b1658 Giorgos Verigakis
    """Update network name"""
500 5d1d131b Giorgos Verigakis
    
501 eb3ca8ca Giorgos Verigakis
    def main(self, network_id, new_name):
502 eb3ca8ca Giorgos Verigakis
        self.client.update_network_name(network_id, new_name)
503 5d1d131b Giorgos Verigakis
504 5d1d131b Giorgos Verigakis
505 b3b32add Giorgos Verigakis
@command(api='cyclades')
506 eb3ca8ca Giorgos Verigakis
class network_delete(object):
507 6a0b1658 Giorgos Verigakis
    """Delete a network"""
508 5d1d131b Giorgos Verigakis
    
509 5d1d131b Giorgos Verigakis
    def main(self, network_id):
510 5d1d131b Giorgos Verigakis
        self.client.delete_network(network_id)
511 5d1d131b Giorgos Verigakis
512 eb3ca8ca Giorgos Verigakis
513 b3b32add Giorgos Verigakis
@command(api='cyclades')
514 eb3ca8ca Giorgos Verigakis
class network_connect(object):
515 6a0b1658 Giorgos Verigakis
    """Connect a server to a network"""
516 5d1d131b Giorgos Verigakis
    
517 5d1d131b Giorgos Verigakis
    def main(self, server_id, network_id):
518 5d1d131b Giorgos Verigakis
        self.client.connect_server(server_id, network_id)
519 5d1d131b Giorgos Verigakis
520 5d1d131b Giorgos Verigakis
521 b3b32add Giorgos Verigakis
@command(api='cyclades')
522 eb3ca8ca Giorgos Verigakis
class network_disconnect(object):
523 6a0b1658 Giorgos Verigakis
    """Disconnect a server from a network"""
524 eb3ca8ca Giorgos Verigakis
    
525 5d1d131b Giorgos Verigakis
    def main(self, server_id, network_id):
526 5d1d131b Giorgos Verigakis
        self.client.disconnect_server(server_id, network_id)
527 5d1d131b Giorgos Verigakis
528 5d1d131b Giorgos Verigakis
529 a1c50326 Giorgos Verigakis
@command(api='image')
530 cb813272 Giorgos Verigakis
class image_public(object):
531 cb813272 Giorgos Verigakis
    """List public images"""
532 5d1d131b Giorgos Verigakis
    
533 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
534 a6757cbc Giorgos Verigakis
        parser.add_option('-l', dest='detail', action='store_true',
535 a6757cbc Giorgos Verigakis
                default=False, help='show detailed output')
536 a6757cbc Giorgos Verigakis
        parser.add_option('--container-format', dest='container_format',
537 a6757cbc Giorgos Verigakis
                metavar='FORMAT', help='filter by container format')
538 a6757cbc Giorgos Verigakis
        parser.add_option('--disk-format', dest='disk_format',
539 a6757cbc Giorgos Verigakis
                metavar='FORMAT', help='filter by disk format')
540 a6757cbc Giorgos Verigakis
        parser.add_option('--name', dest='name', metavar='NAME',
541 a6757cbc Giorgos Verigakis
                help='filter by name')
542 a6757cbc Giorgos Verigakis
        parser.add_option('--size-min', dest='size_min', metavar='BYTES',
543 a6757cbc Giorgos Verigakis
                help='filter by minimum size')
544 a6757cbc Giorgos Verigakis
        parser.add_option('--size-max', dest='size_max', metavar='BYTES',
545 a6757cbc Giorgos Verigakis
                help='filter by maximum size')
546 a6757cbc Giorgos Verigakis
        parser.add_option('--status', dest='status', metavar='STATUS',
547 a6757cbc Giorgos Verigakis
                help='filter by status')
548 a6757cbc Giorgos Verigakis
        parser.add_option('--order', dest='order', metavar='FIELD',
549 a6757cbc Giorgos Verigakis
                help='order by FIELD (use a - prefix to reverse order)')
550 a6757cbc Giorgos Verigakis
    
551 eb3ca8ca Giorgos Verigakis
    def main(self):
552 a6757cbc Giorgos Verigakis
        filters = {}
553 a6757cbc Giorgos Verigakis
        for filter in ('container_format', 'disk_format', 'name', 'size_min',
554 a6757cbc Giorgos Verigakis
                       'size_max', 'status'):
555 a6757cbc Giorgos Verigakis
            val = getattr(self.options, filter, None)
556 a6757cbc Giorgos Verigakis
            if val is not None:
557 a6757cbc Giorgos Verigakis
                filters[filter] = val
558 a6757cbc Giorgos Verigakis
        
559 a6757cbc Giorgos Verigakis
        order = self.options.order or ''
560 a6757cbc Giorgos Verigakis
        images = self.client.list_public(self.options.detail, filters=filters,
561 a6757cbc Giorgos Verigakis
                                         order=order)
562 a6757cbc Giorgos Verigakis
        print_items(images, title=('name',))
563 a6757cbc Giorgos Verigakis
564 a6757cbc Giorgos Verigakis
565 a1c50326 Giorgos Verigakis
@command(api='image')
566 cb813272 Giorgos Verigakis
class image_meta(object):
567 6a0b1658 Giorgos Verigakis
    """Get image metadata"""
568 8ab2c986 Giorgos Verigakis
    
569 8ab2c986 Giorgos Verigakis
    def main(self, image_id):
570 8ab2c986 Giorgos Verigakis
        image = self.client.get_meta(image_id)
571 8ab2c986 Giorgos Verigakis
        print_dict(image)
572 8ab2c986 Giorgos Verigakis
573 8ab2c986 Giorgos Verigakis
574 a1c50326 Giorgos Verigakis
@command(api='image')
575 cb813272 Giorgos Verigakis
class image_register(object):
576 6a0b1658 Giorgos Verigakis
    """Register an image"""
577 a6757cbc Giorgos Verigakis
    
578 77eaa0be Giorgos Verigakis
    def update_parser(self, parser):
579 a6757cbc Giorgos Verigakis
        parser.add_option('--checksum', dest='checksum', metavar='CHECKSUM',
580 a6757cbc Giorgos Verigakis
                help='set image checksum')
581 a6757cbc Giorgos Verigakis
        parser.add_option('--container-format', dest='container_format',
582 a6757cbc Giorgos Verigakis
                metavar='FORMAT', help='set container format')
583 a6757cbc Giorgos Verigakis
        parser.add_option('--disk-format', dest='disk_format',
584 a6757cbc Giorgos Verigakis
                metavar='FORMAT', help='set disk format')
585 a6757cbc Giorgos Verigakis
        parser.add_option('--id', dest='id',
586 a6757cbc Giorgos Verigakis
                metavar='ID', help='set image ID')
587 a6757cbc Giorgos Verigakis
        parser.add_option('--owner', dest='owner',
588 a6757cbc Giorgos Verigakis
                metavar='USER', help='set image owner (admin only)')
589 a6757cbc Giorgos Verigakis
        parser.add_option('--property', dest='properties', action='append',
590 a6757cbc Giorgos Verigakis
                metavar='KEY=VAL',
591 a6757cbc Giorgos Verigakis
                help='add a property (can be used multiple times)')
592 a6757cbc Giorgos Verigakis
        parser.add_option('--public', dest='is_public', action='store_true',
593 a6757cbc Giorgos Verigakis
                help='mark image as public')
594 a6757cbc Giorgos Verigakis
        parser.add_option('--size', dest='size', metavar='SIZE',
595 a6757cbc Giorgos Verigakis
                help='set image size')
596 a6757cbc Giorgos Verigakis
    
597 a6757cbc Giorgos Verigakis
    def main(self, name, location):
598 cb813272 Giorgos Verigakis
        if not location.startswith('pithos://'):
599 cb813272 Giorgos Verigakis
            account = self.config.get('storage', 'account')
600 cb813272 Giorgos Verigakis
            container = self.config.get('storage', 'container')
601 cb813272 Giorgos Verigakis
            location = 'pithos://%s/%s/%s' % (account, container, location)
602 cb813272 Giorgos Verigakis
        
603 a6757cbc Giorgos Verigakis
        params = {}
604 a6757cbc Giorgos Verigakis
        for key in ('checksum', 'container_format', 'disk_format', 'id',
605 6a0b1658 Giorgos Verigakis
                    'owner', 'size'):
606 a6757cbc Giorgos Verigakis
            val = getattr(self.options, key)
607 a6757cbc Giorgos Verigakis
            if val is not None:
608 a6757cbc Giorgos Verigakis
                params[key] = val
609 a6757cbc Giorgos Verigakis
        
610 6a0b1658 Giorgos Verigakis
        if self.options.is_public:
611 6a0b1658 Giorgos Verigakis
            params['is_public'] = 'true'
612 6a0b1658 Giorgos Verigakis
        
613 a6757cbc Giorgos Verigakis
        properties = {}
614 a6757cbc Giorgos Verigakis
        for property in self.options.properties or []:
615 a6757cbc Giorgos Verigakis
            key, sep, val = property.partition('=')
616 a6757cbc Giorgos Verigakis
            if not sep:
617 a6757cbc Giorgos Verigakis
                log.error("Invalid property '%s'", property)
618 a6757cbc Giorgos Verigakis
                return 1
619 a6757cbc Giorgos Verigakis
            properties[key.strip()] = val.strip()
620 a6757cbc Giorgos Verigakis
        
621 a6757cbc Giorgos Verigakis
        self.client.register(name, location, params, properties)
622 eb3ca8ca Giorgos Verigakis
623 eb3ca8ca Giorgos Verigakis
624 a1c50326 Giorgos Verigakis
@command(api='image')
625 cb813272 Giorgos Verigakis
class image_members(object):
626 6a0b1658 Giorgos Verigakis
    """Get image members"""
627 8ab2c986 Giorgos Verigakis
    
628 8ab2c986 Giorgos Verigakis
    def main(self, image_id):
629 8ab2c986 Giorgos Verigakis
        members = self.client.list_members(image_id)
630 8ab2c986 Giorgos Verigakis
        for member in members:
631 8ab2c986 Giorgos Verigakis
            print member['member_id']
632 8ab2c986 Giorgos Verigakis
633 8ab2c986 Giorgos Verigakis
634 a1c50326 Giorgos Verigakis
@command(api='image')
635 cb813272 Giorgos Verigakis
class image_shared(object):
636 6a0b1658 Giorgos Verigakis
    """List shared images"""
637 8ab2c986 Giorgos Verigakis
    
638 8ab2c986 Giorgos Verigakis
    def main(self, member):
639 8ab2c986 Giorgos Verigakis
        images = self.client.list_shared(member)
640 8ab2c986 Giorgos Verigakis
        for image in images:
641 8ab2c986 Giorgos Verigakis
            print image['image_id']
642 8ab2c986 Giorgos Verigakis
643 8ab2c986 Giorgos Verigakis
644 a1c50326 Giorgos Verigakis
@command(api='image')
645 cb813272 Giorgos Verigakis
class image_addmember(object):
646 6a0b1658 Giorgos Verigakis
    """Add a member to an image"""
647 8ab2c986 Giorgos Verigakis
    
648 8ab2c986 Giorgos Verigakis
    def main(self, image_id, member):
649 8ab2c986 Giorgos Verigakis
        self.client.add_member(image_id, member)
650 8ab2c986 Giorgos Verigakis
651 8ab2c986 Giorgos Verigakis
652 a1c50326 Giorgos Verigakis
@command(api='image')
653 cb813272 Giorgos Verigakis
class image_delmember(object):
654 6a0b1658 Giorgos Verigakis
    """Remove a member from an image"""
655 8ab2c986 Giorgos Verigakis
    
656 8ab2c986 Giorgos Verigakis
    def main(self, image_id, member):
657 8ab2c986 Giorgos Verigakis
        self.client.remove_member(image_id, member)
658 8ab2c986 Giorgos Verigakis
659 8ab2c986 Giorgos Verigakis
660 a1c50326 Giorgos Verigakis
@command(api='image')
661 cb813272 Giorgos Verigakis
class image_setmembers(object):
662 6a0b1658 Giorgos Verigakis
    """Set the members of an image"""
663 8ab2c986 Giorgos Verigakis
    
664 8ab2c986 Giorgos Verigakis
    def main(self, image_id, *member):
665 8ab2c986 Giorgos Verigakis
        self.client.set_members(image_id, member)
666 8ab2c986 Giorgos Verigakis
667 8ab2c986 Giorgos Verigakis
668 2bcb595a Giorgos Verigakis
class _store_account_command(object):
669 2bcb595a Giorgos Verigakis
    """Base class for account level storage commands"""
670 d2cea1e2 Giorgos Verigakis
    
671 2bcb595a Giorgos Verigakis
    def update_parser(self, parser):
672 60560d7c Giorgos Verigakis
        parser.add_option('--account', dest='account', metavar='NAME',
673 6a0b1658 Giorgos Verigakis
                          help="Specify an account to use")
674 d2cea1e2 Giorgos Verigakis
    
675 6a0b1658 Giorgos Verigakis
    def progress(self, message):
676 6a0b1658 Giorgos Verigakis
        """Return a generator function to be used for progress tracking"""
677 6a0b1658 Giorgos Verigakis
        
678 6a0b1658 Giorgos Verigakis
        MESSAGE_LENGTH = 25
679 6a0b1658 Giorgos Verigakis
        MAX_PROGRESS_LENGTH = 32
680 6a0b1658 Giorgos Verigakis
        
681 6a0b1658 Giorgos Verigakis
        def progress_gen(n):
682 6a0b1658 Giorgos Verigakis
            msg = message.ljust(MESSAGE_LENGTH)
683 6a0b1658 Giorgos Verigakis
            width = min(n, MAX_PROGRESS_LENGTH)
684 6a0b1658 Giorgos Verigakis
            hide = self.config.get('global', 'silent') or (n < 2)
685 6a0b1658 Giorgos Verigakis
            for i in progress.bar(range(n), msg, width, hide):
686 6a0b1658 Giorgos Verigakis
                yield
687 6a0b1658 Giorgos Verigakis
            yield
688 176894c1 Giorgos Verigakis
        
689 6a0b1658 Giorgos Verigakis
        return progress_gen
690 6a0b1658 Giorgos Verigakis
    
691 6a0b1658 Giorgos Verigakis
    def main(self):
692 6a0b1658 Giorgos Verigakis
        if self.options.account is not None:
693 6a0b1658 Giorgos Verigakis
            self.client.account = self.options.account
694 2bcb595a Giorgos Verigakis
695 2bcb595a Giorgos Verigakis
696 2bcb595a Giorgos Verigakis
class _store_container_command(_store_account_command):
697 2bcb595a Giorgos Verigakis
    """Base class for container level storage commands"""
698 2bcb595a Giorgos Verigakis
    
699 2bcb595a Giorgos Verigakis
    def update_parser(self, parser):
700 2bcb595a Giorgos Verigakis
        super(_store_container_command, self).update_parser(parser)
701 2bcb595a Giorgos Verigakis
        parser.add_option('--container', dest='container', metavar='NAME',
702 2bcb595a Giorgos Verigakis
                          help="Specify a container to use")
703 2bcb595a Giorgos Verigakis
    
704 2bcb595a Giorgos Verigakis
    def main(self):
705 2bcb595a Giorgos Verigakis
        super(_store_container_command, self).main()
706 6a0b1658 Giorgos Verigakis
        if self.options.container is not None:
707 6a0b1658 Giorgos Verigakis
            self.client.container = self.options.container
708 176894c1 Giorgos Verigakis
709 176894c1 Giorgos Verigakis
710 176894c1 Giorgos Verigakis
@command(api='storage')
711 2bcb595a Giorgos Verigakis
class store_create(_store_account_command):
712 6a0b1658 Giorgos Verigakis
    """Create a container"""
713 60560d7c Giorgos Verigakis
    
714 60560d7c Giorgos Verigakis
    def main(self, container):
715 6a0b1658 Giorgos Verigakis
        if self.options.account:
716 6a0b1658 Giorgos Verigakis
            self.client.account = self.options.account
717 60560d7c Giorgos Verigakis
        self.client.create_container(container)
718 60560d7c Giorgos Verigakis
719 60560d7c Giorgos Verigakis
720 60560d7c Giorgos Verigakis
@command(api='storage')
721 2bcb595a Giorgos Verigakis
class store_container(_store_account_command):
722 6a0b1658 Giorgos Verigakis
    """Get container info"""
723 176894c1 Giorgos Verigakis
    
724 6a0b1658 Giorgos Verigakis
    def main(self, container):
725 6a0b1658 Giorgos Verigakis
        if self.options.account:
726 6a0b1658 Giorgos Verigakis
            self.client.account = self.options.account
727 6a0b1658 Giorgos Verigakis
        reply = self.client.get_container_meta(container)
728 d2cea1e2 Giorgos Verigakis
        print_dict(reply)
729 d2cea1e2 Giorgos Verigakis
730 d2cea1e2 Giorgos Verigakis
731 d2cea1e2 Giorgos Verigakis
@command(api='storage')
732 2bcb595a Giorgos Verigakis
class store_list(_store_container_command):
733 2bcb595a Giorgos Verigakis
    """List objects"""
734 2bcb595a Giorgos Verigakis
    
735 2bcb595a Giorgos Verigakis
    def format_size(self, size):
736 2bcb595a Giorgos Verigakis
        units = ('B', 'K', 'M', 'G', 'T')
737 2bcb595a Giorgos Verigakis
        size = float(size)
738 2bcb595a Giorgos Verigakis
        for unit in units:
739 2bcb595a Giorgos Verigakis
            if size <= 1024:
740 2bcb595a Giorgos Verigakis
                break
741 2bcb595a Giorgos Verigakis
            size /= 1024
742 2bcb595a Giorgos Verigakis
        s = ('%.1f' % size).rstrip('.0')
743 2bcb595a Giorgos Verigakis
        return s + unit
744 2bcb595a Giorgos Verigakis
    
745 2bcb595a Giorgos Verigakis
    
746 2bcb595a Giorgos Verigakis
    def main(self, path=''):
747 2bcb595a Giorgos Verigakis
        super(store_list, self).main()
748 2bcb595a Giorgos Verigakis
        for object in self.client.list_objects():
749 2bcb595a Giorgos Verigakis
            size = self.format_size(object['bytes'])
750 2bcb595a Giorgos Verigakis
            print '%6s %s' % (size, object['name'])
751 2bcb595a Giorgos Verigakis
        
752 2bcb595a Giorgos Verigakis
753 2bcb595a Giorgos Verigakis
@command(api='storage')
754 2bcb595a Giorgos Verigakis
class store_upload(_store_container_command):
755 6a0b1658 Giorgos Verigakis
    """Upload a file"""
756 a1c50326 Giorgos Verigakis
    
757 a1c50326 Giorgos Verigakis
    def main(self, path, remote_path=None):
758 6a0b1658 Giorgos Verigakis
        super(store_upload, self).main()
759 6a0b1658 Giorgos Verigakis
        
760 a1c50326 Giorgos Verigakis
        if remote_path is None:
761 a1c50326 Giorgos Verigakis
            remote_path = basename(path)
762 a1c50326 Giorgos Verigakis
        with open(path) as f:
763 6a0b1658 Giorgos Verigakis
            hash_cb = self.progress('Calculating block hashes')
764 6a0b1658 Giorgos Verigakis
            upload_cb = self.progress('Uploading blocks')
765 6a0b1658 Giorgos Verigakis
            self.client.create_object(remote_path, f, hash_cb=hash_cb,
766 6a0b1658 Giorgos Verigakis
                                      upload_cb=upload_cb)
767 d2cea1e2 Giorgos Verigakis
768 d2cea1e2 Giorgos Verigakis
769 d2cea1e2 Giorgos Verigakis
@command(api='storage')
770 2bcb595a Giorgos Verigakis
class store_download(_store_container_command):
771 6a0b1658 Giorgos Verigakis
    """Download a file"""
772 6a0b1658 Giorgos Verigakis
        
773 6a0b1658 Giorgos Verigakis
    def main(self, remote_path, local_path='-'):
774 6a0b1658 Giorgos Verigakis
        super(store_download, self).main()
775 6a0b1658 Giorgos Verigakis
        
776 6a0b1658 Giorgos Verigakis
        f, size = self.client.get_object(remote_path)
777 176894c1 Giorgos Verigakis
        out = open(local_path, 'w') if local_path != '-' else stdout
778 6a0b1658 Giorgos Verigakis
        
779 6a0b1658 Giorgos Verigakis
        blocksize = 4 * 1024**2
780 6a0b1658 Giorgos Verigakis
        nblocks = 1 + (size - 1) // blocksize
781 6a0b1658 Giorgos Verigakis
        
782 6a0b1658 Giorgos Verigakis
        cb = self.progress('Downloading blocks') if local_path != '-' else None
783 6a0b1658 Giorgos Verigakis
        if cb:
784 6a0b1658 Giorgos Verigakis
            gen = cb(nblocks)
785 6a0b1658 Giorgos Verigakis
            gen.next()
786 6a0b1658 Giorgos Verigakis
        
787 6a0b1658 Giorgos Verigakis
        data = f.read(blocksize)
788 176894c1 Giorgos Verigakis
        while data:
789 176894c1 Giorgos Verigakis
            out.write(data)
790 6a0b1658 Giorgos Verigakis
            data = f.read(blocksize)
791 6a0b1658 Giorgos Verigakis
            if cb:
792 6a0b1658 Giorgos Verigakis
                gen.next()
793 176894c1 Giorgos Verigakis
794 176894c1 Giorgos Verigakis
795 176894c1 Giorgos Verigakis
@command(api='storage')
796 2bcb595a Giorgos Verigakis
class store_delete(_store_container_command):
797 6a0b1658 Giorgos Verigakis
    """Delete a file"""
798 d2cea1e2 Giorgos Verigakis
    
799 d2cea1e2 Giorgos Verigakis
    def main(self, path):
800 176894c1 Giorgos Verigakis
        store_command.main(self)
801 d2cea1e2 Giorgos Verigakis
        self.client.delete_object(path)
802 a1c50326 Giorgos Verigakis
803 a1c50326 Giorgos Verigakis
804 43ca98ee Giorgos Verigakis
@command(api='astakos')
805 43ca98ee Giorgos Verigakis
class astakos_authenticate(object):
806 43ca98ee Giorgos Verigakis
    """Authenticate a user"""
807 43ca98ee Giorgos Verigakis
    
808 43ca98ee Giorgos Verigakis
    def main(self):
809 43ca98ee Giorgos Verigakis
        reply = self.client.authenticate()
810 43ca98ee Giorgos Verigakis
        print_dict(reply)
811 43ca98ee Giorgos Verigakis
812 43ca98ee Giorgos Verigakis
813 f3ddb705 Giorgos Verigakis
def print_groups():
814 f3ddb705 Giorgos Verigakis
    puts('\nGroups:')
815 f3ddb705 Giorgos Verigakis
    with indent(2):
816 f3ddb705 Giorgos Verigakis
        for group in _commands:
817 f3ddb705 Giorgos Verigakis
            description = GROUPS.get(group, '')
818 f3ddb705 Giorgos Verigakis
            puts(columns([group, 12], [description, 60]))
819 eb3ca8ca Giorgos Verigakis
820 eb3ca8ca Giorgos Verigakis
821 f3ddb705 Giorgos Verigakis
def print_commands(group):
822 f3ddb705 Giorgos Verigakis
    description = GROUPS.get(group, '')
823 f3ddb705 Giorgos Verigakis
    if description:
824 f3ddb705 Giorgos Verigakis
        puts('\n' + description)
825 f3ddb705 Giorgos Verigakis
    
826 f3ddb705 Giorgos Verigakis
    puts('\nCommands:')
827 f3ddb705 Giorgos Verigakis
    with indent(2):
828 f3ddb705 Giorgos Verigakis
        for name, cls in _commands[group].items():
829 43ca98ee Giorgos Verigakis
            puts(columns([name, 14], [cls.description, 60]))
830 5d1d131b Giorgos Verigakis
831 5d1d131b Giorgos Verigakis
832 6a0b1658 Giorgos Verigakis
def add_handler(name, level, prefix=''):
833 6a0b1658 Giorgos Verigakis
    h = logging.StreamHandler()
834 6a0b1658 Giorgos Verigakis
    fmt = logging.Formatter(prefix + '%(message)s')
835 6a0b1658 Giorgos Verigakis
    h.setFormatter(fmt)
836 6a0b1658 Giorgos Verigakis
    logger = logging.getLogger(name)
837 6a0b1658 Giorgos Verigakis
    logger.addHandler(h)
838 6a0b1658 Giorgos Verigakis
    logger.setLevel(level)
839 6a0b1658 Giorgos Verigakis
840 6a0b1658 Giorgos Verigakis
841 5d1d131b Giorgos Verigakis
def main():
842 eb3ca8ca Giorgos Verigakis
    parser = OptionParser(add_help_option=False)
843 eb3ca8ca Giorgos Verigakis
    parser.usage = '%prog <group> <command> [options]'
844 f3ddb705 Giorgos Verigakis
    parser.add_option('-h', '--help', dest='help', action='store_true',
845 f3ddb705 Giorgos Verigakis
                      default=False,
846 f3ddb705 Giorgos Verigakis
                      help="Show this help message and exit")
847 f3ddb705 Giorgos Verigakis
    parser.add_option('--config', dest='config', metavar='PATH',
848 f3ddb705 Giorgos Verigakis
                      help="Specify the path to the configuration file")
849 6a0b1658 Giorgos Verigakis
    parser.add_option('-d', '--debug', dest='debug', action='store_true',
850 6a0b1658 Giorgos Verigakis
                      default=False,
851 6a0b1658 Giorgos Verigakis
                      help="Include debug output")
852 f3ddb705 Giorgos Verigakis
    parser.add_option('-i', '--include', dest='include', action='store_true',
853 f3ddb705 Giorgos Verigakis
                      default=False,
854 f3ddb705 Giorgos Verigakis
                      help="Include protocol headers in the output")
855 f3ddb705 Giorgos Verigakis
    parser.add_option('-s', '--silent', dest='silent', action='store_true',
856 f3ddb705 Giorgos Verigakis
                      default=False,
857 f3ddb705 Giorgos Verigakis
                      help="Silent mode, don't output anything")
858 f3ddb705 Giorgos Verigakis
    parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
859 f3ddb705 Giorgos Verigakis
                      default=False,
860 f3ddb705 Giorgos Verigakis
                      help="Make the operation more talkative")
861 f3ddb705 Giorgos Verigakis
    parser.add_option('-V', '--version', dest='version', action='store_true',
862 f3ddb705 Giorgos Verigakis
                      default=False,
863 f3ddb705 Giorgos Verigakis
                      help="Show version number and quit")
864 21cf6792 Giorgos Verigakis
    parser.add_option('-o', dest='options', action='append',
865 f3ddb705 Giorgos Verigakis
                      default=[], metavar="KEY=VAL",
866 f3ddb705 Giorgos Verigakis
                      help="Override a config values")
867 f3ddb705 Giorgos Verigakis
    
868 f3ddb705 Giorgos Verigakis
    if args.contains(['-V', '--version']):
869 f3ddb705 Giorgos Verigakis
        import kamaki
870 f3ddb705 Giorgos Verigakis
        print "kamaki %s" % kamaki.__version__
871 f3ddb705 Giorgos Verigakis
        exit(0)
872 f3ddb705 Giorgos Verigakis
    
873 f3ddb705 Giorgos Verigakis
    if '--config' in args:
874 f3ddb705 Giorgos Verigakis
        config_path = args.grouped['--config'].get(0)
875 f3ddb705 Giorgos Verigakis
    else:
876 f3ddb705 Giorgos Verigakis
        config_path = os.environ.get(CONFIG_ENV, CONFIG_PATH)
877 21cf6792 Giorgos Verigakis
    
878 f3ddb705 Giorgos Verigakis
    config = Config(config_path)
879 eb3ca8ca Giorgos Verigakis
    
880 f3ddb705 Giorgos Verigakis
    for option in args.grouped.get('-o', []):
881 f3ddb705 Giorgos Verigakis
        keypath, sep, val = option.partition('=')
882 f3ddb705 Giorgos Verigakis
        if not sep:
883 f3ddb705 Giorgos Verigakis
            log.error("Invalid option '%s'", option)
884 f3ddb705 Giorgos Verigakis
            exit(1)
885 f3ddb705 Giorgos Verigakis
        section, sep, key = keypath.partition('.')
886 f3ddb705 Giorgos Verigakis
        if not sep:
887 f3ddb705 Giorgos Verigakis
            log.error("Invalid option '%s'", option)
888 f3ddb705 Giorgos Verigakis
            exit(1)
889 f3ddb705 Giorgos Verigakis
        config.override(section.strip(), key.strip(), val.strip())
890 f3ddb705 Giorgos Verigakis
    
891 f3ddb705 Giorgos Verigakis
    apis = set(['config'])
892 43ca98ee Giorgos Verigakis
    for api in ('compute', 'image', 'storage', 'astakos'):
893 f3ddb705 Giorgos Verigakis
        if config.getboolean(api, 'enable'):
894 f3ddb705 Giorgos Verigakis
            apis.add(api)
895 f3ddb705 Giorgos Verigakis
    if config.getboolean('compute', 'cyclades_extensions'):
896 f3ddb705 Giorgos Verigakis
        apis.add('cyclades')
897 f3ddb705 Giorgos Verigakis
    if config.getboolean('storage', 'pithos_extensions'):
898 f3ddb705 Giorgos Verigakis
        apis.add('pithos')
899 f3ddb705 Giorgos Verigakis
    
900 f3ddb705 Giorgos Verigakis
    # Remove commands that belong to APIs that are not included
901 eb3ca8ca Giorgos Verigakis
    for group, group_commands in _commands.items():
902 eb3ca8ca Giorgos Verigakis
        for name, cls in group_commands.items():
903 f3ddb705 Giorgos Verigakis
            if cls.api not in apis:
904 f3ddb705 Giorgos Verigakis
                del group_commands[name]
905 f3ddb705 Giorgos Verigakis
        if not group_commands:
906 f3ddb705 Giorgos Verigakis
            del _commands[group]
907 eb3ca8ca Giorgos Verigakis
    
908 f3ddb705 Giorgos Verigakis
    if not args.grouped['_']:
909 eb3ca8ca Giorgos Verigakis
        parser.print_help()
910 f3ddb705 Giorgos Verigakis
        print_groups()
911 a1c50326 Giorgos Verigakis
        exit(0)
912 eb3ca8ca Giorgos Verigakis
    
913 f3ddb705 Giorgos Verigakis
    group = args.grouped['_'][0]
914 eb3ca8ca Giorgos Verigakis
    
915 f3ddb705 Giorgos Verigakis
    if group not in _commands:
916 eb3ca8ca Giorgos Verigakis
        parser.print_help()
917 f3ddb705 Giorgos Verigakis
        print_groups()
918 a1c50326 Giorgos Verigakis
        exit(1)
919 eb3ca8ca Giorgos Verigakis
    
920 eb3ca8ca Giorgos Verigakis
    parser.usage = '%%prog %s <command> [options]' % group
921 eb3ca8ca Giorgos Verigakis
    
922 f3ddb705 Giorgos Verigakis
    if len(args.grouped['_']) == 1:
923 eb3ca8ca Giorgos Verigakis
        parser.print_help()
924 f3ddb705 Giorgos Verigakis
        print_commands(group)
925 a1c50326 Giorgos Verigakis
        exit(0)
926 eb3ca8ca Giorgos Verigakis
    
927 f3ddb705 Giorgos Verigakis
    name = args.grouped['_'][1]
928 5d1d131b Giorgos Verigakis
    
929 f3ddb705 Giorgos Verigakis
    if name not in _commands[group]:
930 eb3ca8ca Giorgos Verigakis
        parser.print_help()
931 f3ddb705 Giorgos Verigakis
        print_commands(group)
932 a1c50326 Giorgos Verigakis
        exit(1)
933 5d1d131b Giorgos Verigakis
    
934 f3ddb705 Giorgos Verigakis
    cmd = _commands[group][name]()
935 5d1d131b Giorgos Verigakis
    
936 f3ddb705 Giorgos Verigakis
    syntax = '%s [options]' % cmd.syntax if cmd.syntax else '[options]'
937 eb3ca8ca Giorgos Verigakis
    parser.usage = '%%prog %s %s %s' % (group, name, syntax)
938 f3ddb705 Giorgos Verigakis
    parser.description = cmd.description
939 eb3ca8ca Giorgos Verigakis
    parser.epilog = ''
940 f3ddb705 Giorgos Verigakis
    if hasattr(cmd, 'update_parser'):
941 f3ddb705 Giorgos Verigakis
        cmd.update_parser(parser)
942 eb3ca8ca Giorgos Verigakis
    
943 6a0b1658 Giorgos Verigakis
    options, arguments = parser.parse_args(argv)
944 6a0b1658 Giorgos Verigakis
    
945 6a0b1658 Giorgos Verigakis
    if options.help:
946 eb3ca8ca Giorgos Verigakis
        parser.print_help()
947 a1c50326 Giorgos Verigakis
        exit(0)
948 eb3ca8ca Giorgos Verigakis
    
949 6a0b1658 Giorgos Verigakis
    if options.silent:
950 6a0b1658 Giorgos Verigakis
        add_handler('', logging.CRITICAL)
951 6a0b1658 Giorgos Verigakis
    elif options.debug:
952 6a0b1658 Giorgos Verigakis
        add_handler('requests', logging.INFO, prefix='* ')
953 6a0b1658 Giorgos Verigakis
        add_handler('clients.send', logging.DEBUG, prefix='> ')
954 6a0b1658 Giorgos Verigakis
        add_handler('clients.recv', logging.DEBUG, prefix='< ')
955 6a0b1658 Giorgos Verigakis
    elif options.verbose:
956 6a0b1658 Giorgos Verigakis
        add_handler('requests', logging.INFO, prefix='* ')
957 6a0b1658 Giorgos Verigakis
        add_handler('clients.send', logging.INFO, prefix='> ')
958 6a0b1658 Giorgos Verigakis
        add_handler('clients.recv', logging.INFO, prefix='< ')
959 6a0b1658 Giorgos Verigakis
    elif options.include:
960 6a0b1658 Giorgos Verigakis
        add_handler('clients.recv', logging.INFO)
961 6a0b1658 Giorgos Verigakis
    else:
962 6a0b1658 Giorgos Verigakis
        add_handler('', logging.WARNING)
963 5d1d131b Giorgos Verigakis
    
964 f3ddb705 Giorgos Verigakis
    api = cmd.api
965 6a0b1658 Giorgos Verigakis
    if api in ('compute', 'cyclades'):
966 6a0b1658 Giorgos Verigakis
        url = config.get('compute', 'url')
967 6a0b1658 Giorgos Verigakis
        token = config.get('compute', 'token') or config.get('global', 'token')
968 6a0b1658 Giorgos Verigakis
        if config.getboolean('compute', 'cyclades_extensions'):
969 6a0b1658 Giorgos Verigakis
            cmd.client = clients.cyclades(url, token)
970 6a0b1658 Giorgos Verigakis
        else:
971 6a0b1658 Giorgos Verigakis
            cmd.client = clients.compute(url, token)
972 6a0b1658 Giorgos Verigakis
    elif api in ('storage', 'pithos'):
973 6a0b1658 Giorgos Verigakis
        url = config.get('storage', 'url')
974 6a0b1658 Giorgos Verigakis
        token = config.get('storage', 'token') or config.get('global', 'token')
975 6a0b1658 Giorgos Verigakis
        account = config.get('storage', 'account')
976 6a0b1658 Giorgos Verigakis
        container = config.get('storage', 'container')
977 6a0b1658 Giorgos Verigakis
        if config.getboolean('storage', 'pithos_extensions'):
978 6a0b1658 Giorgos Verigakis
            cmd.client = clients.pithos(url, token, account, container)
979 6a0b1658 Giorgos Verigakis
        else:
980 6a0b1658 Giorgos Verigakis
            cmd.client = clients.storage(url, token, account, container)
981 6a0b1658 Giorgos Verigakis
    elif api == 'image':
982 6a0b1658 Giorgos Verigakis
        url = config.get('image', 'url')
983 6a0b1658 Giorgos Verigakis
        token = config.get('image', 'token') or config.get('global', 'token')
984 6a0b1658 Giorgos Verigakis
        cmd.client = clients.image(url, token)
985 43ca98ee Giorgos Verigakis
    elif api == 'astakos':
986 43ca98ee Giorgos Verigakis
        url = config.get('astakos', 'url')
987 43ca98ee Giorgos Verigakis
        token = config.get('astakos', 'token') or config.get('global', 'token')
988 43ca98ee Giorgos Verigakis
        cmd.client = clients.astakos(url, token)
989 6a0b1658 Giorgos Verigakis
    
990 6a0b1658 Giorgos Verigakis
    cmd.options = options
991 6a0b1658 Giorgos Verigakis
    cmd.config = config
992 6a0b1658 Giorgos Verigakis
    
993 5d1d131b Giorgos Verigakis
    try:
994 6a0b1658 Giorgos Verigakis
        ret = cmd.main(*arguments[3:])
995 a1c50326 Giorgos Verigakis
        exit(ret)
996 a6757cbc Giorgos Verigakis
    except TypeError as e:
997 a6757cbc Giorgos Verigakis
        if e.args and e.args[0].startswith('main()'):
998 a6757cbc Giorgos Verigakis
            parser.print_help()
999 a1c50326 Giorgos Verigakis
            exit(1)
1000 a6757cbc Giorgos Verigakis
        else:
1001 a6757cbc Giorgos Verigakis
            raise
1002 6a0b1658 Giorgos Verigakis
    except clients.ClientError as err:
1003 6a0b1658 Giorgos Verigakis
        if err.status == 404:
1004 6a0b1658 Giorgos Verigakis
            color = yellow
1005 6a0b1658 Giorgos Verigakis
        elif 500 <= err.status < 600:
1006 6a0b1658 Giorgos Verigakis
            color = magenta
1007 6a0b1658 Giorgos Verigakis
        else:
1008 6a0b1658 Giorgos Verigakis
            color = red
1009 6a0b1658 Giorgos Verigakis
        
1010 6a0b1658 Giorgos Verigakis
        puts_err(color(err.message))
1011 6a0b1658 Giorgos Verigakis
        if err.details and (options.verbose or options.debug):
1012 6a0b1658 Giorgos Verigakis
            puts_err(err.details)
1013 a1c50326 Giorgos Verigakis
        exit(2)
1014 6a0b1658 Giorgos Verigakis
    except ConnectionError as err:
1015 6a0b1658 Giorgos Verigakis
        puts_err(red("Connection error"))
1016 6a0b1658 Giorgos Verigakis
        exit(1)
1017 5d1d131b Giorgos Verigakis
1018 5d1d131b Giorgos Verigakis
1019 5d1d131b Giorgos Verigakis
if __name__ == '__main__':
1020 a1c50326 Giorgos Verigakis
    main()