Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / image_cli.py @ f47417e7

History | View | Annotate | Download (13.2 kB)

1
# Copyright 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 kamaki.cli import command
35
from kamaki.cli.command_tree import CommandTree
36
from kamaki.cli.utils import print_dict, print_items
37
from kamaki.clients.image import ImageClient
38
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument
39
from kamaki.cli.argument import IntArgument
40
from kamaki.cli.commands.cyclades_cli import _init_cyclades
41
from kamaki.cli.commands import _command_init, errors
42

    
43

    
44
image_cmds = CommandTree('image', 'Plankton (and Compute) Image API commands')
45
_commands = [image_cmds]
46

    
47

    
48
about_image_id = [
49
    'To see a list of available image ids: /image list']
50

    
51

    
52
class _init_image(_command_init):
53
    @errors.generic.all
54
    def _run(self):
55
        token = self.config.get('image', 'token')\
56
            or self.config.get('compute', 'token')\
57
            or self.config.get('global', 'token')
58
        base_url = self.config.get('image', 'url')\
59
            or self.config.get('compute', 'url')\
60
            or self.config.get('global', 'url')
61
        self.client = ImageClient(base_url=base_url, token=token)
62
        self._set_log_params()
63
        self._update_max_threads()
64

    
65
    def main(self):
66
        self._run()
67

    
68

    
69
# Plankton Image Commands
70

    
71

    
72
@command(image_cmds)
73
class image_list(_init_image):
74
    """List images accessible by user"""
75

    
76
    arguments = dict(
77
        detail=FlagArgument('show detailed output', ('-l', '--details')),
78
        container_format=ValueArgument(
79
            'filter by container format',
80
            '--container-format'),
81
        disk_format=ValueArgument('filter by disk format', '--disk-format'),
82
        name=ValueArgument('filter by name', '--name'),
83
        size_min=IntArgument('filter by minimum size', '--size-min'),
84
        size_max=IntArgument('filter by maximum size', '--size-max'),
85
        status=ValueArgument('filter by status', '--status'),
86
        order=ValueArgument(
87
            'order by FIELD ( - to reverse order)',
88
            '--order',
89
            default=''),
90
        limit=IntArgument('limit number of listed images', ('-n', '--number')),
91
        more=FlagArgument(
92
            'output results in pages (-n to set items per page, default 10)',
93
            '--more')
94
    )
95

    
96
    @errors.generic.all
97
    @errors.cyclades.connection
98
    def _run(self):
99
        super(self.__class__, self)._run()
100
        filters = {}
101
        for arg in set([
102
                'container_format',
103
                'disk_format',
104
                'name',
105
                'size_min',
106
                'size_max',
107
                'status']).intersection(self.arguments):
108
            filters[arg] = self[arg]
109

    
110
        order = self['order']
111
        detail = self['detail']
112
        images = self.client.list_public(detail, filters, order)
113
        if self['more']:
114
            print_items(
115
                images,
116
                title=('name',),
117
                with_enumeration=True,
118
                page_size=self['limit'] or 10)
119
        elif self['limit']:
120
            print_items(
121
                images[:self['limit']],
122
                title=('name',),
123
                with_enumeration=True)
124
        else:
125
            print_items(images, title=('name',), with_enumeration=True)
126

    
127
    def main(self):
128
        super(self.__class__, self)._run()
129
        self._run()
130

    
131

    
132
@command(image_cmds)
133
class image_meta(_init_image):
134
    """Get image metadata
135
    Image metadata include:
136
    - image file information (location, size, etc.)
137
    - image information (id, name, etc.)
138
    - image os properties (os, fs, etc.)
139
    """
140

    
141
    @errors.generic.all
142
    @errors.plankton.connection
143
    @errors.plankton.id
144
    def _run(self, image_id):
145
        image = self.client.get_meta(image_id)
146
        print_dict(image)
147

    
148
    def main(self, image_id):
149
        super(self.__class__, self)._run()
150
        self._run(image_id=image_id)
151

    
152

    
153
@command(image_cmds)
154
class image_register(_init_image):
155
    """(Re)Register an image"""
156

    
157
    arguments = dict(
158
        checksum=ValueArgument('set image checksum', '--checksum'),
159
        container_format=ValueArgument(
160
            'set container format',
161
            '--container-format'),
162
        disk_format=ValueArgument('set disk format', '--disk-format'),
163
        #id=ValueArgument('set image ID', '--id'),
164
        owner=ValueArgument('set image owner (admin only)', '--owner'),
165
        properties=KeyValueArgument(
166
            'add property in key=value form (can be repeated)',
167
            ('-p, --property')),
168
        is_public=FlagArgument('mark image as public', '--public'),
169
        size=IntArgument('set image size', '--size'),
170
        update=FlagArgument(
171
            'update existing image properties',
172
            ('-u', '--update'))
173
    )
174

    
175
    @errors.generic.all
176
    @errors.plankton.connection
177
    def _run(self, name, location):
178
        if not location.startswith('pithos://'):
179
            account = self.config.get('store', 'account') \
180
                or self.config.get('global', 'account')
181
            assert account, 'No user account provided'
182
            if account[-1] == '/':
183
                account = account[:-1]
184
            container = self.config.get('store', 'container') \
185
                or self.config.get('global', 'container')
186
            if not container:
187
                location = 'pithos://%s/%s' % (account, location)
188
            else:
189
                location = 'pithos://%s/%s/%s' % (account, container, location)
190

    
191
        params = {}
192
        for key in set([
193
                'checksum',
194
                'container_format',
195
                'disk_format',
196
                'owner',
197
                'size',
198
                'is_public']).intersection(self.arguments):
199
            params[key] = self[key]
200

    
201
            properties = self['properties']
202
        if self['update']:
203
            self.client.reregister(location, name, params, properties)
204
        else:
205
            r = self.client.register(name, location, params, properties)
206
            print_dict(r)
207

    
208
    def main(self, name, location):
209
        super(self.__class__, self)._run()
210
        self._run(name, location)
211

    
212

    
213
@command(image_cmds)
214
class image_members(_init_image):
215
    """Get image members"""
216

    
217
    @errors.generic.all
218
    @errors.plankton.connection
219
    @errors.plankton.id
220
    def _run(self, image_id):
221
        members = self.client.list_members(image_id)
222
        print_items(members)
223

    
224
    def main(self, image_id):
225
        super(self.__class__, self)._run()
226
        self._run(image_id=image_id)
227

    
228

    
229
@command(image_cmds)
230
class image_shared(_init_image):
231
    """List images shared by a member"""
232

    
233
    @errors.generic.all
234
    @errors.plankton.connection
235
    def _run(self, member):
236
        images = self.client.list_shared(member)
237
        print_items(images)
238

    
239
    def main(self, member):
240
        super(self.__class__, self)._run()
241
        self._run(member)
242

    
243

    
244
@command(image_cmds)
245
class image_addmember(_init_image):
246
    """Add a member to an image"""
247

    
248
    @errors.generic.all
249
    @errors.plankton.connection
250
    @errors.plankton.id
251
    def _run(self, image_id=None, member=None):
252
            self.client.add_member(image_id, member)
253

    
254
    def main(self, image_id, member):
255
        super(self.__class__, self)._run()
256
        self._run(image_id=image_id, member=member)
257

    
258

    
259
@command(image_cmds)
260
class image_delmember(_init_image):
261
    """Remove a member from an image"""
262

    
263
    @errors.generic.all
264
    @errors.plankton.connection
265
    @errors.plankton.id
266
    def _run(self, image_id=None, member=None):
267
            self.client.remove_member(image_id, member)
268

    
269
    def main(self, image_id, member):
270
        super(self.__class__, self)._run()
271
        self._run(image_id=image_id, member=member)
272

    
273

    
274
@command(image_cmds)
275
class image_setmembers(_init_image):
276
    """Set the members of an image"""
277

    
278
    @errors.generic.all
279
    @errors.plankton.connection
280
    @errors.plankton.id
281
    def _run(self, image_id, members):
282
            self.client.set_members(image_id, members)
283

    
284
    def main(self, image_id, *members):
285
        super(self.__class__, self)._run()
286
        self._run(image_id=image_id, members=members)
287

    
288

    
289
# Compute Image Commands
290

    
291

    
292
@command(image_cmds)
293
class image_compute(_init_cyclades):
294
    """Compute Image API commands"""
295

    
296

    
297
@command(image_cmds)
298
class image_compute_list(_init_cyclades):
299
    """List images"""
300

    
301
    arguments = dict(
302
        detail=FlagArgument('show detailed output', ('-l', '--details')),
303
        limit=IntArgument('limit number listed images', ('-n', '--number')),
304
        more=FlagArgument(
305
            'output results in pages (-n to set items per page, default 10)',
306
            '--more')
307
    )
308

    
309
    def _make_results_pretty(self, images):
310
        for img in images:
311
            if 'metadata' in img:
312
                img['metadata'] = img['metadata']['values']
313

    
314
    @errors.generic.all
315
    @errors.cyclades.connection
316
    def _run(self):
317
        images = self.client.list_images(self['detail'])
318
        if self['detail']:
319
            self._make_results_pretty(images)
320
        if self['more']:
321
            print_items(images, page_size=self['limit'] or 10)
322
        else:
323
            print_items(images[:self['limit']])
324

    
325
    def main(self):
326
        super(self.__class__, self)._run()
327
        self._run()
328

    
329

    
330
@command(image_cmds)
331
class image_compute_info(_init_cyclades):
332
    """Get detailed information on an image"""
333

    
334
    @errors.generic.all
335
    @errors.cyclades.connection
336
    @errors.plankton.id
337
    def _run(self, image_id):
338
        image = self.client.get_image_details(image_id)
339
        if 'metadata' in image:
340
            image['metadata'] = image['metadata']['values']
341
        print_dict(image)
342

    
343
    def main(self, image_id):
344
        super(self.__class__, self)._run()
345
        self._run(image_id=image_id)
346

    
347

    
348
@command(image_cmds)
349
class image_compute_delete(_init_cyclades):
350
    """Delete an image (WARNING: image file is also removed)"""
351

    
352
    @errors.generic.all
353
    @errors.cyclades.connection
354
    @errors.plankton.id
355
    def _run(self, image_id):
356
        self.client.delete_image(image_id)
357

    
358
    def main(self, image_id):
359
        super(self.__class__, self)._run()
360
        self._run(image_id=image_id)
361

    
362

    
363
@command(image_cmds)
364
class image_compute_properties(_init_cyclades):
365
    """Get properties related to OS installation in an image"""
366

    
367
    @errors.generic.all
368
    @errors.cyclades.connection
369
    @errors.plankton.id
370
    @errors.plankton.metadata
371
    def _run(self, image_id, key):
372
        r = self.client.get_image_metadata(image_id, key)
373
        print_dict(r)
374

    
375
    def main(self, image_id, key=''):
376
        super(self.__class__, self)._run()
377
        self._run(image_id=image_id, key=key)
378

    
379

    
380
@command(image_cmds)
381
class image_addproperty(_init_cyclades):
382
    """Add an OS-related property to an image"""
383

    
384
    @errors.generic.all
385
    @errors.cyclades.connection
386
    @errors.plankton.id
387
    @errors.plankton.metadata
388
    def _run(self, image_id, key, val):
389
        r = self.client.create_image_metadata(image_id, key, val)
390
        print_dict(r)
391

    
392
    def main(self, image_id, key, val):
393
        super(self.__class__, self)._run()
394
        self._run(image_id=image_id, key=key, val=val)
395

    
396

    
397
@command(image_cmds)
398
class image_compute_setproperty(_init_cyclades):
399
    """Update an existing property in an image"""
400

    
401
    @errors.generic.all
402
    @errors.cyclades.connection
403
    @errors.plankton.id
404
    @errors.plankton.metadata
405
    def _run(self, image_id, key, val):
406
        metadata = {key: val}
407
        r = self.client.update_image_metadata(image_id, **metadata)
408
        print_dict(r)
409

    
410
    def main(self, image_id, key, val):
411
        super(self.__class__, self)._run()
412
        self._run(image_id=image_id, key=key, val=val)
413

    
414

    
415
@command(image_cmds)
416
class image_compute_delproperty(_init_cyclades):
417
    """Delete a property of an image"""
418

    
419
    @errors.generic.all
420
    @errors.cyclades.connection
421
    @errors.plankton.id
422
    @errors.plankton.metadata
423
    def _run(self, image_id, key):
424
        self.client.delete_image_metadata(image_id, key)
425

    
426
    def main(self, image_id, key):
427
        super(self.__class__, self)._run()
428
        self._run(image_id=image_id, key=key)