Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute.py @ 961e6040

History | View | Annotate | Download (11.6 kB)

1 a1c50326 Giorgos Verigakis
# Copyright 2011 GRNET S.A. All rights reserved.
2 a1c50326 Giorgos Verigakis
#
3 a1c50326 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 a1c50326 Giorgos Verigakis
# without modification, are permitted provided that the following
5 a1c50326 Giorgos Verigakis
# conditions are met:
6 a1c50326 Giorgos Verigakis
#
7 a1c50326 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 a1c50326 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 a1c50326 Giorgos Verigakis
#      disclaimer.
10 a1c50326 Giorgos Verigakis
#
11 a1c50326 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 a1c50326 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 a1c50326 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 a1c50326 Giorgos Verigakis
#      provided with the distribution.
15 a1c50326 Giorgos Verigakis
#
16 a1c50326 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 a1c50326 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 a1c50326 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 a1c50326 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 a1c50326 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 a1c50326 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 a1c50326 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 a1c50326 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 a1c50326 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 a1c50326 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 a1c50326 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 a1c50326 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 a1c50326 Giorgos Verigakis
#
29 a1c50326 Giorgos Verigakis
# The views and conclusions contained in the software and
30 a1c50326 Giorgos Verigakis
# documentation are those of the authors and should not be
31 a1c50326 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 a1c50326 Giorgos Verigakis
# or implied, of GRNET S.A.
33 a1c50326 Giorgos Verigakis
34 c270fe96 Stavros Sachtouris
from kamaki.clients import Client, ClientError
35 c270fe96 Stavros Sachtouris
from kamaki.clients.utils import path4url
36 bf966740 Stavros Sachtouris
import json
37 a1c50326 Giorgos Verigakis
38 3dabe5d2 Stavros Sachtouris
39 6a0b1658 Giorgos Verigakis
class ComputeClient(Client):
40 d2cea1e2 Giorgos Verigakis
    """OpenStack Compute API 1.1 client"""
41 2f749e6e Stavros Sachtouris
42 6a0b1658 Giorgos Verigakis
    def raise_for_status(self, r):
43 c7083665 Stavros Sachtouris
        try:
44 c7083665 Stavros Sachtouris
            d = r.json
45 c7083665 Stavros Sachtouris
            key = d.keys()[0]
46 c7083665 Stavros Sachtouris
            val = d[key]
47 c7083665 Stavros Sachtouris
            message = '%s: %s' % (key, val.get('message', ''))
48 c7083665 Stavros Sachtouris
            details = val.get('details', '')
49 c7083665 Stavros Sachtouris
        except AttributeError:
50 3dabe5d2 Stavros Sachtouris
            message = 'Request responded with error code %s' %\
51 3dabe5d2 Stavros Sachtouris
                unicode(r.status_code)
52 3dabe5d2 Stavros Sachtouris
            details = '%s %s' %\
53 3dabe5d2 Stavros Sachtouris
                (unicode(r.request.method), unicode(r.request.url))
54 25914947 Stavros Sachtouris
        raise ClientError(message, r.status_code, details)
55 3dabe5d2 Stavros Sachtouris
56 6666f3b3 Stavros Sachtouris
    def servers_get(self, server_id='', command='', **kwargs):
57 6666f3b3 Stavros Sachtouris
        """GET base_url/servers[/server_id][/command] request
58 6666f3b3 Stavros Sachtouris
        @param server_id or ''
59 6666f3b3 Stavros Sachtouris
        @param command: can be 'ips', 'stats', or ''
60 6666f3b3 Stavros Sachtouris
        """
61 6666f3b3 Stavros Sachtouris
        path = path4url('servers', server_id, command)
62 6666f3b3 Stavros Sachtouris
        success = kwargs.pop('success', 200)
63 6666f3b3 Stavros Sachtouris
        return self.get(path, success=success, **kwargs)
64 6666f3b3 Stavros Sachtouris
65 6666f3b3 Stavros Sachtouris
    def servers_delete(self, server_id='', command='', **kwargs):
66 6666f3b3 Stavros Sachtouris
        """DEL ETE base_url/servers[/server_id][/command] request
67 6666f3b3 Stavros Sachtouris
        @param server_id or ''
68 6666f3b3 Stavros Sachtouris
        @param command: can be 'ips', 'stats', or ''
69 6666f3b3 Stavros Sachtouris
        """
70 6666f3b3 Stavros Sachtouris
        path = path4url('servers', server_id, command)
71 6666f3b3 Stavros Sachtouris
        success = kwargs.pop('success', 204)
72 6666f3b3 Stavros Sachtouris
        return self.delete(path, success=success, **kwargs)
73 6666f3b3 Stavros Sachtouris
74 6666f3b3 Stavros Sachtouris
    def servers_post(self, server_id='', command='', json_data=None, **kwargs):
75 6666f3b3 Stavros Sachtouris
        """POST base_url/servers[/server_id]/[command] request
76 6666f3b3 Stavros Sachtouris
        @param server_id or ''
77 6666f3b3 Stavros Sachtouris
        @param command: can be 'action' or ''
78 6666f3b3 Stavros Sachtouris
        @param json_data: a json valid dict that will be send as data
79 6666f3b3 Stavros Sachtouris
        """
80 6666f3b3 Stavros Sachtouris
        data = json_data
81 6666f3b3 Stavros Sachtouris
        if json_data is not None:
82 6666f3b3 Stavros Sachtouris
            data = json.dumps(json_data)
83 6666f3b3 Stavros Sachtouris
            self.set_header('Content-Type', 'application/json')
84 6666f3b3 Stavros Sachtouris
            self.set_header('Content-Length', len(data))
85 6666f3b3 Stavros Sachtouris
86 6666f3b3 Stavros Sachtouris
        path = path4url('servers', server_id, command)
87 6666f3b3 Stavros Sachtouris
        success = kwargs.pop('success', 202)
88 6666f3b3 Stavros Sachtouris
        return self.post(path, data=data, success=success, **kwargs)
89 6666f3b3 Stavros Sachtouris
90 6666f3b3 Stavros Sachtouris
    def servers_put(self, server_id='', command='', json_data=None, **kwargs):
91 6666f3b3 Stavros Sachtouris
        """PUT base_url/servers[/server_id]/[command] request
92 6666f3b3 Stavros Sachtouris
        @param server_id or ''
93 6666f3b3 Stavros Sachtouris
        @param command: can be 'action' or ''
94 6666f3b3 Stavros Sachtouris
        @param json_data: a json valid dict that will be send as data
95 6666f3b3 Stavros Sachtouris
        """
96 6666f3b3 Stavros Sachtouris
        data = json_data
97 6666f3b3 Stavros Sachtouris
        if json_data is not None:
98 6666f3b3 Stavros Sachtouris
            data = json.dumps(json_data)
99 6666f3b3 Stavros Sachtouris
            self.set_header('Content-Type', 'application/json')
100 6666f3b3 Stavros Sachtouris
            self.set_header('Content-Length', len(data))
101 6666f3b3 Stavros Sachtouris
102 6666f3b3 Stavros Sachtouris
        path = path4url('servers', server_id, command)
103 6666f3b3 Stavros Sachtouris
        success = kwargs.pop('success', 204)
104 6666f3b3 Stavros Sachtouris
        return self.put(path, data=data, success=success, **kwargs)
105 6666f3b3 Stavros Sachtouris
106 a1c50326 Giorgos Verigakis
    def list_servers(self, detail=False):
107 a1c50326 Giorgos Verigakis
        """List servers, returned detailed output if detailed is True"""
108 6666f3b3 Stavros Sachtouris
        detail = 'detail' if detail else ''
109 6666f3b3 Stavros Sachtouris
        r = self.servers_get(command=detail)
110 6a0b1658 Giorgos Verigakis
        return r.json['servers']['values']
111 3dabe5d2 Stavros Sachtouris
112 c92d159d Stavros Sachtouris
    def get_server_details(self, server_id, **kwargs):
113 a1c50326 Giorgos Verigakis
        """Return detailed output on a server specified by its id"""
114 c92d159d Stavros Sachtouris
        r = self.servers_get(server_id, **kwargs)
115 6a0b1658 Giorgos Verigakis
        return r.json['server']
116 3dabe5d2 Stavros Sachtouris
117 a1c50326 Giorgos Verigakis
    def create_server(self, name, flavor_id, image_id, personality=None):
118 a1c50326 Giorgos Verigakis
        """Submit request to create a new server
119 a1c50326 Giorgos Verigakis

120 a1c50326 Giorgos Verigakis
        The flavor_id specifies the hardware configuration to use,
121 a1c50326 Giorgos Verigakis
        the image_id specifies the OS Image to be deployed inside the new
122 a1c50326 Giorgos Verigakis
        server.
123 a1c50326 Giorgos Verigakis

124 a1c50326 Giorgos Verigakis
        The personality argument is a list of (file path, file contents)
125 a1c50326 Giorgos Verigakis
        tuples, describing files to be injected into the server upon creation.
126 a1c50326 Giorgos Verigakis

127 a1c50326 Giorgos Verigakis
        The call returns a dictionary describing the newly created server.
128 a1c50326 Giorgos Verigakis
        """
129 6a0b1658 Giorgos Verigakis
        req = {'server': {'name': name,
130 6a0b1658 Giorgos Verigakis
                          'flavorRef': flavor_id,
131 6a0b1658 Giorgos Verigakis
                          'imageRef': image_id}}
132 ae3c77f9 Stavros Sachtouris
133 ae3c77f9 Stavros Sachtouris
        image = self.get_image_details(image_id)
134 ae3c77f9 Stavros Sachtouris
        img_meta = image['metadata']['values']
135 ae3c77f9 Stavros Sachtouris
        metadata = {}
136 ae3c77f9 Stavros Sachtouris
        for key in ('os', 'users'):
137 ae3c77f9 Stavros Sachtouris
            try:
138 ae3c77f9 Stavros Sachtouris
                metadata[key] = img_meta[key]
139 ae3c77f9 Stavros Sachtouris
            except KeyError:
140 ae3c77f9 Stavros Sachtouris
                pass
141 ae3c77f9 Stavros Sachtouris
        if metadata:
142 ae3c77f9 Stavros Sachtouris
            req['server']['metadata'] = metadata
143 ae3c77f9 Stavros Sachtouris
144 a1c50326 Giorgos Verigakis
        if personality:
145 c4922a05 Giorgos Verigakis
            req['server']['personality'] = personality
146 3dabe5d2 Stavros Sachtouris
147 486e55f4 Stavros Sachtouris
        try:
148 486e55f4 Stavros Sachtouris
            r = self.servers_post(json_data=req)
149 486e55f4 Stavros Sachtouris
        except ClientError as err:
150 486e55f4 Stavros Sachtouris
            try:
151 3dabe5d2 Stavros Sachtouris
                tmp_err = err.details.split(',')
152 3dabe5d2 Stavros Sachtouris
                tmp_err = tmp_err[0].split(':')
153 3dabe5d2 Stavros Sachtouris
                tmp_err = tmp_err[2].split('"')
154 3dabe5d2 Stavros Sachtouris
                err.message = tmp_err[1]
155 486e55f4 Stavros Sachtouris
            finally:
156 486e55f4 Stavros Sachtouris
                raise err
157 6a0b1658 Giorgos Verigakis
        return r.json['server']
158 3dabe5d2 Stavros Sachtouris
159 a1c50326 Giorgos Verigakis
    def update_server_name(self, server_id, new_name):
160 a1c50326 Giorgos Verigakis
        """Update the name of the server as reported by the API.
161 a1c50326 Giorgos Verigakis

162 a1c50326 Giorgos Verigakis
        This call does not modify the hostname actually used by the server
163 a1c50326 Giorgos Verigakis
        internally.
164 a1c50326 Giorgos Verigakis
        """
165 6a0b1658 Giorgos Verigakis
        req = {'server': {'name': new_name}}
166 6ce9fc72 Stavros Sachtouris
        r = self.servers_put(server_id, json_data=req)
167 6ce9fc72 Stavros Sachtouris
        r.release()
168 3dabe5d2 Stavros Sachtouris
169 a1c50326 Giorgos Verigakis
    def delete_server(self, server_id):
170 a1c50326 Giorgos Verigakis
        """Submit a deletion request for a server specified by id"""
171 6ce9fc72 Stavros Sachtouris
        r = self.servers_delete(server_id)
172 6ce9fc72 Stavros Sachtouris
        r.release()
173 6ce9fc72 Stavros Sachtouris
174 a1c50326 Giorgos Verigakis
    def reboot_server(self, server_id, hard=False):
175 a1c50326 Giorgos Verigakis
        """Submit a reboot request for a server specified by id"""
176 6a0b1658 Giorgos Verigakis
        type = 'HARD' if hard else 'SOFT'
177 6a0b1658 Giorgos Verigakis
        req = {'reboot': {'type': type}}
178 6ce9fc72 Stavros Sachtouris
        r = self.servers_post(server_id, 'action', json_data=req)
179 6ce9fc72 Stavros Sachtouris
        r.release()
180 3dabe5d2 Stavros Sachtouris
181 6666f3b3 Stavros Sachtouris
    def get_server_metadata(self, server_id, key=''):
182 6666f3b3 Stavros Sachtouris
        command = path4url('meta', key)
183 6666f3b3 Stavros Sachtouris
        r = self.servers_get(server_id, command)
184 6666f3b3 Stavros Sachtouris
        return r.json['meta'] if key != '' else r.json['metadata']['values']
185 3dabe5d2 Stavros Sachtouris
186 a1c50326 Giorgos Verigakis
    def create_server_metadata(self, server_id, key, val):
187 6a0b1658 Giorgos Verigakis
        req = {'meta': {key: val}}
188 3dabe5d2 Stavros Sachtouris
        r = self.servers_put(server_id,
189 3dabe5d2 Stavros Sachtouris
            'meta/' + key,
190 3dabe5d2 Stavros Sachtouris
            json_data=req,
191 3dabe5d2 Stavros Sachtouris
            success=201)
192 6a0b1658 Giorgos Verigakis
        return r.json['meta']
193 3dabe5d2 Stavros Sachtouris
194 a1c50326 Giorgos Verigakis
    def update_server_metadata(self, server_id, **metadata):
195 6a0b1658 Giorgos Verigakis
        req = {'metadata': metadata}
196 6666f3b3 Stavros Sachtouris
        r = self.servers_post(server_id, 'meta', json_data=req, success=201)
197 6a0b1658 Giorgos Verigakis
        return r.json['metadata']
198 3dabe5d2 Stavros Sachtouris
199 a1c50326 Giorgos Verigakis
    def delete_server_metadata(self, server_id, key):
200 3dabe5d2 Stavros Sachtouris
        r = self.servers_delete(server_id, 'meta/' + key)
201 6ce9fc72 Stavros Sachtouris
        r.release()
202 6666f3b3 Stavros Sachtouris
203 6666f3b3 Stavros Sachtouris
    def flavors_get(self, flavor_id='', command='', **kwargs):
204 6666f3b3 Stavros Sachtouris
        """GET base_url[/flavor_id][/command]
205 6666f3b3 Stavros Sachtouris
        @param flavor_id
206 6666f3b3 Stavros Sachtouris
        @param command
207 6666f3b3 Stavros Sachtouris
        """
208 6666f3b3 Stavros Sachtouris
        path = path4url('flavors', flavor_id, command)
209 3dabe5d2 Stavros Sachtouris
        success = kwargs.pop('success', 200)
210 14af08c0 Stavros Sachtouris
        return self.get(path, success=success, **kwargs)
211 6666f3b3 Stavros Sachtouris
212 a1c50326 Giorgos Verigakis
    def list_flavors(self, detail=False):
213 6666f3b3 Stavros Sachtouris
        detail = 'detail' if detail else ''
214 9bdc89da Stavros Sachtouris
        r = self.flavors_get(command='detail')
215 6a0b1658 Giorgos Verigakis
        return r.json['flavors']['values']
216 a1c50326 Giorgos Verigakis
217 a1c50326 Giorgos Verigakis
    def get_flavor_details(self, flavor_id):
218 6666f3b3 Stavros Sachtouris
        r = self.flavors_get(flavor_id)
219 6a0b1658 Giorgos Verigakis
        return r.json['flavor']
220 6666f3b3 Stavros Sachtouris
221 6666f3b3 Stavros Sachtouris
    def images_get(self, image_id='', command='', **kwargs):
222 6666f3b3 Stavros Sachtouris
        """GET base_url[/image_id][/command]
223 6666f3b3 Stavros Sachtouris
        @param image_id
224 6666f3b3 Stavros Sachtouris
        @param command
225 6666f3b3 Stavros Sachtouris
        """
226 6666f3b3 Stavros Sachtouris
        path = path4url('images', image_id, command)
227 3dabe5d2 Stavros Sachtouris
        success = kwargs.pop('success', 200)
228 14af08c0 Stavros Sachtouris
        return self.get(path, success=success, **kwargs)
229 6666f3b3 Stavros Sachtouris
230 6666f3b3 Stavros Sachtouris
    def images_delete(self, image_id='', command='', **kwargs):
231 6666f3b3 Stavros Sachtouris
        """DEL ETE base_url[/image_id][/command]
232 6666f3b3 Stavros Sachtouris
        @param image_id
233 6666f3b3 Stavros Sachtouris
        @param command
234 6666f3b3 Stavros Sachtouris
        """
235 6666f3b3 Stavros Sachtouris
        path = path4url('images', image_id, command)
236 3dabe5d2 Stavros Sachtouris
        success = kwargs.pop('success', 204)
237 14af08c0 Stavros Sachtouris
        return self.delete(path, success=success, **kwargs)
238 6666f3b3 Stavros Sachtouris
239 6666f3b3 Stavros Sachtouris
    def images_post(self, image_id='', command='', json_data=None, **kwargs):
240 6666f3b3 Stavros Sachtouris
        """POST base_url/images[/image_id]/[command] request
241 6666f3b3 Stavros Sachtouris
        @param image_id or ''
242 6666f3b3 Stavros Sachtouris
        @param command: can be 'action' or ''
243 6666f3b3 Stavros Sachtouris
        @param json_data: a json valid dict that will be send as data
244 6666f3b3 Stavros Sachtouris
        """
245 6666f3b3 Stavros Sachtouris
        data = json_data
246 6666f3b3 Stavros Sachtouris
        if json_data is not None:
247 6666f3b3 Stavros Sachtouris
            data = json.dumps(json_data)
248 6666f3b3 Stavros Sachtouris
            self.set_header('Content-Type', 'application/json')
249 6666f3b3 Stavros Sachtouris
            self.set_header('Content-Length', len(data))
250 6666f3b3 Stavros Sachtouris
251 6666f3b3 Stavros Sachtouris
        path = path4url('images', image_id, command)
252 6666f3b3 Stavros Sachtouris
        success = kwargs.pop('success', 201)
253 6666f3b3 Stavros Sachtouris
        return self.post(path, data=data, success=success, **kwargs)
254 6666f3b3 Stavros Sachtouris
255 6666f3b3 Stavros Sachtouris
    def images_put(self, image_id='', command='', json_data=None, **kwargs):
256 6666f3b3 Stavros Sachtouris
        """PUT base_url/images[/image_id]/[command] request
257 6666f3b3 Stavros Sachtouris
        @param image_id or ''
258 6666f3b3 Stavros Sachtouris
        @param command: can be 'action' or ''
259 6666f3b3 Stavros Sachtouris
        @param json_data: a json valid dict that will be send as data
260 6666f3b3 Stavros Sachtouris
        """
261 6666f3b3 Stavros Sachtouris
        data = json_data
262 6666f3b3 Stavros Sachtouris
        if json_data is not None:
263 6666f3b3 Stavros Sachtouris
            data = json.dumps(json_data)
264 6666f3b3 Stavros Sachtouris
            self.set_header('Content-Type', 'application/json')
265 6666f3b3 Stavros Sachtouris
            self.set_header('Content-Length', len(data))
266 6666f3b3 Stavros Sachtouris
267 6666f3b3 Stavros Sachtouris
        path = path4url('images', image_id, command)
268 6666f3b3 Stavros Sachtouris
        success = kwargs.pop('success', 201)
269 6666f3b3 Stavros Sachtouris
        return self.put(path, data=data, success=success, **kwargs)
270 6666f3b3 Stavros Sachtouris
271 a1c50326 Giorgos Verigakis
    def list_images(self, detail=False):
272 14af08c0 Stavros Sachtouris
        detail = 'detail' if detail else ''
273 6666f3b3 Stavros Sachtouris
        r = self.images_get(command=detail)
274 6a0b1658 Giorgos Verigakis
        return r.json['images']['values']
275 3dabe5d2 Stavros Sachtouris
276 71286858 Stavros Sachtouris
    def get_image_details(self, image_id, **kwargs):
277 71286858 Stavros Sachtouris
        r = self.images_get(image_id, **kwargs)
278 71286858 Stavros Sachtouris
        try:
279 71286858 Stavros Sachtouris
            return r.json['image']
280 71286858 Stavros Sachtouris
        except KeyError:
281 71286858 Stavros Sachtouris
            raise ClientError('Image not available', 404,
282 71286858 Stavros Sachtouris
                details='Image %d not found or not accessible')
283 3dabe5d2 Stavros Sachtouris
284 a1c50326 Giorgos Verigakis
    def delete_image(self, image_id):
285 6ce9fc72 Stavros Sachtouris
        r = self.images_delete(image_id)
286 6ce9fc72 Stavros Sachtouris
        r.release()
287 6666f3b3 Stavros Sachtouris
288 6666f3b3 Stavros Sachtouris
    def get_image_metadata(self, image_id, key=''):
289 6666f3b3 Stavros Sachtouris
        command = path4url('meta', key)
290 6666f3b3 Stavros Sachtouris
        r = self.images_get(image_id, command)
291 6666f3b3 Stavros Sachtouris
        return r.json['meta'] if key != '' else r.json['metadata']['values']
292 3dabe5d2 Stavros Sachtouris
293 a1c50326 Giorgos Verigakis
    def create_image_metadata(self, image_id, key, val):
294 6a0b1658 Giorgos Verigakis
        req = {'meta': {key: val}}
295 3dabe5d2 Stavros Sachtouris
        r = self.images_put(image_id, 'meta/' + key, json_data=req)
296 6a0b1658 Giorgos Verigakis
        return r.json['meta']
297 a1c50326 Giorgos Verigakis
298 a1c50326 Giorgos Verigakis
    def update_image_metadata(self, image_id, **metadata):
299 6a0b1658 Giorgos Verigakis
        req = {'metadata': metadata}
300 3dabe5d2 Stavros Sachtouris
        r = self.images_post(image_id, 'meta', json_data=req)
301 6a0b1658 Giorgos Verigakis
        return r.json['metadata']
302 a1c50326 Giorgos Verigakis
303 a1c50326 Giorgos Verigakis
    def delete_image_metadata(self, image_id, key):
304 6666f3b3 Stavros Sachtouris
        command = path4url('meta', key)
305 6ce9fc72 Stavros Sachtouris
        r = self.images_delete(image_id, command)
306 6ce9fc72 Stavros Sachtouris
        r.release()