Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / image_cli.py @ 8741c407

History | View | Annotate | Download (13 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 Image API commands')
45
_commands = [image_cmds]
46

    
47

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

    
50

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

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

    
65

    
66
@command(image_cmds)
67
class image_public(_init_image):
68
    """List public images"""
69

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

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

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

    
121
    def main(self):
122
        super(self.__class__, self)._run()
123
        self._run()
124

    
125

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

    
135
    @errors.generic.all
136
    @errors.plankton.connection
137
    @errors.plankton.id
138
    def _run(self, image_id):
139
        image = self.client.get_meta(image_id)
140
        print_dict(image)
141

    
142
    def main(self, image_id):
143
        super(self.__class__, self)._run()
144
        self._run(image_id=image_id)
145

    
146

    
147
@command(image_cmds)
148
class image_register(_init_image):
149
    """(Re)Register an image"""
150

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

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

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

    
196
            properties = self['properties']
197
        if self['update']:
198
            self.client.reregister(location, name, params, properties)
199
        else:
200
            self.client.register(name, location, params, properties)
201

    
202
    def main(self, name, location):
203
        super(self.__class__, self)._run()
204
        self._run(name, location)
205

    
206

    
207
@command(image_cmds)
208
class image_members(_init_image):
209
    """Get image members"""
210

    
211
    @errors.generic.all
212
    @errors.plankton.connection
213
    @errors.plankton.id
214
    def _run(self, image_id):
215
        members = self.client.list_members(image_id)
216
        print_items(members)
217

    
218
    def main(self, image_id):
219
        super(self.__class__, self)._run()
220
        self._run(image_id=image_id)
221

    
222

    
223
@command(image_cmds)
224
class image_shared(_init_image):
225
    """List images shared by a member"""
226

    
227
    @errors.generic.all
228
    @errors.plankton.connection
229
    def _run(self, member):
230
        images = self.client.list_shared(member)
231
        print_items(images)
232

    
233
    def main(self, member):
234
        super(self.__class__, self)._run()
235
        self._run(member)
236

    
237

    
238
@command(image_cmds)
239
class image_addmember(_init_image):
240
    """Add a member to an image"""
241

    
242
    @errors.generic.all
243
    @errors.plankton.connection
244
    @errors.plankton.id
245
    def _run(self, image_id=None, member=None):
246
            self.client.add_member(image_id, member)
247

    
248
    def main(self, image_id, member):
249
        super(self.__class__, self)._run()
250
        self._run(image_id=image_id, member=member)
251

    
252

    
253
@command(image_cmds)
254
class image_delmember(_init_image):
255
    """Remove a member from an image"""
256

    
257
    @errors.generic.all
258
    @errors.plankton.connection
259
    @errors.plankton.id
260
    def _run(self, image_id=None, member=None):
261
            self.client.remove_member(image_id, member)
262

    
263
    def main(self, image_id, member):
264
        super(self.__class__, self)._run()
265
        self._run(image_id=image_id, member=member)
266

    
267

    
268
@command(image_cmds)
269
class image_setmembers(_init_image):
270
    """Set the members of an image"""
271

    
272
    @errors.generic.all
273
    @errors.plankton.connection
274
    @errors.plankton.id
275
    def _run(self, image_id, members):
276
            self.client.set_members(image_id, members)
277

    
278
    def main(self, image_id, *members):
279
        super(self.__class__, self)._run()
280
        self._run(image_id=image_id, members=members)
281

    
282

    
283
@command(image_cmds)
284
class image_compute(_init_cyclades):
285
    """Compute Image API commands"""
286

    
287

    
288
@command(image_cmds)
289
class image_compute_list(_init_cyclades):
290
    """List images"""
291

    
292
    arguments = dict(
293
        detail=FlagArgument('show detailed output', ('-l', '--details')),
294
        limit=IntArgument('limit number listed images', ('-n', '--number')),
295
        more=FlagArgument(
296
            'output results in pages (-n to set items per page, default 10)',
297
            '--more')
298
    )
299

    
300
    def _make_results_pretty(self, images):
301
        for img in images:
302
            if 'metadata' in img:
303
                img['metadata'] = img['metadata']['values']
304

    
305
    @errors.generic.all
306
    @errors.cyclades.connection
307
    def _run(self):
308
        images = self.client.list_images(self['detail'])
309
        if self['detail']:
310
            self._make_results_pretty(images)
311
        if self['more']:
312
            print_items(images, page_size=self['limit'] or 10)
313
        else:
314
            print_items(images[:self['limit']])
315

    
316
    def main(self):
317
        super(self.__class__, self)._run()
318
        self._run()
319

    
320

    
321
@command(image_cmds)
322
class image_compute_info(_init_cyclades):
323
    """Get detailed information on an image"""
324

    
325
    @errors.generic.all
326
    @errors.cyclades.connection
327
    @errors.plankton.id
328
    def _run(self, image_id):
329
        image = self.client.get_image_details(image_id)
330
        if 'metadata' in image:
331
            image['metadata'] = image['metadata']['values']
332
        print_dict(image)
333

    
334
    def main(self, image_id):
335
        super(self.__class__, self)._run()
336
        self._run(image_id=image_id)
337

    
338

    
339
@command(image_cmds)
340
class image_compute_delete(_init_cyclades):
341
    """Delete an image (WARNING: image file is also removed)"""
342

    
343
    @errors.generic.all
344
    @errors.cyclades.connection
345
    @errors.plankton.id
346
    def _run(self, image_id):
347
        self.client.delete_image(image_id)
348

    
349
    def main(self, image_id):
350
        super(self.__class__, self)._run()
351
        self._run(image_id=image_id)
352

    
353

    
354
@command(image_cmds)
355
class image_compute_properties(_init_cyclades):
356
    """Get properties related to OS installation in an image"""
357

    
358
    @errors.generic.all
359
    @errors.cyclades.connection
360
    @errors.plankton.id
361
    @errors.plankton.metadata
362
    def _run(self, image_id, key):
363
        r = self.client.get_image_metadata(image_id, key)
364
        print_dict(r)
365

    
366
    def main(self, image_id, key=''):
367
        super(self.__class__, self)._run()
368
        self._run(image_id=image_id, key=key)
369

    
370

    
371
@command(image_cmds)
372
class image_addproperty(_init_cyclades):
373
    """Add an OS-related property to an image"""
374

    
375
    @errors.generic.all
376
    @errors.cyclades.connection
377
    @errors.plankton.id
378
    @errors.plankton.metadata
379
    def _run(self, image_id, key, val):
380
        r = self.client.create_image_metadata(image_id, key, val)
381
        print_dict(r)
382

    
383
    def main(self, image_id, key, val):
384
        super(self.__class__, self)._run()
385
        self._run(image_id=image_id, key=key, val=val)
386

    
387

    
388
@command(image_cmds)
389
class image_compute_setproperty(_init_cyclades):
390
    """Update an existing property in an image"""
391

    
392
    @errors.generic.all
393
    @errors.cyclades.connection
394
    @errors.plankton.id
395
    @errors.plankton.metadata
396
    def _run(self, image_id, key, val):
397
        metadata = {key: val}
398
        r = self.client.update_image_metadata(image_id, **metadata)
399
        print_dict(r)
400

    
401
    def main(self, image_id, key, val):
402
        super(self.__class__, self)._run()
403
        self._run(image_id=image_id, key=key, val=val)
404

    
405

    
406
@command(image_cmds)
407
class image_compute_delproperty(_init_cyclades):
408
    """Delete a property of an image"""
409

    
410
    @errors.generic.all
411
    @errors.cyclades.connection
412
    @errors.plankton.id
413
    @errors.plankton.metadata
414
    def _run(self, image_id, key):
415
        self.client.delete_image_metadata(image_id, key)
416

    
417
    def main(self, image_id, key):
418
        super(self.__class__, self)._run()
419
        self._run(image_id=image_id, key=key)