Revision de73876b

b/kamaki/cli/__init__.py
57 57
        spec = getargspec(cls.main.im_func)
58 58
        args = spec.args[1:]
59 59
        n = len(args) - len(spec.defaults or ())
60
        required = ' '.join('<%s>' % x\
61
            .replace('____', '[:')\
62
            .replace('___', ':')\
63
            .replace('__', ']').\
64
            replace('_', ' ') for x in args[:n])
65
        optional = ' '.join('[%s]' % x\
66
            .replace('____', '[:')\
67
            .replace('___', ':')\
68
            .replace('__', ']').\
69
            replace('_', ' ') for x in args[n:])
60
        required = ' '.join(
61
            '<%s>' % x.replace(
62
            '____', '[:').replace(
63
            '___', ':').replace(
64
            '__', ']').replace(
65
            '_', ' ') for x in args[:n])
66
        optional = ' '.join(
67
            '[%s]' % x.replace(
68
            '____', '[:').replace(
69
            '___', ':').replace(
70
            '__', ']').replace(
71
            '_', ' ') for x in args[n:])
70 72
        cls.syntax = ' '.join(x for x in [required, optional] if x)
71 73
        if spec.varargs:
72 74
            cls.syntax += ' <%s ...>' % spec.varargs
......
144 146
                kloger.warning('%s failed max_len test' % cls_name)
145 147
            return None
146 148

  
147
        cls.description, sep, cls.long_description\
148
        = cls.__doc__.partition('\n')
149
        (
150
            cls.description, sep, cls.long_description
151
        ) = cls.__doc__.partition('\n')
149 152
        _construct_command_syntax(cls)
150 153

  
151 154
        cmd_tree.add_command(cls_name, cls.description, cls)
......
240 243
            cmds = None
241 244
            try:
242 245
                cmds = [
243
                    cmd for cmd in getattr(pkg, '_commands')\
244
                    if arguments['config'].get(cmd.name, 'cli')
246
                    cmd for cmd in getattr(pkg, '_commands')
247
                        if arguments['config'].get(cmd.name, 'cli')
245 248
                ]
246 249
            except AttributeError:
247 250
                if _debug:
......
260 263

  
261 264
def _load_all_commands(cmd_tree, arguments):
262 265
    _config = arguments['config']
263
    for spec in [spec for spec in _config.get_groups()\
264
            if _config.get(spec, 'cli')]:
266
    for spec in [spec for spec in _config.get_groups()
267
        if _config.get(spec, 'cli')]:
265 268
        try:
266 269
            spec_module = _load_spec_module(spec, arguments, '_commands')
267 270
            spec_commands = getattr(spec_module, '_commands')
......
304 307
    else:
305 308
        parser.syntax += ' <...>'
306 309
    if cmd.has_description:
307
        parser.parser.description = cmd.help\
308
        + (('\n%s' % description) if description else '')
310
        parser.parser.description = cmd.help + (
311
            ('\n%s' % description) if description else '')
309 312
    else:
310 313
        parser.parser.description = description
311 314

  
b/kamaki/cli/argument.py
129 129
        action = 'append' if self.arity < 0\
130 130
            else 'store_true' if self.arity == 0\
131 131
            else 'store'
132
        parser.add_argument(*self.parsed_name, dest=name, action=action,
133
            default=self.default, help=self.help)
132
        parser.add_argument(
133
            *self.parsed_name,
134
            dest=name,
135
            action=action,
136
            default=self.default,
137
            help=self.help)
134 138

  
135 139
    def main(self):
136 140
        """Overide this method to give functionality to your args"""
......
189 193
        for option in options:
190 194
            keypath, sep, val = option.partition('=')
191 195
            if not sep:
192
                raiseCLIError(CLISyntaxError('Argument Syntax Error '),
196
                raiseCLIError(
197
                    CLISyntaxError('Argument Syntax Error '),
193 198
                    details=['%s is missing a "="',
194 199
                    ' (usage: -o section.key=val)' % option]
195 200
                )
......
237 242
        try:
238 243
            self._value = int(newvalue)
239 244
        except ValueError:
240
            raiseCLIError(CLISyntaxError('IntArgument Error',
245
            raiseCLIError(
246
                CLISyntaxError('IntArgument Error',
241 247
                details=['Value %s not an int' % newvalue]))
242 248

  
243 249

  
......
248 254
    :value returns: same date in first of DATE_FORMATS
249 255
    """
250 256

  
251
    DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
257
    DATE_FORMATS = [
258
        "%a %b %d %H:%M:%S %Y",
252 259
        "%A, %d-%b-%y %H:%M:%S GMT",
253 260
        "%a, %d %b %Y %H:%M:%S GMT"]
254 261

  
......
272 279
                continue
273 280
            self._value = t.strftime(self.DATE_FORMATS[0])
274 281
            return
275
        raiseCLIError(None,
282
        raiseCLIError(
283
            None,
276 284
            'Date Argument Error',
277 285
            details='%s not a valid date. correct formats:\n\t%s'\
278 286
            % (datestr, self.INPUT_FORMATS))
......
321 329
        for pair in keyvalue_pairs:
322 330
            key, sep, val = pair.partition('=')
323 331
            if not sep:
324
                raiseCLIError(CLISyntaxError('Argument syntax error '),
332
                raiseCLIError(
333
                    CLISyntaxError('Argument syntax error '),
325 334
                    details='%s is missing a "=" (usage: key1=val1 )\n' % pair)
326 335
            self._value[key.strip()] = val.strip()
327 336

  
......
374 383
            mybar.finish()
375 384

  
376 385

  
377
_arguments = dict(config=_config_arg,
386
_arguments = dict(
387
    config=_config_arg,
378 388
    help=Argument(0, 'Show help message', ('-h', '--help')),
379 389
    debug=FlagArgument('Include debug output', ('-d', '--debug')),
380
    include=FlagArgument('Include raw connection data in the output',
390
    include=FlagArgument(
391
        'Include raw connection data in the output',
381 392
        ('-i', '--include')),
382 393
    silent=FlagArgument('Do not output anything', ('-s', '--silent')),
383 394
    verbose=FlagArgument('More info at response', ('-v', '--verbose')),
384 395
    version=VersionArgument('Print current version', ('-V', '--version')),
385
    options=CmdLineConfigArgument(_config_arg,
396
    options=CmdLineConfigArgument(
397
        _config_arg,
386 398
        'Override a config value',
387 399
        ('-o', '--options'))
388 400
)
......
416 428
        :param arguments: (dict) if given, overrides the global _argument as
417 429
            the parsers arguments specification
418 430
        """
419
        self.parser = ArgumentParser(add_help=False,
431
        self.parser = ArgumentParser(
432
            add_help=False,
420 433
            formatter_class=RawDescriptionHelpFormatter)
421 434
        self.syntax = '%s <cmd_group> [<cmd_subbroup> ...] <cmd>' % exe
422 435
        if arguments:
b/kamaki/cli/command_shell.py
79 79

  
80 80
    def precmd(self, line):
81 81
        if line.startswith('/'):
82
            cur_cmd_path = self.prompt.replace(' ',
83
                '_')[len(self._prefix):-len(self._suffix)]
82
            start, end = len(self._prefix), -len(self._suffix)
83
            cur_cmd_path = self.prompt.replace(' ', '_')[start:end]
84 84
            if cur_cmd_path != self.cmd_tree.name:
85 85
                cur_cmd = self.cmd_tree.get_command(cur_cmd_path)
86 86
                self._context_stack.append(self._backup())
......
94 94
        return line
95 95

  
96 96
    def greet(self, version):
97
        print('kamaki v%s - Interactive Shell\n\t(exit or ^D to exit)\n'\
98
            % version)
97
        print('kamaki v%s - Interactive Shell\n' % version)
98
        print('\t\exit     \tterminate kamaki')
99
        print('\texit or ^D\texit context')
100
        print('\t? or help \tavailable commands')
101
        print('\t?command  \thelp on command')
102
        print('\t!<command>\texecute OS shell command')
103
        print('')
99 104

  
100 105
    def set_prompt(self, new_prompt):
101 106
        self.prompt = '%s%s%s' % (self._prefix, new_prompt, self._suffix)
......
111 116

  
112 117
    def do_exit(self, line):
113 118
        print('')
114
        if self.prompt[len(self._prefix):-len(self._suffix)]\
115
        == self.cmd_tree.name:
119
        start, end = len(self._prefix), -len(self._suffix)
120
        if self.prompt[start:end] == self.cmd_tree.name:
116 121
            exit(0)
117 122
        return True
118 123

  
......
198 203
                    cmd_parser.parse(cmd_args)
199 204

  
200 205
                    for name, arg in instance.arguments.items():
201
                        arg.value = getattr(cmd_parser.parsed, name,
206
                        arg.value = getattr(
207
                            cmd_parser.parsed,
208
                            name,
202 209
                            arg.default)
203 210

  
204 211
                    exec_cmd(
......
209 216
                        #    if not term.startswith('-')],
210 217
                except (ClientError, CLIError) as err:
211 218
                    print_error_message(err)
212
            elif ('-h' in cmd_args or '--help' in cmd_args) \
213
            or len(cmd_args):  # print options
219
            elif ('-h' in cmd_args or '--help' in cmd_args) or len(cmd_args):
220
                # print options
214 221
                print('%s' % cmd.help)
215 222
                print_subcommands_help(cmd)
216 223
            else:  # change context
......
259 266
                instance = cls(dict(arguments))
260 267
                empty, sep, subname = subcmd.path.partition(cmd.path)
261 268
                cmd_name = '%s %s' % (cmd.name, subname.replace('_', ' '))
262
                print('\n%s\nSyntax:\t%s %s'\
263
                    % (cls.description, cmd_name, cls.syntax))
269
                print('\n%s\nSyntax:\t%s %s' % (
270
                    cls.description,
271
                    cmd_name,
272
                    cls.syntax))
264 273
                cmd_args = {}
265 274
                for arg in instance.arguments.values():
266 275
                    cmd_args[','.join(arg.parsed_name)] = arg.help
b/kamaki/cli/command_tree.py
115 115
        return cmd, args[index:]
116 116

  
117 117
    def pretty_print(self, recursive=False):
118
        print('Path: %s (Name: %s) is_cmd: %s\n\thelp: %s'\
119
            % (self.path, self.name, self.is_command, self.help))
118
        print('Path: %s (Name: %s) is_cmd: %s\n\thelp: %s' % (
119
            self.path,
120
            self.name,
121
            self.is_command,
122
            self.help))
120 123
        for cmd in self.get_subcommands():
121 124
            cmd.pretty_print(recursive)
122 125

  
......
204 207
        return self._all_commands[path].get_class()
205 208

  
206 209
    def get_subnames(self, path=None):
207
        return self.get_group_names() if path in (None, '') \
208
        else self._all_commands[path].get_subnames()
210
        if path in (None, ''):
211
            return self.get_group_names()
212
        return self._all_commands[path].get_subnames()
209 213

  
210 214
    def get_subcommands(self, path=None):
211
        return self.get_groups() if path in (None, '') \
212
        else self._all_commands[path].get_subcommands()
215
        if path in (None, ''):
216
            return self.get_groups()
217
        return self._all_commands[path].get_subcommands()
213 218

  
214 219
    def get_parent(self, path):
215 220
        if '_' not in path:
b/kamaki/cli/commands/cyclades_cli.py
44 44
from os.path import exists
45 45

  
46 46

  
47
server_cmds = CommandTree('server',
48
    'Compute/Cyclades API server commands')
49
flavor_cmds = CommandTree('flavor',
50
    'Compute/Cyclades API flavor commands')
51
image_cmds = CommandTree('image',
52
    'Compute/Cyclades or Glance API image commands')
53
network_cmds = CommandTree('network',
54
    'Compute/Cyclades API network commands')
47
server_cmds = CommandTree('server', 'Compute/Cyclades API server commands')
48
flavor_cmds = CommandTree('flavor', 'Compute/Cyclades API flavor commands')
49
image_cmds = CommandTree('image', 'Cyclades/Plankton API image commands')
50
network_cmds = CommandTree('network', 'Compute/Cyclades API network commands')
55 51
_commands = [server_cmds, flavor_cmds, image_cmds, network_cmds]
56 52

  
57 53

  
......
186 182
        for i, terms in enumerate(newvalue):
187 183
            termlist = terms.split(',')
188 184
            if len(termlist) > 5:
189
                raiseCLIError(
190
                CLISyntaxError('Wrong number of terms (should be 1 to 5)'),
191
                details=howto_personality)
185
                msg = 'Wrong number of terms (should be 1 to 5)'
186
                raiseCLIError(CLISyntaxError(msg), details=howto_personality)
192 187
            path = termlist[0]
193 188
            if not exists(path):
194
                raiseCLIError(None,
189
                raiseCLIError(
190
                    None,
195 191
                    '--personality: File %s does not exist' % path,
196 192
                    importance=1,
197 193
                    details=howto_personality)
......
497 493
        detail=FlagArgument('show detailed output', '-l'),
498 494
        limit=IntArgument('limit the number of flavors to list', '-n'),
499 495
        more=FlagArgument(
500
        'output results in pages (-n to set items per page, default 10)',
501
        '--more')
496
            'output results in pages (-n to set items per page, default 10)',
497
            '--more')
502 498
    )
503 499

  
504 500
    @errors.generic.all
......
580 576
        if self['detail']:
581 577
            self._make_results_pretty(networks)
582 578
        if self['more']:
583
            print_items(networks,
584
                page_size=self['limit'] if self['limit'] else 10)
579
            print_items(networks, page_size=self['limit'] or 10)
585 580
        elif self['limit']:
586 581
            print_items(networks[:self['limit']])
587 582
        else:
......
607 602
    @errors.cyclades.connection
608 603
    @errors.cyclades.network_max
609 604
    def _run(self, name):
610
        r = self.client.create_network(name,
605
        r = self.client.create_network(
606
            name,
611 607
            cidr=self['cidr'],
612 608
            gateway=self['gateway'],
613 609
            dhcp=self['dhcp'],
b/kamaki/cli/commands/errors.py
82 82
                    if not client:
83 83
                        raise
84 84
                    url = getattr(client, 'base_url', '<empty>')
85
                    raiseCLIError(ce,
86
                        'Invalid service url %s' % url,
87
                        details=[
85
                    msg = 'Invalid service url %s' % url
86
                    raiseCLIError(ce, msg, details=[
88 87
                        'Please, check if service url is correctly set',
89 88
                        '* to get current url: /config get compute.url',
90 89
                        '* to set url: /config set compute.url <URL>'])
......
112 111
                kloger.warning(
113 112
                    'No permanent token (try: kamaki config set token <tkn>)')
114 113
            if not getattr(client, 'base_url', False):
115
                raise CLIError('Missing astakos server URL',
116
                    importance=3,
117
                    details=['Check if astakos.url is set correctly',
114
                msg = 'Missing astakos server URL'
115
                raise CLIError(msg, importance=3, details=[
116
                    'Check if astakos.url is set correctly',
118 117
                    'To get astakos url:   /config get astakos.url',
119 118
                    'To set astakos url:   /config set astakos.url <URL>'])
120 119
            return r
......
128 127
            except ClientError as ce:
129 128
                if ce.status == 401:
130 129
                    token = kwargs.get('custom_token', 0) or self.client.token
131
                    raiseCLIError(ce,
132
                        'Authorization failed for token %s' % token if token\
133
                            else 'No token provided',
134
                        details=[] if token else this._token_details)
130
                    msg = (
131
                        'Authorization failed for token %s' % token
132
                    ) if token else 'No token provided',
133
                    details = [] if token else this._token_details
134
                    raiseCLIError(ce, msg, details=details)
135 135
            self._raise = foo
136 136
            return r
137 137
        return _raise
......
151 151
    def _get_cmd_ids(this, foo):
152 152
        def _raise(self, cmd_ids, *args, **kwargs):
153 153
            if not cmd_ids:
154
                raise CLISyntaxError('Usage: <id1|id1-id2> [id3|id3-id4] ...',
154
                raise CLISyntaxError(
155
                    'Usage: <id1|id1-id2> [id3|id3-id4] ...',
155 156
                    details=self.__doc__.split('\n'))
156 157
            return foo(self, cmd_ids, *args, **kwargs)
157 158
        return _raise
......
193 194
                network_id = int(network_id)
194 195
                return foo(self, *args, **kwargs)
195 196
            except ValueError as ve:
196
                raiseCLIError(ve, 'Invalid network id %s ' % network_id,
197
                    details='network id must be a positive integer',
198
                    importance=1)
197
                msg = 'Invalid network id %s ' % network_id
198
                details = ['network id must be a positive integer']
199
                raiseCLIError(ve, msg, details=details, importance=1)
199 200
            except ClientError as ce:
200
                if network_id and ce.status == 404 and\
201
                    'network' in ('%s' % ce).lower():
202
                        raiseCLIError(ce,
203
                            'No network with id %s found' % network_id,
204
                            details=this.about_network_id)
201
                if network_id and ce.status == 404 and (
202
                    'network' in ('%s' % ce).lower()
203
                ):
204
                    msg = 'No network with id %s found' % network_id,
205
                    raiseCLIError(ce, msg, details=this.about_network_id)
205 206
                raise
206 207
        return _raise
207 208

  
......
212 213
                return foo(self, *args, **kwargs)
213 214
            except ClientError as ce:
214 215
                if ce.status == 413:
215
                    raiseCLIError(ce,
216
                        'Cannot create another network',
217
                        details=['Maximum number of networks reached',
218
                            '* to get a list of networks: /network list',
219
                            '* to delete a network: /network delete <net id>'])
216
                    msg = 'Cannot create another network',
217
                    details = ['Maximum number of networks reached',
218
                        '* to get a list of networks: /network list',
219
                        '* to delete a network: /network delete <net id>']
220
                    raiseCLIError(ce, msg, details=details)
220 221
                raise
221 222
        return _raise
222 223

  
......
228 229
                return foo(self, *args, **kwargs)
229 230
            except ClientError as ce:
230 231
                if network_id and ce.status == 400:
231
                    raiseCLIError(ce,
232
                        'Network with id %s does not exist' % network_id,
233
                        details=self.about_network_id)
232
                    msg = 'Network with id %s does not exist' % network_id,
233
                    raiseCLIError(ce, msg, details=self.about_network_id)
234 234
                elif network_id or ce.status == 421:
235
                    raiseCLIError(ce,
236
                        'Network with id %s is in use' % network_id,
237
                        details=[
238
                            'Disconnect all nics/VMs of this network first',
239
                            '* to get nics: /network info %s' % network_id,
240
                            '.  (under "attachments" section)',
241
                            '* to disconnect: /network disconnect <nic id>'])
235
                    msg = 'Network with id %s is in use' % network_id,
236
                    raiseCLIError(ce, msg, details=[
237
                        'Disconnect all nics/VMs of this network first',
238
                        '* to get nics: /network info %s' % network_id,
239
                        '.  (under "attachments" section)',
240
                        '* to disconnect: /network disconnect <nic id>'])
242 241
                raise
243 242
        return _raise
244 243

  
......
250 249
                flavor_id = int(flavor_id)
251 250
                return foo(self, *args, **kwargs)
252 251
            except ValueError as ve:
253
                raiseCLIError(ve, 'Invalid flavor id %s ' % flavor_id,
254
                    details='Flavor id must be a positive integer',
255
                    importance=1)
252
                msg = 'Invalid flavor id %s ' % flavor_id,
253
                details = 'Flavor id must be a positive integer',
254
                raiseCLIError(ve, msg, details=details, importance=1)
256 255
            except ClientError as ce:
257
                if flavor_id and ce.status == 404 and\
258
                    'flavor' in ('%s' % ce).lower():
259
                        raiseCLIError(ce,
260
                            'No flavor with id %s found' % flavor_id,
261
                            details=this.about_flavor_id)
256
                if flavor_id and ce.status == 404 and (
257
                    'flavor' in ('%s' % ce).lower()
258
                ):
259
                        msg = 'No flavor with id %s found' % flavor_id,
260
                        raiseCLIError(ce, msg, details=this.about_flavor_id)
262 261
                raise
263 262
        return _raise
264 263

  
......
270 269
                server_id = int(server_id)
271 270
                return foo(self, *args, **kwargs)
272 271
            except ValueError as ve:
273
                raiseCLIError(ve,
274
                    'Invalid server(VM) id %s' % server_id,
275
                    details=['id must be a positive integer'],
276
                    importance=1)
272
                msg = 'Invalid server(VM) id %s' % server_id,
273
                details = ['id must be a positive integer'],
274
                raiseCLIError(ve, msg, details=details, importance=1)
277 275
            except ClientError as ce:
278 276
                err_msg = ('%s' % ce).lower()
279
                if (ce.status == 404 and 'server' in err_msg)\
280
                or (ce.status == 400 and 'not found' in err_msg):
281
                    raiseCLIError(ce,
282
                        'server(VM) with id %s not found' % server_id,
283
                        details=[
284
                            '* to get existing VM ids: /server list',
285
                            '* to get VM details: /server info <VM id>'])
277
                if (
278
                    ce.status == 404 and 'server' in err_msg
279
                ) or (
280
                    ce.status == 400 and 'not found' in err_msg
281
                ):
282
                    msg = 'server(VM) with id %s not found' % server_id,
283
                    raiseCLIError(ce, msg, details=[
284
                        '* to get existing VM ids: /server list',
285
                        '* to get VM details: /server info <VM id>'])
286 286
                raise
287 287
        return _raise
288 288

  
......
293 293
            try:
294 294
                return foo(self, *args, **kwargs)
295 295
            except ClientError as ce:
296
                if ce.status == 400 and profile\
297
                and 'firewall' in ('%s' % ce).lower():
298
                    raiseCLIError(ce,
299
                        '%s is an invalid firewall profile term' % profile,
300
                        details=['Try one of the following:',
301
                            '* DISABLED: Shutdown firewall',
302
                            '* ENABLED: Firewall in normal mode',
303
                            '* PROTECTED: Firewall in secure mode'])
296
                if ce.status == 400 and profile and (
297
                    'firewall' in ('%s' % ce).lower()
298
                ):
299
                    msg = '%s is an invalid firewall profile term' % profile
300
                    raiseCLIError(ce, msg, details=[
301
                        'Try one of the following:',
302
                        '* DISABLED: Shutdown firewall',
303
                        '* ENABLED: Firewall in normal mode',
304
                        '* PROTECTED: Firewall in secure mode'])
304 305
                raise
305 306
        return _raise
306 307

  
......
311 312
                return foo(self, *args, **kwargs)
312 313
            except ClientError as ce:
313 314
                nic_id = kwargs.get('nic_id', None)
314
                if nic_id and ce.status == 404\
315
                and 'network interface' in ('%s' % ce).lower():
315
                if nic_id and ce.status == 404 and (
316
                    'network interface' in ('%s' % ce).lower()
317
                ):
316 318
                    server_id = kwargs.get('server_id', '<no server>')
317 319
                    err_msg = 'No nic %s on server(VM) with id %s' % (
318 320
                        nic_id,
......
333 335
                return foo(self, *args, **kwargs)
334 336
            except IndexError as ie:
335 337
                nic_id = kwargs.get('nic_id', None)
336
                raiseCLIError(ie,
337
                    'Invalid format for network interface (nic) %s' % nic_id,
338
                    importance=1,
339
                    details=[
340
                        'nid_id format: nic-<server id>-<nic id>',
341
                        '* get nics of a network: /network info <net id>',
342
                        '    (listed the "attachments" section)'])
338
                msg = 'Invalid format for network interface (nic) %s' % nic_id
339
                raiseCLIError(ie, msg, importance=1, details=[
340
                    'nid_id format: nic-<server id>-<nic id>',
341
                    '* get nics of a network: /network info <net id>',
342
                    '    (listed the "attachments" section)'])
343 343
        return _raise
344 344

  
345 345
    @classmethod
......
349 349
            try:
350 350
                foo(self, *args, **kwargs)
351 351
            except ClientError as ce:
352
                if key and ce.status == 404\
353
                    and 'metadata' in ('%s' % ce).lower():
352
                if key and ce.status == 404 and (
353
                    'metadata' in ('%s' % ce).lower()
354
                ):
354 355
                        raiseCLIError(ce, 'No VM metadata with key %s' % key)
355 356
                raise
356 357
        return _raise
......
358 359

  
359 360
class plankton(object):
360 361

  
361
    about_image_id = ['How to pick a suitable image:',
362
    about_image_id = [
363
        'How to pick a suitable image:',
362 364
        '* get a list of image ids: /image list',
363 365
        '* details of image: /flavor info <image id>']
364 366

  
......
373 375
            try:
374 376
                foo(self, *args, **kwargs)
375 377
            except ClientError as ce:
376
                if image_id and (ce.status == 404\
377
                    or (ce.status == 400 and
378
                        'image not found' in ('%s' % ce).lower())\
379
                    or ce.status == 411):
380
                        raiseCLIError(ce,
381
                            'No image with id %s found' % image_id,
382
                            details=this.about_image_id)
378
                if image_id and (
379
                    ce.status == 404
380
                    or (
381
                        ce.status == 400
382
                        and 'image not found' in ('%s' % ce).lower())
383
                    or ce.status == 411
384
                ):
385
                        msg = 'No image with id %s found' % image_id
386
                        raiseCLIError(ce, msg, details=this.about_image_id)
383 387
                raise
384 388
        return _raise
385 389

  
......
390 394
            try:
391 395
                foo(self, *args, **kwargs)
392 396
            except ClientError as ce:
393
                if ce.status == 404 or ((ce.status == 400\
394
                    and 'metadata' in ('%s' % ce).lower())):
395
                        raiseCLIError(ce,
396
                            'No properties with key %s in this image' % key)
397
                if ce.status == 404 or (
398
                    (ce.status == 400 and 'metadata' in ('%s' % ce).lower())):
399
                        msg = 'No properties with key %s in this image' % key
400
                        raiseCLIError(ce, msg)
397 401
                raise
398 402
        return _raise
399 403

  
400 404

  
401 405
class pithos(object):
402
    container_howto = ['To specify a container:',
403
    '  1. Set store.container variable (permanent)',
404
    '     /config set store.container <container>',
405
    '  2. --container=<container> (temporary, overrides 1)',
406
    '  3. Use the container:path format (temporary, overrides all)',
407
    'For a list of containers: /store list']
406
    container_howto = [
407
       "" 'To specify a container:',
408
        '  1. Set store.container variable (permanent)',
409
        '     /config set store.container <container>',
410
        '  2. --container=<container> (temporary, overrides 1)',
411
        '  3. Use the container:path format (temporary, overrides all)',
412
        'For a list of containers: /store list']
408 413

  
409 414
    @classmethod
410 415
    def connection(this, foo):
......
435 440
                return foo(self, *args, **kwargs)
436 441
            except ClientError as ce:
437 442
                if ce.status == 404 and 'container' in ('%s' % ce).lower():
438
                        cont = '%s or %s' % (self.container, dst_cont)\
439
                        if dst_cont else self.container
440
                        raiseCLIError(ce,
441
                            'Is container %s in current account?' % (cont),
442
                            details=this.container_howto)
443
                        cont = ('%s or %s' % (
444
                            self.container,
445
                            dst_cont)) if dst_cont else self.container
446
                        msg = 'Is container %s in current account?' % (cont),
447
                        raiseCLIError(ce, msg, details=this.container_howto)
443 448
                raise
444 449
        return _raise
445 450

  
......
450 455
            try:
451 456
                return foo(self, *args, **kwargs)
452 457
            except IOError as ioe:
453
                raiseCLIError(ioe,
454
                    'Failed to access file %s' % local_path,
455
                    importance=2)
458
                msg = 'Failed to access file %s' % local_path,
459
                raiseCLIError(ioe, msg, importance=2)
456 460
        return _raise
457 461

  
458 462
    @classmethod
......
462 466
                return foo(self, *args, **kwargs)
463 467
            except ClientError as ce:
464 468
                err_msg = ('%s' % ce).lower()
465
                if (ce.status == 404 or ce.status == 500)\
466
                and 'object' in err_msg and 'not' in err_msg:
467
                    raiseCLIError(ce,
468
                        'No object %s in container %s'\
469
                        % (self.path, self.container),
470
                        details=this.container_howto)
469
                if (
470
                    ce.status == 404 or ce.status == 500
471
                ) and 'object' in err_msg and 'not' in err_msg:
472
                    msg = 'No object %s in container %s' % (
473
                        self.path,
474
                        self.container)
475
                    raiseCLIError(ce, msg, details=this.container_howto)
471 476
                raise
472 477
        return _raise
473 478

  
......
481 486
                try:
482 487
                    size = int(size)
483 488
                except ValueError as ve:
484
                    raiseCLIError(ve,
485
                        'Invalid file size %s ' % size,
486
                        details=['size must be a positive integer'],
487
                        importance=1)
489
                    msg = 'Invalid file size %s ' % size
490
                    details = ['size must be a positive integer']
491
                    raiseCLIError(ve, msg, details=details, importance=1)
488 492
            else:
489 493
                try:
490 494
                    start = int(start)
491 495
                except ValueError as e:
492
                    raiseCLIError(e,
493
                        'Invalid start value %s in range' % start,
494
                        details=['size must be a positive integer'],
495
                        importance=1)
496
                    msg = 'Invalid start value %s in range' % start,
497
                    details = ['size must be a positive integer'],
498
                    raiseCLIError(e, msg, details=details, importance=1)
496 499
                try:
497 500
                    end = int(end)
498 501
                except ValueError as e:
499
                    raiseCLIError(e,
500
                        'Invalid end value %s in range' % end,
501
                        details=['size must be a positive integer'],
502
                        importance=1)
502
                    msg = 'Invalid end value %s in range' % end
503
                    details = ['size must be a positive integer']
504
                    raiseCLIError(e, msg, details=details, importance=1)
503 505
                if start > end:
504 506
                    raiseCLIError(
505 507
                        'Invalid range %s-%s' % (start, end),
......
511 513
            except ClientError as ce:
512 514
                err_msg = ('%s' % ce).lower()
513 515
                if size and (ce.status == 416 or
514
                (ce.status == 400 and\
516
                (ce.status == 400 and
515 517
                    'object length is smaller than range length' in err_msg)):
516
                    raiseCLIError(ce,
517
                        'Remote object %s:%s <= %s %s' % (
518
                    raiseCLIError(ce, 'Remote object %s:%s <= %s %s' % (
518 519
                            self.container,
519 520
                            self.path,
520 521
                            format_size(size),
b/kamaki/cli/commands/history_cli.py
172 172
            instance.config = self.config
173 173
            prs = ArgumentParseManager(cmd.path.split(),
174 174
                dict(instance.arguments))
175
            prs.syntax = '%s %s' % (cmd.path.replace('_', ' '),
175
            prs.syntax = '%s %s' % (
176
                cmd.path.replace('_', ' '),
176 177
                cmd.get_class().syntax)
177 178
            prs.parse(args)
178 179
            exec_cmd(instance, prs.unparsed, prs.parser.print_help)
b/kamaki/cli/commands/image_cli.py
100 100
                'name',
101 101
                'size_min',
102 102
                'size_max',
103
                'status'
104
            ]).intersection(self.arguments):
103
                'status']).intersection(self.arguments):
105 104
            filters[arg] = self[arg]
106 105

  
107 106
        order = self['order']
......
190 189
                'id',
191 190
                'owner',
192 191
                'size',
193
                'is_public'
194
            ]).intersection(self.arguments):
192
                'is_public']).intersection(self.arguments):
195 193
            params[key] = self[key]
196 194

  
197 195
            properties = self['properties']
b/kamaki/cli/commands/pithos_cli.py
81 81
            '  to set the service url: /config set store.url <url>',
82 82
            ' ',
83 83
            '  to get authentication token: /config get token',
84
            '  to set authentication token: /config set token <token>'
85
            ])
84
            '  to set authentication token: /config set token <token>'])
86 85
    elif e.status == 413:
87 86
        raiseCLIError(e, details=[
88 87
            'Get quotas:',
89 88
            '- total quota:      /store quota',
90 89
            '- container quota:  /store quota <container>',
91 90
            'Users shall set a higher container quota, if available:',
92
            '-                  /store setquota <quota>[unit] <container>'
93
            ])
91
            '-                  /store setquota <quota>[unit] <container>'])
94 92

  
95 93

  
96 94
class DelimiterArgument(ValueArgument):
......
142 140
                    details='Incorrect format',
143 141
                    importance=1)
144 142
            if key.lower() not in ('read', 'write'):
145
                raiseCLIError(err, 'Error in --sharing',
146
                    details='Invalid permission key %s' % key,
147
                    importance=1)
143
                msg = 'Error in --sharing'
144
                raiseCLIError(err, msg, importance=1, details=[
145
                    'Invalid permission key %s' % key])
148 146
            val_list = val.split(',')
149 147
            if not key in perms:
150 148
                perms[key] = []
......
189 187
        self._set_account()
190 188
        self.container = self.config.get('store', 'container')\
191 189
            or self.config.get('global', 'container')
192
        self.client = PithosClient(base_url=self.base_url,
190
        self.client = PithosClient(
191
            base_url=self.base_url,
193 192
            token=self.token,
194 193
            account=self.account,
195 194
            container=self.container)
......
245 244
        dst = dest_container_path.split(':')
246 245
        return (dst[0], dst[1]) if len(dst) > 1 else (None, dst[0])
247 246

  
248
    def extract_container_and_path(self,
247
    def extract_container_and_path(
248
        self,
249 249
        container_with_path,
250 250
        path_is_optional=True):
251 251
        """Contains all heuristics for deciding what should be used as
......
366 366
    def print_objects(self, object_list):
367 367
        limit = int(self['limit']) if self['limit'] > 0 else len(object_list)
368 368
        for index, obj in enumerate(object_list):
369
            if (self['exact_match'] and self.path and\
370
                obj['name'] != self.path) or 'content_type' not in obj:
369
            if (self['exact_match'] and self.path and (
370
                obj['name'] != self.path) or 'content_type' not in obj):
371 371
                continue
372 372
            pretty_obj = obj.copy()
373 373
            index += 1
......
407 407
                print
408 408
            else:
409 409
                if 'count' in container and 'bytes' in container:
410
                    print('%s (%s, %s objects)'\
411
                    % (cname, size, container['count']))
410
                    print('%s (%s, %s objects)' % (
411
                        cname,
412
                        size,
413
                        container['count']))
412 414
                else:
413 415
                    print(cname)
414 416
            if self['more']:
......
511 513
    @errors.pithos.connection
512 514
    @errors.pithos.container
513 515
    def _run(self):
514
        self.client.container_put(quota=self['quota'],
516
        self.client.container_put(
517
            quota=self['quota'],
515 518
            versioning=self['versioning'],
516 519
            metadata=self['meta'])
517 520

  
......
573 576
        if len(r.json) == 1:
574 577
            obj = r.json[0]
575 578
            return [(obj['name'], dst_path or obj['name'])]
576
        return [(obj['name'], '%s%s' % (
579
        return [(
580
            obj['name'],
581
            '%s%s' % (
577 582
                    dst_path,
578 583
                    obj['name'][len(self.path) if self['replace'] else 0:])
579
                ) for obj in r.json]
584
        ) for obj in r.json]
580 585

  
581 586
    @errors.generic.all
582 587
    @errors.pithos.connection
......
598 603
                self.path,
599 604
                self.container))
600 605

  
601
    def main(self,
606
    def main(
607
        self,
602 608
        source_container___path,
603 609
        destination_container___path=None):
604 610
        super(self.__class__, self)._run(
......
652 658
        if len(r.json) == 1:
653 659
            obj = r.json[0]
654 660
            return [(obj['name'], dst_path or obj['name'])]
655
        return [(obj['name'], '%s%s' % (
656
                    dst_path,
657
                    obj['name'][len(self.path) if self['replace'] else 0:])
658
                ) for obj in r.json]
661
        return [(
662
            obj['name'],
663
            '%s%s' % (
664
                dst_path,
665
                obj['name'][len(self.path) if self['replace'] else 0:]
666
            )) for obj in r.json]
659 667

  
660 668
    @errors.generic.all
661 669
    @errors.pithos.connection
......
677 685
                self.path,
678 686
                self.container))
679 687

  
680
    def main(self,
688
    def main(
689
        self,
681 690
        source_container___path,
682 691
        destination_container___path=None):
683 692
        super(self.__class__, self)._run(
......
823 832
            '--content-type',
824 833
            default='application/octet-stream'),
825 834
        sharing=SharingArgument(
826
            'define object sharing policy \n' +\
827
            '    ( "read=user1,grp1,user2,... write=user1,grp2,..." )',
835
            '\n'.join([
836
                'define object sharing policy',
837
                '    ( "read=user1,grp1,user2,... write=user1,grp2,..." )']),
828 838
            '--sharing'),
829 839
        public=FlagArgument('make object publicly accessible', '--public')
830 840
    )
......
867 877
            '--content-disposition'),
868 878
        content_type=ValueArgument('specify content type', '--content-type'),
869 879
        sharing=SharingArgument(
870
            help='define sharing object policy \n' +\
871
            '( "read=user1,grp1,user2,... write=user1,grp2,... )',
880
            help='\n'.join([
881
                'define sharing object policy',
882
                '( "read=user1,grp1,user2,... write=user1,grp2,... )']),
872 883
            parsed_name='--sharing'),
873 884
        public=FlagArgument('make object publicly accessible', '--public'),
874 885
        poolsize=IntArgument('set pool size', '--with-pool-size'),
......
928 939
                    if progress_bar:
929 940
                        hash_bar = progress_bar.clone()
930 941
                        hash_cb = hash_bar.get_generator(
931
                                    'Calculating block hashes')
942
                            'Calculating block hashes'
943
                        )
932 944
                    else:
933 945
                        hash_cb = None
934 946
                    self.client.upload_object(
......
1049 1061
                if_unmodified_since=self['if_unmodified_since'])
1050 1062
            return [(
1051 1063
                '%s/%s' % (outpath, remote['name']),
1052
                    None if self._is_dir(remote) else remote['name'])\
1053
                for remote in remotes.json]
1064
                None if self._is_dir(remote) else remote['name']
1065
            ) for remote in remotes.json]
1054 1066
        raiseCLIError('Illegal destination location %s' % local_path)
1055 1067

  
1056 1068
    @errors.generic.all
......
1201 1213
    @errors.pithos.object_path
1202 1214
    def _run(self):
1203 1215
        if self.path:
1204
            if self['yes'] or ask_user(
1205
                'Delete %s:%s ?' % (self.container, self.path)):
1216
            if self['yes'] or ask_user('Delete %s:%s ?' % (
1217
                self.container,
1218
                self.path)):
1206 1219
                self.client.del_object(
1207 1220
                    self.path,
1208 1221
                    until=self['until'],
......
1210 1223
            else:
1211 1224
                print('Aborted')
1212 1225
        else:
1213
            ask_msg = 'Delete contents of container'\
1214
            if self['recursive'] else 'Delete container'
1226
            if self['resursive']:
1227
                ask_msg = 'Delete container contents'
1228
            else:
1229
                ask_msg = 'Delete container'
1215 1230
            if self['yes'] or ask_user('%s %s ?' % (ask_msg, self.container)):
1216 1231
                self.client.del_container(
1217 1232
                    until=self['until'],
......
1344 1359
                read = False
1345 1360
                write = False
1346 1361
        if not (read or write):
1347
            raiseCLIError(None,
1348
            'Usage:\tread=<groups,users> write=<groups,users>')
1362
            msg = 'Usage:\tread=<groups,users> write=<groups,users>'
1363
            raiseCLIError(None, msg)
1349 1364
        return (read, write)
1350 1365

  
1351 1366
    @errors.generic.all
......
1459 1474
                    r['object-meta'] = pretty_keys(ometa, '-')
1460 1475
        else:
1461 1476
            if self['detail']:
1462
                r = self.client.get_object_info(self.path,
1477
                r = self.client.get_object_info(
1478
                    self.path,
1463 1479
                    version=self['object_version'])
1464 1480
            else:
1465
                r = self.client.get_object_meta(self.path,
1481
                r = self.client.get_object_meta(
1482
                    self.path,
1466 1483
                    version=self['object_version'])
1467 1484
            if r:
1468 1485
                r = pretty_keys(pretty_keys(r, '-'))
......
1529 1546

  
1530 1547
    arguments = dict(
1531 1548
        in_bytes=FlagArgument('Show result in bytes', ('-b', '--bytes'))
1532
        )
1549
    )
1533 1550

  
1534 1551
    @errors.generic.all
1535 1552
    @errors.pithos.connection
......
1574 1591
            try:
1575 1592
                return to_bytes(quota, format)
1576 1593
            except Exception as qe:
1577
                raiseCLIError(qe,
1578
                    'Failed to convert %s to bytes' % user_input,
1579
                    details=[
1580
                        'Syntax: setquota <quota>[format] [container]',
1581
                        'e.g.: setquota 2.3GB mycontainer',
1582
                        'Acceptable formats:',
1583
                        '(*1024): B, KiB, MiB, GiB, TiB',
1584
                        '(*1000): B, KB, MB, GB, TB'])
1594
                msg = 'Failed to convert %s to bytes' % user_input,
1595
                raiseCLIError(qe, msg, details=[
1596
                    'Syntax: setquota <quota>[format] [container]',
1597
                    'e.g.: setquota 2.3GB mycontainer',
1598
                    'Acceptable formats:',
1599
                    '(*1024): B, KiB, MiB, GiB, TiB',
1600
                    '(*1000): B, KB, MB, GB, TB'])
1585 1601
        return quota
1586 1602

  
1587 1603
    @errors.generic.all
......
1705 1721
    @errors.pithos.connection
1706 1722
    def _run(self):
1707 1723
        accounts = self.client.get_sharing_accounts(marker=self['marker'])
1708
        print_items(accounts if self['detail']\
1724
        print_items(accounts if self['detail']
1709 1725
            else [acc['name'] for acc in accounts])
1710 1726

  
1711 1727
    def main(self):
b/kamaki/cli/commands/test_cli.py
156 156

  
157 157
    l2 = [d2, l1, d1]
158 158

  
159
    d3 = {'dict 1': d1, 'dict 2': d2, 'list2': l2,
160
        'long key of size 75 characters is used to' +\
161
        ' check the effects on total result': l1}
159
    spr_msg = 'long key of size 75 characters is used to check the effects on'
160
    spr_msg += ' total result for long messages that drive pep8 completely mad'
161
    d3 = {'dict 1': d1, 'dict 2': d2, 'list2': l2, spr_msg: l1}
162 162

  
163 163
    @errors.generic.all
164 164
    def _run(self):
......
191 191
        print_list(self.l2, with_enumeration=True)
192 192
        print('- - -\n')
193 193
        print('\nTest print_items with id:\n- - -')
194
        print_items([{'id': '42', 'title': 'lalakis 1', 'content': self.d1},
194
        print_items([
195
            {'id': '42', 'title': 'lalakis 1', 'content': self.d1},
195 196
            {'id': '142', 'title': 'lalakis 2', 'content': self.d2}])
196 197
        print('- - -')
197 198
        print('\nTest print_items with id and enumeration:\n- - -')
198
        print_items([{'id': '42', 'title': 'lalakis 1', 'content': self.d1},
199
            {'id': '142', 'title': 'lalakis 2', 'content': self.d2}],
199
        print_items([
200
                {'id': '42', 'title': 'lalakis 1', 'content': self.d1},
201
                {'id': '142', 'title': 'lalakis 2', 'content': self.d2}],
200 202
            with_enumeration=True)
201 203
        print('- - -')
202 204
        print('\nTest print_items with id, title and redundancy:\n- - -')
203
        print_items([{'id': '42', 'title': 'lalakis 1', 'content': self.d1},
204
            {'id': '142', 'title': 'lalakis 2', 'content': self.d2}],
205
        print_items([
206
                {'id': '42', 'title': 'lalakis 1', 'content': self.d1},
207
                {'id': '142', 'title': 'lalakis 2', 'content': self.d2}],
205 208
            title=('id', 'title'),
206 209
            with_redundancy=True)
207 210
        print('- - -')
b/kamaki/cli/errors.py
67 67

  
68 68

  
69 69
class CLICmdSpecError(CLIError):
70
    def __init__(self,
71
        message='Command Specification Error', details=[], importance=0):
70
    def __init__(
71
        self,
72
        message='Command Specification Error',
73
        details=[],
74
        importance=0):
72 75
        super(CLICmdSpecError, self).__init__(message, details, importance)
73 76

  
74 77

  
75 78
class CLICmdIncompleteError(CLICmdSpecError):
76
    def __init__(self,
77
        message='Incomplete Command Error', details=[], importance=1):
79
    def __init__(
80
        self,
81
        message='Incomplete Command Error',
82
        details=[],
83
        importance=1):
78 84
        super(CLICmdSpecError, self).__init__(message, details, importance)
79 85

  
80 86

  
b/kamaki/cli/history.py
49 49

  
50 50
    def get(self, match_terms=None, limit=0):
51 51
        f = open(self.filepath, 'r')
52
        result = ['%s.  \t%s' % (index + 1, line)\
53
            for index, line in enumerate(f.readlines())\
52
        result = ['%s.  \t%s' % (index + 1, line)
53
            for index, line in enumerate(f.readlines())
54 54
            if self._match(line, match_terms)]
55 55
        offset = len(result) - limit if limit and len(result) > limit else 0
56 56
        return result[offset:]
b/kamaki/cli/utils.py
71 71
    return new_d
72 72

  
73 73

  
74
def print_dict(d,
74
def print_dict(
75
    d,
75 76
    exclude=(),
76 77
    ident=0,
77 78
    with_enumeration=False,
......
96 97
        raiseCLIError(TypeError('Cannot dict_print a non-dict object'))
97 98

  
98 99
    if d:
99
        margin = max(len(unicode(key).strip())\
100
        margin = max(len(unicode(key).strip())
100 101
            for key in d.keys() if key not in exclude)
101 102

  
102 103
    counter = 1
......
113 114
        print_str += ': '
114 115
        if isinstance(val, dict):
115 116
            print(print_str)
116
            print_dict(val,
117
            print_dict(
118
                val,
117 119
                exclude=exclude,
118 120
                ident=margin + ident,
119 121
                with_enumeration=recursive_enumeration,
120 122
                recursive_enumeration=recursive_enumeration)
121 123
        elif isinstance(val, list):
122 124
            print(print_str)
123
            print_list(val,
125
            print_list(
126
                val,
124 127
                exclude=exclude,
125 128
                ident=margin + ident,
126 129
                with_enumeration=recursive_enumeration,
......
129 132
            print print_str + ' ' + unicode(val).strip()
130 133

  
131 134

  
132
def print_list(l,
135
def print_list(
136
    l,
133 137
    exclude=(),
134 138
    ident=0,
135 139
    with_enumeration=False,
......
155 159

  
156 160
    if l:
157 161
        try:
158
            margin = max(len(unicode(item).strip()) for item in l\
159
                if not (isinstance(item, dict)\
160
                or isinstance(item, list)\
162
            margin = max(len(unicode(item).strip()) for item in l
163
                if not (isinstance(item, dict)
164
                or isinstance(item, list)
161 165
                or item in exclude))
162 166
        except ValueError:
163 167
            margin = (2 + len(unicode(len(l)))) if enumerate else 1
......
176 180
        if isinstance(item, dict):
177 181
            if with_enumeration:
178 182
                print(prefix)
179
            print_dict(item,
183
            print_dict(
184
                item,
180 185
                exclude=exclude,
181 186
                ident=margin + ident,
182 187
                with_enumeration=recursive_enumeration,
......
184 189
        elif isinstance(item, list):
185 190
            if with_enumeration:
186 191
                print(prefix)
187
            print_list(item,
192
            print_list(
193
                item,
188 194
                exclude=exclude,
189 195
                ident=margin + ident,
190 196
                with_enumeration=recursive_enumeration,
......
214 220
    return True
215 221

  
216 222

  
217
def print_items(items,
223
def print_items(
224
    items,
218 225
    title=('id', 'name'),
219 226
    with_enumeration=False,
220 227
    with_redundancy=False,
......
408 415
        yield
409 416

  
410 417
if __name__ == '__main__':
411
    examples = ['la_la le_le li_li',
418
    examples = [
419
        'la_la le_le li_li',
412 420
        '\'la la\' \'le le\' \'li li\'',
413 421
        '\'la la\' le_le \'li li\'',
414 422
        'la_la \'le le\' li_li',
......
427 435
        '"la \'le le\' la"',
428 436
        '\'la "le le" la\'',
429 437
        '\'la "la" la\' "le \'le\' le" li_"li"_li',
430
        '\'\' \'L\' "" "A"'
431
    ]
438
        '\'\' \'L\' "" "A"']
432 439

  
433 440
    for i, example in enumerate(examples):
434 441
        print('%s. Split this: (%s)' % (i + 1, example))
b/kamaki/clients/__init__.py
111 111
        self.base_url = base_url
112 112
        self.token = token
113 113
        self.headers = {}
114
        self.DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
115
            "%A, %d-%b-%y %H:%M:%S GMT",
116
            "%a, %d %b %Y %H:%M:%S GMT"]
114
        self.DATE_FORMATS = [
115
            '%a %b %d %H:%M:%S %Y',
116
            '%A, %d-%b-%y %H:%M:%S GMT',
117
            '%a, %d %b %Y %H:%M:%S GMT']
117 118
        self.http_client = http_client
118 119

  
119 120
    def _init_thread_limit(self, limit=1):
......
123 124

  
124 125
    def _watch_thread_limit(self, threadlist):
125 126
        recvlog.debug('# running threads: %s' % len(threadlist))
126
        if self._elapsed_old > self._elapsed_new\
127
        and self._thread_limit < self.POOL_SIZE:
127
        if (
128
            self._elapsed_old > self._elapsed_new) and (
129
            self._thread_limit < self.POOL_SIZE):
128 130
            self._thread_limit += 1
129 131
        elif self._elapsed_old < self._elapsed_new and self._thread_limit > 1:
130 132
            self._thread_limit -= 1
......
161 163
    def set_default_header(self, name, value):
162 164
        self.http_client.headers.setdefault(name, value)
163 165

  
164
    def request(self,
166
    def request(
167
        self,
165 168
        method,
166 169
        path,
167 170
        async_headers={},
......
193 196

  
194 197
            self.http_client.url = self.base_url
195 198
            self.http_client.path = path
196
            r = self.http_client.perform_request(method,
199
            r = self.http_client.perform_request(
200
                method,
197 201
                data,
198 202
                async_headers,
199 203
                async_params)
......
223 227
            errstr = '%s' % err
224 228
            if not errstr:
225 229
                errstr = ('%s' % type(err))[7:-2]
226
            raise ClientError('%s\n' % errstr,
227
                status=getattr(err, 'status', 0) or getattr(err, 'errno', 0))
230
            status = getattr(err, 'status', getattr(err, 'errno', 0))
231
            raise ClientError('%s\n' % errstr, status=status)
228 232

  
229 233
        self.http_client.reset_headers()
230 234
        self.http_client.reset_params()
b/kamaki/clients/compute.py
98 98
            r = self.servers_post(json_data=req)
99 99
        except ClientError as err:
100 100
            try:
101
                tmp_err = err.details if isinstance(err.details, list)\
102
                else unicode(err.details).split(',')
101
                if isinstance(err.details, list):
102
                    tmp_err = err.details
103
                else:
104
                    tmp_err = unicode(err.details).split(',')
103 105
                tmp_err = tmp_err[0].split(':')
104 106
                tmp_err = tmp_err[2].split('"')
105 107
                err.message = tmp_err[1]
......
161 163
        :returns: dict of updated key:val metadata
162 164
        """
163 165
        req = {'meta': {key: val}}
164
        r = self.servers_put(server_id,
166
        r = self.servers_put(
167
            server_id,
165 168
            'meta/' + key,
166 169
            json_data=req,
167 170
            success=201)
......
229 232
        try:
230 233
            return r.json['image']
231 234
        except KeyError:
232
            raise ClientError('Image not available', 404,
233
                details='Image %d not found or not accessible')
235
            raise ClientError('Image not available', 404, details=[
236
                'Image %d not found or not accessible'])
234 237

  
235 238
    def delete_image(self, image_id):
236 239
        """
b/kamaki/clients/compute_rest_api.py
70 70
        path = path4url('servers', server_id, command)
71 71
        return self.delete(path, success=success, **kwargs)
72 72

  
73
    def servers_post(self,
74
        server_id='', command='', json_data=None, success=202, **kwargs):
73
    def servers_post(
74
        self,
75
        server_id='',
76
        command='',
77
        json_data=None,
78
        success=202,
79
        **kwargs):
75 80
        """POST base_url/servers[/server_id]/[command] request
76 81

  
77 82
        :param server_id: integer (as int or str)
......
95 100
        path = path4url('servers', server_id, command)
96 101
        return self.post(path, data=data, success=success, **kwargs)
97 102

  
98
    def servers_put(self,
99
        server_id='', command='', json_data=None, success=204, **kwargs):
103
    def servers_put(
104
        self,
105
        server_id='',
106
        command='',
107
        json_data=None,
108
        success=204,
109
        **kwargs):
100 110
        """PUT base_url/servers[/server_id]/[command] request
101 111

  
102 112
        :param server_id: integer (as int or str)
......
168 178
        path = path4url('images', image_id, command)
169 179
        return self.delete(path, success=success, **kwargs)
170 180

  
171
    def images_post(self,
172
        image_id='', command='', json_data=None, success=201, **kwargs):
181
    def images_post(
182
        self,
183
        image_id='',
184
        command='',
185
        json_data=None,
186
        success=201,
187
        **kwargs):
173 188
        """POST base_url/images[/image_id]/[command] request
174 189

  
175 190
        :param image_id: string
......
193 208
        path = path4url('images', image_id, command)
194 209
        return self.post(path, data=data, success=success, **kwargs)
195 210

  
196
    def images_put(self,
197
        image_id='', command='', json_data=None, success=201, **kwargs):
211
    def images_put(
212
        self,
213
        image_id='',
214
        command='',
215
        json_data=None,
216
        success=201,
217
        **kwargs):
198 218
        """PUT base_url/images[/image_id]/[command] request
199 219

  
200 220
        :param image_id: string

Also available in: Unified diff