Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / __init__.py @ 0b179c69

History | View | Annotate | Download (12.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
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
            server=None,
116
            metadata=None,
117
            personality=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 denoting the OS image to run on the VM
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 VM upon creation.
131

132
        :returns: a dict with the new VMs details
133

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

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

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

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

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

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

160
        :param new_name: (str)
161

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

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

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

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

    
178
    def reboot_server(self, server_id, hard=False):
179
        """
180
        :param server_id: integer (str or int)
181

182
        :param hard: perform a hard reboot if true, soft reboot otherwise
183
        """
184
        boot_type = 'HARD' if hard else 'SOFT'
185
        req = {'reboot': {'type': boot_type}}
186
        r = self.servers_post(server_id, 'action', json_data=req)
187
        return r.headers
188

    
189
    def resize_server(self, server_id, flavor_id):
190
        """
191
        :param server_id: (str)
192

193
        :param flavor_id: (int)
194

195
        :returns: (dict) request headers
196
        """
197
        req = {'resize': {'flavorRef': flavor_id}}
198
        r = self.servers_post(server_id, 'action', json_data=req)
199
        return r.headers
200

    
201
    def get_server_metadata(self, server_id, key='', response_headers=dict(
202
            previous=None, next=None)):
203
        """
204
        :param server_id: integer (str or int)
205

206
        :param key: (str) the metadatum key (all metadata if not given)
207

208
        :returns: a key:val dict of requests metadata
209
        """
210
        r = self.servers_metadata_get(server_id, key)
211
        for k, v in response_headers.items():
212
            response_headers[k] = r.headers.get(k, v)
213
        return r.json['meta' if key else 'metadata']
214

    
215
    def create_server_metadata(self, server_id, key, val):
216
        """
217
        :param server_id: integer (str or int)
218

219
        :param key: (str)
220

221
        :param val: (str)
222

223
        :returns: dict of updated key:val metadata
224
        """
225
        req = {'meta': {key: val}}
226
        r = self.servers_put(server_id, key, json_data=req, success=201)
227
        return r.json['meta']
228

    
229
    def update_server_metadata(
230
            self, server_id,
231
            response_headers=dict(previous=None, next=None), **metadata):
232
        """
233
        :param server_id: integer (str or int)
234

235
        :param metadata: dict of key:val metadata
236

237
        :returns: dict of updated key:val metadata
238
        """
239
        req = {'metadata': metadata}
240
        r = self.servers_post(server_id, json_data=req, success=201)
241
        for k, v in response_headers.items():
242
            response_headers[k] = r.headers.get(k, v)
243
        return r.json['metadata']
244

    
245
    def delete_server_metadata(self, server_id, key):
246
        """
247
        :param server_id: integer (str or int)
248

249
        :param key: (str) the meta key
250

251
        :returns: (dict) response headers
252
        """
253
        r = self.servers_delete(server_id, key)
254
        return r.headers
255

    
256
    def list_flavors(self, detail=False):
257
        """
258
        :param detail: (bool) detailed flavor info if set, short if not
259

260
        :returns: (list) flavor info
261
        """
262
        r = self.flavors_get(command='detail' if detail else '')
263
        return r.json['flavors']
264

    
265
    def get_flavor_details(self, flavor_id):
266
        """
267
        :param flavor_id: integer (str or int)
268

269
        :returns: dict
270
        """
271
        r = self.flavors_get(flavor_id)
272
        return r.json['flavor']
273

    
274
    def list_images(self, detail=False):
275
        """
276
        :param detail: (bool) detailed info if set, short if not
277

278
        :returns: dict id,name + full info if detail
279
        """
280
        detail = 'detail' if detail else ''
281
        r = self.images_get(command=detail)
282
        return r.json['images']
283

    
284
    def get_image_details(self, image_id, **kwargs):
285
        """
286
        :param image_id: integer (str or int)
287

288
        :returns: dict
289

290
        :raises ClientError: 404 if image not available
291
        """
292
        r = self.images_get(image_id, **kwargs)
293
        try:
294
            return r.json['image']
295
        except KeyError:
296
            raise ClientError('Image not available', 404, details=[
297
                'Image %d not found or not accessible'])
298

    
299
    def delete_image(self, image_id):
300
        """
301
        :param image_id: (str)
302
        """
303
        r = self.images_delete(image_id)
304
        return r.headers
305

    
306
    def get_image_metadata(self, image_id, key=''):
307
        """
308
        :param image_id: (str)
309

310
        :param key: (str) the metadatum key
311

312
        :returns (dict) metadata if key not set, specific metadatum otherwise
313
        """
314
        command = path4url('metadata', key)
315
        r = self.images_get(image_id, command)
316
        return r.json['meta' if key else 'metadata']
317

    
318
    def create_image_metadata(self, image_id, key, val):
319
        """
320
        :param image_id: integer (str or int)
321

322
        :param key: (str) metadatum key
323

324
        :param val: (str) metadatum value
325

326
        :returns: (dict) updated metadata
327
        """
328
        req = {'meta': {key: val}}
329
        r = self.images_put(image_id, 'metadata/' + key, json_data=req)
330
        return r.json['meta']
331

    
332
    def update_image_metadata(self, image_id, **metadata):
333
        """
334
        :param image_id: (str)
335

336
        :param metadata: dict
337

338
        :returns: updated metadata
339
        """
340
        req = {'metadata': metadata}
341
        r = self.images_post(image_id, 'metadata', json_data=req)
342
        return r.json['metadata']
343

    
344
    def delete_image_metadata(self, image_id, key):
345
        """
346
        :param image_id: (str)
347

348
        :param key: (str) metadatum key
349

350
        :returns: (dict) response headers
351
        """
352
        command = path4url('metadata', key)
353
        r = self.images_delete(image_id, command)
354
        return r.headers
355

    
356
    def get_floating_ip_pools(self, tenant_id):
357
        """
358
        :param tenant_id: (str)
359

360
        :returns: (dict) {floating_ip_pools:[{name: ...}, ...]}
361
        """
362
        r = self.floating_ip_pools_get(tenant_id)
363
        return r.json
364

    
365
    def get_floating_ips(self, tenant_id):
366
        """
367
        :param tenant_id: (str)
368

369
        :returns: (dict) {floating_ips:[
370
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
371
            ... ]}
372
        """
373
        r = self.floating_ips_get(tenant_id)
374
        return r.json
375

    
376
    def alloc_floating_ip(self, tenant_id, pool=None):
377
        """
378
        :param tenant_id: (str)
379

380
        :param pool: (str) pool of ips to allocate from
381

382
        :returns: (dict) {fixed_ip: . id: . instance_id: . ip: . pool: .}
383
        """
384
        json_data = dict(pool=pool) if pool else dict()
385
        r = self.floating_ips_post(tenant_id, json_data)
386
        return r.json['floating_ip']
387

    
388
    def get_floating_ip(self, tenant_id, fip_id=None):
389
        """
390
        :param tenant_id: (str)
391

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

394
        :returns: (list) [
395
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
396
            ... ]
397
        """
398
        r = self.floating_ips_get(tenant_id, fip_id)
399
        return r.json['floating_ips']
400

    
401
    def delete_floating_ip(self, tenant_id, fip_id=None):
402
        """
403
        :param tenant_id: (str)
404

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

407
        :returns: (dict) request headers
408
        """
409
        r = self.floating_ips_delete(tenant_id, fip_id)
410
        return r.headers