Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / image.py @ cf115aed

History | View | Annotate | Download (29.5 kB)

1 e3f01d64 Stavros Sachtouris
# Copyright 2012-2013 GRNET S.A. All rights reserved.
2 7493ccb6 Stavros Sachtouris
#
3 7493ccb6 Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 7493ccb6 Stavros Sachtouris
# without modification, are permitted provided that the following
5 7493ccb6 Stavros Sachtouris
# conditions are met:
6 7493ccb6 Stavros Sachtouris
#
7 7493ccb6 Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 7493ccb6 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
9 7493ccb6 Stavros Sachtouris
#      disclaimer.
10 7493ccb6 Stavros Sachtouris
#
11 7493ccb6 Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 7493ccb6 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
13 7493ccb6 Stavros Sachtouris
#      disclaimer in the documentation and/or other materials
14 7493ccb6 Stavros Sachtouris
#      provided with the distribution.
15 7493ccb6 Stavros Sachtouris
#
16 7493ccb6 Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 7493ccb6 Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 7493ccb6 Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 7493ccb6 Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 7493ccb6 Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 7493ccb6 Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 7493ccb6 Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 7493ccb6 Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 7493ccb6 Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 7493ccb6 Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 7493ccb6 Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 7493ccb6 Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 7493ccb6 Stavros Sachtouris
#
29 7493ccb6 Stavros Sachtouris
# The views and conclusions contained in the software and
30 7493ccb6 Stavros Sachtouris
# documentation are those of the authors and should not be
31 7493ccb6 Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 7493ccb6 Stavros Sachtouris
# or implied, of GRNET S.A.command
33 7493ccb6 Stavros Sachtouris
34 623a4ceb Stavros Sachtouris
from json import load, dumps
35 d77e33d4 Stavros Sachtouris
from os import path
36 623a4ceb Stavros Sachtouris
from logging import getLogger
37 623a4ceb Stavros Sachtouris
38 f3e94e06 Stavros Sachtouris
from kamaki.cli import command
39 d486baec Stavros Sachtouris
from kamaki.cli.command_tree import CommandTree
40 854222c7 Stavros Sachtouris
from kamaki.cli.utils import print_dict, print_json, filter_dicts_by_dict
41 1395c40e Stavros Sachtouris
from kamaki.clients.image import ImageClient
42 00336c85 Stavros Sachtouris
from kamaki.clients.pithos import PithosClient
43 00336c85 Stavros Sachtouris
from kamaki.clients.astakos import AstakosClient
44 00336c85 Stavros Sachtouris
from kamaki.clients import ClientError
45 83c3ba87 Stavros Sachtouris
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument
46 d77e33d4 Stavros Sachtouris
from kamaki.cli.argument import IntArgument, ProgressBarArgument
47 bfb54881 Stavros Sachtouris
from kamaki.cli.commands.cyclades import _init_cyclades
48 8cec3671 Stavros Sachtouris
from kamaki.cli.errors import raiseCLIError, CLIBaseUrlError
49 b4f69041 Stavros Sachtouris
from kamaki.cli.commands import _command_init, errors, addLogSettings
50 6d190dd1 Stavros Sachtouris
from kamaki.cli.commands import (
51 6d190dd1 Stavros Sachtouris
    _optional_output_cmd, _optional_json, _name_filter, _id_filter)
52 0b368c8c Stavros Sachtouris
53 234954d1 Stavros Sachtouris
54 a29d2f88 Stavros Sachtouris
image_cmds = CommandTree(
55 a29d2f88 Stavros Sachtouris
    'image',
56 a29d2f88 Stavros Sachtouris
    'Cyclades/Plankton API image commands\n'
57 a29d2f88 Stavros Sachtouris
    'image compute:\tCyclades/Compute API image commands')
58 d486baec Stavros Sachtouris
_commands = [image_cmds]
59 234954d1 Stavros Sachtouris
60 234954d1 Stavros Sachtouris
61 aa82dd5a Stavros Sachtouris
howto_image_file = [
62 aa82dd5a Stavros Sachtouris
    'Kamaki commands to:',
63 c626151a Stavros Sachtouris
    ' get current user id: /user authenticate',
64 aa82dd5a Stavros Sachtouris
    ' check available containers: /file list',
65 aa82dd5a Stavros Sachtouris
    ' create a new container: /file create <container>',
66 aa82dd5a Stavros Sachtouris
    ' check container contents: /file list <container>',
67 aa82dd5a Stavros Sachtouris
    ' upload files: /file upload <image file> <container>']
68 aa82dd5a Stavros Sachtouris
69 aa82dd5a Stavros Sachtouris
about_image_id = ['To see a list of available image ids: /image list']
70 15142309 Stavros Sachtouris
71 15142309 Stavros Sachtouris
72 623a4ceb Stavros Sachtouris
log = getLogger(__name__)
73 623a4ceb Stavros Sachtouris
74 623a4ceb Stavros Sachtouris
75 5eae854d Stavros Sachtouris
class _init_image(_command_init):
76 a03ade9e Stavros Sachtouris
    @errors.generic.all
77 b4f69041 Stavros Sachtouris
    @addLogSettings
78 a03ade9e Stavros Sachtouris
    def _run(self):
79 b4f69041 Stavros Sachtouris
        if getattr(self, 'cloud', None):
80 b4f69041 Stavros Sachtouris
            img_url = self._custom_url('image') or self._custom_url('plankton')
81 b4f69041 Stavros Sachtouris
            if img_url:
82 b4f69041 Stavros Sachtouris
                token = self._custom_token('image')\
83 b4f69041 Stavros Sachtouris
                    or self._custom_token('plankton')\
84 144b3551 Stavros Sachtouris
                    or self.config.get_cloud(self.cloud, 'token')
85 b4f69041 Stavros Sachtouris
                self.client = ImageClient(base_url=img_url, token=token)
86 b4f69041 Stavros Sachtouris
                return
87 8cec3671 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
88 8cec3671 Stavros Sachtouris
            plankton_endpoints = self.auth_base.get_service_endpoints(
89 72fa0010 Stavros Sachtouris
                self._custom_type('image') or self._custom_type(
90 72fa0010 Stavros Sachtouris
                    'plankton') or 'image',
91 72fa0010 Stavros Sachtouris
                self._custom_version('image') or self._custom_version(
92 72fa0010 Stavros Sachtouris
                    'plankton') or '')
93 8cec3671 Stavros Sachtouris
            base_url = plankton_endpoints['publicURL']
94 b4f69041 Stavros Sachtouris
            token = self.auth_base.token
95 8cec3671 Stavros Sachtouris
        else:
96 8cec3671 Stavros Sachtouris
            raise CLIBaseUrlError(service='plankton')
97 a03ade9e Stavros Sachtouris
        self.client = ImageClient(base_url=base_url, token=token)
98 a03ade9e Stavros Sachtouris
99 7493ccb6 Stavros Sachtouris
    def main(self):
100 a03ade9e Stavros Sachtouris
        self._run()
101 7493ccb6 Stavros Sachtouris
102 234954d1 Stavros Sachtouris
103 573be34f Stavros Sachtouris
# Plankton Image Commands
104 573be34f Stavros Sachtouris
105 573be34f Stavros Sachtouris
106 aa82dd5a Stavros Sachtouris
def _validate_image_meta(json_dict, return_str=False):
107 623a4ceb Stavros Sachtouris
    """
108 623a4ceb Stavros Sachtouris
    :param json_dict" (dict) json-formated, of the form
109 623a4ceb Stavros Sachtouris
        {"key1": "val1", "key2": "val2", ...}
110 623a4ceb Stavros Sachtouris

111 623a4ceb Stavros Sachtouris
    :param return_str: (boolean) if true, return a json dump
112 623a4ceb Stavros Sachtouris

113 aa82dd5a Stavros Sachtouris
    :returns: (dict) if return_str is not True, else return str
114 623a4ceb Stavros Sachtouris

115 623a4ceb Stavros Sachtouris
    :raises TypeError, AttributeError: Invalid json format
116 623a4ceb Stavros Sachtouris

117 623a4ceb Stavros Sachtouris
    :raises AssertionError: Valid json but invalid image properties dict
118 623a4ceb Stavros Sachtouris
    """
119 9553da85 Stavros Sachtouris
    json_str = dumps(json_dict, indent=2)
120 623a4ceb Stavros Sachtouris
    for k, v in json_dict.items():
121 aa82dd5a Stavros Sachtouris
        if k.lower() == 'properties':
122 aa82dd5a Stavros Sachtouris
            for pk, pv in v.items():
123 aa82dd5a Stavros Sachtouris
                prop_ok = not (isinstance(pv, dict) or isinstance(pv, list))
124 aa82dd5a Stavros Sachtouris
                assert prop_ok, 'Invalid property value for key %s' % pk
125 aa82dd5a Stavros Sachtouris
                key_ok = not (' ' in k or '-' in k)
126 aa82dd5a Stavros Sachtouris
                assert key_ok, 'Invalid property key %s' % k
127 aa82dd5a Stavros Sachtouris
            continue
128 aa82dd5a Stavros Sachtouris
        meta_ok = not (isinstance(v, dict) or isinstance(v, list))
129 aa82dd5a Stavros Sachtouris
        assert meta_ok, 'Invalid value for meta key %s' % k
130 aa82dd5a Stavros Sachtouris
        meta_ok = ' ' not in k
131 aa82dd5a Stavros Sachtouris
        assert meta_ok, 'Invalid meta key [%s]' % k
132 623a4ceb Stavros Sachtouris
        json_dict[k] = '%s' % v
133 623a4ceb Stavros Sachtouris
    return json_str if return_str else json_dict
134 623a4ceb Stavros Sachtouris
135 623a4ceb Stavros Sachtouris
136 aa82dd5a Stavros Sachtouris
def _load_image_meta(filepath):
137 623a4ceb Stavros Sachtouris
    """
138 623a4ceb Stavros Sachtouris
    :param filepath: (str) the (relative) path of the metafile
139 623a4ceb Stavros Sachtouris

140 623a4ceb Stavros Sachtouris
    :returns: (dict) json_formated
141 623a4ceb Stavros Sachtouris

142 623a4ceb Stavros Sachtouris
    :raises TypeError, AttributeError: Invalid json format
143 623a4ceb Stavros Sachtouris

144 623a4ceb Stavros Sachtouris
    :raises AssertionError: Valid json but invalid image properties dict
145 623a4ceb Stavros Sachtouris
    """
146 d77e33d4 Stavros Sachtouris
    with open(path.abspath(filepath)) as f:
147 623a4ceb Stavros Sachtouris
        meta_dict = load(f)
148 623a4ceb Stavros Sachtouris
        try:
149 aa82dd5a Stavros Sachtouris
            return _validate_image_meta(meta_dict)
150 623a4ceb Stavros Sachtouris
        except AssertionError:
151 623a4ceb Stavros Sachtouris
            log.debug('Failed to load properties from file %s' % filepath)
152 623a4ceb Stavros Sachtouris
            raise
153 623a4ceb Stavros Sachtouris
154 623a4ceb Stavros Sachtouris
155 aa82dd5a Stavros Sachtouris
def _validate_image_location(location):
156 aa82dd5a Stavros Sachtouris
    """
157 f2ea1314 Stavros Sachtouris
    :param location: (str) pithos://<user-id>/<container>/<image-path>
158 aa82dd5a Stavros Sachtouris

159 f2ea1314 Stavros Sachtouris
    :returns: (<user-id>, <container>, <image-path>)
160 aa82dd5a Stavros Sachtouris

161 aa82dd5a Stavros Sachtouris
    :raises AssertionError: if location is invalid
162 aa82dd5a Stavros Sachtouris
    """
163 aa82dd5a Stavros Sachtouris
    prefix = 'pithos://'
164 aa82dd5a Stavros Sachtouris
    msg = 'Invalid prefix for location %s , try: %s' % (location, prefix)
165 aa82dd5a Stavros Sachtouris
    assert location.startswith(prefix), msg
166 aa82dd5a Stavros Sachtouris
    service, sep, rest = location.partition('://')
167 c626151a Stavros Sachtouris
    assert sep and rest, 'Location %s is missing user-id' % location
168 aa82dd5a Stavros Sachtouris
    uuid, sep, rest = rest.partition('/')
169 aa82dd5a Stavros Sachtouris
    assert sep and rest, 'Location %s is missing container' % location
170 aa82dd5a Stavros Sachtouris
    container, sep, img_path = rest.partition('/')
171 aa82dd5a Stavros Sachtouris
    assert sep and img_path, 'Location %s is missing image path' % location
172 aa82dd5a Stavros Sachtouris
    return uuid, container, img_path
173 aa82dd5a Stavros Sachtouris
174 aa82dd5a Stavros Sachtouris
175 d486baec Stavros Sachtouris
@command(image_cmds)
176 6d190dd1 Stavros Sachtouris
class image_list(_init_image, _optional_json, _name_filter, _id_filter):
177 573be34f Stavros Sachtouris
    """List images accessible by user"""
178 7493ccb6 Stavros Sachtouris
179 2d1202ee Stavros Sachtouris
    PERMANENTS = (
180 2d1202ee Stavros Sachtouris
        'id', 'name',
181 2d1202ee Stavros Sachtouris
        'status', 'container_format', 'disk_format', 'size')
182 2d1202ee Stavros Sachtouris
183 1ae79e60 Stavros Sachtouris
    arguments = dict(
184 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
185 1ae79e60 Stavros Sachtouris
        container_format=ValueArgument(
186 1ae79e60 Stavros Sachtouris
            'filter by container format',
187 1ae79e60 Stavros Sachtouris
            '--container-format'),
188 1ae79e60 Stavros Sachtouris
        disk_format=ValueArgument('filter by disk format', '--disk-format'),
189 1ae79e60 Stavros Sachtouris
        size_min=IntArgument('filter by minimum size', '--size-min'),
190 1ae79e60 Stavros Sachtouris
        size_max=IntArgument('filter by maximum size', '--size-max'),
191 1ae79e60 Stavros Sachtouris
        status=ValueArgument('filter by status', '--status'),
192 f9457c89 Stavros Sachtouris
        owner=ValueArgument('filter by owner', '--owner'),
193 95641ecc Stavros Sachtouris
        owner_name=ValueArgument('filter by owners username', '--owner-name'),
194 1ae79e60 Stavros Sachtouris
        order=ValueArgument(
195 1ae79e60 Stavros Sachtouris
            'order by FIELD ( - to reverse order)',
196 1ae79e60 Stavros Sachtouris
            '--order',
197 83c3ba87 Stavros Sachtouris
            default=''),
198 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number of listed images', ('-n', '--number')),
199 83c3ba87 Stavros Sachtouris
        more=FlagArgument(
200 83c3ba87 Stavros Sachtouris
            'output results in pages (-n to set items per page, default 10)',
201 ed9af02c Stavros Sachtouris
            '--more'),
202 2d1202ee Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
203 5576a4eb Stavros Sachtouris
        prop=KeyValueArgument('filter by property key=value', ('--property')),
204 5576a4eb Stavros Sachtouris
        prop_like=KeyValueArgument(
205 5576a4eb Stavros Sachtouris
            'fliter by property key=value where value is part of actual value',
206 1716a15d Stavros Sachtouris
            ('--property-like')),
207 1ae79e60 Stavros Sachtouris
    )
208 7493ccb6 Stavros Sachtouris
209 6d190dd1 Stavros Sachtouris
    def _filter_by_owner(self, images):
210 95641ecc Stavros Sachtouris
        ouuid = self['owner'] or self._username2uuid(self['owner_name'])
211 854222c7 Stavros Sachtouris
        return filter_dicts_by_dict(images, dict(owner=ouuid))
212 f9457c89 Stavros Sachtouris
213 5576a4eb Stavros Sachtouris
    def _add_owner_name(self, images):
214 5576a4eb Stavros Sachtouris
        uuids = self._uuids2usernames(
215 5576a4eb Stavros Sachtouris
            list(set([img['owner'] for img in images])))
216 5576a4eb Stavros Sachtouris
        for img in images:
217 5576a4eb Stavros Sachtouris
            img['owner'] += ' (%s)' % uuids[img['owner']]
218 5576a4eb Stavros Sachtouris
        return images
219 f1e5b343 Stavros Sachtouris
220 6d190dd1 Stavros Sachtouris
    def _filter_by_properties(self, images):
221 2d1202ee Stavros Sachtouris
        new_images = []
222 2d1202ee Stavros Sachtouris
        for img in images:
223 854222c7 Stavros Sachtouris
            props = [dict(img['properties'])]
224 854222c7 Stavros Sachtouris
            if self['prop']:
225 854222c7 Stavros Sachtouris
                props = filter_dicts_by_dict(props, self['prop'])
226 854222c7 Stavros Sachtouris
            if props and self['prop_like']:
227 854222c7 Stavros Sachtouris
                props = filter_dicts_by_dict(
228 854222c7 Stavros Sachtouris
                    props, self['prop_like'], exact_match=False)
229 854222c7 Stavros Sachtouris
            if props:
230 854222c7 Stavros Sachtouris
                new_images.append(img)
231 2d1202ee Stavros Sachtouris
        return new_images
232 2d1202ee Stavros Sachtouris
233 a03ade9e Stavros Sachtouris
    @errors.generic.all
234 a03ade9e Stavros Sachtouris
    @errors.cyclades.connection
235 a03ade9e Stavros Sachtouris
    def _run(self):
236 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
237 7493ccb6 Stavros Sachtouris
        filters = {}
238 fa984c2c Stavros Sachtouris
        for arg in set([
239 1ae79e60 Stavros Sachtouris
                'container_format',
240 1ae79e60 Stavros Sachtouris
                'disk_format',
241 1ae79e60 Stavros Sachtouris
                'name',
242 1ae79e60 Stavros Sachtouris
                'size_min',
243 1ae79e60 Stavros Sachtouris
                'size_max',
244 de73876b Stavros Sachtouris
                'status']).intersection(self.arguments):
245 1ae79e60 Stavros Sachtouris
            filters[arg] = self[arg]
246 1ae79e60 Stavros Sachtouris
247 1ae79e60 Stavros Sachtouris
        order = self['order']
248 854222c7 Stavros Sachtouris
        detail = self['detail'] or (
249 854222c7 Stavros Sachtouris
            self['prop'] or self['prop_like']) or (
250 854222c7 Stavros Sachtouris
            self['owner'] or self['owner_name'])
251 f9457c89 Stavros Sachtouris
252 854222c7 Stavros Sachtouris
        images = self.client.list_public(detail, filters, order)
253 854222c7 Stavros Sachtouris
254 854222c7 Stavros Sachtouris
        if self['owner'] or self['owner_name']:
255 6d190dd1 Stavros Sachtouris
            images = self._filter_by_owner(images)
256 854222c7 Stavros Sachtouris
        if self['prop'] or self['prop_like']:
257 6d190dd1 Stavros Sachtouris
            images = self._filter_by_properties(images)
258 6d190dd1 Stavros Sachtouris
        images = self._filter_by_id(images)
259 6d190dd1 Stavros Sachtouris
        images = self._non_exact_name_filter(images)
260 854222c7 Stavros Sachtouris
261 5576a4eb Stavros Sachtouris
        if self['detail'] and not self['json_output']:
262 5576a4eb Stavros Sachtouris
            images = self._add_owner_name(images)
263 854222c7 Stavros Sachtouris
        elif detail and not self['detail']:
264 854222c7 Stavros Sachtouris
            for img in images:
265 854222c7 Stavros Sachtouris
                for key in set(img).difference(self.PERMANENTS):
266 854222c7 Stavros Sachtouris
                    img.pop(key)
267 545c6c29 Stavros Sachtouris
        kwargs = dict(with_enumeration=self['enum'])
268 83c3ba87 Stavros Sachtouris
        if self['more']:
269 545c6c29 Stavros Sachtouris
            kwargs['page_size'] = self['limit'] or 10
270 83c3ba87 Stavros Sachtouris
        elif self['limit']:
271 545c6c29 Stavros Sachtouris
            images = images[:self['limit']]
272 545c6c29 Stavros Sachtouris
        self._print(images, **kwargs)
273 7493ccb6 Stavros Sachtouris
274 a03ade9e Stavros Sachtouris
    def main(self):
275 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
276 a03ade9e Stavros Sachtouris
        self._run()
277 a03ade9e Stavros Sachtouris
278 234954d1 Stavros Sachtouris
279 d486baec Stavros Sachtouris
@command(image_cmds)
280 545c6c29 Stavros Sachtouris
class image_meta(_init_image, _optional_json):
281 15142309 Stavros Sachtouris
    """Get image metadata
282 15142309 Stavros Sachtouris
    Image metadata include:
283 439826ec Stavros Sachtouris
    - image file information (location, size, etc.)
284 439826ec Stavros Sachtouris
    - image information (id, name, etc.)
285 439826ec Stavros Sachtouris
    - image os properties (os, fs, etc.)
286 15142309 Stavros Sachtouris
    """
287 7493ccb6 Stavros Sachtouris
288 a03ade9e Stavros Sachtouris
    @errors.generic.all
289 a03ade9e Stavros Sachtouris
    @errors.plankton.connection
290 a03ade9e Stavros Sachtouris
    @errors.plankton.id
291 a03ade9e Stavros Sachtouris
    def _run(self, image_id):
292 545c6c29 Stavros Sachtouris
        self._print([self.client.get_meta(image_id)])
293 7493ccb6 Stavros Sachtouris
294 a03ade9e Stavros Sachtouris
    def main(self, image_id):
295 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
296 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
297 a03ade9e Stavros Sachtouris
298 234954d1 Stavros Sachtouris
299 d486baec Stavros Sachtouris
@command(image_cmds)
300 545c6c29 Stavros Sachtouris
class image_register(_init_image, _optional_json):
301 fc903073 Stavros Sachtouris
    """(Re)Register an image"""
302 7493ccb6 Stavros Sachtouris
303 d77e33d4 Stavros Sachtouris
    container_info_cache = {}
304 d77e33d4 Stavros Sachtouris
305 1ae79e60 Stavros Sachtouris
    arguments = dict(
306 1ae79e60 Stavros Sachtouris
        checksum=ValueArgument('set image checksum', '--checksum'),
307 1ae79e60 Stavros Sachtouris
        container_format=ValueArgument(
308 1ae79e60 Stavros Sachtouris
            'set container format',
309 1ae79e60 Stavros Sachtouris
            '--container-format'),
310 1ae79e60 Stavros Sachtouris
        disk_format=ValueArgument('set disk format', '--disk-format'),
311 1ae79e60 Stavros Sachtouris
        owner=ValueArgument('set image owner (admin only)', '--owner'),
312 1ae79e60 Stavros Sachtouris
        properties=KeyValueArgument(
313 1ae79e60 Stavros Sachtouris
            'add property in key=value form (can be repeated)',
314 1736e06d Stavros Sachtouris
            ('-p', '--property')),
315 1ae79e60 Stavros Sachtouris
        is_public=FlagArgument('mark image as public', '--public'),
316 1ae79e60 Stavros Sachtouris
        size=IntArgument('set image size', '--size'),
317 aa82dd5a Stavros Sachtouris
        metafile=ValueArgument(
318 aa82dd5a Stavros Sachtouris
            'Load metadata from a json-formated file <img-file>.meta :'
319 aa82dd5a Stavros Sachtouris
            '{"key1": "val1", "key2": "val2", ..., "properties: {...}"}',
320 aa82dd5a Stavros Sachtouris
            ('--metafile')),
321 aa82dd5a Stavros Sachtouris
        metafile_force=FlagArgument(
322 aa82dd5a Stavros Sachtouris
            'Store remote metadata object, even if it already exists',
323 aa82dd5a Stavros Sachtouris
            ('-f', '--force')),
324 aa82dd5a Stavros Sachtouris
        no_metafile_upload=FlagArgument(
325 aa82dd5a Stavros Sachtouris
            'Do not store metadata in remote meta file',
326 aa82dd5a Stavros Sachtouris
            ('--no-metafile-upload')),
327 f2ea1314 Stavros Sachtouris
        container=ValueArgument(
328 f2ea1314 Stavros Sachtouris
            'Pithos+ container containing the image file',
329 f2ea1314 Stavros Sachtouris
            ('-C', '--container')),
330 d77e33d4 Stavros Sachtouris
        uuid=ValueArgument('Custom user uuid', '--uuid'),
331 d77e33d4 Stavros Sachtouris
        local_image_path=ValueArgument(
332 d77e33d4 Stavros Sachtouris
            'Local image file path to upload and register '
333 d77e33d4 Stavros Sachtouris
            '(still need target file in the form container:remote-path )',
334 d77e33d4 Stavros Sachtouris
            '--upload-image-file'),
335 d77e33d4 Stavros Sachtouris
        progress_bar=ProgressBarArgument(
336 d77e33d4 Stavros Sachtouris
            'Do not use progress bar', '--no-progress-bar', default=False)
337 1ae79e60 Stavros Sachtouris
    )
338 7493ccb6 Stavros Sachtouris
339 f5c28bfa Stavros Sachtouris
    def _get_user_id(self):
340 00336c85 Stavros Sachtouris
        atoken = self.client.token
341 ef00bc31 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
342 9d8737a2 Stavros Sachtouris
            return self.auth_base.term('id', atoken)
343 ef00bc31 Stavros Sachtouris
        else:
344 f5c28bfa Stavros Sachtouris
            astakos_url = self.config.get('user', 'url')\
345 f5c28bfa Stavros Sachtouris
                or self.config.get('astakos', 'url')
346 ef00bc31 Stavros Sachtouris
            if not astakos_url:
347 ef00bc31 Stavros Sachtouris
                raise CLIBaseUrlError(service='astakos')
348 ef00bc31 Stavros Sachtouris
            user = AstakosClient(astakos_url, atoken)
349 819311d3 Stavros Sachtouris
            return user.term('id')
350 00336c85 Stavros Sachtouris
351 aa82dd5a Stavros Sachtouris
    def _get_pithos_client(self, container):
352 aa82dd5a Stavros Sachtouris
        if self['no_metafile_upload']:
353 aa82dd5a Stavros Sachtouris
            return None
354 00336c85 Stavros Sachtouris
        ptoken = self.client.token
355 ef00bc31 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
356 ef00bc31 Stavros Sachtouris
            pithos_endpoints = self.auth_base.get_service_endpoints(
357 df0045d8 Stavros Sachtouris
                'object-store')
358 ef00bc31 Stavros Sachtouris
            purl = pithos_endpoints['publicURL']
359 ef00bc31 Stavros Sachtouris
        else:
360 df0045d8 Stavros Sachtouris
            purl = self.config.get_cloud('pithos', 'url')
361 df0045d8 Stavros Sachtouris
        if not purl:
362 df0045d8 Stavros Sachtouris
            raise CLIBaseUrlError(service='pithos')
363 f5c28bfa Stavros Sachtouris
        return PithosClient(purl, ptoken, self._get_user_id(), container)
364 00336c85 Stavros Sachtouris
365 aa82dd5a Stavros Sachtouris
    def _store_remote_metafile(self, pclient, remote_path, metadata):
366 00336c85 Stavros Sachtouris
        return pclient.upload_from_string(
367 d77e33d4 Stavros Sachtouris
            remote_path, _validate_image_meta(metadata, return_str=True),
368 d77e33d4 Stavros Sachtouris
            container_info_cache=self.container_info_cache)
369 00336c85 Stavros Sachtouris
370 aa82dd5a Stavros Sachtouris
    def _load_params_from_file(self, location):
371 aa82dd5a Stavros Sachtouris
        params, properties = dict(), dict()
372 aa82dd5a Stavros Sachtouris
        pfile = self['metafile']
373 aa82dd5a Stavros Sachtouris
        if pfile:
374 00336c85 Stavros Sachtouris
            try:
375 aa82dd5a Stavros Sachtouris
                for k, v in _load_image_meta(pfile).items():
376 aa82dd5a Stavros Sachtouris
                    key = k.lower().replace('-', '_')
377 aa82dd5a Stavros Sachtouris
                    if k == 'properties':
378 aa82dd5a Stavros Sachtouris
                        for pk, pv in v.items():
379 aa82dd5a Stavros Sachtouris
                            properties[pk.upper().replace('-', '_')] = pv
380 aa82dd5a Stavros Sachtouris
                    elif key == 'name':
381 aa82dd5a Stavros Sachtouris
                            continue
382 aa82dd5a Stavros Sachtouris
                    elif key == 'location':
383 aa82dd5a Stavros Sachtouris
                        if location:
384 aa82dd5a Stavros Sachtouris
                            continue
385 aa82dd5a Stavros Sachtouris
                        location = v
386 aa82dd5a Stavros Sachtouris
                    else:
387 aa82dd5a Stavros Sachtouris
                        params[key] = v
388 aa82dd5a Stavros Sachtouris
            except Exception as e:
389 aa82dd5a Stavros Sachtouris
                raiseCLIError(e, 'Invalid json metadata config file')
390 aa82dd5a Stavros Sachtouris
        return params, properties, location
391 7493ccb6 Stavros Sachtouris
392 aa82dd5a Stavros Sachtouris
    def _load_params_from_args(self, params, properties):
393 f769a16a Stavros Sachtouris
        for key in set([
394 1ae79e60 Stavros Sachtouris
                'checksum',
395 1ae79e60 Stavros Sachtouris
                'container_format',
396 1ae79e60 Stavros Sachtouris
                'disk_format',
397 1ae79e60 Stavros Sachtouris
                'owner',
398 1ae79e60 Stavros Sachtouris
                'size',
399 de73876b Stavros Sachtouris
                'is_public']).intersection(self.arguments):
400 1ae79e60 Stavros Sachtouris
            params[key] = self[key]
401 aa82dd5a Stavros Sachtouris
        for k, v in self['properties'].items():
402 aa82dd5a Stavros Sachtouris
            properties[k.upper().replace('-', '_')] = v
403 1ae79e60 Stavros Sachtouris
404 aa82dd5a Stavros Sachtouris
    def _validate_location(self, location):
405 aa82dd5a Stavros Sachtouris
        if not location:
406 aa82dd5a Stavros Sachtouris
            raiseCLIError(
407 aa82dd5a Stavros Sachtouris
                'No image file location provided',
408 aa82dd5a Stavros Sachtouris
                importance=2, details=[
409 aa82dd5a Stavros Sachtouris
                    'An image location is needed. Image location format:',
410 c626151a Stavros Sachtouris
                    '  pithos://<user-id>/<container>/<path>',
411 f2ea1314 Stavros Sachtouris
                    ' where an image file at the above location must exist.'
412 aa82dd5a Stavros Sachtouris
                    ] + howto_image_file)
413 aa82dd5a Stavros Sachtouris
        try:
414 aa82dd5a Stavros Sachtouris
            return _validate_image_location(location)
415 aa82dd5a Stavros Sachtouris
        except AssertionError as ae:
416 aa82dd5a Stavros Sachtouris
            raiseCLIError(
417 aa82dd5a Stavros Sachtouris
                ae, 'Invalid image location format',
418 aa82dd5a Stavros Sachtouris
                importance=1, details=[
419 aa82dd5a Stavros Sachtouris
                    'Valid image location format:',
420 c626151a Stavros Sachtouris
                    '  pithos://<user-id>/<container>/<img-file-path>'
421 aa82dd5a Stavros Sachtouris
                    ] + howto_image_file)
422 aa82dd5a Stavros Sachtouris
423 f2ea1314 Stavros Sachtouris
    def _mine_location(self, container_path):
424 f2ea1314 Stavros Sachtouris
        uuid = self['uuid'] or self._get_user_id()
425 f2ea1314 Stavros Sachtouris
        if self['container']:
426 f2ea1314 Stavros Sachtouris
            return uuid, self['container'], container_path
427 f2ea1314 Stavros Sachtouris
        container, sep, path = container_path.partition(':')
428 f2ea1314 Stavros Sachtouris
        if not (bool(container) and bool(path)):
429 f2ea1314 Stavros Sachtouris
            raiseCLIError(
430 f2ea1314 Stavros Sachtouris
                'Incorrect container-path format', importance=1, details=[
431 f2ea1314 Stavros Sachtouris
                'Use : to seperate container form path',
432 f2ea1314 Stavros Sachtouris
                '  <container>:<image-path>',
433 f2ea1314 Stavros Sachtouris
                'OR',
434 f2ea1314 Stavros Sachtouris
                'Use -C to specifiy a container',
435 f2ea1314 Stavros Sachtouris
                '  -C <container> <image-path>'] + howto_image_file)
436 f2ea1314 Stavros Sachtouris
437 f2ea1314 Stavros Sachtouris
        return uuid, container, path
438 f2ea1314 Stavros Sachtouris
439 aa82dd5a Stavros Sachtouris
    @errors.generic.all
440 aa82dd5a Stavros Sachtouris
    @errors.plankton.connection
441 f2ea1314 Stavros Sachtouris
    def _run(self, name, uuid, container, img_path):
442 d77e33d4 Stavros Sachtouris
        if self['local_image_path']:
443 d77e33d4 Stavros Sachtouris
            with open(self['local_image_path']) as f:
444 d77e33d4 Stavros Sachtouris
                pithos = self._get_pithos_client(container)
445 d77e33d4 Stavros Sachtouris
                (pbar, upload_cb) = self._safe_progress_bar('Uploading')
446 d77e33d4 Stavros Sachtouris
                if pbar:
447 d77e33d4 Stavros Sachtouris
                    hash_bar = pbar.clone()
448 d77e33d4 Stavros Sachtouris
                    hash_cb = hash_bar.get_generator('Calculating hashes')
449 d77e33d4 Stavros Sachtouris
                pithos.upload_object(
450 d77e33d4 Stavros Sachtouris
                    img_path, f,
451 d77e33d4 Stavros Sachtouris
                    hash_cb=hash_cb, upload_cb=upload_cb,
452 d77e33d4 Stavros Sachtouris
                    container_info_cache=self.container_info_cache)
453 d77e33d4 Stavros Sachtouris
                pbar.finish()
454 d77e33d4 Stavros Sachtouris
455 f2ea1314 Stavros Sachtouris
        location = 'pithos://%s/%s/%s' % (uuid, container, img_path)
456 f2ea1314 Stavros Sachtouris
        (params, properties, new_loc) = self._load_params_from_file(location)
457 f2ea1314 Stavros Sachtouris
        if location != new_loc:
458 f2ea1314 Stavros Sachtouris
            uuid, container, img_path = self._validate_location(new_loc)
459 aa82dd5a Stavros Sachtouris
        self._load_params_from_args(params, properties)
460 aa82dd5a Stavros Sachtouris
        pclient = self._get_pithos_client(container)
461 aa82dd5a Stavros Sachtouris
462 aa82dd5a Stavros Sachtouris
        #check if metafile exists
463 aa82dd5a Stavros Sachtouris
        meta_path = '%s.meta' % img_path
464 aa82dd5a Stavros Sachtouris
        if pclient and not self['metafile_force']:
465 c4aefeaf Stavros Sachtouris
            try:
466 aa82dd5a Stavros Sachtouris
                pclient.get_object_info(meta_path)
467 81b0838d Stavros Sachtouris
                raiseCLIError(
468 81b0838d Stavros Sachtouris
                    'Metadata file %s:%s already exists, abort' % (
469 81b0838d Stavros Sachtouris
                        container, meta_path),
470 81b0838d Stavros Sachtouris
                    details=['Registration ABORTED', 'Try -f to overwrite'])
471 aa82dd5a Stavros Sachtouris
            except ClientError as ce:
472 aa82dd5a Stavros Sachtouris
                if ce.status != 404:
473 aa82dd5a Stavros Sachtouris
                    raise
474 aa82dd5a Stavros Sachtouris
475 aa82dd5a Stavros Sachtouris
        #register the image
476 aa82dd5a Stavros Sachtouris
        try:
477 aa82dd5a Stavros Sachtouris
            r = self.client.register(name, location, params, properties)
478 aa82dd5a Stavros Sachtouris
        except ClientError as ce:
479 aa82dd5a Stavros Sachtouris
            if ce.status in (400, ):
480 c4aefeaf Stavros Sachtouris
                raiseCLIError(
481 aa82dd5a Stavros Sachtouris
                    ce, 'Nonexistent image file location %s' % location,
482 c4aefeaf Stavros Sachtouris
                    details=[
483 aa82dd5a Stavros Sachtouris
                        'Make sure the image file exists'] + howto_image_file)
484 aa82dd5a Stavros Sachtouris
            raise
485 cf115aed Stavros Sachtouris
        r['owner'] += '( %s)' % self._uuid2username(r['owner'])
486 aa82dd5a Stavros Sachtouris
        self._print(r, print_dict)
487 a03ade9e Stavros Sachtouris
488 aa82dd5a Stavros Sachtouris
        #upload the metadata file
489 00336c85 Stavros Sachtouris
        if pclient:
490 aa82dd5a Stavros Sachtouris
            try:
491 aa82dd5a Stavros Sachtouris
                meta_headers = pclient.upload_from_string(
492 d77e33d4 Stavros Sachtouris
                    meta_path, dumps(r, indent=2),
493 d77e33d4 Stavros Sachtouris
                    container_info_cache=self.container_info_cache)
494 aa82dd5a Stavros Sachtouris
            except TypeError:
495 aa82dd5a Stavros Sachtouris
                print('Failed to dump metafile %s:%s' % (container, meta_path))
496 aa82dd5a Stavros Sachtouris
                return
497 9553da85 Stavros Sachtouris
            if self['json_output']:
498 9553da85 Stavros Sachtouris
                print_json(dict(
499 aa82dd5a Stavros Sachtouris
                    metafile_location='%s:%s' % (container, meta_path),
500 aa82dd5a Stavros Sachtouris
                    headers=meta_headers))
501 9553da85 Stavros Sachtouris
            else:
502 aa82dd5a Stavros Sachtouris
                print('Metadata file uploaded as %s:%s (version %s)' % (
503 aa82dd5a Stavros Sachtouris
                    container, meta_path, meta_headers['x-object-version']))
504 00336c85 Stavros Sachtouris
505 f2ea1314 Stavros Sachtouris
    def main(self, name, container___image_path):
506 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
507 f2ea1314 Stavros Sachtouris
        self._run(name, *self._mine_location(container___image_path))
508 7493ccb6 Stavros Sachtouris
509 234954d1 Stavros Sachtouris
510 d486baec Stavros Sachtouris
@command(image_cmds)
511 f5f35422 Stavros Sachtouris
class image_unregister(_init_image, _optional_output_cmd):
512 4a17d307 Stavros Sachtouris
    """Unregister an image (does not delete the image file)"""
513 4a17d307 Stavros Sachtouris
514 4a17d307 Stavros Sachtouris
    @errors.generic.all
515 4a17d307 Stavros Sachtouris
    @errors.plankton.connection
516 4a17d307 Stavros Sachtouris
    @errors.plankton.id
517 4a17d307 Stavros Sachtouris
    def _run(self, image_id):
518 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.unregister(image_id))
519 4a17d307 Stavros Sachtouris
520 4a17d307 Stavros Sachtouris
    def main(self, image_id):
521 4a17d307 Stavros Sachtouris
        super(self.__class__, self)._run()
522 4a17d307 Stavros Sachtouris
        self._run(image_id=image_id)
523 4a17d307 Stavros Sachtouris
524 4a17d307 Stavros Sachtouris
525 4a17d307 Stavros Sachtouris
@command(image_cmds)
526 545c6c29 Stavros Sachtouris
class image_shared(_init_image, _optional_json):
527 f5f35422 Stavros Sachtouris
    """List images shared by a member"""
528 f5f35422 Stavros Sachtouris
529 236e7d08 Stavros Sachtouris
    @errors.generic.all
530 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
531 f5f35422 Stavros Sachtouris
    def _run(self, member):
532 cf115aed Stavros Sachtouris
        r = self.client.list_shared(member)
533 cf115aed Stavros Sachtouris
        if r:
534 cf115aed Stavros Sachtouris
            uuid = self._username2uuid(member)
535 cf115aed Stavros Sachtouris
            r = self.client.list_shared(uuid) if uuid else []
536 cf115aed Stavros Sachtouris
        self._print(r, title=('image_id',))
537 7493ccb6 Stavros Sachtouris
538 cf115aed Stavros Sachtouris
    def main(self, member_id_or_username):
539 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
540 cf115aed Stavros Sachtouris
        self._run(member_id_or_username)
541 236e7d08 Stavros Sachtouris
542 234954d1 Stavros Sachtouris
543 d486baec Stavros Sachtouris
@command(image_cmds)
544 f5f35422 Stavros Sachtouris
class image_members(_init_image):
545 f5f35422 Stavros Sachtouris
    """Manage members. Members of an image are users who can modify it"""
546 f5f35422 Stavros Sachtouris
547 f5f35422 Stavros Sachtouris
548 f5f35422 Stavros Sachtouris
@command(image_cmds)
549 545c6c29 Stavros Sachtouris
class image_members_list(_init_image, _optional_json):
550 f5f35422 Stavros Sachtouris
    """List members of an image"""
551 f5f35422 Stavros Sachtouris
552 236e7d08 Stavros Sachtouris
    @errors.generic.all
553 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
554 f5f35422 Stavros Sachtouris
    @errors.plankton.id
555 f5f35422 Stavros Sachtouris
    def _run(self, image_id):
556 cf115aed Stavros Sachtouris
        members = self.client.list_members(image_id)
557 cf115aed Stavros Sachtouris
        if not self['json_output']:
558 cf115aed Stavros Sachtouris
            uuids = [member['member_id'] for member in members]
559 cf115aed Stavros Sachtouris
            usernames = self._uuids2usernames(uuids)
560 cf115aed Stavros Sachtouris
            for member in members:
561 cf115aed Stavros Sachtouris
                member['member_id'] += ' (%s)' % usernames[member['member_id']]
562 cf115aed Stavros Sachtouris
        self._print(members, title=('member_id',))
563 7493ccb6 Stavros Sachtouris
564 f5f35422 Stavros Sachtouris
    def main(self, image_id):
565 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
566 f5f35422 Stavros Sachtouris
        self._run(image_id=image_id)
567 236e7d08 Stavros Sachtouris
568 234954d1 Stavros Sachtouris
569 d486baec Stavros Sachtouris
@command(image_cmds)
570 f5f35422 Stavros Sachtouris
class image_members_add(_init_image, _optional_output_cmd):
571 7493ccb6 Stavros Sachtouris
    """Add a member to an image"""
572 7493ccb6 Stavros Sachtouris
573 236e7d08 Stavros Sachtouris
    @errors.generic.all
574 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
575 236e7d08 Stavros Sachtouris
    @errors.plankton.id
576 b04288f7 Stavros Sachtouris
    def _run(self, image_id=None, member=None):
577 f5f35422 Stavros Sachtouris
            self._optional_output(self.client.add_member(image_id, member))
578 236e7d08 Stavros Sachtouris
579 cf115aed Stavros Sachtouris
    def main(self, image_id, member_id):
580 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
581 cf115aed Stavros Sachtouris
        self._run(image_id=image_id, member=member_id)
582 7493ccb6 Stavros Sachtouris
583 234954d1 Stavros Sachtouris
584 d486baec Stavros Sachtouris
@command(image_cmds)
585 f5f35422 Stavros Sachtouris
class image_members_delete(_init_image, _optional_output_cmd):
586 7493ccb6 Stavros Sachtouris
    """Remove a member from an image"""
587 7493ccb6 Stavros Sachtouris
588 236e7d08 Stavros Sachtouris
    @errors.generic.all
589 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
590 236e7d08 Stavros Sachtouris
    @errors.plankton.id
591 b04288f7 Stavros Sachtouris
    def _run(self, image_id=None, member=None):
592 f5f35422 Stavros Sachtouris
            self._optional_output(self.client.remove_member(image_id, member))
593 236e7d08 Stavros Sachtouris
594 236e7d08 Stavros Sachtouris
    def main(self, image_id, member):
595 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
596 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, member=member)
597 7493ccb6 Stavros Sachtouris
598 234954d1 Stavros Sachtouris
599 d486baec Stavros Sachtouris
@command(image_cmds)
600 f5f35422 Stavros Sachtouris
class image_members_set(_init_image, _optional_output_cmd):
601 7493ccb6 Stavros Sachtouris
    """Set the members of an image"""
602 7493ccb6 Stavros Sachtouris
603 236e7d08 Stavros Sachtouris
    @errors.generic.all
604 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
605 236e7d08 Stavros Sachtouris
    @errors.plankton.id
606 b04288f7 Stavros Sachtouris
    def _run(self, image_id, members):
607 f5f35422 Stavros Sachtouris
            self._optional_output(self.client.set_members(image_id, members))
608 236e7d08 Stavros Sachtouris
609 cf115aed Stavros Sachtouris
    def main(self, image_id, *member_ids):
610 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
611 cf115aed Stavros Sachtouris
        self._run(image_id=image_id, members=member_ids)
612 f3e94e06 Stavros Sachtouris
613 f3e94e06 Stavros Sachtouris
614 573be34f Stavros Sachtouris
# Compute Image Commands
615 573be34f Stavros Sachtouris
616 573be34f Stavros Sachtouris
617 d486baec Stavros Sachtouris
@command(image_cmds)
618 8741c407 Stavros Sachtouris
class image_compute(_init_cyclades):
619 a29d2f88 Stavros Sachtouris
    """Cyclades/Compute API image commands"""
620 8741c407 Stavros Sachtouris
621 8741c407 Stavros Sachtouris
622 8741c407 Stavros Sachtouris
@command(image_cmds)
623 6d190dd1 Stavros Sachtouris
class image_compute_list(
624 6d190dd1 Stavros Sachtouris
        _init_cyclades, _optional_json, _name_filter, _id_filter):
625 f3e94e06 Stavros Sachtouris
    """List images"""
626 f3e94e06 Stavros Sachtouris
627 1716a15d Stavros Sachtouris
    PERMANENTS = ('id', 'name')
628 1716a15d Stavros Sachtouris
629 1ae79e60 Stavros Sachtouris
    arguments = dict(
630 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
631 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number listed images', ('-n', '--number')),
632 15142309 Stavros Sachtouris
        more=FlagArgument(
633 15142309 Stavros Sachtouris
            'output results in pages (-n to set items per page, default 10)',
634 ed9af02c Stavros Sachtouris
            '--more'),
635 1716a15d Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
636 fc48b144 Stavros Sachtouris
        user_id=ValueArgument('filter by user_id', '--user-id'),
637 fc48b144 Stavros Sachtouris
        user_name=ValueArgument('filter by username', '--user-name'),
638 1716a15d Stavros Sachtouris
        meta=KeyValueArgument(
639 1716a15d Stavros Sachtouris
            'filter by metadata key=value (can be repeated)', ('--metadata')),
640 1716a15d Stavros Sachtouris
        meta_like=KeyValueArgument(
641 1716a15d Stavros Sachtouris
            'filter by metadata key=value (can be repeated)',
642 1716a15d Stavros Sachtouris
            ('--metadata-like'))
643 1ae79e60 Stavros Sachtouris
    )
644 f3e94e06 Stavros Sachtouris
645 1716a15d Stavros Sachtouris
    def _filter_by_metadata(self, images):
646 1716a15d Stavros Sachtouris
        new_images = []
647 1716a15d Stavros Sachtouris
        for img in images:
648 854222c7 Stavros Sachtouris
            meta = [dict(img['metadata'])]
649 854222c7 Stavros Sachtouris
            if self['meta']:
650 854222c7 Stavros Sachtouris
                meta = filter_dicts_by_dict(meta, self['meta'])
651 854222c7 Stavros Sachtouris
            if meta and self['meta_like']:
652 854222c7 Stavros Sachtouris
                meta = filter_dicts_by_dict(
653 854222c7 Stavros Sachtouris
                    meta, self['meta_like'], exact_match=False)
654 854222c7 Stavros Sachtouris
            if meta:
655 854222c7 Stavros Sachtouris
                new_images.append(img)
656 1716a15d Stavros Sachtouris
        return new_images
657 1716a15d Stavros Sachtouris
658 fc48b144 Stavros Sachtouris
    def _filter_by_user(self, images):
659 fc48b144 Stavros Sachtouris
        uuid = self['user_id'] or self._username2uuid(self['user_name'])
660 fc48b144 Stavros Sachtouris
        return filter_dicts_by_dict(images, dict(user_id=uuid))
661 fc48b144 Stavros Sachtouris
662 1716a15d Stavros Sachtouris
    def _add_name(self, images, key='user_id'):
663 1716a15d Stavros Sachtouris
        uuids = self._uuids2usernames(
664 1716a15d Stavros Sachtouris
            list(set([img[key] for img in images])))
665 1716a15d Stavros Sachtouris
        for img in images:
666 1716a15d Stavros Sachtouris
            img[key] += ' (%s)' % uuids[img[key]]
667 1716a15d Stavros Sachtouris
        return images
668 1716a15d Stavros Sachtouris
669 236e7d08 Stavros Sachtouris
    @errors.generic.all
670 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
671 236e7d08 Stavros Sachtouris
    def _run(self):
672 1716a15d Stavros Sachtouris
        withmeta = bool(self['meta'] or self['meta_like'])
673 fc48b144 Stavros Sachtouris
        withuser = bool(self['user_id'] or self['user_name'])
674 fc48b144 Stavros Sachtouris
        detail = self['detail'] or withmeta or withuser
675 1716a15d Stavros Sachtouris
        images = self.client.list_images(detail)
676 6d190dd1 Stavros Sachtouris
        images = self._filter_by_name(images)
677 6d190dd1 Stavros Sachtouris
        images = self._filter_by_id(images)
678 fc48b144 Stavros Sachtouris
        if withuser:
679 fc48b144 Stavros Sachtouris
            images = self._filter_by_user(images)
680 1716a15d Stavros Sachtouris
        if withmeta:
681 1716a15d Stavros Sachtouris
            images = self._filter_by_metadata(images)
682 1716a15d Stavros Sachtouris
        if self['detail'] and not self['json_output']:
683 1716a15d Stavros Sachtouris
            images = self._add_name(self._add_name(images, 'tenant_id'))
684 fc48b144 Stavros Sachtouris
        elif detail and not self['detail']:
685 fc48b144 Stavros Sachtouris
            for img in images:
686 fc48b144 Stavros Sachtouris
                for key in set(img).difference(self.PERMANENTS):
687 fc48b144 Stavros Sachtouris
                    img.pop(key)
688 545c6c29 Stavros Sachtouris
        kwargs = dict(with_enumeration=self['enum'])
689 236e7d08 Stavros Sachtouris
        if self['more']:
690 545c6c29 Stavros Sachtouris
            kwargs['page_size'] = self['limit'] or 10
691 bcef3ac9 Stavros Sachtouris
        elif self['limit']:
692 545c6c29 Stavros Sachtouris
            images = images[:self['limit']]
693 545c6c29 Stavros Sachtouris
        self._print(images, **kwargs)
694 236e7d08 Stavros Sachtouris
695 f3e94e06 Stavros Sachtouris
    def main(self):
696 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
697 236e7d08 Stavros Sachtouris
        self._run()
698 f3e94e06 Stavros Sachtouris
699 234954d1 Stavros Sachtouris
700 d486baec Stavros Sachtouris
@command(image_cmds)
701 545c6c29 Stavros Sachtouris
class image_compute_info(_init_cyclades, _optional_json):
702 15142309 Stavros Sachtouris
    """Get detailed information on an image"""
703 f3e94e06 Stavros Sachtouris
704 236e7d08 Stavros Sachtouris
    @errors.generic.all
705 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
706 236e7d08 Stavros Sachtouris
    @errors.plankton.id
707 236e7d08 Stavros Sachtouris
    def _run(self, image_id):
708 236e7d08 Stavros Sachtouris
        image = self.client.get_image_details(image_id)
709 cf115aed Stavros Sachtouris
        uuids = [image['user_id'], image['tenant_id']]
710 cf115aed Stavros Sachtouris
        usernames = self._uuids2usernames(uuids)
711 cf115aed Stavros Sachtouris
        image['user_id'] += ' (%s)' % usernames[image['user_id']]
712 cf115aed Stavros Sachtouris
        image['tenant_id'] += ' (%s)' % usernames[image['tenant_id']]
713 bcef3ac9 Stavros Sachtouris
        self._print(image, print_dict)
714 f3e94e06 Stavros Sachtouris
715 f3e94e06 Stavros Sachtouris
    def main(self, image_id):
716 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
717 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
718 f3e94e06 Stavros Sachtouris
719 234954d1 Stavros Sachtouris
720 d486baec Stavros Sachtouris
@command(image_cmds)
721 f5f35422 Stavros Sachtouris
class image_compute_delete(_init_cyclades, _optional_output_cmd):
722 24ff0a35 Stavros Sachtouris
    """Delete an image (WARNING: image file is also removed)"""
723 f3e94e06 Stavros Sachtouris
724 236e7d08 Stavros Sachtouris
    @errors.generic.all
725 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
726 236e7d08 Stavros Sachtouris
    @errors.plankton.id
727 236e7d08 Stavros Sachtouris
    def _run(self, image_id):
728 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.delete_image(image_id))
729 236e7d08 Stavros Sachtouris
730 f3e94e06 Stavros Sachtouris
    def main(self, image_id):
731 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
732 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
733 f3e94e06 Stavros Sachtouris
734 234954d1 Stavros Sachtouris
735 d486baec Stavros Sachtouris
@command(image_cmds)
736 8741c407 Stavros Sachtouris
class image_compute_properties(_init_cyclades):
737 395fbf9e Stavros Sachtouris
    """Manage properties related to OS installation in an image"""
738 f5f35422 Stavros Sachtouris
739 f5f35422 Stavros Sachtouris
740 f5f35422 Stavros Sachtouris
@command(image_cmds)
741 545c6c29 Stavros Sachtouris
class image_compute_properties_list(_init_cyclades, _optional_json):
742 f5f35422 Stavros Sachtouris
    """List all image properties"""
743 f5f35422 Stavros Sachtouris
744 f5f35422 Stavros Sachtouris
    @errors.generic.all
745 f5f35422 Stavros Sachtouris
    @errors.cyclades.connection
746 f5f35422 Stavros Sachtouris
    @errors.plankton.id
747 f5f35422 Stavros Sachtouris
    def _run(self, image_id):
748 545c6c29 Stavros Sachtouris
        self._print(self.client.get_image_metadata(image_id), print_dict)
749 f5f35422 Stavros Sachtouris
750 f5f35422 Stavros Sachtouris
    def main(self, image_id):
751 f5f35422 Stavros Sachtouris
        super(self.__class__, self)._run()
752 f5f35422 Stavros Sachtouris
        self._run(image_id=image_id)
753 f5f35422 Stavros Sachtouris
754 f5f35422 Stavros Sachtouris
755 f5f35422 Stavros Sachtouris
@command(image_cmds)
756 545c6c29 Stavros Sachtouris
class image_compute_properties_get(_init_cyclades, _optional_json):
757 f5f35422 Stavros Sachtouris
    """Get an image property"""
758 f5f35422 Stavros Sachtouris
759 236e7d08 Stavros Sachtouris
    @errors.generic.all
760 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
761 236e7d08 Stavros Sachtouris
    @errors.plankton.id
762 236e7d08 Stavros Sachtouris
    @errors.plankton.metadata
763 236e7d08 Stavros Sachtouris
    def _run(self, image_id, key):
764 545c6c29 Stavros Sachtouris
        self._print(self.client.get_image_metadata(image_id, key), print_dict)
765 236e7d08 Stavros Sachtouris
766 f5f35422 Stavros Sachtouris
    def main(self, image_id, key):
767 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
768 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, key=key)
769 f3e94e06 Stavros Sachtouris
770 234954d1 Stavros Sachtouris
771 d486baec Stavros Sachtouris
@command(image_cmds)
772 545c6c29 Stavros Sachtouris
class image_compute_properties_add(_init_cyclades, _optional_json):
773 f5f35422 Stavros Sachtouris
    """Add a property to an image"""
774 f5f35422 Stavros Sachtouris
775 236e7d08 Stavros Sachtouris
    @errors.generic.all
776 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
777 236e7d08 Stavros Sachtouris
    @errors.plankton.id
778 b04288f7 Stavros Sachtouris
    @errors.plankton.metadata
779 236e7d08 Stavros Sachtouris
    def _run(self, image_id, key, val):
780 545c6c29 Stavros Sachtouris
        self._print(
781 545c6c29 Stavros Sachtouris
            self.client.create_image_metadata(image_id, key, val), print_dict)
782 236e7d08 Stavros Sachtouris
783 f3e94e06 Stavros Sachtouris
    def main(self, image_id, key, val):
784 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
785 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, key=key, val=val)
786 f3e94e06 Stavros Sachtouris
787 234954d1 Stavros Sachtouris
788 d486baec Stavros Sachtouris
@command(image_cmds)
789 545c6c29 Stavros Sachtouris
class image_compute_properties_set(_init_cyclades, _optional_json):
790 f5f35422 Stavros Sachtouris
    """Add / update a set of properties for an image
791 466636c9 Stavros Sachtouris
    properties must be given in the form key=value, e.v.
792 f5f35422 Stavros Sachtouris
    /image compute properties set <image-id> key1=val1 key2=val2
793 f5f35422 Stavros Sachtouris
    """
794 f3e94e06 Stavros Sachtouris
795 236e7d08 Stavros Sachtouris
    @errors.generic.all
796 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
797 236e7d08 Stavros Sachtouris
    @errors.plankton.id
798 f5f35422 Stavros Sachtouris
    def _run(self, image_id, keyvals):
799 545c6c29 Stavros Sachtouris
        meta = dict()
800 f5f35422 Stavros Sachtouris
        for keyval in keyvals:
801 89ea97e1 Stavros Sachtouris
            key, sep, val = keyval.partition('=')
802 545c6c29 Stavros Sachtouris
            meta[key] = val
803 545c6c29 Stavros Sachtouris
        self._print(
804 545c6c29 Stavros Sachtouris
            self.client.update_image_metadata(image_id, **meta), print_dict)
805 f5f35422 Stavros Sachtouris
806 f5f35422 Stavros Sachtouris
    def main(self, image_id, *key_equals_value):
807 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
808 89ea97e1 Stavros Sachtouris
        print key_equals_value
809 f5f35422 Stavros Sachtouris
        self._run(image_id=image_id, keyvals=key_equals_value)
810 f3e94e06 Stavros Sachtouris
811 234954d1 Stavros Sachtouris
812 d486baec Stavros Sachtouris
@command(image_cmds)
813 f5f35422 Stavros Sachtouris
class image_compute_properties_delete(_init_cyclades, _optional_output_cmd):
814 f5f35422 Stavros Sachtouris
    """Delete a property from an image"""
815 f3e94e06 Stavros Sachtouris
816 236e7d08 Stavros Sachtouris
    @errors.generic.all
817 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
818 236e7d08 Stavros Sachtouris
    @errors.plankton.id
819 236e7d08 Stavros Sachtouris
    @errors.plankton.metadata
820 236e7d08 Stavros Sachtouris
    def _run(self, image_id, key):
821 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.delete_image_metadata(image_id, key))
822 236e7d08 Stavros Sachtouris
823 f3e94e06 Stavros Sachtouris
    def main(self, image_id, key):
824 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
825 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, key=key)