Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli.py @ f3ddb705

History | View | Annotate | Download (27.5 kB)

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