Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / errors.py @ 5a673575

History | View | Annotate | Download (15.3 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.command
33

    
34
from traceback import print_stack, print_exc
35
import logging
36

    
37
from kamaki.clients import ClientError
38
from kamaki.cli.errors import CLIError, raiseCLIError, CLISyntaxError
39
from kamaki.cli import _debug, kloger
40

    
41
sendlog = logging.getLogger('clients.send')
42
datasendlog = logging.getLogger('data.send')
43
recvlog = logging.getLogger('clients.recv')
44
datarecvlog = logging.getLogger('data.recv')
45

    
46

    
47
class generic(object):
48

    
49
    @classmethod
50
    def all(this, foo):
51
        def _raise(self, *args, **kwargs):
52
            try:
53
                return foo(self, *args, **kwargs)
54
            except Exception as e:
55
                if _debug:
56
                    print_stack()
57
                    print_exc(e)
58
                raiseCLIError(e)
59
        return _raise
60

    
61
    @classmethod
62
    def _connection(this, foo, base_url):
63
        def _raise(self, *args, **kwargs):
64
            try:
65
                foo(self, *args, **kwargs)
66
            except ClientError as ce:
67
                if ce.status == 401:
68
                    raiseCLIError(ce, 'Authorization failed', details=[
69
                        'Make sure a valid token is provided:',
70
                        '  to check if token is valid: /astakos authenticate',
71
                        '  to set token: /config set [.server.]token <token>',
72
                        '  to get current token: /config get [server.]token'])
73
                elif ce.status in range(-12, 200) + [403, 302, 500]:
74
                    raiseCLIError(ce, importance=3, details=[
75
                        'Check if service is up or set to url %s' % base_url,
76
                        '  to get url: /config get %s' % base_url,
77
                        '  to set url: /config set %s <URL>' % base_url])
78
                elif ce.status == 404\
79
                and 'kamakihttpresponse' in ('%s' % ce).lower():
80
                    client = getattr(self, 'client', None)
81
                    if not client:
82
                        raise
83
                    url = getattr(client, 'base_url', '<empty>')
84
                    raiseCLIError(ce,
85
                        'Invalid service url %s' % url,
86
                        details=[
87
                        'Please, check if service url is correctly set',
88
                        '* to get current url: /config get compute.url',
89
                        '* to set url: /config set compute.url <URL>'])
90
                raise
91
        return _raise
92

    
93

    
94
class astakos(object):
95

    
96
    _token_details = [
97
        'To check default token: /config get token',
98
        'If set/update a token:',
99
        '*  (permanent):    /config set token <token>',
100
        '*  (temporary):    re-run with <token> parameter']
101

    
102
    @classmethod
103
    def load(this, foo):
104
        def _raise(self, *args, **kwargs):
105
            r = foo(self, *args, **kwargs)
106
            try:
107
                client = getattr(self, 'client')
108
            except AttributeError as ae:
109
                raiseCLIError(ae, 'Client setup failure', importance=3)
110
            if not getattr(client, 'token', False):
111
                kloger.warning(
112
                    'No permanent token (try: kamaki config set token <tkn>)')
113
            if not getattr(client, 'base_url', False):
114
                raise CLIError('Missing astakos server URL',
115
                    importance=3,
116
                    details=['Check if astakos.url is set correctly',
117
                    'To get astakos url:   /config get astakos.url',
118
                    'To set astakos url:   /config set astakos.url <URL>'])
119
            return r
120
        return _raise
121

    
122
    @classmethod
123
    def authenticate(this, foo):
124
        def _raise(self, *args, **kwargs):
125
            try:
126
                r = foo(self, *args, **kwargs)
127
            except ClientError as ce:
128
                if ce.status == 401:
129
                    token = kwargs.get('custom_token', 0) or self.client.token
130
                    raiseCLIError(ce,
131
                        'Authorization failed for token %s' % token if token\
132
                            else 'No token provided',
133
                        details=[] if token else this._token_details)
134
            self._raise = foo
135
            return r
136
        return _raise
137

    
138

    
139
class history(object):
140
    @classmethod
141
    def init(this, foo):
142
        def _raise(self, *args, **kwargs):
143
            r = foo(self, *args, **kwargs)
144
            if not hasattr(self, 'history'):
145
                raise CLIError('Failed to load history', importance=2)
146
            return r
147
        return _raise
148

    
149
    @classmethod
150
    def _get_cmd_ids(this, foo):
151
        def _raise(self, cmd_ids, *args, **kwargs):
152
            if not cmd_ids:
153
                raise CLISyntaxError('Usage: <id1|id1-id2> [id3|id3-id4] ...',
154
                    details=self.__doc__.split('\n'))
155
            return foo(self, cmd_ids, *args, **kwargs)
156
        return _raise
157

    
158

    
159
class cyclades(object):
160
    about_flavor_id = [
161
        'How to pick a valid flavor id:',
162
        '* get a list of flavor ids: /flavor list',
163
        '* details of flavor: /flavor info <flavor id>']
164

    
165
    about_network_id = [
166
        'How to pick a valid network id:',
167
        '* get a list of network ids: /network list',
168
        '* details of network: /network info <network id>']
169

    
170
    @classmethod
171
    def connection(this, foo):
172
        return generic._connection(foo, 'compute.url')
173

    
174
    @classmethod
175
    def date(this, foo):
176
        def _raise(self, *args, **kwargs):
177
            try:
178
                return foo(self, *args, **kwargs)
179
            except ClientError as ce:
180
                if ce.status == 400 and 'changes-since' in ('%s' % ce):
181
                    raise CLIError(
182
                        'Incorrect date format for --since',
183
                        details=['Accepted date format: d/m/y'])
184
                raise
185
        return _raise
186

    
187
    @classmethod
188
    def network_id(this, foo):
189
        def _raise(self, *args, **kwargs):
190
            network_id = kwargs.get('network_id', None)
191
            try:
192
                network_id = int(network_id)
193
                return foo(self, *args, **kwargs)
194
            except ValueError as ve:
195
                raiseCLIError(ve, 'Invalid network id %s ' % network_id,
196
                    details='network id must be a positive integer',
197
                    importance=1)
198
            except ClientError as ce:
199
                if network_id and ce.status == 404 and\
200
                    'network' in ('%s' % ce).lower():
201
                        raiseCLIError(ce,
202
                            'No network with id %s found' % network_id,
203
                            details=this.about_network_id)
204
                raise
205
        return _raise
206

    
207
    @classmethod
208
    def network_max(this, foo):
209
        def _raise(self, *args, **kwargs):
210
            try:
211
                return foo(self, *args, **kwargs)
212
            except ClientError as ce:
213
                if ce.status == 413:
214
                    raiseCLIError(ce,
215
                        'Cannot create another network',
216
                        details=['Maximum number of networks reached',
217
                            '* to get a list of networks: /network list',
218
                            '* to delete a network: /network delete <net id>'])
219
                raise
220
        return _raise
221

    
222
    @classmethod
223
    def network_in_use(this, foo):
224
        def _raise(self, *args, **kwargs):
225
            network_id = kwargs.get('network_id', None)
226
            try:
227
                return foo(self, *args, **kwargs)
228
            except ClientError as ce:
229
                if network_id or ce.status == 421:
230
                    raiseCLIError(ce,
231
                        'Network with id %s is in use' % network_id,
232
                        details=[
233
                            'Disconnect all nics/VMs of this network first',
234
                            '* to get nics: /network info %s' % network_id,
235
                            '.  (under "attachments" section)',
236
                            '* to disconnect: /network disconnect <nic id>'])
237
                raise
238
        return _raise
239

    
240
    @classmethod
241
    def flavor_id(this, foo):
242
        def _raise(self, *args, **kwargs):
243
            flavor_id = kwargs.get('flavor_id', None)
244
            try:
245
                flavor_id = int(flavor_id)
246
                return foo(self, *args, **kwargs)
247
            except ValueError as ve:
248
                raiseCLIError(ve, 'Invalid flavor id %s ' % flavor_id,
249
                    details='Flavor id must be a positive integer',
250
                    importance=1)
251
            except ClientError as ce:
252
                if flavor_id and ce.status == 404 and\
253
                    'flavor' in ('%s' % ce).lower():
254
                        raiseCLIError(ce,
255
                            'No flavor with id %s found' % flavor_id,
256
                            details=this.about_flavor_id)
257
                raise
258
        return _raise
259

    
260
    @classmethod
261
    def server_id(this, foo):
262
        def _raise(self, *args, **kwargs):
263
            server_id = kwargs.get('server_id', None)
264
            try:
265
                server_id = int(server_id)
266
                return foo(self, *args, **kwargs)
267
            except ValueError as ve:
268
                raiseCLIError(ve,
269
                    'Invalid server(VM) id %s' % server_id,
270
                    details=['id must be a positive integer'],
271
                    importance=1)
272
            except ClientError as ce:
273
                err_msg = ('%s' % ce).lower()
274
                if (ce.status == 404 and 'server' in err_msg)\
275
                or (ce.status == 400 and 'not found' in err_msg):
276
                    raiseCLIError(ce,
277
                        'server(VM) with id %s not found' % server_id,
278
                        details=[
279
                            '* to get existing VM ids: /server list',
280
                            '* to get VM details: /server info <VM id>'])
281
                raise
282
        return _raise
283

    
284
    @classmethod
285
    def firewall(this, foo):
286
        def _raise(self, *args, **kwargs):
287
            profile = kwargs.get('profile', None)
288
            try:
289
                return foo(self, *args, **kwargs)
290
            except ClientError as ce:
291
                if ce.status == 400 and profile\
292
                and 'firewall' in ('%s' % ce).lower():
293
                    raiseCLIError(ce,
294
                        '%s is an invalid firewall profile term' % profile,
295
                        details=['Try one of the following:',
296
                            '* DISABLED: Shutdown firewall',
297
                            '* ENABLED: Firewall in normal mode',
298
                            '* PROTECTED: Firewall in secure mode'])
299
                raise
300
        return _raise
301

    
302
    @classmethod
303
    def nic_id(this, foo):
304
        def _raise(self, *args, **kwargs):
305
            try:
306
                return foo(self, *args, **kwargs)
307
            except ClientError as ce:
308
                nic_id = kwargs.get('nic_id', None)
309
                if nic_id and ce.status == 404\
310
                and 'network interface' in ('%s' % ce).lower():
311
                    server_id = kwargs.get('server_id', '<no server>')
312
                    err_msg = 'No nic %s on server(VM) with id %s' % (
313
                        nic_id,
314
                        server_id)
315
                    raiseCLIError(ce, err_msg, details=[
316
                        '* check server(VM) with id %s: /server info %s' % (
317
                            server_id,
318
                            server_id),
319
                        '* list nics for server(VM) with id %s:' % server_id,
320
                        '      /server addr %s' % server_id])
321
                raise
322
        return _raise
323

    
324
    @classmethod
325
    def nic_format(this, foo):
326
        def _raise(self, *args, **kwargs):
327
            try:
328
                return foo(self, *args, **kwargs)
329
            except IndexError as ie:
330
                nic_id = kwargs.get('nic_id', None)
331
                raiseCLIError(ie,
332
                    'Invalid format for network interface (nic) %s' % nic_id,
333
                    importance=1,
334
                    details=[
335
                        'nid_id format: nic-<server id>-<nic id>',
336
                        '* get nics of a network: /network info <net id>',
337
                        '    (listed the "attachments" section)'])
338
        return _raise
339

    
340
    @classmethod
341
    def metadata(this, foo):
342
        def _raise(self, *args, **kwargs):
343
            key = kwargs.get('key', None)
344
            try:
345
                foo(self, *args, **kwargs)
346
            except ClientError as ce:
347
                if key and ce.status == 404\
348
                    and 'metadata' in ('%s' % ce).lower():
349
                        raiseCLIError(ce, 'No VM metadata with key %s' % key)
350
                raise
351
        return _raise
352

    
353

    
354
class plankton(object):
355

    
356
    about_image_id = ['How to pick a suitable image:',
357
        '* get a list of image ids: /image list',
358
        '* details of image: /flavor info <image id>']
359

    
360
    @classmethod
361
    def connection(this, foo):
362
        return generic._connection(foo, 'image.url')
363

    
364
    @classmethod
365
    def id(this, foo):
366
        def _raise(self, *args, **kwargs):
367
            image_id = kwargs.get('image_id', None)
368
            try:
369
                foo(self, *args, **kwargs)
370
            except ClientError as ce:
371
                if image_id and (ce.status == 404\
372
                    or (ce.status == 400 and
373
                        'image not found' in ('%s' % ce).lower())\
374
                    or ce.status == 411):
375
                        raiseCLIError(ce,
376
                            'No image with id %s found' % image_id,
377
                            details=this.about_image_id)
378
                raise
379
        return _raise
380

    
381
    @classmethod
382
    def metadata(this, foo):
383
        def _raise(self, *args, **kwargs):
384
            key = kwargs.get('key', None)
385
            try:
386
                foo(self, *args, **kwargs)
387
            except ClientError as ce:
388
                if ce.status == 404 or ((ce.status == 400\
389
                    and 'metadata' in ('%s' % ce).lower())):
390
                        raiseCLIError(ce,
391
                            'No properties with key %s in this image' % key)
392
                raise
393
        return _raise