Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute / __init__.py @ dbcbf446

History | View | Annotate | Download (12.3 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
        response_headers['previous'] = r.headers.get('previous', None)
74
        response_headers['next'] = r.headers.get('next', None)
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
        response_headers['previous'] = r.headers.get('previous', None)
107
        response_headers['next'] = r.headers.get('next', None)
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
        response_headers['location'] = r.headers.get('location', None)
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 VM)
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 reboot_server(self, server_id, hard=False):
178
        """
179
        :param server_id: integer (str or int)
180

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

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

192
        :param flavor_id: (int)
193

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

    
200
    def get_server_metadata(self, server_id, key=''):
201
        """
202
        :param server_id: integer (str or int)
203

204
        :param key: (str) the metadatum key (all metadata if not given)
205

206
        :returns: a key:val dict of requests metadata
207
        """
208
        command = path4url('metadata', key)
209
        r = self.servers_get(server_id, command)
210
        return r.json['meta' if key else 'metadata']
211

    
212
    def create_server_metadata(self, server_id, key, val):
213
        """
214
        :param server_id: integer (str or int)
215

216
        :param key: (str)
217

218
        :param val: (str)
219

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

    
227
    def update_server_metadata(self, server_id, **metadata):
228
        """
229
        :param server_id: integer (str or int)
230

231
        :param metadata: dict of key:val metadata
232

233
        :returns: dict of updated key:val metadata
234
        """
235
        req = {'metadata': metadata}
236
        r = self.servers_post(
237
            server_id, 'metadata', json_data=req, success=201)
238
        return r.json['metadata']
239

    
240
    def delete_server_metadata(self, server_id, key):
241
        """
242
        :param server_id: integer (str or int)
243

244
        :param key: (str) the meta key
245

246
        :returns: (dict) response headers
247
        """
248
        r = self.servers_delete(server_id, 'metadata/' + key)
249
        return r.headers
250

    
251
    def list_flavors(self, detail=False):
252
        """
253
        :param detail: (bool) detailed flavor info if set, short if not
254

255
        :returns: (list) flavor info
256
        """
257
        r = self.flavors_get(command='detail' if detail else '')
258
        return r.json['flavors']
259

    
260
    def get_flavor_details(self, flavor_id):
261
        """
262
        :param flavor_id: integer (str or int)
263

264
        :returns: dict
265
        """
266
        r = self.flavors_get(flavor_id)
267
        return r.json['flavor']
268

    
269
    def list_images(self, detail=False):
270
        """
271
        :param detail: (bool) detailed info if set, short if not
272

273
        :returns: dict id,name + full info if detail
274
        """
275
        detail = 'detail' if detail else ''
276
        r = self.images_get(command=detail)
277
        return r.json['images']
278

    
279
    def get_image_details(self, image_id, **kwargs):
280
        """
281
        :param image_id: integer (str or int)
282

283
        :returns: dict
284

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

    
294
    def delete_image(self, image_id):
295
        """
296
        :param image_id: (str)
297
        """
298
        r = self.images_delete(image_id)
299
        return r.headers
300

    
301
    def get_image_metadata(self, image_id, key=''):
302
        """
303
        :param image_id: (str)
304

305
        :param key: (str) the metadatum key
306

307
        :returns (dict) metadata if key not set, specific metadatum otherwise
308
        """
309
        command = path4url('metadata', key)
310
        r = self.images_get(image_id, command)
311
        return r.json['meta' if key else 'metadata']
312

    
313
    def create_image_metadata(self, image_id, key, val):
314
        """
315
        :param image_id: integer (str or int)
316

317
        :param key: (str) metadatum key
318

319
        :param val: (str) metadatum value
320

321
        :returns: (dict) updated metadata
322
        """
323
        req = {'meta': {key: val}}
324
        r = self.images_put(image_id, 'metadata/' + key, json_data=req)
325
        return r.json['meta']
326

    
327
    def update_image_metadata(self, image_id, **metadata):
328
        """
329
        :param image_id: (str)
330

331
        :param metadata: dict
332

333
        :returns: updated metadata
334
        """
335
        req = {'metadata': metadata}
336
        r = self.images_post(image_id, 'metadata', json_data=req)
337
        return r.json['metadata']
338

    
339
    def delete_image_metadata(self, image_id, key):
340
        """
341
        :param image_id: (str)
342

343
        :param key: (str) metadatum key
344

345
        :returns: (dict) response headers
346
        """
347
        command = path4url('metadata', key)
348
        r = self.images_delete(image_id, command)
349
        return r.headers
350

    
351
    def get_floating_ip_pools(self, tenant_id):
352
        """
353
        :param tenant_id: (str)
354

355
        :returns: (dict) {floating_ip_pools:[{name: ...}, ...]}
356
        """
357
        r = self.floating_ip_pools_get(tenant_id)
358
        return r.json
359

    
360
    def get_floating_ips(self, tenant_id):
361
        """
362
        :param tenant_id: (str)
363

364
        :returns: (dict) {floating_ips:[
365
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
366
            ... ]}
367
        """
368
        r = self.floating_ips_get(tenant_id)
369
        return r.json
370

    
371
    def alloc_floating_ip(self, tenant_id, pool=None):
372
        """
373
        :param tenant_id: (str)
374

375
        :param pool: (str) pool of ips to allocate from
376

377
        :returns: (dict) {fixed_ip: . id: . instance_id: . ip: . pool: .}
378
        """
379
        json_data = dict(pool=pool) if pool else dict()
380
        r = self.floating_ips_post(tenant_id, json_data)
381
        return r.json['floating_ip']
382

    
383
    def get_floating_ip(self, tenant_id, fip_id=None):
384
        """
385
        :param tenant_id: (str)
386

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

389
        :returns: (list) [
390
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
391
            ... ]
392
        """
393
        r = self.floating_ips_get(tenant_id, fip_id)
394
        return r.json['floating_ips']
395

    
396
    def delete_floating_ip(self, tenant_id, fip_id=None):
397
        """
398
        :param tenant_id: (str)
399

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

402
        :returns: (dict) request headers
403
        """
404
        r = self.floating_ips_delete(tenant_id, fip_id)
405
        return r.headers