Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / image_cli.py @ 573be34f

History | View | Annotate | Download (13.1 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

    
63
    def main(self):
64
        self._run()
65

    
66

    
67
# Plankton Image Commands
68

    
69

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

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

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

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

    
125
    def main(self):
126
        super(self.__class__, self)._run()
127
        self._run()
128

    
129

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

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

    
146
    def main(self, image_id):
147
        super(self.__class__, self)._run()
148
        self._run(image_id=image_id)
149

    
150

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

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

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

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

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

    
206
    def main(self, name, location):
207
        super(self.__class__, self)._run()
208
        self._run(name, location)
209

    
210

    
211
@command(image_cmds)
212
class image_members(_init_image):
213
    """Get image members"""
214

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

    
222
    def main(self, image_id):
223
        super(self.__class__, self)._run()
224
        self._run(image_id=image_id)
225

    
226

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

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

    
237
    def main(self, member):
238
        super(self.__class__, self)._run()
239
        self._run(member)
240

    
241

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

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

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

    
256

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

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

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

    
271

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

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

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

    
286

    
287
# Compute Image Commands
288

    
289

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

    
294

    
295
@command(image_cmds)
296
class image_compute_list(_init_cyclades):
297
    """List images"""
298

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

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

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

    
323
    def main(self):
324
        super(self.__class__, self)._run()
325
        self._run()
326

    
327

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

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

    
341
    def main(self, image_id):
342
        super(self.__class__, self)._run()
343
        self._run(image_id=image_id)
344

    
345

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

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

    
356
    def main(self, image_id):
357
        super(self.__class__, self)._run()
358
        self._run(image_id=image_id)
359

    
360

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

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

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

    
377

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

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

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

    
394

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

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

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

    
412

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

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

    
424
    def main(self, image_id, key):
425
        super(self.__class__, self)._run()
426
        self._run(image_id=image_id, key=key)