Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / __init__.py @ 7749f75a

History | View | Annotate | Download (10.8 kB)

1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from kamaki.clients import ClientError
35
from kamaki.clients.compute.rest_api import ComputeRestClient
36
from kamaki.clients.utils import path4url
37

    
38

    
39
class ComputeClient(ComputeRestClient):
40
    """OpenStack Compute API 1.1 client"""
41

    
42
    def list_servers(self, detail=False):
43
        """
44
        :param detail: if true, append full server details to each item
45

46
        :returns: list of server ids and names
47
        """
48
        detail = 'detail' if detail else ''
49
        r = self.servers_get(command=detail)
50
        return r.json['servers']
51

    
52
    def get_server_details(self, server_id, **kwargs):
53
        """Return detailed info for a server
54

55
        :param server_id: integer (int or str)
56

57
        :returns: dict with server details
58
        """
59
        r = self.servers_get(server_id, **kwargs)
60
        return r.json['server']
61

    
62
    def create_server(self, name, flavor_id, image_id, personality=None):
63
        """Submit request to create a new server
64

65
        :param name: (str)
66

67
        :param flavor_id: integer id denoting a preset hardware configuration
68

69
        :param image_id: (str) id denoting the OS image to run on the VM
70

71
        :param personality: a list of (file path, file contents) tuples,
72
            describing files to be injected into VM upon creation.
73

74
        :returns: a dict with the new VMs details
75

76
        :raises ClientError: wraps request errors
77
        """
78
        req = {'server': {'name': name,
79
                          'flavorRef': flavor_id,
80
                          'imageRef': image_id}}
81

    
82
        image = self.get_image_details(image_id)
83
        metadata = {}
84
        for key in ('os', 'users'):
85
            try:
86
                metadata[key] = image['metadata'][key]
87
            except KeyError:
88
                pass
89
        if metadata:
90
            req['server']['metadata'] = metadata
91

    
92
        if personality:
93
            req['server']['personality'] = personality
94

    
95
        try:
96
            r = self.servers_post(json_data=req)
97
        except ClientError as err:
98
            try:
99
                if isinstance(err.details, list):
100
                    tmp_err = err.details
101
                else:
102
                    errd = '%s' % err.details
103
                    tmp_err = errd.split(',')
104
                tmp_err = tmp_err[0].split(':')
105
                tmp_err = tmp_err[2].split('"')
106
                err.message = tmp_err[1]
107
            finally:
108
                raise err
109
        return r.json['server']
110

    
111
    def update_server_name(self, server_id, new_name):
112
        """Update the name of the server as reported by the API (does not
113
            modify the hostname used inside the VM)
114

115
        :param server_id: integer (str or int)
116

117
        :param new_name: (str)
118

119
        :returns: (dict) response headers
120
        """
121
        req = {'server': {'name': new_name}}
122
        r = self.servers_put(server_id, json_data=req)
123
        return r.headers
124

    
125
    def delete_server(self, server_id):
126
        """Submit a deletion request for a server specified by id
127

128
        :param server_id: integer (str or int)
129

130
        :returns: (dict) response headers
131
        """
132
        r = self.servers_delete(server_id)
133
        return r.headers
134

    
135
    def reboot_server(self, server_id, hard=False):
136
        """
137
        :param server_id: integer (str or int)
138

139
        :param hard: perform a hard reboot if true, soft reboot otherwise
140
        """
141
        boot_type = 'HARD' if hard else 'SOFT'
142
        req = {'reboot': {'type': boot_type}}
143
        r = self.servers_post(server_id, 'action', json_data=req)
144
        return r.headers
145

    
146
    def get_server_metadata(self, server_id, key=''):
147
        """
148
        :param server_id: integer (str or int)
149

150
        :param key: (str) the metadatum key (all metadata if not given)
151

152
        :returns: a key:val dict of requests metadata
153
        """
154
        command = path4url('metadata', key)
155
        r = self.servers_get(server_id, command)
156
        return r.json['meta' if key else 'metadata']
157

    
158
    def create_server_metadata(self, server_id, key, val):
159
        """
160
        :param server_id: integer (str or int)
161

162
        :param key: (str)
163

164
        :param val: (str)
165

166
        :returns: dict of updated key:val metadata
167
        """
168
        req = {'meta': {key: val}}
169
        r = self.servers_put(
170
            server_id, 'metadata/' + key, json_data=req, success=201)
171
        return r.json['meta']
172

    
173
    def update_server_metadata(self, server_id, **metadata):
174
        """
175
        :param server_id: integer (str or int)
176

177
        :param metadata: dict of key:val metadata
178

179
        :returns: dict of updated key:val metadata
180
        """
181
        req = {'metadata': metadata}
182
        r = self.servers_post(
183
            server_id, 'metadata', json_data=req, success=201)
184
        return r.json['metadata']
185

    
186
    def delete_server_metadata(self, server_id, key):
187
        """
188
        :param server_id: integer (str or int)
189

190
        :param key: (str) the meta key
191

192
        :returns: (dict) response headers
193
        """
194
        r = self.servers_delete(server_id, 'metadata/' + key)
195
        return r.headers
196

    
197
    def list_flavors(self, detail=False):
198
        """
199
        :param detail: (bool) detailed flavor info if set, short if not
200

201
        :returns: (list) flavor info
202
        """
203
        r = self.flavors_get(command='detail' if detail else '')
204
        return r.json['flavors']
205

    
206
    def get_flavor_details(self, flavor_id):
207
        """
208
        :param flavor_id: integer (str or int)
209

210
        :returns: dict
211
        """
212
        r = self.flavors_get(flavor_id)
213
        return r.json['flavor']
214

    
215
    def list_images(self, detail=False):
216
        """
217
        :param detail: (bool) detailed info if set, short if not
218

219
        :returns: dict id,name + full info if detail
220
        """
221
        detail = 'detail' if detail else ''
222
        r = self.images_get(command=detail)
223
        return r.json['images']
224

    
225
    def get_image_details(self, image_id, **kwargs):
226
        """
227
        :param image_id: integer (str or int)
228

229
        :returns: dict
230

231
        :raises ClientError: 404 if image not available
232
        """
233
        r = self.images_get(image_id, **kwargs)
234
        try:
235
            return r.json['image']
236
        except KeyError:
237
            raise ClientError('Image not available', 404, details=[
238
                'Image %d not found or not accessible'])
239

    
240
    def delete_image(self, image_id):
241
        """
242
        :param image_id: (str)
243
        """
244
        r = self.images_delete(image_id)
245
        return r.headers
246

    
247
    def get_image_metadata(self, image_id, key=''):
248
        """
249
        :param image_id: (str)
250

251
        :param key: (str) the metadatum key
252

253
        :returns (dict) metadata if key not set, specific metadatum otherwise
254
        """
255
        command = path4url('metadata', key)
256
        r = self.images_get(image_id, command)
257
        return r.json['meta' if key else 'metadata']
258

    
259
    def create_image_metadata(self, image_id, key, val):
260
        """
261
        :param image_id: integer (str or int)
262

263
        :param key: (str) metadatum key
264

265
        :param val: (str) metadatum value
266

267
        :returns: (dict) updated metadata
268
        """
269
        req = {'meta': {key: val}}
270
        r = self.images_put(image_id, 'metadata/' + key, json_data=req)
271
        return r.json['meta']
272

    
273
    def update_image_metadata(self, image_id, **metadata):
274
        """
275
        :param image_id: (str)
276

277
        :param metadata: dict
278

279
        :returns: updated metadata
280
        """
281
        req = {'metadata': metadata}
282
        r = self.images_post(image_id, 'metadata', json_data=req)
283
        return r.json['metadata']
284

    
285
    def delete_image_metadata(self, image_id, key):
286
        """
287
        :param image_id: (str)
288

289
        :param key: (str) metadatum key
290

291
        :returns: (dict) response headers
292
        """
293
        command = path4url('metadata', key)
294
        r = self.images_delete(image_id, command)
295
        return r.headers
296

    
297
    def get_floating_ip_pools(self, tenant_id):
298
        """
299
        :param tenant_id: (str)
300

301
        :returns: (dict) {floating_ip_pools:[{name: ...}, ...]}
302
        """
303
        r = self.floating_ip_pools_get(tenant_id)
304
        return r.json
305

    
306
    def get_floating_ips(self, tenant_id):
307
        """
308
        :param tenant_id: (str)
309

310
        :returns: (dict) {floating_ips:[
311
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
312
            ... ]}
313
        """
314
        r = self.floating_ips_get(tenant_id)
315
        return r.json
316

    
317
    def alloc_floating_ip(self, tenant_id, pool=None):
318
        """
319
        :param tenant_id: (str)
320

321
        :param pool: (str) pool of ips to allocate from
322

323
        :returns: (dict) {
324
                fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...
325
            }
326
        """
327
        json_data = dict(pool=pool) if pool else dict()
328
        r = self.floating_ips_post(tenant_id, json_data)
329
        return r.json['floating_ip']
330

    
331
    def get_floating_ip(self, tenant_id, fip_id=None):
332
        """
333
        :param tenant_id: (str)
334

335
        :param fip_id: (str) floating ip id (if None, all ips are returned)
336

337
        :returns: (list) [
338
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
339
            ... ]
340
        """
341
        r = self.floating_ips_get(tenant_id, fip_id)
342
        return r.json['floating_ips']
343

    
344
    def delete_floating_ip(self, tenant_id, fip_id=None):
345
        """
346
        :param tenant_id: (str)
347

348
        :param fip_id: (str) floating ip id (if None, all ips are deleted)
349

350
        :returns: (dict) request headers
351
        """
352
        r = self.floating_ips_delete(tenant_id, fip_id)
353
        return r.headers