Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / image.py @ 034fcba3

History | View | Annotate | Download (29.6 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 import ClientError
46 ca5528f1 Stavros Sachtouris
from kamaki.cli.argument import (
47 ca5528f1 Stavros Sachtouris
    FlagArgument, ValueArgument, RepeatableArgument, KeyValueArgument,
48 ca5528f1 Stavros Sachtouris
    IntArgument, ProgressBarArgument)
49 bfb54881 Stavros Sachtouris
from kamaki.cli.commands.cyclades import _init_cyclades
50 432cea25 Stavros Sachtouris
from kamaki.cli.errors import (
51 432cea25 Stavros Sachtouris
    raiseCLIError, CLIBaseUrlError, CLIInvalidArgument)
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 5c5eb675 Stavros Sachtouris
image_cmds = CommandTree('image', 'Cyclades/Plankton API image commands')
58 5c5eb675 Stavros Sachtouris
imagecompute_cmds = CommandTree(
59 5c5eb675 Stavros Sachtouris
    'imagecompute', 'Cyclades/Compute API image commands')
60 5c5eb675 Stavros Sachtouris
_commands = [image_cmds, imagecompute_cmds]
61 234954d1 Stavros Sachtouris
62 234954d1 Stavros Sachtouris
63 aa82dd5a Stavros Sachtouris
howto_image_file = [
64 aa82dd5a Stavros Sachtouris
    'Kamaki commands to:',
65 034fcba3 Stavros Sachtouris
    ' get current user id: kamaki user info',
66 034fcba3 Stavros Sachtouris
    ' check available containers: kamaki container list',
67 034fcba3 Stavros Sachtouris
    ' create a new container: kamaki container create CONTAINER',
68 034fcba3 Stavros Sachtouris
    ' check container contents: kamaki file list /CONTAINER',
69 034fcba3 Stavros Sachtouris
    ' upload files: kamaki file upload IMAGE_FILE /CONTAINER[/PATH]',
70 034fcba3 Stavros Sachtouris
    ' register an image:',
71 034fcba3 Stavros Sachtouris
    '   kamaki image register --name=IMAGE_NAME --location=/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 5c5eb675 Stavros Sachtouris
        image_ID_for_members=ValueArgument(
211 5c5eb675 Stavros Sachtouris
            'List members of an image', '--members-of')
212 1ae79e60 Stavros Sachtouris
    )
213 7493ccb6 Stavros Sachtouris
214 6d190dd1 Stavros Sachtouris
    def _filter_by_owner(self, images):
215 95641ecc Stavros Sachtouris
        ouuid = self['owner'] or self._username2uuid(self['owner_name'])
216 854222c7 Stavros Sachtouris
        return filter_dicts_by_dict(images, dict(owner=ouuid))
217 f9457c89 Stavros Sachtouris
218 5576a4eb Stavros Sachtouris
    def _add_owner_name(self, images):
219 5576a4eb Stavros Sachtouris
        uuids = self._uuids2usernames(
220 5576a4eb Stavros Sachtouris
            list(set([img['owner'] for img in images])))
221 5576a4eb Stavros Sachtouris
        for img in images:
222 5576a4eb Stavros Sachtouris
            img['owner'] += ' (%s)' % uuids[img['owner']]
223 5576a4eb Stavros Sachtouris
        return images
224 f1e5b343 Stavros Sachtouris
225 6d190dd1 Stavros Sachtouris
    def _filter_by_properties(self, images):
226 2d1202ee Stavros Sachtouris
        new_images = []
227 2d1202ee Stavros Sachtouris
        for img in images:
228 854222c7 Stavros Sachtouris
            props = [dict(img['properties'])]
229 854222c7 Stavros Sachtouris
            if self['prop']:
230 854222c7 Stavros Sachtouris
                props = filter_dicts_by_dict(props, self['prop'])
231 854222c7 Stavros Sachtouris
            if props and self['prop_like']:
232 854222c7 Stavros Sachtouris
                props = filter_dicts_by_dict(
233 854222c7 Stavros Sachtouris
                    props, self['prop_like'], exact_match=False)
234 854222c7 Stavros Sachtouris
            if props:
235 854222c7 Stavros Sachtouris
                new_images.append(img)
236 2d1202ee Stavros Sachtouris
        return new_images
237 2d1202ee Stavros Sachtouris
238 5c5eb675 Stavros Sachtouris
    def _members(self, image_id):
239 5c5eb675 Stavros Sachtouris
        members = self.client.list_members(image_id)
240 5c5eb675 Stavros Sachtouris
        if not (self['json_output'] or self['output_format']):
241 5c5eb675 Stavros Sachtouris
            uuids = [member['member_id'] for member in members]
242 5c5eb675 Stavros Sachtouris
            usernames = self._uuids2usernames(uuids)
243 5c5eb675 Stavros Sachtouris
            for member in members:
244 5c5eb675 Stavros Sachtouris
                member['member_id'] += ' (%s)' % usernames[member['member_id']]
245 5c5eb675 Stavros Sachtouris
        self._print(members, title=('member_id',))
246 5c5eb675 Stavros Sachtouris
247 a03ade9e Stavros Sachtouris
    @errors.generic.all
248 a03ade9e Stavros Sachtouris
    @errors.cyclades.connection
249 a03ade9e Stavros Sachtouris
    def _run(self):
250 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
251 5c5eb675 Stavros Sachtouris
        if self['image_ID_for_members']:
252 5c5eb675 Stavros Sachtouris
            return self._members(self['image_ID_for_members'])
253 7493ccb6 Stavros Sachtouris
        filters = {}
254 fa984c2c Stavros Sachtouris
        for arg in set([
255 1ae79e60 Stavros Sachtouris
                'container_format',
256 1ae79e60 Stavros Sachtouris
                'disk_format',
257 1ae79e60 Stavros Sachtouris
                'name',
258 1ae79e60 Stavros Sachtouris
                'size_min',
259 1ae79e60 Stavros Sachtouris
                'size_max',
260 de73876b Stavros Sachtouris
                'status']).intersection(self.arguments):
261 1ae79e60 Stavros Sachtouris
            filters[arg] = self[arg]
262 1ae79e60 Stavros Sachtouris
263 1ae79e60 Stavros Sachtouris
        order = self['order']
264 854222c7 Stavros Sachtouris
        detail = self['detail'] or (
265 854222c7 Stavros Sachtouris
            self['prop'] or self['prop_like']) or (
266 854222c7 Stavros Sachtouris
            self['owner'] or self['owner_name'])
267 f9457c89 Stavros Sachtouris
268 854222c7 Stavros Sachtouris
        images = self.client.list_public(detail, filters, order)
269 854222c7 Stavros Sachtouris
270 854222c7 Stavros Sachtouris
        if self['owner'] or self['owner_name']:
271 6d190dd1 Stavros Sachtouris
            images = self._filter_by_owner(images)
272 854222c7 Stavros Sachtouris
        if self['prop'] or self['prop_like']:
273 6d190dd1 Stavros Sachtouris
            images = self._filter_by_properties(images)
274 6d190dd1 Stavros Sachtouris
        images = self._filter_by_id(images)
275 6d190dd1 Stavros Sachtouris
        images = self._non_exact_name_filter(images)
276 854222c7 Stavros Sachtouris
277 f76c6bbc Stavros Sachtouris
        if self['detail'] and not (
278 f76c6bbc Stavros Sachtouris
                self['json_output'] or self['output_format']):
279 5576a4eb Stavros Sachtouris
            images = self._add_owner_name(images)
280 854222c7 Stavros Sachtouris
        elif detail and not self['detail']:
281 854222c7 Stavros Sachtouris
            for img in images:
282 854222c7 Stavros Sachtouris
                for key in set(img).difference(self.PERMANENTS):
283 854222c7 Stavros Sachtouris
                    img.pop(key)
284 545c6c29 Stavros Sachtouris
        kwargs = dict(with_enumeration=self['enum'])
285 6430d3a0 Stavros Sachtouris
        if self['limit']:
286 545c6c29 Stavros Sachtouris
            images = images[:self['limit']]
287 6430d3a0 Stavros Sachtouris
        if self['more']:
288 6430d3a0 Stavros Sachtouris
            kwargs['out'] = StringIO()
289 6430d3a0 Stavros Sachtouris
            kwargs['title'] = ()
290 545c6c29 Stavros Sachtouris
        self._print(images, **kwargs)
291 6430d3a0 Stavros Sachtouris
        if self['more']:
292 6430d3a0 Stavros Sachtouris
            pager(kwargs['out'].getvalue())
293 7493ccb6 Stavros Sachtouris
294 a03ade9e Stavros Sachtouris
    def main(self):
295 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
296 a03ade9e Stavros Sachtouris
        self._run()
297 a03ade9e Stavros Sachtouris
298 234954d1 Stavros Sachtouris
299 d486baec Stavros Sachtouris
@command(image_cmds)
300 82cc4b8f Stavros Sachtouris
class image_info(_init_image, _optional_json):
301 00b1248e Stavros Sachtouris
    """Get image metadata"""
302 7493ccb6 Stavros Sachtouris
303 a03ade9e Stavros Sachtouris
    @errors.generic.all
304 a03ade9e Stavros Sachtouris
    @errors.plankton.connection
305 a03ade9e Stavros Sachtouris
    @errors.plankton.id
306 a03ade9e Stavros Sachtouris
    def _run(self, image_id):
307 ca5528f1 Stavros Sachtouris
        meta = self.client.get_meta(image_id)
308 f76c6bbc Stavros Sachtouris
        if not (self['json_output'] or self['output_format']):
309 ca5528f1 Stavros Sachtouris
            meta['owner'] += ' (%s)' % self._uuid2username(meta['owner'])
310 76f58e2e Stavros Sachtouris
        self._print(meta, self.print_dict)
311 ca5528f1 Stavros Sachtouris
312 ca5528f1 Stavros Sachtouris
    def main(self, image_id):
313 ca5528f1 Stavros Sachtouris
        super(self.__class__, self)._run()
314 ca5528f1 Stavros Sachtouris
        self._run(image_id=image_id)
315 ca5528f1 Stavros Sachtouris
316 ca5528f1 Stavros Sachtouris
317 ca5528f1 Stavros Sachtouris
@command(image_cmds)
318 5c5eb675 Stavros Sachtouris
class image_modify(_init_image, _optional_output_cmd):
319 ca5528f1 Stavros Sachtouris
    """Add / update metadata and properties for an image
320 ca5528f1 Stavros Sachtouris
    The original image preserves the values that are not affected
321 ca5528f1 Stavros Sachtouris
    """
322 ca5528f1 Stavros Sachtouris
323 ca5528f1 Stavros Sachtouris
    arguments = dict(
324 00b1248e Stavros Sachtouris
        image_name=ValueArgument('Change name', '--name'),
325 00b1248e Stavros Sachtouris
        disk_format=ValueArgument('Change disk format', '--disk-format'),
326 ca5528f1 Stavros Sachtouris
        container_format=ValueArgument(
327 00b1248e Stavros Sachtouris
            'Change container format', '--container-format'),
328 00b1248e Stavros Sachtouris
        status=ValueArgument('Change status', '--status'),
329 00b1248e Stavros Sachtouris
        publish=FlagArgument('Publish the image', '--publish'),
330 00b1248e Stavros Sachtouris
        unpublish=FlagArgument('Unpublish the image', '--unpublish'),
331 00b1248e Stavros Sachtouris
        property_to_set=KeyValueArgument(
332 ca5528f1 Stavros Sachtouris
            'set property in key=value form (can be repeated)',
333 00b1248e Stavros Sachtouris
            ('-p', '--property-set')),
334 00b1248e Stavros Sachtouris
        property_to_del=RepeatableArgument(
335 5c5eb675 Stavros Sachtouris
            'Delete property by key (can be repeated)', '--property-del'),
336 5c5eb675 Stavros Sachtouris
        member_ID_to_add=RepeatableArgument(
337 5c5eb675 Stavros Sachtouris
            'Add member to image (can be repeated)', '--member-add'),
338 5c5eb675 Stavros Sachtouris
        member_ID_to_remove=RepeatableArgument(
339 5c5eb675 Stavros Sachtouris
            'Remove a member (can be repeated)', '--member-del'),
340 ca5528f1 Stavros Sachtouris
    )
341 00b1248e Stavros Sachtouris
    required = [
342 00b1248e Stavros Sachtouris
        'image_name', 'disk_format', 'container_format', 'status', 'publish',
343 5c5eb675 Stavros Sachtouris
        'unpublish', 'property_to_set', 'member_ID_to_add',
344 e9c8f12e Stavros Sachtouris
        'member_ID_to_remove', 'property_to_del']
345 ca5528f1 Stavros Sachtouris
346 ca5528f1 Stavros Sachtouris
    @errors.generic.all
347 ca5528f1 Stavros Sachtouris
    @errors.plankton.connection
348 ca5528f1 Stavros Sachtouris
    @errors.plankton.id
349 ca5528f1 Stavros Sachtouris
    def _run(self, image_id):
350 f084bdc8 Stavros Sachtouris
        for mid in (self['member_ID_to_add'] or []):
351 5c5eb675 Stavros Sachtouris
            self.client.add_member(image_id, mid)
352 f084bdc8 Stavros Sachtouris
        for mid in (self['member_ID_to_remove'] or []):
353 5c5eb675 Stavros Sachtouris
            self.client.remove_member(image_id, mid)
354 f084bdc8 Stavros Sachtouris
        meta = self.client.get_meta(image_id)
355 f084bdc8 Stavros Sachtouris
        for k, v in self['property_to_set'].items():
356 f084bdc8 Stavros Sachtouris
            meta['properties'][k.upper()] = v
357 f084bdc8 Stavros Sachtouris
        for k in (self['property_to_del'] or []):
358 f084bdc8 Stavros Sachtouris
            meta['properties'][k.upper()] = None
359 f084bdc8 Stavros Sachtouris
        self._optional_output(self.client.update_image(
360 f084bdc8 Stavros Sachtouris
            image_id,
361 f084bdc8 Stavros Sachtouris
            name=self['image_name'],
362 f084bdc8 Stavros Sachtouris
            disk_format=self['disk_format'],
363 f084bdc8 Stavros Sachtouris
            container_format=self['container_format'],
364 f084bdc8 Stavros Sachtouris
            status=self['status'],
365 0e32e717 Stavros Sachtouris
            public=self['publish'] or (False if self['unpublish'] else None),
366 f084bdc8 Stavros Sachtouris
            **meta['properties']))
367 5c5eb675 Stavros Sachtouris
        if self['with_output']:
368 5c5eb675 Stavros Sachtouris
            self._optional_output(self.get_image_details(image_id))
369 ca5528f1 Stavros Sachtouris
370 ca5528f1 Stavros Sachtouris
    def main(self, image_id):
371 ca5528f1 Stavros Sachtouris
        super(self.__class__, self)._run()
372 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
373 a03ade9e Stavros Sachtouris
374 234954d1 Stavros Sachtouris
375 432cea25 Stavros Sachtouris
class PithosLocationArgument(ValueArgument):
376 432cea25 Stavros Sachtouris
    """Resolve pithos url, return in the form pithos://uuid/container/path"""
377 432cea25 Stavros Sachtouris
378 432cea25 Stavros Sachtouris
    def __init__(
379 432cea25 Stavros Sachtouris
            self, help=None, parsed_name=None, default=None, user_uuid=None):
380 432cea25 Stavros Sachtouris
        super(PithosLocationArgument, self).__init__(
381 432cea25 Stavros Sachtouris
            help=help, parsed_name=parsed_name, default=default)
382 432cea25 Stavros Sachtouris
        self.uuid, self.container, self.path = user_uuid, None, None
383 432cea25 Stavros Sachtouris
384 432cea25 Stavros Sachtouris
    def setdefault(self, term, value):
385 432cea25 Stavros Sachtouris
        if not getattr(self, term, None):
386 432cea25 Stavros Sachtouris
            setattr(self, term, value)
387 432cea25 Stavros Sachtouris
388 432cea25 Stavros Sachtouris
    @property
389 432cea25 Stavros Sachtouris
    def value(self):
390 432cea25 Stavros Sachtouris
        return 'pithos://%s/%s/%s' % (self.uuid, self.container, self.path)
391 432cea25 Stavros Sachtouris
392 432cea25 Stavros Sachtouris
    @value.setter
393 432cea25 Stavros Sachtouris
    def value(self, location):
394 432cea25 Stavros Sachtouris
        if location:
395 432cea25 Stavros Sachtouris
            from kamaki.cli.commands.pithos import _pithos_container as pc
396 432cea25 Stavros Sachtouris
            try:
397 432cea25 Stavros Sachtouris
                uuid, self.container, self.path = pc._resolve_pithos_url(
398 432cea25 Stavros Sachtouris
                    location)
399 432cea25 Stavros Sachtouris
                self.uuid = uuid or self.uuid
400 432cea25 Stavros Sachtouris
                for term in ('container', 'path'):
401 432cea25 Stavros Sachtouris
                    assert getattr(self, term, None), 'No %s' % term
402 432cea25 Stavros Sachtouris
            except Exception as e:
403 432cea25 Stavros Sachtouris
                raise CLIInvalidArgument(
404 432cea25 Stavros Sachtouris
                    'Invalid Pithos+ location %s (%s)' % (location, e),
405 432cea25 Stavros Sachtouris
                    details=[
406 432cea25 Stavros Sachtouris
                        'The image location must be a valid Pithos+',
407 432cea25 Stavros Sachtouris
                        'location. There are two valid formats:',
408 432cea25 Stavros Sachtouris
                        '  pithos://USER_UUID/CONTAINER/PATH',
409 432cea25 Stavros Sachtouris
                        'OR',
410 432cea25 Stavros Sachtouris
                        '  /CONTAINER/PATH',
411 432cea25 Stavros Sachtouris
                        'To see all containers:',
412 432cea25 Stavros Sachtouris
                        '  [kamaki] container list',
413 432cea25 Stavros Sachtouris
                        'To list the contents of a container:',
414 432cea25 Stavros Sachtouris
                        '  [kamaki] container list CONTAINER'])
415 432cea25 Stavros Sachtouris
416 432cea25 Stavros Sachtouris
417 d486baec Stavros Sachtouris
@command(image_cmds)
418 545c6c29 Stavros Sachtouris
class image_register(_init_image, _optional_json):
419 38db356b Stavros Sachtouris
    """(Re)Register an image file to an Image service
420 38db356b Stavros Sachtouris
    The image file must be stored at a pithos repository
421 16d7b9ff Stavros Sachtouris
    Some metadata can be set by user (e.g., disk-format) while others are set
422 16d7b9ff Stavros Sachtouris
    only automatically (e.g., image id). There are also some custom user
423 38db356b Stavros Sachtouris
    metadata, called properties.
424 38db356b Stavros Sachtouris
    A register command creates a remote meta file at
425 432cea25 Stavros Sachtouris
    /<container>/<image path>.meta
426 38db356b Stavros Sachtouris
    Users may download and edit this file and use it to re-register one or more
427 38db356b Stavros Sachtouris
    images.
428 38db356b Stavros Sachtouris
    In case of a meta file, runtime arguments for metadata or properties
429 38db356b Stavros Sachtouris
    override meta file settings.
430 38db356b Stavros Sachtouris
    """
431 7493ccb6 Stavros Sachtouris
432 d77e33d4 Stavros Sachtouris
    container_info_cache = {}
433 d77e33d4 Stavros Sachtouris
434 1ae79e60 Stavros Sachtouris
    arguments = dict(
435 38db356b Stavros Sachtouris
        checksum=ValueArgument('Set image checksum', '--checksum'),
436 1ae79e60 Stavros Sachtouris
        container_format=ValueArgument(
437 1d0f1ffa Stavros Sachtouris
            'Set container format', '--container-format'),
438 38db356b Stavros Sachtouris
        disk_format=ValueArgument('Set disk format', '--disk-format'),
439 38db356b Stavros Sachtouris
        owner_name=ValueArgument('Set user uuid by user name', '--owner-name'),
440 1ae79e60 Stavros Sachtouris
        properties=KeyValueArgument(
441 38db356b Stavros Sachtouris
            'Add property (user-specified metadata) in key=value form'
442 38db356b Stavros Sachtouris
            '(can be repeated)',
443 1736e06d Stavros Sachtouris
            ('-p', '--property')),
444 38db356b Stavros Sachtouris
        is_public=FlagArgument('Mark image as public', '--public'),
445 38db356b Stavros Sachtouris
        size=IntArgument('Set image size in bytes', '--size'),
446 aa82dd5a Stavros Sachtouris
        metafile=ValueArgument(
447 aa82dd5a Stavros Sachtouris
            'Load metadata from a json-formated file <img-file>.meta :'
448 aa82dd5a Stavros Sachtouris
            '{"key1": "val1", "key2": "val2", ..., "properties: {...}"}',
449 aa82dd5a Stavros Sachtouris
            ('--metafile')),
450 2973f6b0 Stavros Sachtouris
        force_upload=FlagArgument(
451 2973f6b0 Stavros Sachtouris
            'Overwrite remote files (image file, metadata file)',
452 2973f6b0 Stavros Sachtouris
            ('-f', '--force')),
453 aa82dd5a Stavros Sachtouris
        no_metafile_upload=FlagArgument(
454 aa82dd5a Stavros Sachtouris
            'Do not store metadata in remote meta file',
455 aa82dd5a Stavros Sachtouris
            ('--no-metafile-upload')),
456 f2ea1314 Stavros Sachtouris
        container=ValueArgument(
457 f2ea1314 Stavros Sachtouris
            'Pithos+ container containing the image file',
458 f2ea1314 Stavros Sachtouris
            ('-C', '--container')),
459 d77e33d4 Stavros Sachtouris
        uuid=ValueArgument('Custom user uuid', '--uuid'),
460 d77e33d4 Stavros Sachtouris
        local_image_path=ValueArgument(
461 d77e33d4 Stavros Sachtouris
            'Local image file path to upload and register '
462 c8b1d760 Dionysis Grigoropoulos
            '(still need target file in the form /container/remote-path )',
463 d77e33d4 Stavros Sachtouris
            '--upload-image-file'),
464 d77e33d4 Stavros Sachtouris
        progress_bar=ProgressBarArgument(
465 432cea25 Stavros Sachtouris
            'Do not use progress bar', '--no-progress-bar', default=False),
466 432cea25 Stavros Sachtouris
        name=ValueArgument('The name of the new image', '--name'),
467 432cea25 Stavros Sachtouris
        pithos_location=PithosLocationArgument(
468 432cea25 Stavros Sachtouris
            'The Pithos+ image location to put the image at. Format:       '
469 432cea25 Stavros Sachtouris
            'pithos://USER_UUID/CONTAINER/IMAGE                  or   '
470 432cea25 Stavros Sachtouris
            '/CONTAINER/IMAGE',
471 432cea25 Stavros Sachtouris
            '--location')
472 1ae79e60 Stavros Sachtouris
    )
473 432cea25 Stavros Sachtouris
    required = ('name', 'pithos_location')
474 7493ccb6 Stavros Sachtouris
475 432cea25 Stavros Sachtouris
    def _get_pithos_client(self, locator):
476 00336c85 Stavros Sachtouris
        ptoken = self.client.token
477 ef00bc31 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
478 ef00bc31 Stavros Sachtouris
            pithos_endpoints = self.auth_base.get_service_endpoints(
479 df0045d8 Stavros Sachtouris
                'object-store')
480 ef00bc31 Stavros Sachtouris
            purl = pithos_endpoints['publicURL']
481 ef00bc31 Stavros Sachtouris
        else:
482 df0045d8 Stavros Sachtouris
            purl = self.config.get_cloud('pithos', 'url')
483 df0045d8 Stavros Sachtouris
        if not purl:
484 df0045d8 Stavros Sachtouris
            raise CLIBaseUrlError(service='pithos')
485 432cea25 Stavros Sachtouris
        return PithosClient(purl, ptoken, locator.uuid, locator.container)
486 00336c85 Stavros Sachtouris
487 aa82dd5a Stavros Sachtouris
    def _load_params_from_file(self, location):
488 aa82dd5a Stavros Sachtouris
        params, properties = dict(), dict()
489 aa82dd5a Stavros Sachtouris
        pfile = self['metafile']
490 aa82dd5a Stavros Sachtouris
        if pfile:
491 00336c85 Stavros Sachtouris
            try:
492 aa82dd5a Stavros Sachtouris
                for k, v in _load_image_meta(pfile).items():
493 aa82dd5a Stavros Sachtouris
                    key = k.lower().replace('-', '_')
494 1d0f1ffa Stavros Sachtouris
                    if key == 'properties':
495 aa82dd5a Stavros Sachtouris
                        for pk, pv in v.items():
496 aa82dd5a Stavros Sachtouris
                            properties[pk.upper().replace('-', '_')] = pv
497 aa82dd5a Stavros Sachtouris
                    elif key == 'name':
498 aa82dd5a Stavros Sachtouris
                            continue
499 aa82dd5a Stavros Sachtouris
                    elif key == 'location':
500 aa82dd5a Stavros Sachtouris
                        if location:
501 aa82dd5a Stavros Sachtouris
                            continue
502 aa82dd5a Stavros Sachtouris
                        location = v
503 aa82dd5a Stavros Sachtouris
                    else:
504 aa82dd5a Stavros Sachtouris
                        params[key] = v
505 aa82dd5a Stavros Sachtouris
            except Exception as e:
506 aa82dd5a Stavros Sachtouris
                raiseCLIError(e, 'Invalid json metadata config file')
507 aa82dd5a Stavros Sachtouris
        return params, properties, location
508 7493ccb6 Stavros Sachtouris
509 aa82dd5a Stavros Sachtouris
    def _load_params_from_args(self, params, properties):
510 f769a16a Stavros Sachtouris
        for key in set([
511 1ae79e60 Stavros Sachtouris
                'checksum',
512 1ae79e60 Stavros Sachtouris
                'container_format',
513 1ae79e60 Stavros Sachtouris
                'disk_format',
514 1ae79e60 Stavros Sachtouris
                'owner',
515 1ae79e60 Stavros Sachtouris
                'size',
516 de73876b Stavros Sachtouris
                'is_public']).intersection(self.arguments):
517 1ae79e60 Stavros Sachtouris
            params[key] = self[key]
518 aa82dd5a Stavros Sachtouris
        for k, v in self['properties'].items():
519 aa82dd5a Stavros Sachtouris
            properties[k.upper().replace('-', '_')] = v
520 1ae79e60 Stavros Sachtouris
521 2973f6b0 Stavros Sachtouris
    def _assert_remote_file_not_exist(self, pithos, path):
522 2973f6b0 Stavros Sachtouris
        if pithos and not self['force_upload']:
523 2973f6b0 Stavros Sachtouris
            try:
524 2973f6b0 Stavros Sachtouris
                pithos.get_object_info(path)
525 2973f6b0 Stavros Sachtouris
                raiseCLIError(
526 2973f6b0 Stavros Sachtouris
                    'Remote file /%s/%s already exists' % (
527 2973f6b0 Stavros Sachtouris
                        pithos.container, path),
528 2973f6b0 Stavros Sachtouris
                    importance=2,
529 2973f6b0 Stavros Sachtouris
                    details=[
530 2973f6b0 Stavros Sachtouris
                        'Registration ABORTED',
531 2973f6b0 Stavros Sachtouris
                        'Use %s to force upload' % self.arguments[
532 2973f6b0 Stavros Sachtouris
                            'force_upload'].lvalue])
533 2973f6b0 Stavros Sachtouris
            except ClientError as ce:
534 2973f6b0 Stavros Sachtouris
                if ce.status != 404:
535 2973f6b0 Stavros Sachtouris
                    raise
536 2973f6b0 Stavros Sachtouris
537 aa82dd5a Stavros Sachtouris
    @errors.generic.all
538 aa82dd5a Stavros Sachtouris
    @errors.plankton.connection
539 432cea25 Stavros Sachtouris
    def _run(self, name, location):
540 2973f6b0 Stavros Sachtouris
        locator, pithos = self.arguments['pithos_location'], None
541 d77e33d4 Stavros Sachtouris
        if self['local_image_path']:
542 d77e33d4 Stavros Sachtouris
            with open(self['local_image_path']) as f:
543 432cea25 Stavros Sachtouris
                pithos = self._get_pithos_client(locator)
544 2973f6b0 Stavros Sachtouris
                self._assert_remote_file_not_exist(pithos, locator.path)
545 d77e33d4 Stavros Sachtouris
                (pbar, upload_cb) = self._safe_progress_bar('Uploading')
546 d77e33d4 Stavros Sachtouris
                if pbar:
547 d77e33d4 Stavros Sachtouris
                    hash_bar = pbar.clone()
548 d77e33d4 Stavros Sachtouris
                    hash_cb = hash_bar.get_generator('Calculating hashes')
549 d77e33d4 Stavros Sachtouris
                pithos.upload_object(
550 432cea25 Stavros Sachtouris
                    locator.path, f,
551 d77e33d4 Stavros Sachtouris
                    hash_cb=hash_cb, upload_cb=upload_cb,
552 d77e33d4 Stavros Sachtouris
                    container_info_cache=self.container_info_cache)
553 d77e33d4 Stavros Sachtouris
                pbar.finish()
554 d77e33d4 Stavros Sachtouris
555 f2ea1314 Stavros Sachtouris
        (params, properties, new_loc) = self._load_params_from_file(location)
556 f2ea1314 Stavros Sachtouris
        if location != new_loc:
557 432cea25 Stavros Sachtouris
            locator.value = new_loc
558 aa82dd5a Stavros Sachtouris
        self._load_params_from_args(params, properties)
559 aa82dd5a Stavros Sachtouris
560 2973f6b0 Stavros Sachtouris
        if not self['no_metafile_upload']:
561 2973f6b0 Stavros Sachtouris
            #check if metafile exists
562 2973f6b0 Stavros Sachtouris
            pithos = pithos or self._get_pithos_client(locator)
563 2973f6b0 Stavros Sachtouris
            meta_path = '%s.meta' % locator.path
564 2973f6b0 Stavros Sachtouris
            self._assert_remote_file_not_exist(pithos, meta_path)
565 aa82dd5a Stavros Sachtouris
566 aa82dd5a Stavros Sachtouris
        #register the image
567 aa82dd5a Stavros Sachtouris
        try:
568 aa82dd5a Stavros Sachtouris
            r = self.client.register(name, location, params, properties)
569 aa82dd5a Stavros Sachtouris
        except ClientError as ce:
570 7bbfeb1a Stavros Sachtouris
            if ce.status in (400, 404):
571 c4aefeaf Stavros Sachtouris
                raiseCLIError(
572 7bbfeb1a Stavros Sachtouris
                    ce, 'Nonexistent image file location\n\t%s' % location,
573 c4aefeaf Stavros Sachtouris
                    details=[
574 7bbfeb1a Stavros Sachtouris
                        'Does the image file %s exist at container %s ?' % (
575 432cea25 Stavros Sachtouris
                            locator.path,
576 432cea25 Stavros Sachtouris
                            locator.container)] + howto_image_file)
577 aa82dd5a Stavros Sachtouris
            raise
578 6b8a403c Stavros Sachtouris
        r['owner'] += ' (%s)' % self._uuid2username(r['owner'])
579 76f58e2e Stavros Sachtouris
        self._print(r, self.print_dict)
580 a03ade9e Stavros Sachtouris
581 aa82dd5a Stavros Sachtouris
        #upload the metadata file
582 2973f6b0 Stavros Sachtouris
        if not self['no_metafile_upload']:
583 aa82dd5a Stavros Sachtouris
            try:
584 2973f6b0 Stavros Sachtouris
                meta_headers = pithos.upload_from_string(
585 d77e33d4 Stavros Sachtouris
                    meta_path, dumps(r, indent=2),
586 d77e33d4 Stavros Sachtouris
                    container_info_cache=self.container_info_cache)
587 aa82dd5a Stavros Sachtouris
            except TypeError:
588 1d0f1ffa Stavros Sachtouris
                self.error(
589 432cea25 Stavros Sachtouris
                    'Failed to dump metafile /%s/%s' % (
590 432cea25 Stavros Sachtouris
                        locator.container, meta_path))
591 aa82dd5a Stavros Sachtouris
                return
592 f76c6bbc Stavros Sachtouris
            if self['json_output'] or self['output_format']:
593 76f58e2e Stavros Sachtouris
                self.print_json(dict(
594 432cea25 Stavros Sachtouris
                    metafile_location='/%s/%s' % (
595 432cea25 Stavros Sachtouris
                        locator.container, meta_path),
596 aa82dd5a Stavros Sachtouris
                    headers=meta_headers))
597 9553da85 Stavros Sachtouris
            else:
598 432cea25 Stavros Sachtouris
                self.error('Metadata file uploaded as /%s/%s (version %s)' % (
599 432cea25 Stavros Sachtouris
                    locator.container,
600 432cea25 Stavros Sachtouris
                    meta_path,
601 432cea25 Stavros Sachtouris
                    meta_headers['x-object-version']))
602 00336c85 Stavros Sachtouris
603 432cea25 Stavros Sachtouris
    def main(self):
604 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
605 432cea25 Stavros Sachtouris
        self.arguments['pithos_location'].setdefault(
606 432cea25 Stavros Sachtouris
            'uuid', self.auth_base.user_term('id'))
607 432cea25 Stavros Sachtouris
        self._run(self['name'], self['pithos_location'])
608 7493ccb6 Stavros Sachtouris
609 234954d1 Stavros Sachtouris
610 d486baec Stavros Sachtouris
@command(image_cmds)
611 f5f35422 Stavros Sachtouris
class image_unregister(_init_image, _optional_output_cmd):
612 4a17d307 Stavros Sachtouris
    """Unregister an image (does not delete the image file)"""
613 4a17d307 Stavros Sachtouris
614 4a17d307 Stavros Sachtouris
    @errors.generic.all
615 4a17d307 Stavros Sachtouris
    @errors.plankton.connection
616 4a17d307 Stavros Sachtouris
    @errors.plankton.id
617 4a17d307 Stavros Sachtouris
    def _run(self, image_id):
618 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.unregister(image_id))
619 4a17d307 Stavros Sachtouris
620 4a17d307 Stavros Sachtouris
    def main(self, image_id):
621 4a17d307 Stavros Sachtouris
        super(self.__class__, self)._run()
622 4a17d307 Stavros Sachtouris
        self._run(image_id=image_id)
623 4a17d307 Stavros Sachtouris
624 4a17d307 Stavros Sachtouris
625 573be34f Stavros Sachtouris
# Compute Image Commands
626 573be34f Stavros Sachtouris
627 5c5eb675 Stavros Sachtouris
@command(imagecompute_cmds)
628 5c5eb675 Stavros Sachtouris
class imagecompute_list(
629 6d190dd1 Stavros Sachtouris
        _init_cyclades, _optional_json, _name_filter, _id_filter):
630 f3e94e06 Stavros Sachtouris
    """List images"""
631 f3e94e06 Stavros Sachtouris
632 1716a15d Stavros Sachtouris
    PERMANENTS = ('id', 'name')
633 1716a15d Stavros Sachtouris
634 1ae79e60 Stavros Sachtouris
    arguments = dict(
635 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
636 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number listed images', ('-n', '--number')),
637 1d0f1ffa Stavros Sachtouris
        more=FlagArgument('handle long lists of results', '--more'),
638 1716a15d Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
639 fc48b144 Stavros Sachtouris
        user_id=ValueArgument('filter by user_id', '--user-id'),
640 fc48b144 Stavros Sachtouris
        user_name=ValueArgument('filter by username', '--user-name'),
641 1716a15d Stavros Sachtouris
        meta=KeyValueArgument(
642 1716a15d Stavros Sachtouris
            'filter by metadata key=value (can be repeated)', ('--metadata')),
643 1716a15d Stavros Sachtouris
        meta_like=KeyValueArgument(
644 1716a15d Stavros Sachtouris
            'filter by metadata key=value (can be repeated)',
645 1716a15d Stavros Sachtouris
            ('--metadata-like'))
646 1ae79e60 Stavros Sachtouris
    )
647 f3e94e06 Stavros Sachtouris
648 1716a15d Stavros Sachtouris
    def _filter_by_metadata(self, images):
649 1716a15d Stavros Sachtouris
        new_images = []
650 1716a15d Stavros Sachtouris
        for img in images:
651 854222c7 Stavros Sachtouris
            meta = [dict(img['metadata'])]
652 854222c7 Stavros Sachtouris
            if self['meta']:
653 854222c7 Stavros Sachtouris
                meta = filter_dicts_by_dict(meta, self['meta'])
654 854222c7 Stavros Sachtouris
            if meta and self['meta_like']:
655 854222c7 Stavros Sachtouris
                meta = filter_dicts_by_dict(
656 854222c7 Stavros Sachtouris
                    meta, self['meta_like'], exact_match=False)
657 854222c7 Stavros Sachtouris
            if meta:
658 854222c7 Stavros Sachtouris
                new_images.append(img)
659 1716a15d Stavros Sachtouris
        return new_images
660 1716a15d Stavros Sachtouris
661 fc48b144 Stavros Sachtouris
    def _filter_by_user(self, images):
662 fc48b144 Stavros Sachtouris
        uuid = self['user_id'] or self._username2uuid(self['user_name'])
663 fc48b144 Stavros Sachtouris
        return filter_dicts_by_dict(images, dict(user_id=uuid))
664 fc48b144 Stavros Sachtouris
665 1716a15d Stavros Sachtouris
    def _add_name(self, images, key='user_id'):
666 1716a15d Stavros Sachtouris
        uuids = self._uuids2usernames(
667 1716a15d Stavros Sachtouris
            list(set([img[key] for img in images])))
668 1716a15d Stavros Sachtouris
        for img in images:
669 1716a15d Stavros Sachtouris
            img[key] += ' (%s)' % uuids[img[key]]
670 1716a15d Stavros Sachtouris
        return images
671 1716a15d Stavros Sachtouris
672 236e7d08 Stavros Sachtouris
    @errors.generic.all
673 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
674 236e7d08 Stavros Sachtouris
    def _run(self):
675 1716a15d Stavros Sachtouris
        withmeta = bool(self['meta'] or self['meta_like'])
676 fc48b144 Stavros Sachtouris
        withuser = bool(self['user_id'] or self['user_name'])
677 fc48b144 Stavros Sachtouris
        detail = self['detail'] or withmeta or withuser
678 1716a15d Stavros Sachtouris
        images = self.client.list_images(detail)
679 6d190dd1 Stavros Sachtouris
        images = self._filter_by_name(images)
680 6d190dd1 Stavros Sachtouris
        images = self._filter_by_id(images)
681 fc48b144 Stavros Sachtouris
        if withuser:
682 fc48b144 Stavros Sachtouris
            images = self._filter_by_user(images)
683 1716a15d Stavros Sachtouris
        if withmeta:
684 1716a15d Stavros Sachtouris
            images = self._filter_by_metadata(images)
685 f76c6bbc Stavros Sachtouris
        if self['detail'] and not (
686 f76c6bbc Stavros Sachtouris
                self['json_output'] or self['output_format']):
687 1716a15d Stavros Sachtouris
            images = self._add_name(self._add_name(images, 'tenant_id'))
688 fc48b144 Stavros Sachtouris
        elif detail and not self['detail']:
689 fc48b144 Stavros Sachtouris
            for img in images:
690 fc48b144 Stavros Sachtouris
                for key in set(img).difference(self.PERMANENTS):
691 fc48b144 Stavros Sachtouris
                    img.pop(key)
692 545c6c29 Stavros Sachtouris
        kwargs = dict(with_enumeration=self['enum'])
693 6430d3a0 Stavros Sachtouris
        if self['limit']:
694 545c6c29 Stavros Sachtouris
            images = images[:self['limit']]
695 6430d3a0 Stavros Sachtouris
        if self['more']:
696 6430d3a0 Stavros Sachtouris
            kwargs['out'] = StringIO()
697 6430d3a0 Stavros Sachtouris
            kwargs['title'] = ()
698 545c6c29 Stavros Sachtouris
        self._print(images, **kwargs)
699 6430d3a0 Stavros Sachtouris
        if self['more']:
700 6430d3a0 Stavros Sachtouris
            pager(kwargs['out'].getvalue())
701 236e7d08 Stavros Sachtouris
702 f3e94e06 Stavros Sachtouris
    def main(self):
703 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
704 236e7d08 Stavros Sachtouris
        self._run()
705 f3e94e06 Stavros Sachtouris
706 234954d1 Stavros Sachtouris
707 5c5eb675 Stavros Sachtouris
@command(imagecompute_cmds)
708 5c5eb675 Stavros Sachtouris
class imagecompute_info(_init_cyclades, _optional_json):
709 15142309 Stavros Sachtouris
    """Get detailed information on an image"""
710 f3e94e06 Stavros Sachtouris
711 236e7d08 Stavros Sachtouris
    @errors.generic.all
712 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
713 236e7d08 Stavros Sachtouris
    @errors.plankton.id
714 236e7d08 Stavros Sachtouris
    def _run(self, image_id):
715 236e7d08 Stavros Sachtouris
        image = self.client.get_image_details(image_id)
716 cf115aed Stavros Sachtouris
        uuids = [image['user_id'], image['tenant_id']]
717 cf115aed Stavros Sachtouris
        usernames = self._uuids2usernames(uuids)
718 cf115aed Stavros Sachtouris
        image['user_id'] += ' (%s)' % usernames[image['user_id']]
719 cf115aed Stavros Sachtouris
        image['tenant_id'] += ' (%s)' % usernames[image['tenant_id']]
720 76f58e2e Stavros Sachtouris
        self._print(image, self.print_dict)
721 f3e94e06 Stavros Sachtouris
722 f3e94e06 Stavros Sachtouris
    def main(self, image_id):
723 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
724 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
725 f3e94e06 Stavros Sachtouris
726 234954d1 Stavros Sachtouris
727 5c5eb675 Stavros Sachtouris
@command(imagecompute_cmds)
728 5c5eb675 Stavros Sachtouris
class imagecompute_delete(_init_cyclades, _optional_output_cmd):
729 24ff0a35 Stavros Sachtouris
    """Delete an image (WARNING: image file is also removed)"""
730 f3e94e06 Stavros Sachtouris
731 236e7d08 Stavros Sachtouris
    @errors.generic.all
732 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
733 236e7d08 Stavros Sachtouris
    @errors.plankton.id
734 236e7d08 Stavros Sachtouris
    def _run(self, image_id):
735 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.delete_image(image_id))
736 236e7d08 Stavros Sachtouris
737 f3e94e06 Stavros Sachtouris
    def main(self, image_id):
738 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
739 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
740 f3e94e06 Stavros Sachtouris
741 234954d1 Stavros Sachtouris
742 5c5eb675 Stavros Sachtouris
@command(imagecompute_cmds)
743 5c5eb675 Stavros Sachtouris
class imagecompute_modify(_init_cyclades, _optional_output_cmd):
744 5c5eb675 Stavros Sachtouris
    """Modify image properties (metadata)"""
745 f5f35422 Stavros Sachtouris
746 5c5eb675 Stavros Sachtouris
    arguments = dict(
747 5c5eb675 Stavros Sachtouris
        property_to_add=KeyValueArgument(
748 5c5eb675 Stavros Sachtouris
            'Add property in key=value format (can be repeated)',
749 5c5eb675 Stavros Sachtouris
            ('--property-add')),
750 5c5eb675 Stavros Sachtouris
        property_to_del=RepeatableArgument(
751 5c5eb675 Stavros Sachtouris
            'Delete property by key (can be repeated)',
752 5c5eb675 Stavros Sachtouris
            ('--property-del'))
753 5c5eb675 Stavros Sachtouris
    )
754 5c5eb675 Stavros Sachtouris
    required = ['property_to_add', 'property_to_del']
755 f5f35422 Stavros Sachtouris
756 f5f35422 Stavros Sachtouris
    @errors.generic.all
757 f5f35422 Stavros Sachtouris
    @errors.cyclades.connection
758 f5f35422 Stavros Sachtouris
    @errors.plankton.id
759 f5f35422 Stavros Sachtouris
    def _run(self, image_id):
760 5c5eb675 Stavros Sachtouris
        if self['property_to_add']:
761 5c5eb675 Stavros Sachtouris
            self.client.update_image_metadata(
762 5c5eb675 Stavros Sachtouris
                image_id, **self['property_to_add'])
763 f084bdc8 Stavros Sachtouris
        for key in (self['property_to_del'] or []):
764 5c5eb675 Stavros Sachtouris
            self.client.delete_image_metadata(image_id, key)
765 5c5eb675 Stavros Sachtouris
        if self['with_output']:
766 5c5eb675 Stavros Sachtouris
            self._optional_output(self.client.get_image_details(image_id))
767 f5f35422 Stavros Sachtouris
768 f5f35422 Stavros Sachtouris
    def main(self, image_id):
769 f5f35422 Stavros Sachtouris
        super(self.__class__, self)._run()
770 f5f35422 Stavros Sachtouris
        self._run(image_id=image_id)