Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli.py @ f6d137ea

History | View | Annotate | Download (30.8 kB)

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