Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / __init__.py @ 75ae8a08

History | View | Annotate | Download (14.5 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
            project=None,
118
            response_headers=dict(location=None)):
119
        """Submit request to create a new server
120

121
        :param name: (str)
122

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

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

127
        :param metadata: (dict) vm metadata
128

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

132
        :param networks: (list of dicts) Networks to connect to, list this:
133
            [
134
            {"uuid": <network_uuid>},
135
            {"uuid": <network_uuid>, "fixed_ip": address},
136
            {"port": <port_id>}, ...]
137
            ATTENTION: Empty list is different to None.
138

139
        :returns: a dict with the new virtual server details
140

141
        :raises ClientError: wraps request errors
142
        """
143
        req = {'server': {
144
            'name': name, 'flavorRef': flavor_id, 'imageRef': image_id}}
145

    
146
        if metadata:
147
            req['server']['metadata'] = metadata
148

    
149
        if personality:
150
            req['server']['personality'] = personality
151

    
152
        if networks is not None:
153
            req['server']['networks'] = networks
154

    
155
        if project:
156
            req['server']['project'] = project
157

    
158
        r = self.servers_post(
159
            json_data=req,
160
            security_group=security_group,
161
            user_data=user_data,
162
            availability_zone=availability_zone)
163
        for k, v in response_headers.items():
164
            response_headers[k] = r.headers.get(k, v)
165
        return r.json['server']
166

    
167
    def update_server_name(self, server_id, new_name):
168
        """Update the name of the server as reported by the API (does not
169
            modify the hostname used inside the virtual server)
170

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

173
        :param new_name: (str)
174

175
        :returns: (dict) response headers
176
        """
177
        req = {'server': {'name': new_name}}
178
        r = self.servers_put(server_id, json_data=req)
179
        return r.headers
180

    
181
    def delete_server(self, server_id):
182
        """Submit a deletion request for a server specified by id
183

184
        :param server_id: integer (str or int)
185

186
        :returns: (dict) response headers
187
        """
188
        r = self.servers_delete(server_id)
189
        return r.headers
190

    
191
    def change_admin_password(self, server_id, new_password):
192
        """
193
        :param server_id: (int)
194

195
        :param new_password: (str)
196
        """
197
        req = {"changePassword": {"adminPass": new_password}}
198
        r = self.servers_action_post(server_id, json_data=req)
199
        return r.headers
200

    
201
    def rebuild_server(self, server_id, response_headers=dict(location=None)):
202
        """OS"""
203
        server = self.get_server_details(server_id)
204
        r = self.servers_action_post(
205
            server_id, json_data=dict(rebuild=server['server']))
206
        for k, v in response_headers.items():
207
            response_headers[k] = r.headers.get(k, v)
208
        return r.json['server']
209

    
210
    def reboot_server(self, server_id, hard=False):
211
        """
212
        :param server_id: integer (str or int)
213

214
        :param hard: perform a hard reboot if true, soft reboot otherwise
215
        """
216
        req = {'reboot': {'type': 'HARD' if hard else 'SOFT'}}
217
        r = self.servers_action_post(server_id, json_data=req)
218
        return r.headers
219

    
220
    def resize_server(self, server_id, flavor_id):
221
        """
222
        :param server_id: (str)
223

224
        :param flavor_id: (int)
225

226
        :returns: (dict) request headers
227
        """
228
        req = {'resize': {'flavorRef': flavor_id}}
229
        r = self.servers_action_post(server_id, json_data=req)
230
        return r.headers
231

    
232
    def confirm_resize_server(self, server_id):
233
        """OS"""
234
        r = self.servers_action_post(
235
            server_id, json_data=dict(confirmResize=None))
236
        return r.headers
237

    
238
    def revert_resize_server(self, server_id):
239
        """OS"""
240
        r = self.servers_action_post(
241
            server_id, json_data=dict(revertResize=None))
242
        return r.headers
243

    
244
    def create_server_image(self, server_id, image_name, **metadata):
245
        """OpenStack method for taking snapshots"""
246
        req = dict(createImage=dict(name=image_name, metadata=metadata))
247
        r = self.servers_action_post(server_id, json_data=req)
248
        return r.headers['location']
249

    
250
    def start_server(self, server_id):
251
        """OS Extentions"""
252
        req = {'os-start': None}
253
        r = self.servers_action_post(server_id, json_data=req, success=202)
254
        return r.headers
255

    
256
    def shutdown_server(self, server_id):
257
        """OS Extentions"""
258
        req = {'os-stop': None}
259
        r = self.servers_action_post(server_id, json_data=req, success=202)
260
        return r.headers
261

    
262
    def get_server_metadata(self, server_id, key='', response_headers=dict(
263
            previous=None, next=None)):
264
        r = self.servers_metadata_get(server_id, key)
265
        for k, v in response_headers.items():
266
            response_headers[k] = r.headers.get(k, v)
267
        return r.json['meta' if key else 'metadata']
268

    
269
    def create_server_metadata(self, server_id, key, val):
270
        req = {'meta': {key: val}}
271
        r = self.servers_metadata_put(
272
            server_id, key, json_data=req, success=201)
273
        return r.json['meta']
274

    
275
    def update_server_metadata(
276
            self, server_id,
277
            response_headers=dict(previous=None, next=None), **metadata):
278
        req = {'metadata': metadata}
279
        r = self.servers_metadata_post(server_id, json_data=req, success=201)
280
        for k, v in response_headers.items():
281
            response_headers[k] = r.headers.get(k, v)
282
        return r.json['metadata']
283

    
284
    def delete_server_metadata(self, server_id, key):
285
        r = self.servers_metadata_delete(server_id, key)
286
        return r.headers
287

    
288
    def get_server_nics(self, server_id, changes_since=None):
289
        r = self.servers_ips_get(server_id, changes_since=changes_since)
290
        return r.json
291

    
292
    def get_server_network_nics(
293
            self, server_id, network_id, changes_since=None):
294
        r = self.servers_ips_get(
295
            server_id, network_id=network_id, changes_since=changes_since)
296
        return r.json['network']
297

    
298
    def list_flavors(self, detail=False, response_headers=dict(
299
            previous=None, next=None)):
300
        r = self.flavors_get(detail=bool(detail))
301
        for k, v in response_headers.items():
302
            response_headers[k] = r.headers.get(k, v)
303
        return r.json['flavors']
304

    
305
    def get_flavor_details(self, flavor_id):
306
        r = self.flavors_get(flavor_id)
307
        return r.json['flavor']
308

    
309
    def list_images(self, detail=False, response_headers=dict(
310
            next=None, previous=None)):
311
        r = self.images_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['images']
315

    
316
    def get_image_details(self, image_id, **kwargs):
317
        """
318
        :returns: dict
319

320
        :raises ClientError: 404 if image not available
321
        """
322
        r = self.images_get(image_id, **kwargs)
323
        try:
324
            return r.json['image']
325
        except KeyError:
326
            raise ClientError('Image not available', 404, details=[
327
                'Image %d not found or not accessible'])
328

    
329
    def delete_image(self, image_id):
330
        """
331
        :param image_id: (str)
332
        """
333
        r = self.images_delete(image_id)
334
        return r.headers
335

    
336
    def get_image_metadata(self, image_id, key='', response_headers=dict(
337
            previous=None, next=None)):
338
        """
339
        :param image_id: (str)
340

341
        :param key: (str) the metadatum key
342

343
        :returns (dict) metadata if key not set, specific metadatum otherwise
344
        """
345
        r = self.images_metadata_get(image_id, key)
346
        for k, v in response_headers.items():
347
            response_headers[k] = r.headers.get(k, v)
348
        return r.json['meta' if key else 'metadata']
349

    
350
    # def create_image_metadata(self, image_id, key, val):
351
    #     """
352
    #     :param image_id: integer (str or int)
353

    
354
    #     :param key: (str) metadatum key
355

    
356
    #     :param val: (str) metadatum value
357

    
358
    #     :returns: (dict) updated metadata
359
    #     """
360
    #     req = {'meta': {key: val}}
361
    #     r = self.images_metadata_put(image_id, key, json_data=req)
362
    #     return r.json['meta']
363

    
364
    def update_image_metadata(
365
            self, image_id,
366
            response_headers=dict(previous=None, next=None), **metadata):
367
        """
368
        :param image_id: (str)
369

370
        :param metadata: dict
371

372
        :returns: updated metadata
373
        """
374
        req = {'metadata': metadata}
375
        r = self.images_metadata_post(image_id, json_data=req)
376
        for k, v in response_headers.items():
377
            response_headers[k] = r.headers.get(k, v)
378
        return r.json['metadata']
379

    
380
    def delete_image_metadata(self, image_id, key):
381
        """
382
        :param image_id: (str)
383

384
        :param key: (str) metadatum key
385

386
        :returns: (dict) response headers
387
        """
388
        r = self.images_metadata_delete(image_id, key)
389
        return r.headers
390

    
391
    #  Extensions
392

    
393
    def get_floating_ip_pools(self, tenant_id):
394
        """
395
        :param tenant_id: (str)
396

397
        :returns: (dict) {floating_ip_pools:[{name: ...}, ...]}
398
        """
399
        r = self.floating_ip_pools_get(tenant_id)
400
        return r.json
401

    
402
    def get_floating_ips(self, tenant_id):
403
        """
404
        :param tenant_id: (str)
405

406
        :returns: (dict) {floating_ips:[
407
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
408
            ... ]}
409
        """
410
        r = self.floating_ips_get(tenant_id)
411
        return r.json
412

    
413
    def alloc_floating_ip(self, tenant_id, pool=None):
414
        """
415
        :param tenant_id: (str)
416

417
        :param pool: (str) pool of ips to allocate from
418

419
        :returns: (dict) {fixed_ip: . id: . instance_id: . ip: . pool: .}
420
        """
421
        json_data = dict(pool=pool) if pool else dict()
422
        r = self.floating_ips_post(tenant_id, json_data)
423
        return r.json['floating_ip']
424

    
425
    def get_floating_ip(self, tenant_id, fip_id=None):
426
        """
427
        :param tenant_id: (str)
428

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

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

    
438
    def delete_floating_ip(self, tenant_id, fip_id=None):
439
        """
440
        :param tenant_id: (str)
441

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

444
        :returns: (dict) request headers
445
        """
446
        r = self.floating_ips_delete(tenant_id, fip_id)
447
        return r.headers