Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (29.3 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 ec6c3949 Stavros Sachtouris
from kamaki.cli.errors import (
52 ec6c3949 Stavros Sachtouris
    raiseCLIError, CLIBaseUrlError, CLIInvalidArgument)
53 b4f69041 Stavros Sachtouris
from kamaki.cli.commands import _command_init, errors, addLogSettings
54 6d190dd1 Stavros Sachtouris
from kamaki.cli.commands import (
55 6d190dd1 Stavros Sachtouris
    _optional_output_cmd, _optional_json, _name_filter, _id_filter)
56 0b368c8c Stavros Sachtouris
57 234954d1 Stavros Sachtouris
58 5c5eb675 Stavros Sachtouris
image_cmds = CommandTree('image', 'Cyclades/Plankton API image commands')
59 5c5eb675 Stavros Sachtouris
imagecompute_cmds = CommandTree(
60 5c5eb675 Stavros Sachtouris
    'imagecompute', 'Cyclades/Compute API image commands')
61 5c5eb675 Stavros Sachtouris
_commands = [image_cmds, imagecompute_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 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 5c5eb675 Stavros Sachtouris
        'member_ID_to_remove']
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 5c5eb675 Stavros Sachtouris
        for mid in self['member_ID_to_add']:
351 5c5eb675 Stavros Sachtouris
            self.client.add_member(image_id, mid)
352 5c5eb675 Stavros Sachtouris
        for mid in self['member_ID_to_remove']:
353 5c5eb675 Stavros Sachtouris
            self.client.remove_member(image_id, mid)
354 5c5eb675 Stavros Sachtouris
        if len([term for term in self.required if (
355 5c5eb675 Stavros Sachtouris
                self[term] and not term.startswith('member_ID'))]) > 1:
356 5c5eb675 Stavros Sachtouris
            meta = self.client.get_meta(image_id)
357 5c5eb675 Stavros Sachtouris
            for k, v in self['property_to_set'].items():
358 5c5eb675 Stavros Sachtouris
                meta['properties'][k.upper()] = v
359 5c5eb675 Stavros Sachtouris
            for k in self['property_to_del']:
360 5c5eb675 Stavros Sachtouris
                meta['properties'][k.upper()] = None
361 5c5eb675 Stavros Sachtouris
            self._optional_output(self.client.update_image(
362 5c5eb675 Stavros Sachtouris
                image_id,
363 5c5eb675 Stavros Sachtouris
                name=self['image_name'],
364 5c5eb675 Stavros Sachtouris
                disk_format=self['disk_format'],
365 5c5eb675 Stavros Sachtouris
                container_format=self['container_format'],
366 5c5eb675 Stavros Sachtouris
                status=self['status'],
367 5c5eb675 Stavros Sachtouris
                public=self['publish'] or self['unpublish'] or None,
368 5c5eb675 Stavros Sachtouris
                **meta['properties']))
369 5c5eb675 Stavros Sachtouris
        if self['with_output']:
370 5c5eb675 Stavros Sachtouris
            self._optional_output(self.get_image_details(image_id))
371 ca5528f1 Stavros Sachtouris
372 ca5528f1 Stavros Sachtouris
    def main(self, image_id):
373 ca5528f1 Stavros Sachtouris
        super(self.__class__, self)._run()
374 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
375 a03ade9e Stavros Sachtouris
376 234954d1 Stavros Sachtouris
377 ec6c3949 Stavros Sachtouris
class PithosLocationArgument(ValueArgument):
378 ec6c3949 Stavros Sachtouris
    """Resolve pithos url, return in the form pithos://uuid/container/path"""
379 ec6c3949 Stavros Sachtouris
380 ec6c3949 Stavros Sachtouris
    def __init__(
381 ec6c3949 Stavros Sachtouris
            self, help=None, parsed_name=None, default=None, user_uuid=None):
382 ec6c3949 Stavros Sachtouris
        super(PithosLocationArgument, self).__init__(
383 ec6c3949 Stavros Sachtouris
            help=help, parsed_name=parsed_name, default=default)
384 ec6c3949 Stavros Sachtouris
        self.uuid, self.container, self.path = user_uuid, None, None
385 ec6c3949 Stavros Sachtouris
386 ec6c3949 Stavros Sachtouris
    def setdefault(self, term, value):
387 ec6c3949 Stavros Sachtouris
        if not getattr(self, term, None):
388 ec6c3949 Stavros Sachtouris
            setattr(self, term, value)
389 ec6c3949 Stavros Sachtouris
390 ec6c3949 Stavros Sachtouris
    @property
391 ec6c3949 Stavros Sachtouris
    def value(self):
392 ec6c3949 Stavros Sachtouris
        return 'pithos://%s/%s/%s' % (self.uuid, self.container, self.path)
393 ec6c3949 Stavros Sachtouris
394 ec6c3949 Stavros Sachtouris
    @value.setter
395 ec6c3949 Stavros Sachtouris
    def value(self, location):
396 ec6c3949 Stavros Sachtouris
        if location:
397 ec6c3949 Stavros Sachtouris
            from kamaki.cli.commands.pithos import _pithos_container as pc
398 ec6c3949 Stavros Sachtouris
            try:
399 ec6c3949 Stavros Sachtouris
                uuid, self.container, self.path = pc._resolve_pithos_url(
400 ec6c3949 Stavros Sachtouris
                    location)
401 ec6c3949 Stavros Sachtouris
                self.uuid = uuid or self.uuid
402 ec6c3949 Stavros Sachtouris
                for term in ('container', 'path'):
403 ec6c3949 Stavros Sachtouris
                    assert getattr(self, term, None), 'No %s' % term
404 ec6c3949 Stavros Sachtouris
            except Exception as e:
405 ec6c3949 Stavros Sachtouris
                raise CLIInvalidArgument(
406 ec6c3949 Stavros Sachtouris
                    'Invalid Pithos+ location %s (%s)' % (location, e),
407 ec6c3949 Stavros Sachtouris
                    details=[
408 ec6c3949 Stavros Sachtouris
                        'The image location must be a valid Pithos+',
409 ec6c3949 Stavros Sachtouris
                        'location. There are two valid formats:',
410 ec6c3949 Stavros Sachtouris
                        '  pithos://USER_UUID/CONTAINER/PATH',
411 ec6c3949 Stavros Sachtouris
                        'OR',
412 ec6c3949 Stavros Sachtouris
                        '  /CONTAINER/PATH',
413 ec6c3949 Stavros Sachtouris
                        'To see all containers:',
414 ec6c3949 Stavros Sachtouris
                        '  [kamaki] container list',
415 ec6c3949 Stavros Sachtouris
                        'To list the contents of a container:',
416 ec6c3949 Stavros Sachtouris
                        '  [kamaki] container list CONTAINER'])
417 ec6c3949 Stavros Sachtouris
418 ec6c3949 Stavros Sachtouris
419 d486baec Stavros Sachtouris
@command(image_cmds)
420 545c6c29 Stavros Sachtouris
class image_register(_init_image, _optional_json):
421 38db356b Stavros Sachtouris
    """(Re)Register an image file to an Image service
422 38db356b Stavros Sachtouris
    The image file must be stored at a pithos repository
423 16d7b9ff Stavros Sachtouris
    Some metadata can be set by user (e.g., disk-format) while others are set
424 16d7b9ff Stavros Sachtouris
    only automatically (e.g., image id). There are also some custom user
425 38db356b Stavros Sachtouris
    metadata, called properties.
426 38db356b Stavros Sachtouris
    A register command creates a remote meta file at
427 ec6c3949 Stavros Sachtouris
    /<container>/<image path>.meta
428 38db356b Stavros Sachtouris
    Users may download and edit this file and use it to re-register one or more
429 38db356b Stavros Sachtouris
    images.
430 38db356b Stavros Sachtouris
    In case of a meta file, runtime arguments for metadata or properties
431 38db356b Stavros Sachtouris
    override meta file settings.
432 38db356b Stavros Sachtouris
    """
433 7493ccb6 Stavros Sachtouris
434 d77e33d4 Stavros Sachtouris
    container_info_cache = {}
435 d77e33d4 Stavros Sachtouris
436 1ae79e60 Stavros Sachtouris
    arguments = dict(
437 38db356b Stavros Sachtouris
        checksum=ValueArgument('Set image checksum', '--checksum'),
438 1ae79e60 Stavros Sachtouris
        container_format=ValueArgument(
439 1d0f1ffa Stavros Sachtouris
            'Set container format', '--container-format'),
440 38db356b Stavros Sachtouris
        disk_format=ValueArgument('Set disk format', '--disk-format'),
441 38db356b Stavros Sachtouris
        owner_name=ValueArgument('Set user uuid by user name', '--owner-name'),
442 1ae79e60 Stavros Sachtouris
        properties=KeyValueArgument(
443 38db356b Stavros Sachtouris
            'Add property (user-specified metadata) in key=value form'
444 38db356b Stavros Sachtouris
            '(can be repeated)',
445 1736e06d Stavros Sachtouris
            ('-p', '--property')),
446 38db356b Stavros Sachtouris
        is_public=FlagArgument('Mark image as public', '--public'),
447 38db356b Stavros Sachtouris
        size=IntArgument('Set image size in bytes', '--size'),
448 aa82dd5a Stavros Sachtouris
        metafile=ValueArgument(
449 aa82dd5a Stavros Sachtouris
            'Load metadata from a json-formated file <img-file>.meta :'
450 aa82dd5a Stavros Sachtouris
            '{"key1": "val1", "key2": "val2", ..., "properties: {...}"}',
451 aa82dd5a Stavros Sachtouris
            ('--metafile')),
452 aa82dd5a Stavros Sachtouris
        metafile_force=FlagArgument(
453 38db356b Stavros Sachtouris
            'Overide remote metadata file', ('-f', '--force')),
454 aa82dd5a Stavros Sachtouris
        no_metafile_upload=FlagArgument(
455 aa82dd5a Stavros Sachtouris
            'Do not store metadata in remote meta file',
456 aa82dd5a Stavros Sachtouris
            ('--no-metafile-upload')),
457 f2ea1314 Stavros Sachtouris
        container=ValueArgument(
458 f2ea1314 Stavros Sachtouris
            'Pithos+ container containing the image file',
459 f2ea1314 Stavros Sachtouris
            ('-C', '--container')),
460 d77e33d4 Stavros Sachtouris
        uuid=ValueArgument('Custom user uuid', '--uuid'),
461 d77e33d4 Stavros Sachtouris
        local_image_path=ValueArgument(
462 d77e33d4 Stavros Sachtouris
            'Local image file path to upload and register '
463 ec6c3949 Stavros Sachtouris
            '(still need target file in the form /ontainer/remote-path )',
464 d77e33d4 Stavros Sachtouris
            '--upload-image-file'),
465 d77e33d4 Stavros Sachtouris
        progress_bar=ProgressBarArgument(
466 ec6c3949 Stavros Sachtouris
            'Do not use progress bar', '--no-progress-bar', default=False),
467 ec6c3949 Stavros Sachtouris
        name=ValueArgument('The name of the new image', '--name'),
468 ec6c3949 Stavros Sachtouris
        pithos_location=PithosLocationArgument(
469 ec6c3949 Stavros Sachtouris
            'The Pithos+ image location to put the image at. Format:       '
470 ec6c3949 Stavros Sachtouris
            'pithos://USER_UUID/CONTAINER/IMAGE                  or   '
471 ec6c3949 Stavros Sachtouris
            '/CONTAINER/IMAGE',
472 ec6c3949 Stavros Sachtouris
            '--location')
473 1ae79e60 Stavros Sachtouris
    )
474 ec6c3949 Stavros Sachtouris
    required = ('name', 'pithos_location')
475 7493ccb6 Stavros Sachtouris
476 ec6c3949 Stavros Sachtouris
    def _get_pithos_client(self, locator):
477 aa82dd5a Stavros Sachtouris
        if self['no_metafile_upload']:
478 aa82dd5a Stavros Sachtouris
            return None
479 00336c85 Stavros Sachtouris
        ptoken = self.client.token
480 ef00bc31 Stavros Sachtouris
        if getattr(self, 'auth_base', False):
481 ef00bc31 Stavros Sachtouris
            pithos_endpoints = self.auth_base.get_service_endpoints(
482 df0045d8 Stavros Sachtouris
                'object-store')
483 ef00bc31 Stavros Sachtouris
            purl = pithos_endpoints['publicURL']
484 ef00bc31 Stavros Sachtouris
        else:
485 df0045d8 Stavros Sachtouris
            purl = self.config.get_cloud('pithos', 'url')
486 df0045d8 Stavros Sachtouris
        if not purl:
487 df0045d8 Stavros Sachtouris
            raise CLIBaseUrlError(service='pithos')
488 ec6c3949 Stavros Sachtouris
        return PithosClient(purl, ptoken, locator.uuid, locator.container)
489 00336c85 Stavros Sachtouris
490 aa82dd5a Stavros Sachtouris
    def _load_params_from_file(self, location):
491 aa82dd5a Stavros Sachtouris
        params, properties = dict(), dict()
492 aa82dd5a Stavros Sachtouris
        pfile = self['metafile']
493 aa82dd5a Stavros Sachtouris
        if pfile:
494 00336c85 Stavros Sachtouris
            try:
495 aa82dd5a Stavros Sachtouris
                for k, v in _load_image_meta(pfile).items():
496 aa82dd5a Stavros Sachtouris
                    key = k.lower().replace('-', '_')
497 1d0f1ffa Stavros Sachtouris
                    if key == 'properties':
498 aa82dd5a Stavros Sachtouris
                        for pk, pv in v.items():
499 aa82dd5a Stavros Sachtouris
                            properties[pk.upper().replace('-', '_')] = pv
500 aa82dd5a Stavros Sachtouris
                    elif key == 'name':
501 aa82dd5a Stavros Sachtouris
                            continue
502 aa82dd5a Stavros Sachtouris
                    elif key == 'location':
503 aa82dd5a Stavros Sachtouris
                        if location:
504 aa82dd5a Stavros Sachtouris
                            continue
505 aa82dd5a Stavros Sachtouris
                        location = v
506 aa82dd5a Stavros Sachtouris
                    else:
507 aa82dd5a Stavros Sachtouris
                        params[key] = v
508 aa82dd5a Stavros Sachtouris
            except Exception as e:
509 aa82dd5a Stavros Sachtouris
                raiseCLIError(e, 'Invalid json metadata config file')
510 aa82dd5a Stavros Sachtouris
        return params, properties, location
511 7493ccb6 Stavros Sachtouris
512 aa82dd5a Stavros Sachtouris
    def _load_params_from_args(self, params, properties):
513 f769a16a Stavros Sachtouris
        for key in set([
514 1ae79e60 Stavros Sachtouris
                'checksum',
515 1ae79e60 Stavros Sachtouris
                'container_format',
516 1ae79e60 Stavros Sachtouris
                'disk_format',
517 1ae79e60 Stavros Sachtouris
                'owner',
518 1ae79e60 Stavros Sachtouris
                'size',
519 de73876b Stavros Sachtouris
                'is_public']).intersection(self.arguments):
520 1ae79e60 Stavros Sachtouris
            params[key] = self[key]
521 aa82dd5a Stavros Sachtouris
        for k, v in self['properties'].items():
522 aa82dd5a Stavros Sachtouris
            properties[k.upper().replace('-', '_')] = v
523 1ae79e60 Stavros Sachtouris
524 aa82dd5a Stavros Sachtouris
    @errors.generic.all
525 aa82dd5a Stavros Sachtouris
    @errors.plankton.connection
526 ec6c3949 Stavros Sachtouris
    def _run(self, name, location):
527 ec6c3949 Stavros Sachtouris
        locator = self.arguments['pithos_location']
528 d77e33d4 Stavros Sachtouris
        if self['local_image_path']:
529 d77e33d4 Stavros Sachtouris
            with open(self['local_image_path']) as f:
530 ec6c3949 Stavros Sachtouris
                pithos = self._get_pithos_client(locator)
531 d77e33d4 Stavros Sachtouris
                (pbar, upload_cb) = self._safe_progress_bar('Uploading')
532 d77e33d4 Stavros Sachtouris
                if pbar:
533 d77e33d4 Stavros Sachtouris
                    hash_bar = pbar.clone()
534 d77e33d4 Stavros Sachtouris
                    hash_cb = hash_bar.get_generator('Calculating hashes')
535 d77e33d4 Stavros Sachtouris
                pithos.upload_object(
536 ec6c3949 Stavros Sachtouris
                    locator.path, f,
537 d77e33d4 Stavros Sachtouris
                    hash_cb=hash_cb, upload_cb=upload_cb,
538 d77e33d4 Stavros Sachtouris
                    container_info_cache=self.container_info_cache)
539 d77e33d4 Stavros Sachtouris
                pbar.finish()
540 d77e33d4 Stavros Sachtouris
541 f2ea1314 Stavros Sachtouris
        (params, properties, new_loc) = self._load_params_from_file(location)
542 f2ea1314 Stavros Sachtouris
        if location != new_loc:
543 ec6c3949 Stavros Sachtouris
            locator.value = new_loc
544 aa82dd5a Stavros Sachtouris
        self._load_params_from_args(params, properties)
545 ec6c3949 Stavros Sachtouris
        pclient = self._get_pithos_client(locator)
546 aa82dd5a Stavros Sachtouris
547 aa82dd5a Stavros Sachtouris
        #check if metafile exists
548 ec6c3949 Stavros Sachtouris
        meta_path = '%s.meta' % locator.path
549 aa82dd5a Stavros Sachtouris
        if pclient and not self['metafile_force']:
550 c4aefeaf Stavros Sachtouris
            try:
551 aa82dd5a Stavros Sachtouris
                pclient.get_object_info(meta_path)
552 81b0838d Stavros Sachtouris
                raiseCLIError(
553 ec6c3949 Stavros Sachtouris
                    'Metadata file /%s/%s already exists, abort' % (
554 ec6c3949 Stavros Sachtouris
                        locator.container, meta_path),
555 81b0838d Stavros Sachtouris
                    details=['Registration ABORTED', 'Try -f to overwrite'])
556 aa82dd5a Stavros Sachtouris
            except ClientError as ce:
557 aa82dd5a Stavros Sachtouris
                if ce.status != 404:
558 aa82dd5a Stavros Sachtouris
                    raise
559 aa82dd5a Stavros Sachtouris
560 aa82dd5a Stavros Sachtouris
        #register the image
561 aa82dd5a Stavros Sachtouris
        try:
562 aa82dd5a Stavros Sachtouris
            r = self.client.register(name, location, params, properties)
563 aa82dd5a Stavros Sachtouris
        except ClientError as ce:
564 7bbfeb1a Stavros Sachtouris
            if ce.status in (400, 404):
565 c4aefeaf Stavros Sachtouris
                raiseCLIError(
566 7bbfeb1a Stavros Sachtouris
                    ce, 'Nonexistent image file location\n\t%s' % location,
567 c4aefeaf Stavros Sachtouris
                    details=[
568 7bbfeb1a Stavros Sachtouris
                        'Does the image file %s exist at container %s ?' % (
569 ec6c3949 Stavros Sachtouris
                            locator.path,
570 ec6c3949 Stavros Sachtouris
                            locator.container)] + howto_image_file)
571 aa82dd5a Stavros Sachtouris
            raise
572 6b8a403c Stavros Sachtouris
        r['owner'] += ' (%s)' % self._uuid2username(r['owner'])
573 76f58e2e Stavros Sachtouris
        self._print(r, self.print_dict)
574 a03ade9e Stavros Sachtouris
575 aa82dd5a Stavros Sachtouris
        #upload the metadata file
576 00336c85 Stavros Sachtouris
        if pclient:
577 aa82dd5a Stavros Sachtouris
            try:
578 aa82dd5a Stavros Sachtouris
                meta_headers = pclient.upload_from_string(
579 d77e33d4 Stavros Sachtouris
                    meta_path, dumps(r, indent=2),
580 d77e33d4 Stavros Sachtouris
                    container_info_cache=self.container_info_cache)
581 aa82dd5a Stavros Sachtouris
            except TypeError:
582 1d0f1ffa Stavros Sachtouris
                self.error(
583 ec6c3949 Stavros Sachtouris
                    'Failed to dump metafile /%s/%s' % (
584 ec6c3949 Stavros Sachtouris
                        locator.container, meta_path))
585 aa82dd5a Stavros Sachtouris
                return
586 f76c6bbc Stavros Sachtouris
            if self['json_output'] or self['output_format']:
587 76f58e2e Stavros Sachtouris
                self.print_json(dict(
588 ec6c3949 Stavros Sachtouris
                    metafile_location='/%s/%s' % (
589 ec6c3949 Stavros Sachtouris
                        locator.container, meta_path),
590 aa82dd5a Stavros Sachtouris
                    headers=meta_headers))
591 9553da85 Stavros Sachtouris
            else:
592 ec6c3949 Stavros Sachtouris
                self.error('Metadata file uploaded as /%s/%s (version %s)' % (
593 ec6c3949 Stavros Sachtouris
                    locator.container,
594 ec6c3949 Stavros Sachtouris
                    meta_path,
595 ec6c3949 Stavros Sachtouris
                    meta_headers['x-object-version']))
596 00336c85 Stavros Sachtouris
597 ec6c3949 Stavros Sachtouris
    def main(self):
598 a03ade9e Stavros Sachtouris
        super(self.__class__, self)._run()
599 ec6c3949 Stavros Sachtouris
        self.arguments['pithos_location'].setdefault(
600 ec6c3949 Stavros Sachtouris
            'uuid', self.auth_base.user_term('id'))
601 ec6c3949 Stavros Sachtouris
        self._run(self['name'], self['pithos_location'])
602 7493ccb6 Stavros Sachtouris
603 234954d1 Stavros Sachtouris
604 d486baec Stavros Sachtouris
@command(image_cmds)
605 f5f35422 Stavros Sachtouris
class image_unregister(_init_image, _optional_output_cmd):
606 4a17d307 Stavros Sachtouris
    """Unregister an image (does not delete the image file)"""
607 4a17d307 Stavros Sachtouris
608 4a17d307 Stavros Sachtouris
    @errors.generic.all
609 4a17d307 Stavros Sachtouris
    @errors.plankton.connection
610 4a17d307 Stavros Sachtouris
    @errors.plankton.id
611 4a17d307 Stavros Sachtouris
    def _run(self, image_id):
612 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.unregister(image_id))
613 4a17d307 Stavros Sachtouris
614 4a17d307 Stavros Sachtouris
    def main(self, image_id):
615 4a17d307 Stavros Sachtouris
        super(self.__class__, self)._run()
616 4a17d307 Stavros Sachtouris
        self._run(image_id=image_id)
617 4a17d307 Stavros Sachtouris
618 4a17d307 Stavros Sachtouris
619 573be34f Stavros Sachtouris
# Compute Image Commands
620 573be34f Stavros Sachtouris
621 5c5eb675 Stavros Sachtouris
@command(imagecompute_cmds)
622 5c5eb675 Stavros Sachtouris
class imagecompute_list(
623 6d190dd1 Stavros Sachtouris
        _init_cyclades, _optional_json, _name_filter, _id_filter):
624 f3e94e06 Stavros Sachtouris
    """List images"""
625 f3e94e06 Stavros Sachtouris
626 1716a15d Stavros Sachtouris
    PERMANENTS = ('id', 'name')
627 1716a15d Stavros Sachtouris
628 1ae79e60 Stavros Sachtouris
    arguments = dict(
629 f40f0cb7 Stavros Sachtouris
        detail=FlagArgument('show detailed output', ('-l', '--details')),
630 f40f0cb7 Stavros Sachtouris
        limit=IntArgument('limit number listed images', ('-n', '--number')),
631 1d0f1ffa Stavros Sachtouris
        more=FlagArgument('handle long lists of results', '--more'),
632 1716a15d Stavros Sachtouris
        enum=FlagArgument('Enumerate results', '--enumerate'),
633 fc48b144 Stavros Sachtouris
        user_id=ValueArgument('filter by user_id', '--user-id'),
634 fc48b144 Stavros Sachtouris
        user_name=ValueArgument('filter by username', '--user-name'),
635 1716a15d Stavros Sachtouris
        meta=KeyValueArgument(
636 1716a15d Stavros Sachtouris
            'filter by metadata key=value (can be repeated)', ('--metadata')),
637 1716a15d Stavros Sachtouris
        meta_like=KeyValueArgument(
638 1716a15d Stavros Sachtouris
            'filter by metadata key=value (can be repeated)',
639 1716a15d Stavros Sachtouris
            ('--metadata-like'))
640 1ae79e60 Stavros Sachtouris
    )
641 f3e94e06 Stavros Sachtouris
642 1716a15d Stavros Sachtouris
    def _filter_by_metadata(self, images):
643 1716a15d Stavros Sachtouris
        new_images = []
644 1716a15d Stavros Sachtouris
        for img in images:
645 854222c7 Stavros Sachtouris
            meta = [dict(img['metadata'])]
646 854222c7 Stavros Sachtouris
            if self['meta']:
647 854222c7 Stavros Sachtouris
                meta = filter_dicts_by_dict(meta, self['meta'])
648 854222c7 Stavros Sachtouris
            if meta and self['meta_like']:
649 854222c7 Stavros Sachtouris
                meta = filter_dicts_by_dict(
650 854222c7 Stavros Sachtouris
                    meta, self['meta_like'], exact_match=False)
651 854222c7 Stavros Sachtouris
            if meta:
652 854222c7 Stavros Sachtouris
                new_images.append(img)
653 1716a15d Stavros Sachtouris
        return new_images
654 1716a15d Stavros Sachtouris
655 fc48b144 Stavros Sachtouris
    def _filter_by_user(self, images):
656 fc48b144 Stavros Sachtouris
        uuid = self['user_id'] or self._username2uuid(self['user_name'])
657 fc48b144 Stavros Sachtouris
        return filter_dicts_by_dict(images, dict(user_id=uuid))
658 fc48b144 Stavros Sachtouris
659 1716a15d Stavros Sachtouris
    def _add_name(self, images, key='user_id'):
660 1716a15d Stavros Sachtouris
        uuids = self._uuids2usernames(
661 1716a15d Stavros Sachtouris
            list(set([img[key] for img in images])))
662 1716a15d Stavros Sachtouris
        for img in images:
663 1716a15d Stavros Sachtouris
            img[key] += ' (%s)' % uuids[img[key]]
664 1716a15d Stavros Sachtouris
        return images
665 1716a15d Stavros Sachtouris
666 236e7d08 Stavros Sachtouris
    @errors.generic.all
667 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
668 236e7d08 Stavros Sachtouris
    def _run(self):
669 1716a15d Stavros Sachtouris
        withmeta = bool(self['meta'] or self['meta_like'])
670 fc48b144 Stavros Sachtouris
        withuser = bool(self['user_id'] or self['user_name'])
671 fc48b144 Stavros Sachtouris
        detail = self['detail'] or withmeta or withuser
672 1716a15d Stavros Sachtouris
        images = self.client.list_images(detail)
673 6d190dd1 Stavros Sachtouris
        images = self._filter_by_name(images)
674 6d190dd1 Stavros Sachtouris
        images = self._filter_by_id(images)
675 fc48b144 Stavros Sachtouris
        if withuser:
676 fc48b144 Stavros Sachtouris
            images = self._filter_by_user(images)
677 1716a15d Stavros Sachtouris
        if withmeta:
678 1716a15d Stavros Sachtouris
            images = self._filter_by_metadata(images)
679 f76c6bbc Stavros Sachtouris
        if self['detail'] and not (
680 f76c6bbc Stavros Sachtouris
                self['json_output'] or self['output_format']):
681 1716a15d Stavros Sachtouris
            images = self._add_name(self._add_name(images, 'tenant_id'))
682 fc48b144 Stavros Sachtouris
        elif detail and not self['detail']:
683 fc48b144 Stavros Sachtouris
            for img in images:
684 fc48b144 Stavros Sachtouris
                for key in set(img).difference(self.PERMANENTS):
685 fc48b144 Stavros Sachtouris
                    img.pop(key)
686 545c6c29 Stavros Sachtouris
        kwargs = dict(with_enumeration=self['enum'])
687 6430d3a0 Stavros Sachtouris
        if self['limit']:
688 545c6c29 Stavros Sachtouris
            images = images[:self['limit']]
689 6430d3a0 Stavros Sachtouris
        if self['more']:
690 6430d3a0 Stavros Sachtouris
            kwargs['out'] = StringIO()
691 6430d3a0 Stavros Sachtouris
            kwargs['title'] = ()
692 545c6c29 Stavros Sachtouris
        self._print(images, **kwargs)
693 6430d3a0 Stavros Sachtouris
        if self['more']:
694 6430d3a0 Stavros Sachtouris
            pager(kwargs['out'].getvalue())
695 236e7d08 Stavros Sachtouris
696 f3e94e06 Stavros Sachtouris
    def main(self):
697 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
698 236e7d08 Stavros Sachtouris
        self._run()
699 f3e94e06 Stavros Sachtouris
700 234954d1 Stavros Sachtouris
701 5c5eb675 Stavros Sachtouris
@command(imagecompute_cmds)
702 5c5eb675 Stavros Sachtouris
class imagecompute_info(_init_cyclades, _optional_json):
703 15142309 Stavros Sachtouris
    """Get detailed information on an image"""
704 f3e94e06 Stavros Sachtouris
705 236e7d08 Stavros Sachtouris
    @errors.generic.all
706 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
707 236e7d08 Stavros Sachtouris
    @errors.plankton.id
708 236e7d08 Stavros Sachtouris
    def _run(self, image_id):
709 236e7d08 Stavros Sachtouris
        image = self.client.get_image_details(image_id)
710 cf115aed Stavros Sachtouris
        uuids = [image['user_id'], image['tenant_id']]
711 cf115aed Stavros Sachtouris
        usernames = self._uuids2usernames(uuids)
712 cf115aed Stavros Sachtouris
        image['user_id'] += ' (%s)' % usernames[image['user_id']]
713 cf115aed Stavros Sachtouris
        image['tenant_id'] += ' (%s)' % usernames[image['tenant_id']]
714 76f58e2e Stavros Sachtouris
        self._print(image, self.print_dict)
715 f3e94e06 Stavros Sachtouris
716 f3e94e06 Stavros Sachtouris
    def main(self, image_id):
717 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
718 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
719 f3e94e06 Stavros Sachtouris
720 234954d1 Stavros Sachtouris
721 5c5eb675 Stavros Sachtouris
@command(imagecompute_cmds)
722 5c5eb675 Stavros Sachtouris
class imagecompute_delete(_init_cyclades, _optional_output_cmd):
723 24ff0a35 Stavros Sachtouris
    """Delete an image (WARNING: image file is also removed)"""
724 f3e94e06 Stavros Sachtouris
725 236e7d08 Stavros Sachtouris
    @errors.generic.all
726 236e7d08 Stavros Sachtouris
    @errors.cyclades.connection
727 236e7d08 Stavros Sachtouris
    @errors.plankton.id
728 236e7d08 Stavros Sachtouris
    def _run(self, image_id):
729 f5f35422 Stavros Sachtouris
        self._optional_output(self.client.delete_image(image_id))
730 236e7d08 Stavros Sachtouris
731 f3e94e06 Stavros Sachtouris
    def main(self, image_id):
732 236e7d08 Stavros Sachtouris
        super(self.__class__, self)._run()
733 b04288f7 Stavros Sachtouris
        self._run(image_id=image_id)
734 f3e94e06 Stavros Sachtouris
735 234954d1 Stavros Sachtouris
736 5c5eb675 Stavros Sachtouris
@command(imagecompute_cmds)
737 5c5eb675 Stavros Sachtouris
class imagecompute_modify(_init_cyclades, _optional_output_cmd):
738 5c5eb675 Stavros Sachtouris
    """Modify image properties (metadata)"""
739 f5f35422 Stavros Sachtouris
740 5c5eb675 Stavros Sachtouris
    arguments = dict(
741 5c5eb675 Stavros Sachtouris
        property_to_add=KeyValueArgument(
742 5c5eb675 Stavros Sachtouris
            'Add property in key=value format (can be repeated)',
743 5c5eb675 Stavros Sachtouris
            ('--property-add')),
744 5c5eb675 Stavros Sachtouris
        property_to_del=RepeatableArgument(
745 5c5eb675 Stavros Sachtouris
            'Delete property by key (can be repeated)',
746 5c5eb675 Stavros Sachtouris
            ('--property-del'))
747 5c5eb675 Stavros Sachtouris
    )
748 5c5eb675 Stavros Sachtouris
    required = ['property_to_add', 'property_to_del']
749 f5f35422 Stavros Sachtouris
750 f5f35422 Stavros Sachtouris
    @errors.generic.all
751 f5f35422 Stavros Sachtouris
    @errors.cyclades.connection
752 f5f35422 Stavros Sachtouris
    @errors.plankton.id
753 f5f35422 Stavros Sachtouris
    def _run(self, image_id):
754 5c5eb675 Stavros Sachtouris
        if self['property_to_add']:
755 5c5eb675 Stavros Sachtouris
            self.client.update_image_metadata(
756 5c5eb675 Stavros Sachtouris
                image_id, **self['property_to_add'])
757 5c5eb675 Stavros Sachtouris
        for key in self['property_to_del']:
758 5c5eb675 Stavros Sachtouris
            self.client.delete_image_metadata(image_id, key)
759 5c5eb675 Stavros Sachtouris
        if self['with_output']:
760 5c5eb675 Stavros Sachtouris
            self._optional_output(self.client.get_image_details(image_id))
761 f5f35422 Stavros Sachtouris
762 f5f35422 Stavros Sachtouris
    def main(self, image_id):
763 f5f35422 Stavros Sachtouris
        super(self.__class__, self)._run()
764 f5f35422 Stavros Sachtouris
        self._run(image_id=image_id)