Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (34.8 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 6430d3a0 Stavros Sachtouris
from io import StringIO
38 76f58e2e Stavros Sachtouris
from pydoc import pager
39 623a4ceb Stavros Sachtouris
40 f3e94e06 Stavros Sachtouris
from kamaki.cli import command
41 d486baec Stavros Sachtouris
from kamaki.cli.command_tree import CommandTree
42 76f58e2e Stavros Sachtouris
from kamaki.cli.utils import filter_dicts_by_dict
43 1395c40e Stavros Sachtouris
from kamaki.clients.image import ImageClient
44 00336c85 Stavros Sachtouris
from kamaki.clients.pithos import PithosClient
45 00336c85 Stavros Sachtouris
from kamaki.clients.astakos import AstakosClient
46 00336c85 Stavros Sachtouris
from kamaki.clients import ClientError
47 ca5528f1 Stavros Sachtouris
from kamaki.cli.argument import (
48 ca5528f1 Stavros Sachtouris
    FlagArgument, ValueArgument, RepeatableArgument, KeyValueArgument,
49 ca5528f1 Stavros Sachtouris
    IntArgument, ProgressBarArgument)
50 bfb54881 Stavros Sachtouris
from kamaki.cli.commands.cyclades import _init_cyclades
51 8cec3671 Stavros Sachtouris
from kamaki.cli.errors import raiseCLIError, CLIBaseUrlError
52 b4f69041 Stavros Sachtouris
from kamaki.cli.commands import _command_init, errors, addLogSettings
53 6d190dd1 Stavros Sachtouris
from kamaki.cli.commands import (
54 6d190dd1 Stavros Sachtouris
    _optional_output_cmd, _optional_json, _name_filter, _id_filter)
55 0b368c8c Stavros Sachtouris
56 234954d1 Stavros Sachtouris
57 a29d2f88 Stavros Sachtouris
image_cmds = CommandTree(
58 a29d2f88 Stavros Sachtouris
    'image',
59 a29d2f88 Stavros Sachtouris
    'Cyclades/Plankton API image commands\n'
60 a29d2f88 Stavros Sachtouris
    'image compute:\tCyclades/Compute API image commands')
61 d486baec Stavros Sachtouris
_commands = [image_cmds]
62 234954d1 Stavros Sachtouris
63 234954d1 Stavros Sachtouris
64 aa82dd5a Stavros Sachtouris
howto_image_file = [
65 aa82dd5a Stavros Sachtouris
    'Kamaki commands to:',
66 c626151a Stavros Sachtouris
    ' get current user id: /user authenticate',
67 aa82dd5a Stavros Sachtouris
    ' check available containers: /file list',
68 aa82dd5a Stavros Sachtouris
    ' create a new container: /file create <container>',
69 aa82dd5a Stavros Sachtouris
    ' check container contents: /file list <container>',
70 7bbfeb1a Stavros Sachtouris
    ' upload files: /file upload <image file> <container>',
71 7bbfeb1a Stavros Sachtouris
    ' register an image: /image register <image name> <container>:<path>']
72 aa82dd5a Stavros Sachtouris
73 aa82dd5a Stavros Sachtouris
about_image_id = ['To see a list of available image ids: /image list']
74 15142309 Stavros Sachtouris
75 15142309 Stavros Sachtouris
76 623a4ceb Stavros Sachtouris
log = getLogger(__name__)
77 623a4ceb Stavros Sachtouris
78 623a4ceb Stavros Sachtouris
79 5eae854d Stavros Sachtouris
class _init_image(_command_init):
80 a03ade9e Stavros Sachtouris
    @errors.generic.all
81 b4f69041 Stavros Sachtouris
    @addLogSettings
82 a03ade9e Stavros Sachtouris
    def _run(self):
83 b4f69041 Stavros Sachtouris
        if getattr(self, 'cloud', None):
84 b4f69041 Stavros Sachtouris
            img_url = self._custom_url('image') or self._custom_url('plankton')
85 b4f69041 Stavros Sachtouris
            if img_url:
86 1d0f1ffa Stavros Sachtouris
                token = self._custom_token('image') or self._custom_token(
87 1d0f1ffa Stavros Sachtouris
                    'plankton') or self.config.get_cloud(self.cloud, 'token')
88 b4f69041 Stavros Sachtouris
                self.client = ImageClient(base_url=img_url, token=token)
89 b4f69041 Stavros Sachtouris
                return
90 8cec3671 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
91 8cec3671 Stavros Sachtouris
            plankton_endpoints = self.auth_base.get_service_endpoints(
92 72fa0010 Stavros Sachtouris
                self._custom_type('image') or self._custom_type(
93 72fa0010 Stavros Sachtouris
                    'plankton') or 'image',
94 72fa0010 Stavros Sachtouris
                self._custom_version('image') or self._custom_version(
95 72fa0010 Stavros Sachtouris
                    'plankton') or '')
96 8cec3671 Stavros Sachtouris
            base_url = plankton_endpoints['publicURL']
97 b4f69041 Stavros Sachtouris
            token = self.auth_base.token
98 8cec3671 Stavros Sachtouris
        else:
99 8cec3671 Stavros Sachtouris
            raise CLIBaseUrlError(service='plankton')
100 a03ade9e Stavros Sachtouris
        self.client = ImageClient(base_url=base_url, token=token)
101 a03ade9e Stavros Sachtouris
102 7493ccb6 Stavros Sachtouris
    def main(self):
103 a03ade9e Stavros Sachtouris
        self._run()
104 7493ccb6 Stavros Sachtouris
105 234954d1 Stavros Sachtouris
106 573be34f Stavros Sachtouris
# Plankton Image Commands
107 573be34f Stavros Sachtouris
108 573be34f Stavros Sachtouris
109 aa82dd5a Stavros Sachtouris
def _validate_image_meta(json_dict, return_str=False):
110 623a4ceb Stavros Sachtouris
    """
111 623a4ceb Stavros Sachtouris
    :param json_dict" (dict) json-formated, of the form
112 623a4ceb Stavros Sachtouris
        {"key1": "val1", "key2": "val2", ...}
113 623a4ceb Stavros Sachtouris

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

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

118 623a4ceb Stavros Sachtouris
    :raises TypeError, AttributeError: Invalid json format
119 623a4ceb Stavros Sachtouris

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

143 623a4ceb Stavros Sachtouris
    :returns: (dict) json_formated
144 623a4ceb Stavros Sachtouris

145 623a4ceb Stavros Sachtouris
    :raises TypeError, AttributeError: Invalid json format
146 623a4ceb Stavros Sachtouris

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

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

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