Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / image_cli.py @ 89ecfa57

History | View | Annotate | Download (12.8 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(
45
    'image',
46
    'Compute/Cyclades or Glance API image commands')
47
_commands = [image_cmds]
48

    
49

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

    
52

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

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

    
67

    
68
@command(image_cmds)
69
class image_public(_init_image):
70
    """List public images"""
71

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

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

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

    
123
    def main(self):
124
        super(self.__class__, self)._run()
125
        self._run()
126

    
127

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

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

    
144
    def main(self, image_id):
145
        super(self.__class__, self)._run()
146
        self._run(image_id=image_id)
147

    
148

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

    
153
    arguments = dict(
154
        checksum=ValueArgument('set image checksum', '--checksum'),
155
        container_format=ValueArgument(
156
            'set container format',
157
            '--container-format'),
158
        disk_format=ValueArgument('set disk format', '--disk-format'),
159
        id=ValueArgument('set image ID', '--id'),
160
        owner=ValueArgument('set image owner (admin only)', '--owner'),
161
        properties=KeyValueArgument(
162
            'add property in key=value form (can be repeated)',
163
            '--property'),
164
        is_public=FlagArgument('mark image as public', '--public'),
165
        size=IntArgument('set image size', '--size'),
166
        update=FlagArgument('update existing image properties', '--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
            if account[-1] == '/':
176
                account = account[:-1]
177
            container = self.config.get('store', 'container') \
178
                or self.config.get('global', 'container')
179
            if not container:
180
                location = 'pithos://%s/%s' % (account, location)
181
            else:
182
                location = 'pithos://%s/%s/%s' % (account, container, location)
183

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

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

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

    
205

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

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

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

    
221

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

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

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

    
236

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

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

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

    
251

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

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

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

    
266

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

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

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

    
281

    
282
@command(image_cmds)
283
class image_list(_init_cyclades):
284
    """List images"""
285

    
286
    arguments = dict(
287
        detail=FlagArgument('show detailed output', '-l'),
288
        limit=IntArgument('limit the number of images to list', '-n'),
289
        more=FlagArgument(
290
            'output results in pages (-n to set items per page, default 10)',
291
            '--more')
292
    )
293

    
294
    def _make_results_pretty(self, images):
295
        for img in images:
296
            if 'metadata' in img:
297
                img['metadata'] = img['metadata']['values']
298

    
299
    @errors.generic.all
300
    @errors.cyclades.connection
301
    def _run(self):
302
        images = self.client.list_images(self['detail'])
303
        if self['detail']:
304
            self._make_results_pretty(images)
305
        if self['more']:
306
            print_items(images, page_size=self['limit'] or 10)
307
        else:
308
            print_items(images[:self['limit']])
309

    
310
    def main(self):
311
        super(self.__class__, self)._run()
312
        self._run()
313

    
314

    
315
@command(image_cmds)
316
class image_info(_init_cyclades):
317
    """Get detailed information on an image"""
318

    
319
    @errors.generic.all
320
    @errors.cyclades.connection
321
    @errors.plankton.id
322
    def _run(self, image_id):
323
        image = self.client.get_image_details(image_id)
324
        if 'metadata' in image:
325
            image['metadata'] = image['metadata']['values']
326
        print_dict(image)
327

    
328
    def main(self, image_id):
329
        super(self.__class__, self)._run()
330
        self._run(image_id=image_id)
331

    
332

    
333
@command(image_cmds)
334
class image_delete(_init_cyclades):
335
    """Delete an image (WARNING: image file is also removed)"""
336

    
337
    @errors.generic.all
338
    @errors.cyclades.connection
339
    @errors.plankton.id
340
    def _run(self, image_id):
341
        self.client.delete_image(image_id)
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_properties(_init_cyclades):
350
    """Get properties related to OS installation in an image"""
351

    
352
    @errors.generic.all
353
    @errors.cyclades.connection
354
    @errors.plankton.id
355
    @errors.plankton.metadata
356
    def _run(self, image_id, key):
357
        r = self.client.get_image_metadata(image_id, key)
358
        print_dict(r)
359

    
360
    def main(self, image_id, key=''):
361
        super(self.__class__, self)._run()
362
        self._run(image_id=image_id, key=key)
363

    
364

    
365
@command(image_cmds)
366
class image_addproperty(_init_cyclades):
367
    """Add an OS-related property to an image"""
368

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

    
377
    def main(self, image_id, key, val):
378
        super(self.__class__, self)._run()
379
        self._run(image_id=image_id, key=key, val=val)
380

    
381

    
382
@command(image_cmds)
383
class image_setproperty(_init_cyclades):
384
    """Update an existing property in an image"""
385

    
386
    @errors.generic.all
387
    @errors.cyclades.connection
388
    @errors.plankton.id
389
    @errors.plankton.metadata
390
    def _run(self, image_id, key, val):
391
        metadata = {key: val}
392
        r = self.client.update_image_metadata(image_id, **metadata)
393
        print_dict(r)
394

    
395
    def main(self, image_id, key, val):
396
        super(self.__class__, self)._run()
397
        self._run(image_id=image_id, key=key, val=val)
398

    
399

    
400
@command(image_cmds)
401
class image_delproperty(_init_cyclades):
402
    """Delete a property of an image"""
403

    
404
    @errors.generic.all
405
    @errors.cyclades.connection
406
    @errors.plankton.id
407
    @errors.plankton.metadata
408
    def _run(self, image_id, key):
409
        self.client.delete_image_metadata(image_id, key)
410

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