Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (23.9 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 623a4ceb Stavros Sachtouris
from os.path import abspath
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 117ca598 Stavros Sachtouris
from kamaki.cli.utils import print_dict, print_json
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 83c3ba87 Stavros Sachtouris
from kamaki.cli.argument import IntArgument
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 545c6c29 Stavros Sachtouris
from kamaki.cli.commands import _optional_output_cmd, _optional_json
51 0b368c8c Stavros Sachtouris
52 234954d1 Stavros Sachtouris
53 a29d2f88 Stavros Sachtouris
image_cmds = CommandTree(
54 a29d2f88 Stavros Sachtouris
    'image',
55 a29d2f88 Stavros Sachtouris
    'Cyclades/Plankton API image commands\n'
56 a29d2f88 Stavros Sachtouris
    'image compute:\tCyclades/Compute API image commands')
57 d486baec Stavros Sachtouris
_commands = [image_cmds]
58 234954d1 Stavros Sachtouris
59 234954d1 Stavros Sachtouris
60 aa82dd5a Stavros Sachtouris
howto_image_file = [
61 aa82dd5a Stavros Sachtouris
    'Kamaki commands to:',
62 c626151a Stavros Sachtouris
    ' get current user id: /user authenticate',
63 aa82dd5a Stavros Sachtouris
    ' check available containers: /file list',
64 aa82dd5a Stavros Sachtouris
    ' create a new container: /file create <container>',
65 aa82dd5a Stavros Sachtouris
    ' check container contents: /file list <container>',
66 aa82dd5a Stavros Sachtouris
    ' upload files: /file upload <image file> <container>']
67 aa82dd5a Stavros Sachtouris
68 aa82dd5a Stavros Sachtouris
about_image_id = ['To see a list of available image ids: /image list']
69 15142309 Stavros Sachtouris
70 15142309 Stavros Sachtouris
71 623a4ceb Stavros Sachtouris
log = getLogger(__name__)
72 623a4ceb Stavros Sachtouris
73 623a4ceb Stavros Sachtouris
74 5eae854d Stavros Sachtouris
class _init_image(_command_init):
75 a03ade9e Stavros Sachtouris
    @errors.generic.all
76 b4f69041 Stavros Sachtouris
    @addLogSettings
77 a03ade9e Stavros Sachtouris
    def _run(self):
78 b4f69041 Stavros Sachtouris
        if getattr(self, 'cloud', None):
79 b4f69041 Stavros Sachtouris
            img_url = self._custom_url('image') or self._custom_url('plankton')
80 b4f69041 Stavros Sachtouris
            if img_url:
81 b4f69041 Stavros Sachtouris
                token = self._custom_token('image')\
82 b4f69041 Stavros Sachtouris
                    or self._custom_token('plankton')\
83 144b3551 Stavros Sachtouris
                    or self.config.get_cloud(self.cloud, 'token')
84 b4f69041 Stavros Sachtouris
                self.client = ImageClient(base_url=img_url, token=token)
85 b4f69041 Stavros Sachtouris
                return
86 8cec3671 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
87 8cec3671 Stavros Sachtouris
            plankton_endpoints = self.auth_base.get_service_endpoints(
88 72fa0010 Stavros Sachtouris
                self._custom_type('image') or self._custom_type(
89 72fa0010 Stavros Sachtouris
                    'plankton') or 'image',
90 72fa0010 Stavros Sachtouris
                self._custom_version('image') or self._custom_version(
91 72fa0010 Stavros Sachtouris
                    'plankton') or '')
92 8cec3671 Stavros Sachtouris
            base_url = plankton_endpoints['publicURL']
93 b4f69041 Stavros Sachtouris
            token = self.auth_base.token
94 8cec3671 Stavros Sachtouris
        else:
95 8cec3671 Stavros Sachtouris
            raise CLIBaseUrlError(service='plankton')
96 a03ade9e Stavros Sachtouris
        self.client = ImageClient(base_url=base_url, token=token)
97 a03ade9e Stavros Sachtouris
98 7493ccb6 Stavros Sachtouris
    def main(self):
99 a03ade9e Stavros Sachtouris
        self._run()
100 7493ccb6 Stavros Sachtouris
101 234954d1 Stavros Sachtouris
102 573be34f Stavros Sachtouris
# Plankton Image Commands
103 573be34f Stavros Sachtouris
104 573be34f Stavros Sachtouris
105 aa82dd5a Stavros Sachtouris
def _validate_image_meta(json_dict, return_str=False):
106 623a4ceb Stavros Sachtouris
    """
107 623a4ceb Stavros Sachtouris
    :param json_dict" (dict) json-formated, of the form
108 623a4ceb Stavros Sachtouris
        {"key1": "val1", "key2": "val2", ...}
109 623a4ceb Stavros Sachtouris

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

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

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

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

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

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

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

158 c626151a Stavros Sachtouris
    :returns: (<user-id>, <container>, <img-file-path>)
159 aa82dd5a Stavros Sachtouris

160 aa82dd5a Stavros Sachtouris
    :raises AssertionError: if location is invalid
161 aa82dd5a Stavros Sachtouris
    """
162 aa82dd5a Stavros Sachtouris
    prefix = 'pithos://'
163 aa82dd5a Stavros Sachtouris
    msg = 'Invalid prefix for location %s , try: %s' % (location, prefix)
164 aa82dd5a Stavros Sachtouris
    assert location.startswith(prefix), msg
165 aa82dd5a Stavros Sachtouris
    service, sep, rest = location.partition('://')
166 c626151a Stavros Sachtouris
    assert sep and rest, 'Location %s is missing user-id' % location
167 aa82dd5a Stavros Sachtouris
    uuid, sep, rest = rest.partition('/')
168 aa82dd5a Stavros Sachtouris
    assert sep and rest, 'Location %s is missing container' % location
169 aa82dd5a Stavros Sachtouris
    container, sep, img_path = rest.partition('/')
170 aa82dd5a Stavros Sachtouris
    assert sep and img_path, 'Location %s is missing image path' % location
171 aa82dd5a Stavros Sachtouris
    return uuid, container, img_path
172 aa82dd5a Stavros Sachtouris
173 aa82dd5a Stavros Sachtouris
174 d486baec Stavros Sachtouris
@command(image_cmds)
175 545c6c29 Stavros Sachtouris
class image_list(_init_image, _optional_json):
176 573be34f Stavros Sachtouris
    """List images accessible by user"""
177 7493ccb6 Stavros Sachtouris
178 1ae79e60 Stavros Sachtouris
    arguments = dict(
179 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
180 1ae79e60 Stavros Sachtouris
        container_format=ValueArgument(
181 1ae79e60 Stavros Sachtouris
            'filter by container format',
182 1ae79e60 Stavros Sachtouris
            '--container-format'),
183 1ae79e60 Stavros Sachtouris
        disk_format=ValueArgument('filter by disk format', '--disk-format'),
184 1ae79e60 Stavros Sachtouris
        name=ValueArgument('filter by name', '--name'),
185 faeccbf9 Stavros Sachtouris
        name_pref=ValueArgument(
186 faeccbf9 Stavros Sachtouris
            'filter by name prefix (case insensitive)',
187 faeccbf9 Stavros Sachtouris
            '--name-prefix'),
188 faeccbf9 Stavros Sachtouris
        name_suff=ValueArgument(
189 faeccbf9 Stavros Sachtouris
            'filter by name suffix (case insensitive)',
190 faeccbf9 Stavros Sachtouris
            '--name-suffix'),
191 faeccbf9 Stavros Sachtouris
        name_like=ValueArgument(
192 faeccbf9 Stavros Sachtouris
            'print only if name contains this (case insensitive)',
193 faeccbf9 Stavros Sachtouris
            '--name-like'),
194 1ae79e60 Stavros Sachtouris
        size_min=IntArgument('filter by minimum size', '--size-min'),
195 1ae79e60 Stavros Sachtouris
        size_max=IntArgument('filter by maximum size', '--size-max'),
196 1ae79e60 Stavros Sachtouris
        status=ValueArgument('filter by status', '--status'),
197 f9457c89 Stavros Sachtouris
        owner=ValueArgument('filter by owner', '--owner'),
198 1ae79e60 Stavros Sachtouris
        order=ValueArgument(
199 1ae79e60 Stavros Sachtouris
            'order by FIELD ( - to reverse order)',
200 1ae79e60 Stavros Sachtouris
            '--order',
201 83c3ba87 Stavros Sachtouris
            default=''),
202 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number of listed images', ('-n', '--number')),
203 83c3ba87 Stavros Sachtouris
        more=FlagArgument(
204 83c3ba87 Stavros Sachtouris
            'output results in pages (-n to set items per page, default 10)',
205 ed9af02c Stavros Sachtouris
            '--more'),
206 545c6c29 Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate')
207 1ae79e60 Stavros Sachtouris
    )
208 7493ccb6 Stavros Sachtouris
209 f9457c89 Stavros Sachtouris
    def _filtered_by_owner(self, detail, *list_params):
210 f9457c89 Stavros Sachtouris
        images = []
211 f9457c89 Stavros Sachtouris
        MINKEYS = set([
212 f9457c89 Stavros Sachtouris
            'id', 'size', 'status', 'disk_format', 'container_format', 'name'])
213 f9457c89 Stavros Sachtouris
        for img in self.client.list_public(True, *list_params):
214 f9457c89 Stavros Sachtouris
            if img['owner'] == self['owner']:
215 f9457c89 Stavros Sachtouris
                if not detail:
216 f9457c89 Stavros Sachtouris
                    for key in set(img.keys()).difference(MINKEYS):
217 f9457c89 Stavros Sachtouris
                        img.pop(key)
218 f9457c89 Stavros Sachtouris
                images.append(img)
219 f9457c89 Stavros Sachtouris
        return images
220 f9457c89 Stavros Sachtouris
221 faeccbf9 Stavros Sachtouris
    def _filtered_by_name(self, images):
222 faeccbf9 Stavros Sachtouris
        np, ns, nl = self['name_pref'], self['name_suff'], self['name_like']
223 faeccbf9 Stavros Sachtouris
        return [img for img in images if (
224 faeccbf9 Stavros Sachtouris
            (not np) or img['name'].lower().startswith(np.lower())) and (
225 faeccbf9 Stavros Sachtouris
            (not ns) or img['name'].lower().endswith(ns.lower())) and (
226 faeccbf9 Stavros Sachtouris
            (not nl) or nl.lower() in img['name'].lower())]
227 faeccbf9 Stavros Sachtouris
228 a03ade9e Stavros Sachtouris
    @errors.generic.all
229 a03ade9e Stavros Sachtouris
    @errors.cyclades.connection
230 a03ade9e Stavros Sachtouris
    def _run(self):
231 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
232 7493ccb6 Stavros Sachtouris
        filters = {}
233 fa984c2c Stavros Sachtouris
        for arg in set([
234 1ae79e60 Stavros Sachtouris
                'container_format',
235 1ae79e60 Stavros Sachtouris
                'disk_format',
236 1ae79e60 Stavros Sachtouris
                'name',
237 1ae79e60 Stavros Sachtouris
                'size_min',
238 1ae79e60 Stavros Sachtouris
                'size_max',
239 de73876b Stavros Sachtouris
                'status']).intersection(self.arguments):
240 1ae79e60 Stavros Sachtouris
            filters[arg] = self[arg]
241 1ae79e60 Stavros Sachtouris
242 1ae79e60 Stavros Sachtouris
        order = self['order']
243 1ae79e60 Stavros Sachtouris
        detail = self['detail']
244 f9457c89 Stavros Sachtouris
        if self['owner']:
245 f9457c89 Stavros Sachtouris
            images = self._filtered_by_owner(detail, filters, order)
246 f9457c89 Stavros Sachtouris
        else:
247 f9457c89 Stavros Sachtouris
            images = self.client.list_public(detail, filters, order)
248 f9457c89 Stavros Sachtouris
249 f5f35422 Stavros Sachtouris
        images = self._filtered_by_name(images)
250 545c6c29 Stavros Sachtouris
        kwargs = dict(with_enumeration=self['enum'])
251 83c3ba87 Stavros Sachtouris
        if self['more']:
252 545c6c29 Stavros Sachtouris
            kwargs['page_size'] = self['limit'] or 10
253 83c3ba87 Stavros Sachtouris
        elif self['limit']:
254 545c6c29 Stavros Sachtouris
            images = images[:self['limit']]
255 545c6c29 Stavros Sachtouris
        self._print(images, **kwargs)
256 7493ccb6 Stavros Sachtouris
257 a03ade9e Stavros Sachtouris
    def main(self):
258 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
259 a03ade9e Stavros Sachtouris
        self._run()
260 a03ade9e Stavros Sachtouris
261 234954d1 Stavros Sachtouris
262 d486baec Stavros Sachtouris
@command(image_cmds)
263 545c6c29 Stavros Sachtouris
class image_meta(_init_image, _optional_json):
264 15142309 Stavros Sachtouris
    """Get image metadata
265 15142309 Stavros Sachtouris
    Image metadata include:
266 439826ec Stavros Sachtouris
    - image file information (location, size, etc.)
267 439826ec Stavros Sachtouris
    - image information (id, name, etc.)
268 439826ec Stavros Sachtouris
    - image os properties (os, fs, etc.)
269 15142309 Stavros Sachtouris
    """
270 7493ccb6 Stavros Sachtouris
271 a03ade9e Stavros Sachtouris
    @errors.generic.all
272 a03ade9e Stavros Sachtouris
    @errors.plankton.connection
273 a03ade9e Stavros Sachtouris
    @errors.plankton.id
274 a03ade9e Stavros Sachtouris
    def _run(self, image_id):
275 545c6c29 Stavros Sachtouris
        self._print([self.client.get_meta(image_id)])
276 7493ccb6 Stavros Sachtouris
277 a03ade9e Stavros Sachtouris
    def main(self, image_id):
278 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
279 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
280 a03ade9e Stavros Sachtouris
281 234954d1 Stavros Sachtouris
282 d486baec Stavros Sachtouris
@command(image_cmds)
283 545c6c29 Stavros Sachtouris
class image_register(_init_image, _optional_json):
284 fc903073 Stavros Sachtouris
    """(Re)Register an image"""
285 7493ccb6 Stavros Sachtouris
286 1ae79e60 Stavros Sachtouris
    arguments = dict(
287 1ae79e60 Stavros Sachtouris
        checksum=ValueArgument('set image checksum', '--checksum'),
288 1ae79e60 Stavros Sachtouris
        container_format=ValueArgument(
289 1ae79e60 Stavros Sachtouris
            'set container format',
290 1ae79e60 Stavros Sachtouris
            '--container-format'),
291 1ae79e60 Stavros Sachtouris
        disk_format=ValueArgument('set disk format', '--disk-format'),
292 1ae79e60 Stavros Sachtouris
        owner=ValueArgument('set image owner (admin only)', '--owner'),
293 1ae79e60 Stavros Sachtouris
        properties=KeyValueArgument(
294 1ae79e60 Stavros Sachtouris
            'add property in key=value form (can be repeated)',
295 1736e06d Stavros Sachtouris
            ('-p', '--property')),
296 1ae79e60 Stavros Sachtouris
        is_public=FlagArgument('mark image as public', '--public'),
297 1ae79e60 Stavros Sachtouris
        size=IntArgument('set image size', '--size'),
298 aa82dd5a Stavros Sachtouris
        metafile=ValueArgument(
299 aa82dd5a Stavros Sachtouris
            'Load metadata from a json-formated file <img-file>.meta :'
300 aa82dd5a Stavros Sachtouris
            '{"key1": "val1", "key2": "val2", ..., "properties: {...}"}',
301 aa82dd5a Stavros Sachtouris
            ('--metafile')),
302 aa82dd5a Stavros Sachtouris
        metafile_force=FlagArgument(
303 aa82dd5a Stavros Sachtouris
            'Store remote metadata object, even if it already exists',
304 aa82dd5a Stavros Sachtouris
            ('-f', '--force')),
305 aa82dd5a Stavros Sachtouris
        no_metafile_upload=FlagArgument(
306 aa82dd5a Stavros Sachtouris
            'Do not store metadata in remote meta file',
307 aa82dd5a Stavros Sachtouris
            ('--no-metafile-upload')),
308 117ca598 Stavros Sachtouris
309 1ae79e60 Stavros Sachtouris
    )
310 7493ccb6 Stavros Sachtouris
311 f5c28bfa Stavros Sachtouris
    def _get_user_id(self):
312 00336c85 Stavros Sachtouris
        atoken = self.client.token
313 ef00bc31 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
314 9d8737a2 Stavros Sachtouris
            return self.auth_base.term('id', atoken)
315 ef00bc31 Stavros Sachtouris
        else:
316 f5c28bfa Stavros Sachtouris
            astakos_url = self.config.get('user', 'url')\
317 f5c28bfa Stavros Sachtouris
                or self.config.get('astakos', 'url')
318 ef00bc31 Stavros Sachtouris
            if not astakos_url:
319 ef00bc31 Stavros Sachtouris
                raise CLIBaseUrlError(service='astakos')
320 ef00bc31 Stavros Sachtouris
            user = AstakosClient(astakos_url, atoken)
321 819311d3 Stavros Sachtouris
            return user.term('id')
322 00336c85 Stavros Sachtouris
323 aa82dd5a Stavros Sachtouris
    def _get_pithos_client(self, container):
324 aa82dd5a Stavros Sachtouris
        if self['no_metafile_upload']:
325 aa82dd5a Stavros Sachtouris
            return None
326 00336c85 Stavros Sachtouris
        ptoken = self.client.token
327 ef00bc31 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
328 ef00bc31 Stavros Sachtouris
            pithos_endpoints = self.auth_base.get_service_endpoints(
329 df0045d8 Stavros Sachtouris
                'object-store')
330 ef00bc31 Stavros Sachtouris
            purl = pithos_endpoints['publicURL']
331 ef00bc31 Stavros Sachtouris
        else:
332 df0045d8 Stavros Sachtouris
            purl = self.config.get_cloud('pithos', 'url')
333 df0045d8 Stavros Sachtouris
        if not purl:
334 df0045d8 Stavros Sachtouris
            raise CLIBaseUrlError(service='pithos')
335 f5c28bfa Stavros Sachtouris
        return PithosClient(purl, ptoken, self._get_user_id(), container)
336 00336c85 Stavros Sachtouris
337 aa82dd5a Stavros Sachtouris
    def _store_remote_metafile(self, pclient, remote_path, metadata):
338 00336c85 Stavros Sachtouris
        return pclient.upload_from_string(
339 aa82dd5a Stavros Sachtouris
            remote_path, _validate_image_meta(metadata, return_str=True))
340 00336c85 Stavros Sachtouris
341 aa82dd5a Stavros Sachtouris
    def _load_params_from_file(self, location):
342 aa82dd5a Stavros Sachtouris
        params, properties = dict(), dict()
343 aa82dd5a Stavros Sachtouris
        pfile = self['metafile']
344 aa82dd5a Stavros Sachtouris
        if pfile:
345 00336c85 Stavros Sachtouris
            try:
346 aa82dd5a Stavros Sachtouris
                for k, v in _load_image_meta(pfile).items():
347 aa82dd5a Stavros Sachtouris
                    key = k.lower().replace('-', '_')
348 aa82dd5a Stavros Sachtouris
                    if k == 'properties':
349 aa82dd5a Stavros Sachtouris
                        for pk, pv in v.items():
350 aa82dd5a Stavros Sachtouris
                            properties[pk.upper().replace('-', '_')] = pv
351 aa82dd5a Stavros Sachtouris
                    elif key == 'name':
352 aa82dd5a Stavros Sachtouris
                            continue
353 aa82dd5a Stavros Sachtouris
                    elif key == 'location':
354 aa82dd5a Stavros Sachtouris
                        if location:
355 aa82dd5a Stavros Sachtouris
                            continue
356 aa82dd5a Stavros Sachtouris
                        location = v
357 aa82dd5a Stavros Sachtouris
                    else:
358 aa82dd5a Stavros Sachtouris
                        params[key] = v
359 aa82dd5a Stavros Sachtouris
            except Exception as e:
360 aa82dd5a Stavros Sachtouris
                raiseCLIError(e, 'Invalid json metadata config file')
361 aa82dd5a Stavros Sachtouris
        return params, properties, location
362 7493ccb6 Stavros Sachtouris
363 aa82dd5a Stavros Sachtouris
    def _load_params_from_args(self, params, properties):
364 f769a16a Stavros Sachtouris
        for key in set([
365 1ae79e60 Stavros Sachtouris
                'checksum',
366 1ae79e60 Stavros Sachtouris
                'container_format',
367 1ae79e60 Stavros Sachtouris
                'disk_format',
368 1ae79e60 Stavros Sachtouris
                'owner',
369 1ae79e60 Stavros Sachtouris
                'size',
370 de73876b Stavros Sachtouris
                'is_public']).intersection(self.arguments):
371 1ae79e60 Stavros Sachtouris
            params[key] = self[key]
372 aa82dd5a Stavros Sachtouris
        for k, v in self['properties'].items():
373 aa82dd5a Stavros Sachtouris
            properties[k.upper().replace('-', '_')] = v
374 1ae79e60 Stavros Sachtouris
375 aa82dd5a Stavros Sachtouris
    def _validate_location(self, location):
376 aa82dd5a Stavros Sachtouris
        if not location:
377 aa82dd5a Stavros Sachtouris
            raiseCLIError(
378 aa82dd5a Stavros Sachtouris
                'No image file location provided',
379 aa82dd5a Stavros Sachtouris
                importance=2, details=[
380 aa82dd5a Stavros Sachtouris
                    'An image location is needed. Image location format:',
381 c626151a Stavros Sachtouris
                    '  pithos://<user-id>/<container>/<path>',
382 aa82dd5a Stavros Sachtouris
                    ' an image file at the above location must exist.'
383 aa82dd5a Stavros Sachtouris
                    ] + howto_image_file)
384 aa82dd5a Stavros Sachtouris
        try:
385 aa82dd5a Stavros Sachtouris
            return _validate_image_location(location)
386 aa82dd5a Stavros Sachtouris
        except AssertionError as ae:
387 aa82dd5a Stavros Sachtouris
            raiseCLIError(
388 aa82dd5a Stavros Sachtouris
                ae, 'Invalid image location format',
389 aa82dd5a Stavros Sachtouris
                importance=1, details=[
390 aa82dd5a Stavros Sachtouris
                    'Valid image location format:',
391 c626151a Stavros Sachtouris
                    '  pithos://<user-id>/<container>/<img-file-path>'
392 aa82dd5a Stavros Sachtouris
                    ] + howto_image_file)
393 aa82dd5a Stavros Sachtouris
394 aa82dd5a Stavros Sachtouris
    @errors.generic.all
395 aa82dd5a Stavros Sachtouris
    @errors.plankton.connection
396 aa82dd5a Stavros Sachtouris
    def _run(self, name, location):
397 aa82dd5a Stavros Sachtouris
        (params, properties, location) = self._load_params_from_file(location)
398 aa82dd5a Stavros Sachtouris
        uuid, container, img_path = self._validate_location(location)
399 aa82dd5a Stavros Sachtouris
        self._load_params_from_args(params, properties)
400 aa82dd5a Stavros Sachtouris
        pclient = self._get_pithos_client(container)
401 aa82dd5a Stavros Sachtouris
402 aa82dd5a Stavros Sachtouris
        #check if metafile exists
403 aa82dd5a Stavros Sachtouris
        meta_path = '%s.meta' % img_path
404 aa82dd5a Stavros Sachtouris
        if pclient and not self['metafile_force']:
405 c4aefeaf Stavros Sachtouris
            try:
406 aa82dd5a Stavros Sachtouris
                pclient.get_object_info(meta_path)
407 aa82dd5a Stavros Sachtouris
                raiseCLIError('Metadata file %s:%s already exists' % (
408 aa82dd5a Stavros Sachtouris
                    container, meta_path))
409 aa82dd5a Stavros Sachtouris
            except ClientError as ce:
410 aa82dd5a Stavros Sachtouris
                if ce.status != 404:
411 aa82dd5a Stavros Sachtouris
                    raise
412 aa82dd5a Stavros Sachtouris
413 aa82dd5a Stavros Sachtouris
        #register the image
414 aa82dd5a Stavros Sachtouris
        try:
415 aa82dd5a Stavros Sachtouris
            r = self.client.register(name, location, params, properties)
416 aa82dd5a Stavros Sachtouris
        except ClientError as ce:
417 aa82dd5a Stavros Sachtouris
            if ce.status in (400, ):
418 c4aefeaf Stavros Sachtouris
                raiseCLIError(
419 aa82dd5a Stavros Sachtouris
                    ce, 'Nonexistent image file location %s' % location,
420 c4aefeaf Stavros Sachtouris
                    details=[
421 aa82dd5a Stavros Sachtouris
                        'Make sure the image file exists'] + howto_image_file)
422 aa82dd5a Stavros Sachtouris
            raise
423 aa82dd5a Stavros Sachtouris
        self._print(r, print_dict)
424 a03ade9e Stavros Sachtouris
425 aa82dd5a Stavros Sachtouris
        #upload the metadata file
426 00336c85 Stavros Sachtouris
        if pclient:
427 aa82dd5a Stavros Sachtouris
            try:
428 aa82dd5a Stavros Sachtouris
                meta_headers = pclient.upload_from_string(
429 aa82dd5a Stavros Sachtouris
                    meta_path, dumps(r, indent=2))
430 aa82dd5a Stavros Sachtouris
            except TypeError:
431 aa82dd5a Stavros Sachtouris
                print('Failed to dump metafile %s:%s' % (container, meta_path))
432 aa82dd5a Stavros Sachtouris
                return
433 9553da85 Stavros Sachtouris
            if self['json_output']:
434 9553da85 Stavros Sachtouris
                print_json(dict(
435 aa82dd5a Stavros Sachtouris
                    metafile_location='%s:%s' % (container, meta_path),
436 aa82dd5a Stavros Sachtouris
                    headers=meta_headers))
437 9553da85 Stavros Sachtouris
            else:
438 aa82dd5a Stavros Sachtouris
                print('Metadata file uploaded as %s:%s (version %s)' % (
439 aa82dd5a Stavros Sachtouris
                    container, meta_path, meta_headers['x-object-version']))
440 00336c85 Stavros Sachtouris
441 0b158e6b Stavros Sachtouris
    def main(self, name, location):
442 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
443 aa82dd5a Stavros Sachtouris
        self._run(name, location)
444 7493ccb6 Stavros Sachtouris
445 234954d1 Stavros Sachtouris
446 d486baec Stavros Sachtouris
@command(image_cmds)
447 f5f35422 Stavros Sachtouris
class image_unregister(_init_image, _optional_output_cmd):
448 4a17d307 Stavros Sachtouris
    """Unregister an image (does not delete the image file)"""
449 4a17d307 Stavros Sachtouris
450 4a17d307 Stavros Sachtouris
    @errors.generic.all
451 4a17d307 Stavros Sachtouris
    @errors.plankton.connection
452 4a17d307 Stavros Sachtouris
    @errors.plankton.id
453 4a17d307 Stavros Sachtouris
    def _run(self, image_id):
454 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.unregister(image_id))
455 4a17d307 Stavros Sachtouris
456 4a17d307 Stavros Sachtouris
    def main(self, image_id):
457 4a17d307 Stavros Sachtouris
        super(self.__class__, self)._run()
458 4a17d307 Stavros Sachtouris
        self._run(image_id=image_id)
459 4a17d307 Stavros Sachtouris
460 4a17d307 Stavros Sachtouris
461 4a17d307 Stavros Sachtouris
@command(image_cmds)
462 545c6c29 Stavros Sachtouris
class image_shared(_init_image, _optional_json):
463 f5f35422 Stavros Sachtouris
    """List images shared by a member"""
464 f5f35422 Stavros Sachtouris
465 236e7d08 Stavros Sachtouris
    @errors.generic.all
466 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
467 f5f35422 Stavros Sachtouris
    def _run(self, member):
468 545c6c29 Stavros Sachtouris
        self._print(self.client.list_shared(member), title=('image_id',))
469 7493ccb6 Stavros Sachtouris
470 f5f35422 Stavros Sachtouris
    def main(self, member):
471 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
472 f5f35422 Stavros Sachtouris
        self._run(member)
473 236e7d08 Stavros Sachtouris
474 234954d1 Stavros Sachtouris
475 d486baec Stavros Sachtouris
@command(image_cmds)
476 f5f35422 Stavros Sachtouris
class image_members(_init_image):
477 f5f35422 Stavros Sachtouris
    """Manage members. Members of an image are users who can modify it"""
478 f5f35422 Stavros Sachtouris
479 f5f35422 Stavros Sachtouris
480 f5f35422 Stavros Sachtouris
@command(image_cmds)
481 545c6c29 Stavros Sachtouris
class image_members_list(_init_image, _optional_json):
482 f5f35422 Stavros Sachtouris
    """List members of an image"""
483 f5f35422 Stavros Sachtouris
484 236e7d08 Stavros Sachtouris
    @errors.generic.all
485 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
486 f5f35422 Stavros Sachtouris
    @errors.plankton.id
487 f5f35422 Stavros Sachtouris
    def _run(self, image_id):
488 545c6c29 Stavros Sachtouris
        self._print(self.client.list_members(image_id), title=('member_id',))
489 7493ccb6 Stavros Sachtouris
490 f5f35422 Stavros Sachtouris
    def main(self, image_id):
491 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
492 f5f35422 Stavros Sachtouris
        self._run(image_id=image_id)
493 236e7d08 Stavros Sachtouris
494 234954d1 Stavros Sachtouris
495 d486baec Stavros Sachtouris
@command(image_cmds)
496 f5f35422 Stavros Sachtouris
class image_members_add(_init_image, _optional_output_cmd):
497 7493ccb6 Stavros Sachtouris
    """Add a member to an image"""
498 7493ccb6 Stavros Sachtouris
499 236e7d08 Stavros Sachtouris
    @errors.generic.all
500 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
501 236e7d08 Stavros Sachtouris
    @errors.plankton.id
502 b04288f7 Stavros Sachtouris
    def _run(self, image_id=None, member=None):
503 f5f35422 Stavros Sachtouris
            self._optional_output(self.client.add_member(image_id, member))
504 236e7d08 Stavros Sachtouris
505 236e7d08 Stavros Sachtouris
    def main(self, image_id, member):
506 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
507 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, member=member)
508 7493ccb6 Stavros Sachtouris
509 234954d1 Stavros Sachtouris
510 d486baec Stavros Sachtouris
@command(image_cmds)
511 f5f35422 Stavros Sachtouris
class image_members_delete(_init_image, _optional_output_cmd):
512 7493ccb6 Stavros Sachtouris
    """Remove a member from an image"""
513 7493ccb6 Stavros Sachtouris
514 236e7d08 Stavros Sachtouris
    @errors.generic.all
515 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
516 236e7d08 Stavros Sachtouris
    @errors.plankton.id
517 b04288f7 Stavros Sachtouris
    def _run(self, image_id=None, member=None):
518 f5f35422 Stavros Sachtouris
            self._optional_output(self.client.remove_member(image_id, member))
519 236e7d08 Stavros Sachtouris
520 236e7d08 Stavros Sachtouris
    def main(self, image_id, member):
521 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
522 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, member=member)
523 7493ccb6 Stavros Sachtouris
524 234954d1 Stavros Sachtouris
525 d486baec Stavros Sachtouris
@command(image_cmds)
526 f5f35422 Stavros Sachtouris
class image_members_set(_init_image, _optional_output_cmd):
527 7493ccb6 Stavros Sachtouris
    """Set the members of an image"""
528 7493ccb6 Stavros Sachtouris
529 236e7d08 Stavros Sachtouris
    @errors.generic.all
530 236e7d08 Stavros Sachtouris
    @errors.plankton.connection
531 236e7d08 Stavros Sachtouris
    @errors.plankton.id
532 b04288f7 Stavros Sachtouris
    def _run(self, image_id, members):
533 f5f35422 Stavros Sachtouris
            self._optional_output(self.client.set_members(image_id, members))
534 236e7d08 Stavros Sachtouris
535 236e7d08 Stavros Sachtouris
    def main(self, image_id, *members):
536 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
537 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, members=members)
538 f3e94e06 Stavros Sachtouris
539 f3e94e06 Stavros Sachtouris
540 573be34f Stavros Sachtouris
# Compute Image Commands
541 573be34f Stavros Sachtouris
542 573be34f Stavros Sachtouris
543 d486baec Stavros Sachtouris
@command(image_cmds)
544 8741c407 Stavros Sachtouris
class image_compute(_init_cyclades):
545 a29d2f88 Stavros Sachtouris
    """Cyclades/Compute API image commands"""
546 8741c407 Stavros Sachtouris
547 8741c407 Stavros Sachtouris
548 8741c407 Stavros Sachtouris
@command(image_cmds)
549 545c6c29 Stavros Sachtouris
class image_compute_list(_init_cyclades, _optional_json):
550 f3e94e06 Stavros Sachtouris
    """List images"""
551 f3e94e06 Stavros Sachtouris
552 1ae79e60 Stavros Sachtouris
    arguments = dict(
553 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
554 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number listed images', ('-n', '--number')),
555 15142309 Stavros Sachtouris
        more=FlagArgument(
556 15142309 Stavros Sachtouris
            'output results in pages (-n to set items per page, default 10)',
557 ed9af02c Stavros Sachtouris
            '--more'),
558 545c6c29 Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate')
559 1ae79e60 Stavros Sachtouris
    )
560 f3e94e06 Stavros Sachtouris
561 236e7d08 Stavros Sachtouris
    @errors.generic.all
562 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
563 236e7d08 Stavros Sachtouris
    def _run(self):
564 236e7d08 Stavros Sachtouris
        images = self.client.list_images(self['detail'])
565 545c6c29 Stavros Sachtouris
        kwargs = dict(with_enumeration=self['enum'])
566 236e7d08 Stavros Sachtouris
        if self['more']:
567 545c6c29 Stavros Sachtouris
            kwargs['page_size'] = self['limit'] or 10
568 bcef3ac9 Stavros Sachtouris
        elif self['limit']:
569 545c6c29 Stavros Sachtouris
            images = images[:self['limit']]
570 545c6c29 Stavros Sachtouris
        self._print(images, **kwargs)
571 236e7d08 Stavros Sachtouris
572 f3e94e06 Stavros Sachtouris
    def main(self):
573 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
574 236e7d08 Stavros Sachtouris
        self._run()
575 f3e94e06 Stavros Sachtouris
576 234954d1 Stavros Sachtouris
577 d486baec Stavros Sachtouris
@command(image_cmds)
578 545c6c29 Stavros Sachtouris
class image_compute_info(_init_cyclades, _optional_json):
579 15142309 Stavros Sachtouris
    """Get detailed information on an image"""
580 f3e94e06 Stavros Sachtouris
581 236e7d08 Stavros Sachtouris
    @errors.generic.all
582 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
583 236e7d08 Stavros Sachtouris
    @errors.plankton.id
584 236e7d08 Stavros Sachtouris
    def _run(self, image_id):
585 236e7d08 Stavros Sachtouris
        image = self.client.get_image_details(image_id)
586 bcef3ac9 Stavros Sachtouris
        self._print(image, print_dict)
587 f3e94e06 Stavros Sachtouris
588 f3e94e06 Stavros Sachtouris
    def main(self, image_id):
589 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
590 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
591 f3e94e06 Stavros Sachtouris
592 234954d1 Stavros Sachtouris
593 d486baec Stavros Sachtouris
@command(image_cmds)
594 f5f35422 Stavros Sachtouris
class image_compute_delete(_init_cyclades, _optional_output_cmd):
595 24ff0a35 Stavros Sachtouris
    """Delete an image (WARNING: image file is also removed)"""
596 f3e94e06 Stavros Sachtouris
597 236e7d08 Stavros Sachtouris
    @errors.generic.all
598 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
599 236e7d08 Stavros Sachtouris
    @errors.plankton.id
600 236e7d08 Stavros Sachtouris
    def _run(self, image_id):
601 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.delete_image(image_id))
602 236e7d08 Stavros Sachtouris
603 f3e94e06 Stavros Sachtouris
    def main(self, image_id):
604 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
605 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
606 f3e94e06 Stavros Sachtouris
607 234954d1 Stavros Sachtouris
608 d486baec Stavros Sachtouris
@command(image_cmds)
609 8741c407 Stavros Sachtouris
class image_compute_properties(_init_cyclades):
610 395fbf9e Stavros Sachtouris
    """Manage properties related to OS installation in an image"""
611 f5f35422 Stavros Sachtouris
612 f5f35422 Stavros Sachtouris
613 f5f35422 Stavros Sachtouris
@command(image_cmds)
614 545c6c29 Stavros Sachtouris
class image_compute_properties_list(_init_cyclades, _optional_json):
615 f5f35422 Stavros Sachtouris
    """List all image properties"""
616 f5f35422 Stavros Sachtouris
617 f5f35422 Stavros Sachtouris
    @errors.generic.all
618 f5f35422 Stavros Sachtouris
    @errors.cyclades.connection
619 f5f35422 Stavros Sachtouris
    @errors.plankton.id
620 f5f35422 Stavros Sachtouris
    def _run(self, image_id):
621 545c6c29 Stavros Sachtouris
        self._print(self.client.get_image_metadata(image_id), print_dict)
622 f5f35422 Stavros Sachtouris
623 f5f35422 Stavros Sachtouris
    def main(self, image_id):
624 f5f35422 Stavros Sachtouris
        super(self.__class__, self)._run()
625 f5f35422 Stavros Sachtouris
        self._run(image_id=image_id)
626 f5f35422 Stavros Sachtouris
627 f5f35422 Stavros Sachtouris
628 f5f35422 Stavros Sachtouris
@command(image_cmds)
629 545c6c29 Stavros Sachtouris
class image_compute_properties_get(_init_cyclades, _optional_json):
630 f5f35422 Stavros Sachtouris
    """Get an image property"""
631 f5f35422 Stavros Sachtouris
632 236e7d08 Stavros Sachtouris
    @errors.generic.all
633 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
634 236e7d08 Stavros Sachtouris
    @errors.plankton.id
635 236e7d08 Stavros Sachtouris
    @errors.plankton.metadata
636 236e7d08 Stavros Sachtouris
    def _run(self, image_id, key):
637 545c6c29 Stavros Sachtouris
        self._print(self.client.get_image_metadata(image_id, key), print_dict)
638 236e7d08 Stavros Sachtouris
639 f5f35422 Stavros Sachtouris
    def main(self, image_id, key):
640 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
641 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, key=key)
642 f3e94e06 Stavros Sachtouris
643 234954d1 Stavros Sachtouris
644 d486baec Stavros Sachtouris
@command(image_cmds)
645 545c6c29 Stavros Sachtouris
class image_compute_properties_add(_init_cyclades, _optional_json):
646 f5f35422 Stavros Sachtouris
    """Add a property to an image"""
647 f5f35422 Stavros Sachtouris
648 236e7d08 Stavros Sachtouris
    @errors.generic.all
649 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
650 236e7d08 Stavros Sachtouris
    @errors.plankton.id
651 b04288f7 Stavros Sachtouris
    @errors.plankton.metadata
652 236e7d08 Stavros Sachtouris
    def _run(self, image_id, key, val):
653 545c6c29 Stavros Sachtouris
        self._print(
654 545c6c29 Stavros Sachtouris
            self.client.create_image_metadata(image_id, key, val), print_dict)
655 236e7d08 Stavros Sachtouris
656 f3e94e06 Stavros Sachtouris
    def main(self, image_id, key, val):
657 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
658 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, key=key, val=val)
659 f3e94e06 Stavros Sachtouris
660 234954d1 Stavros Sachtouris
661 d486baec Stavros Sachtouris
@command(image_cmds)
662 545c6c29 Stavros Sachtouris
class image_compute_properties_set(_init_cyclades, _optional_json):
663 f5f35422 Stavros Sachtouris
    """Add / update a set of properties for an image
664 f5f35422 Stavros Sachtouris
    proeprties must be given in the form key=value, e.v.
665 f5f35422 Stavros Sachtouris
    /image compute properties set <image-id> key1=val1 key2=val2
666 f5f35422 Stavros Sachtouris
    """
667 f3e94e06 Stavros Sachtouris
668 236e7d08 Stavros Sachtouris
    @errors.generic.all
669 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
670 236e7d08 Stavros Sachtouris
    @errors.plankton.id
671 f5f35422 Stavros Sachtouris
    def _run(self, image_id, keyvals):
672 545c6c29 Stavros Sachtouris
        meta = dict()
673 f5f35422 Stavros Sachtouris
        for keyval in keyvals:
674 f5f35422 Stavros Sachtouris
            key, val = keyval.split('=')
675 545c6c29 Stavros Sachtouris
            meta[key] = val
676 545c6c29 Stavros Sachtouris
        self._print(
677 545c6c29 Stavros Sachtouris
            self.client.update_image_metadata(image_id, **meta), print_dict)
678 f5f35422 Stavros Sachtouris
679 f5f35422 Stavros Sachtouris
    def main(self, image_id, *key_equals_value):
680 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
681 f5f35422 Stavros Sachtouris
        self._run(image_id=image_id, keyvals=key_equals_value)
682 f3e94e06 Stavros Sachtouris
683 234954d1 Stavros Sachtouris
684 d486baec Stavros Sachtouris
@command(image_cmds)
685 f5f35422 Stavros Sachtouris
class image_compute_properties_delete(_init_cyclades, _optional_output_cmd):
686 f5f35422 Stavros Sachtouris
    """Delete a property from an image"""
687 f3e94e06 Stavros Sachtouris
688 236e7d08 Stavros Sachtouris
    @errors.generic.all
689 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
690 236e7d08 Stavros Sachtouris
    @errors.plankton.id
691 236e7d08 Stavros Sachtouris
    @errors.plankton.metadata
692 236e7d08 Stavros Sachtouris
    def _run(self, image_id, key):
693 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.delete_image_metadata(image_id, key))
694 236e7d08 Stavros Sachtouris
695 f3e94e06 Stavros Sachtouris
    def main(self, image_id, key):
696 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
697 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id, key=key)