Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / __init__.py @ 264a13f7

History | View | Annotate | Download (14.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

    
37

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

    
41
    def list_servers(
42
            self,
43
            detail=False,
44
            changes_since=None,
45
            image=None,
46
            flavor=None,
47
            name=None,
48
            marker=None,
49
            limit=None,
50
            status=None,
51
            host=None,
52
            response_headers=dict(previous=None, next=None)):
53
        """
54
        :param detail: if true, append full server details to each item
55

56
        :param response_headers: (dict) use it to get previous/next responses
57
            Keep the existing dict format to actually get the server responses
58
            Use it with very long lists or with marker
59

60
        :returns: list of server ids and names
61
        """
62
        r = self.servers_get(
63
            detail=bool(detail),
64
            changes_since=changes_since,
65
            image=image,
66
            flavor=flavor,
67
            name=name,
68
            marker=marker,
69
            limit=limit,
70
            status=status,
71
            host=host)
72
        for k, v in response_headers.items():
73
            response_headers[k] = r.headers.get(k, v)
74
        return r.json['servers']
75

    
76
    def get_server_details(
77
            self, server_id,
78
            changes_since=None,
79
            image=None,
80
            flavor=None,
81
            name=None,
82
            marker=None,
83
            limit=None,
84
            status=None,
85
            host=None,
86
            response_headers=dict(previous=None, next=None),
87
            **kwargs):
88
        """Return detailed info for a server
89

90
        :param server_id: integer (int or str)
91

92
        :returns: dict with server details
93
        """
94
        r = self.servers_get(
95
            server_id,
96
            changes_since=changes_since,
97
            image=image,
98
            flavor=flavor,
99
            name=name,
100
            marker=marker,
101
            limit=limit,
102
            status=status,
103
            host=host,
104
            **kwargs)
105
        for k, v in response_headers.items():
106
            response_headers[k] = r.headers.get(k, v)
107
        return r.json['server']
108

    
109
    def create_server(
110
            self, name, flavor_id, image_id,
111
            security_group=None,
112
            user_data=None,
113
            availability_zone=None,
114
            metadata=None,
115
            personality=None,
116
            networks=None,
117
            response_headers=dict(location=None)):
118
        """Submit request to create a new server
119

120
        :param name: (str)
121

122
        :param flavor_id: integer id denoting a preset hardware configuration
123

124
        :param image_id: (str) id of the image of the virtual server
125

126
        :param metadata: (dict) vm metadata
127

128
        :param personality: a list of (file path, file contents) tuples,
129
            describing files to be injected into virtual server upon creation
130

131
        :returns: a dict with the new virtual server details
132

133
        :raises ClientError: wraps request errors
134
        """
135
        req = {'server': {
136
            'name': name, 'flavorRef': flavor_id, 'imageRef': image_id}}
137

    
138
        if metadata:
139
            req['server']['metadata'] = metadata
140

    
141
        if personality:
142
            req['server']['personality'] = personality
143

    
144
        if networks:
145
            req['server']['networks'] = networks
146

    
147
        r = self.servers_post(
148
            json_data=req,
149
            security_group=security_group,
150
            user_data=user_data,
151
            availability_zone=availability_zone)
152
        for k, v in response_headers.items():
153
            response_headers[k] = r.headers.get(k, v)
154
        return r.json['server']
155

    
156
    def update_server_name(self, server_id, new_name):
157
        """Update the name of the server as reported by the API (does not
158
            modify the hostname used inside the virtual server)
159

160
        :param server_id: integer (str or int)
161

162
        :param new_name: (str)
163

164
        :returns: (dict) response headers
165
        """
166
        req = {'server': {'name': new_name}}
167
        r = self.servers_put(server_id, json_data=req)
168
        return r.headers
169

    
170
    def delete_server(self, server_id):
171
        """Submit a deletion request for a server specified by id
172

173
        :param server_id: integer (str or int)
174

175
        :returns: (dict) response headers
176
        """
177
        r = self.servers_delete(server_id)
178
        return r.headers
179

    
180
    def change_admin_password(self, server_id, new_password):
181
        """
182
        :param server_id: (int)
183

184
        :param new_password: (str)
185
        """
186
        req = {"changePassword": {"adminPass": new_password}}
187
        r = self.servers_action_post(server_id, json_data=req)
188
        return r.headers
189

    
190
    def rebuild_server(self, server_id, response_headers=dict(location=None)):
191
        """OS"""
192
        server = self.get_server_details(server_id)
193
        r = self.servers_action_post(
194
            server_id, json_data=dict(rebuild=server['server']))
195
        for k, v in response_headers.items():
196
            response_headers[k] = r.headers.get(k, v)
197
        return r.json['server']
198

    
199
    def reboot_server(self, server_id, hard=False):
200
        """
201
        :param server_id: integer (str or int)
202

203
        :param hard: perform a hard reboot if true, soft reboot otherwise
204
        """
205
        req = {'reboot': {'type': 'HARD' if hard else 'SOFT'}}
206
        r = self.servers_action_post(server_id, json_data=req)
207
        return r.headers
208

    
209
    def resize_server(self, server_id, flavor_id):
210
        """
211
        :param server_id: (str)
212

213
        :param flavor_id: (int)
214

215
        :returns: (dict) request headers
216
        """
217
        req = {'resize': {'flavorRef': flavor_id}}
218
        r = self.servers_action_post(server_id, json_data=req)
219
        return r.headers
220

    
221
    def confirm_resize_server(self, server_id):
222
        """OS"""
223
        r = self.servers_action_post(
224
            server_id, json_data=dict(confirmResize=None))
225
        return r.headers
226

    
227
    def revert_resize_server(self, server_id):
228
        """OS"""
229
        r = self.servers_action_post(
230
            server_id, json_data=dict(revertResize=None))
231
        return r.headers
232

    
233
    def create_server_image(self, server_id, image_name, **metadata):
234
        """OpenStack method for taking snapshots"""
235
        req = dict(createImage=dict(name=image_name, metadata=metadata))
236
        r = self.servers_action_post(server_id, json_data=req)
237
        return r.headers['location']
238

    
239
    def start_server(self, server_id):
240
        """OS Extentions"""
241
        req = {'os-start': None}
242
        r = self.servers_action_post(server_id, json_data=req, success=202)
243
        return r.headers
244

    
245
    def shutdown_server(self, server_id):
246
        """OS Extentions"""
247
        req = {'os-stop': None}
248
        r = self.servers_action_post(server_id, json_data=req, success=202)
249
        return r.headers
250

    
251
    def get_server_metadata(self, server_id, key='', response_headers=dict(
252
            previous=None, next=None)):
253
        """
254
        :param server_id: integer (str or int)
255

256
        :param key: (str) the metadatum key (all metadata if not given)
257

258
        :returns: a key:val dict of requests metadata
259
        """
260
        r = self.servers_metadata_get(server_id, key)
261
        for k, v in response_headers.items():
262
            response_headers[k] = r.headers.get(k, v)
263
        return r.json['meta' if key else 'metadata']
264

    
265
    def create_server_metadata(self, server_id, key, val):
266
        """
267
        :param server_id: integer (str or int)
268

269
        :param key: (str)
270

271
        :param val: (str)
272

273
        :returns: dict of updated key:val metadata
274
        """
275
        req = {'meta': {key: val}}
276
        r = self.servers_metadata_put(
277
            server_id, key, json_data=req, success=201)
278
        return r.json['meta']
279

    
280
    def update_server_metadata(
281
            self, server_id,
282
            response_headers=dict(previous=None, next=None), **metadata):
283
        """
284
        :param server_id: integer (str or int)
285

286
        :param metadata: dict of key:val metadata
287

288
        :returns: dict of updated key:val metadata
289
        """
290
        req = {'metadata': metadata}
291
        r = self.servers_metadata_post(server_id, json_data=req, success=201)
292
        for k, v in response_headers.items():
293
            response_headers[k] = r.headers.get(k, v)
294
        return r.json['metadata']
295

    
296
    def delete_server_metadata(self, server_id, key):
297
        """
298
        :param server_id: integer (str or int)
299

300
        :param key: (str) the meta key
301

302
        :returns: (dict) response headers
303
        """
304
        r = self.servers_metadata_delete(server_id, key)
305
        return r.headers
306

    
307
    def list_flavors(self, detail=False, response_headers=dict(
308
            previous=None, next=None)):
309
        """
310
        :param detail: (bool) detailed flavor info if set, short if not
311

312
        :returns: (list) flavor info
313
        """
314
        r = self.flavors_get(detail=bool(detail))
315
        for k, v in response_headers.items():
316
            response_headers[k] = r.headers.get(k, v)
317
        return r.json['flavors']
318

    
319
    def get_flavor_details(self, flavor_id):
320
        """
321
        :param flavor_id: integer (str or int)
322

323
        :returns: dict
324
        """
325
        r = self.flavors_get(flavor_id)
326
        return r.json['flavor']
327

    
328
    def list_images(self, detail=False, response_headers=dict(
329
            next=None, previous=None)):
330
        """
331
        :param detail: (bool) detailed info if set, short if not
332

333
        :returns: dict id,name + full info if detail
334
        """
335
        r = self.images_get(detail=bool(detail))
336
        for k, v in response_headers.items():
337
            response_headers[k] = r.headers.get(k, v)
338
        return r.json['images']
339

    
340
    def get_image_details(self, image_id, **kwargs):
341
        """
342
        :param image_id: integer (str or int)
343

344
        :returns: dict
345

346
        :raises ClientError: 404 if image not available
347
        """
348
        r = self.images_get(image_id, **kwargs)
349
        try:
350
            return r.json['image']
351
        except KeyError:
352
            raise ClientError('Image not available', 404, details=[
353
                'Image %d not found or not accessible'])
354

    
355
    def delete_image(self, image_id):
356
        """
357
        :param image_id: (str)
358
        """
359
        r = self.images_delete(image_id)
360
        return r.headers
361

    
362
    def get_image_metadata(self, image_id, key='', response_headers=dict(
363
            previous=None, next=None)):
364
        """
365
        :param image_id: (str)
366

367
        :param key: (str) the metadatum key
368

369
        :returns (dict) metadata if key not set, specific metadatum otherwise
370
        """
371
        r = self.images_metadata_get(image_id, key)
372
        for k, v in response_headers.items():
373
            response_headers[k] = r.headers.get(k, v)
374
        return r.json['meta' if key else 'metadata']
375

    
376
    # def create_image_metadata(self, image_id, key, val):
377
    #     """
378
    #     :param image_id: integer (str or int)
379

    
380
    #     :param key: (str) metadatum key
381

    
382
    #     :param val: (str) metadatum value
383

    
384
    #     :returns: (dict) updated metadata
385
    #     """
386
    #     req = {'meta': {key: val}}
387
    #     r = self.images_metadata_put(image_id, key, json_data=req)
388
    #     return r.json['meta']
389

    
390
    def update_image_metadata(
391
            self, image_id,
392
            response_headers=dict(previous=None, next=None), **metadata):
393
        """
394
        :param image_id: (str)
395

396
        :param metadata: dict
397

398
        :returns: updated metadata
399
        """
400
        req = {'metadata': metadata}
401
        r = self.images_metadata_post(image_id, json_data=req)
402
        for k, v in response_headers.items():
403
            response_headers[k] = r.headers.get(k, v)
404
        return r.json['metadata']
405

    
406
    def delete_image_metadata(self, image_id, key):
407
        """
408
        :param image_id: (str)
409

410
        :param key: (str) metadatum key
411

412
        :returns: (dict) response headers
413
        """
414
        r = self.images_metadata_delete(image_id, key)
415
        return r.headers
416

    
417
    def get_floating_ip_pools(self, tenant_id):
418
        """
419
        :param tenant_id: (str)
420

421
        :returns: (dict) {floating_ip_pools:[{name: ...}, ...]}
422
        """
423
        r = self.floating_ip_pools_get(tenant_id)
424
        return r.json
425

    
426
    def get_floating_ips(self, tenant_id):
427
        """
428
        :param tenant_id: (str)
429

430
        :returns: (dict) {floating_ips:[
431
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
432
            ... ]}
433
        """
434
        r = self.floating_ips_get(tenant_id)
435
        return r.json
436

    
437
    def alloc_floating_ip(self, tenant_id, pool=None):
438
        """
439
        :param tenant_id: (str)
440

441
        :param pool: (str) pool of ips to allocate from
442

443
        :returns: (dict) {fixed_ip: . id: . instance_id: . ip: . pool: .}
444
        """
445
        json_data = dict(pool=pool) if pool else dict()
446
        r = self.floating_ips_post(tenant_id, json_data)
447
        return r.json['floating_ip']
448

    
449
    def get_floating_ip(self, tenant_id, fip_id=None):
450
        """
451
        :param tenant_id: (str)
452

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

455
        :returns: (list) [
456
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
457
            ... ]
458
        """
459
        r = self.floating_ips_get(tenant_id, fip_id)
460
        return r.json['floating_ips']
461

    
462
    def delete_floating_ip(self, tenant_id, fip_id=None):
463
        """
464
        :param tenant_id: (str)
465

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

468
        :returns: (dict) request headers
469
        """
470
        r = self.floating_ips_delete(tenant_id, fip_id)
471
        return r.headers