Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / __init__.py @ 2bd23362

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
from kamaki.clients.utils import path4url
37

    
38

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

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

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

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

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

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

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

    
110
    def create_server(
111
            self, name, flavor_id, image_id,
112
            security_group=None,
113
            user_data=None,
114
            availability_zone=None,
115
            metadata=None,
116
            personality=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
        r = self.servers_post(
145
            json_data=req,
146
            security_group=security_group,
147
            user_data=user_data,
148
            availability_zone=availability_zone)
149
        for k, v in response_headers.items():
150
            response_headers[k] = r.headers.get(k, v)
151
        return r.json['server']
152

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

157
        :param server_id: integer (str or int)
158

159
        :param new_name: (str)
160

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

    
167
    def delete_server(self, server_id):
168
        """Submit a deletion request for a server specified by id
169

170
        :param server_id: integer (str or int)
171

172
        :returns: (dict) response headers
173
        """
174
        r = self.servers_delete(server_id)
175
        return r.headers
176

    
177
    def change_admin_password(self, server_id, new_password):
178
        """
179
        :param server_id: (int)
180

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

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

    
196
    def reboot_server(self, server_id, hard=False):
197
        """
198
        :param server_id: integer (str or int)
199

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

    
206
    def resize_server(self, server_id, flavor_id):
207
        """
208
        :param server_id: (str)
209

210
        :param flavor_id: (int)
211

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

    
218
    def confirm_resize_server(self, server_id):
219
        """OS"""
220
        r = self.servers_action_post(
221
            server_id, json_data=dict(confirmResize=None))
222
        return r.headers
223

    
224
    def revert_resize_server(self, server_id):
225
        """OS"""
226
        r = self.servers_action_post(
227
            server_id, json_data=dict(revertResize=None))
228
        return r.headers
229

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

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

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

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

253
        :param key: (str) the metadatum key (all metadata if not given)
254

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

    
262
    def create_server_metadata(self, server_id, key, val):
263
        """
264
        :param server_id: integer (str or int)
265

266
        :param key: (str)
267

268
        :param val: (str)
269

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

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

283
        :param metadata: dict of key:val metadata
284

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

    
293
    def delete_server_metadata(self, server_id, key):
294
        """
295
        :param server_id: integer (str or int)
296

297
        :param key: (str) the meta key
298

299
        :returns: (dict) response headers
300
        """
301
        r = self.servers_metadata_delete(server_id, key)
302
        return r.headers
303

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

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

    
316
    def get_flavor_details(self, flavor_id):
317
        """
318
        :param flavor_id: integer (str or int)
319

320
        :returns: dict
321
        """
322
        r = self.flavors_get(flavor_id)
323
        return r.json['flavor']
324

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

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

    
337
    def get_image_details(self, image_id, **kwargs):
338
        """
339
        :param image_id: integer (str or int)
340

341
        :returns: dict
342

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

    
352
    def delete_image(self, image_id):
353
        """
354
        :param image_id: (str)
355
        """
356
        r = self.images_delete(image_id)
357
        return r.headers
358

    
359
    def get_image_metadata(self, image_id, key='', response_headers=dict(
360
            previous=None, next=None)):
361
        """
362
        :param image_id: (str)
363

364
        :param key: (str) the metadatum key
365

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

    
373
    # def create_image_metadata(self, image_id, key, val):
374
    #     """
375
    #     :param image_id: integer (str or int)
376

    
377
    #     :param key: (str) metadatum key
378

    
379
    #     :param val: (str) metadatum value
380

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

    
387
    def update_image_metadata(
388
            self, image_id,
389
            response_headers=dict(previous=None, next=None), **metadata):
390
        """
391
        :param image_id: (str)
392

393
        :param metadata: dict
394

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

    
403
    def delete_image_metadata(self, image_id, key):
404
        """
405
        :param image_id: (str)
406

407
        :param key: (str) metadatum key
408

409
        :returns: (dict) response headers
410
        """
411
        r = self.images_metadata_delete(image_id, key)
412
        return r.headers
413

    
414
    def get_floating_ip_pools(self, tenant_id):
415
        """
416
        :param tenant_id: (str)
417

418
        :returns: (dict) {floating_ip_pools:[{name: ...}, ...]}
419
        """
420
        r = self.floating_ip_pools_get(tenant_id)
421
        return r.json
422

    
423
    def get_floating_ips(self, tenant_id):
424
        """
425
        :param tenant_id: (str)
426

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

    
434
    def alloc_floating_ip(self, tenant_id, pool=None):
435
        """
436
        :param tenant_id: (str)
437

438
        :param pool: (str) pool of ips to allocate from
439

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

    
446
    def get_floating_ip(self, tenant_id, fip_id=None):
447
        """
448
        :param tenant_id: (str)
449

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

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

    
459
    def delete_floating_ip(self, tenant_id, fip_id=None):
460
        """
461
        :param tenant_id: (str)
462

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

465
        :returns: (dict) request headers
466
        """
467
        r = self.floating_ips_delete(tenant_id, fip_id)
468
        return r.headers