__init__.py 10.8 KB
Newer Older
1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
Giorgos Verigakis's avatar
Giorgos Verigakis committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

34
from kamaki.clients import ClientError
35
from kamaki.clients.compute.rest_api import ComputeRestClient
36
from kamaki.clients.utils import path4url
Giorgos Verigakis's avatar
Giorgos Verigakis committed
37

38

39
class ComputeClient(ComputeRestClient):
Giorgos Verigakis's avatar
Giorgos Verigakis committed
40
    """OpenStack Compute API 1.1 client"""
41

Giorgos Verigakis's avatar
Giorgos Verigakis committed
42
    def list_servers(self, detail=False):
43
44
45
46
47
        """
        :param detail: if true, append full server details to each item

        :returns: list of server ids and names
        """
48
49
        detail = 'detail' if detail else ''
        r = self.servers_get(command=detail)
50
        return r.json['servers']
51

52
    def get_server_details(self, server_id, **kwargs):
53
54
55
56
57
58
        """Return detailed info for a server

        :param server_id: integer (int or str)

        :returns: dict with server details
        """
59
        r = self.servers_get(server_id, **kwargs)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
60
        return r.json['server']
61

Giorgos Verigakis's avatar
Giorgos Verigakis committed
62
63
64
    def create_server(self, name, flavor_id, image_id, personality=None):
        """Submit request to create a new server

65
66
67
68
69
        :param name: (str)

        :param flavor_id: integer id denoting a preset hardware configuration

        :param image_id: (str) id denoting the OS image to run on the VM
Giorgos Verigakis's avatar
Giorgos Verigakis committed
70

71
72
        :param personality: a list of (file path, file contents) tuples,
            describing files to be injected into VM upon creation.
Giorgos Verigakis's avatar
Giorgos Verigakis committed
73

74
75
76
        :returns: a dict with the new VMs details

        :raises ClientError: wraps request errors
Giorgos Verigakis's avatar
Giorgos Verigakis committed
77
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
78
79
80
        req = {'server': {'name': name,
                          'flavorRef': flavor_id,
                          'imageRef': image_id}}
81
82
83
84
85

        image = self.get_image_details(image_id)
        metadata = {}
        for key in ('os', 'users'):
            try:
86
                metadata[key] = image['metadata'][key]
87
88
89
90
91
            except KeyError:
                pass
        if metadata:
            req['server']['metadata'] = metadata

Giorgos Verigakis's avatar
Giorgos Verigakis committed
92
        if personality:
93
            req['server']['personality'] = personality
94

Stavros Sachtouris's avatar
Stavros Sachtouris committed
95
96
97
98
        try:
            r = self.servers_post(json_data=req)
        except ClientError as err:
            try:
99
100
101
                if isinstance(err.details, list):
                    tmp_err = err.details
                else:
102
103
                    errd = '%s' % err.details
                    tmp_err = errd.split(',')
104
105
106
                tmp_err = tmp_err[0].split(':')
                tmp_err = tmp_err[2].split('"')
                err.message = tmp_err[1]
Stavros Sachtouris's avatar
Stavros Sachtouris committed
107
108
            finally:
                raise err
Giorgos Verigakis's avatar
Giorgos Verigakis committed
109
        return r.json['server']
110

Giorgos Verigakis's avatar
Giorgos Verigakis committed
111
    def update_server_name(self, server_id, new_name):
112
113
114
115
        """Update the name of the server as reported by the API (does not
            modify the hostname used inside the VM)

        :param server_id: integer (str or int)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
116

117
        :param new_name: (str)
118
119

        :returns: (dict) response headers
Giorgos Verigakis's avatar
Giorgos Verigakis committed
120
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
121
        req = {'server': {'name': new_name}}
122
123
        r = self.servers_put(server_id, json_data=req)
        return r.headers
124

Giorgos Verigakis's avatar
Giorgos Verigakis committed
125
    def delete_server(self, server_id):
126
127
128
        """Submit a deletion request for a server specified by id

        :param server_id: integer (str or int)
129
130

        :returns: (dict) response headers
131
        """
132
133
        r = self.servers_delete(server_id)
        return r.headers
134

Giorgos Verigakis's avatar
Giorgos Verigakis committed
135
    def reboot_server(self, server_id, hard=False):
136
137
138
139
140
        """
        :param server_id: integer (str or int)

        :param hard: perform a hard reboot if true, soft reboot otherwise
        """
141
142
        boot_type = 'HARD' if hard else 'SOFT'
        req = {'reboot': {'type': boot_type}}
143
144
        r = self.servers_post(server_id, 'action', json_data=req)
        return r.headers
145

146
    def get_server_metadata(self, server_id, key=''):
147
148
149
150
151
152
153
        """
        :param server_id: integer (str or int)

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

        :returns: a key:val dict of requests metadata
        """
154
        command = path4url('metadata', key)
155
        r = self.servers_get(server_id, command)
156
        return r.json['meta' if key else 'metadata']
157

Giorgos Verigakis's avatar
Giorgos Verigakis committed
158
    def create_server_metadata(self, server_id, key, val):
159
160
161
162
163
164
165
166
167
        """
        :param server_id: integer (str or int)

        :param key: (str)

        :param val: (str)

        :returns: dict of updated key:val metadata
        """
168
        req = {'meta': {key: val}}
169
        r = self.servers_put(
170
171
            server_id, 'metadata/' + key, json_data=req, success=201)
        return r.json['meta']
172

Giorgos Verigakis's avatar
Giorgos Verigakis committed
173
    def update_server_metadata(self, server_id, **metadata):
174
175
176
177
178
179
180
        """
        :param server_id: integer (str or int)

        :param metadata: dict of key:val metadata

        :returns: dict of updated key:val metadata
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
181
        req = {'metadata': metadata}
182
        r = self.servers_post(
183
            server_id, 'metadata', json_data=req, success=201)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
184
        return r.json['metadata']
185

Giorgos Verigakis's avatar
Giorgos Verigakis committed
186
    def delete_server_metadata(self, server_id, key):
187
188
189
190
        """
        :param server_id: integer (str or int)

        :param key: (str) the meta key
191
192

        :returns: (dict) response headers
193
        """
194
        r = self.servers_delete(server_id, 'metadata/' + key)
195
        return r.headers
196

197
    def list_flavors(self, detail=False):
198
        """
199
        :param detail: (bool) detailed flavor info if set, short if not
200

201
        :returns: (list) flavor info
202
        """
203
        r = self.flavors_get(command='detail' if detail else '')
204
        return r.json['flavors']
Giorgos Verigakis's avatar
Giorgos Verigakis committed
205
206

    def get_flavor_details(self, flavor_id):
207
208
209
210
211
        """
        :param flavor_id: integer (str or int)

        :returns: dict
        """
212
        r = self.flavors_get(flavor_id)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
213
        return r.json['flavor']
214

Giorgos Verigakis's avatar
Giorgos Verigakis committed
215
    def list_images(self, detail=False):
216
217
218
219
220
        """
        :param detail: (bool) detailed info if set, short if not

        :returns: dict id,name + full info if detail
        """
221
        detail = 'detail' if detail else ''
222
        r = self.images_get(command=detail)
223
        return r.json['images']
224

225
    def get_image_details(self, image_id, **kwargs):
226
227
228
229
230
231
232
        """
        :param image_id: integer (str or int)

        :returns: dict

        :raises ClientError: 404 if image not available
        """
233
234
235
236
        r = self.images_get(image_id, **kwargs)
        try:
            return r.json['image']
        except KeyError:
237
238
            raise ClientError('Image not available', 404, details=[
                'Image %d not found or not accessible'])
239

Giorgos Verigakis's avatar
Giorgos Verigakis committed
240
    def delete_image(self, image_id):
241
242
243
        """
        :param image_id: (str)
        """
244
245
        r = self.images_delete(image_id)
        return r.headers
246
247

    def get_image_metadata(self, image_id, key=''):
248
249
250
251
252
253
254
        """
        :param image_id: (str)

        :param key: (str) the metadatum key

        :returns (dict) metadata if key not set, specific metadatum otherwise
        """
255
        command = path4url('metadata', key)
256
        r = self.images_get(image_id, command)
257
        return r.json['meta' if key else 'metadata']
258

Giorgos Verigakis's avatar
Giorgos Verigakis committed
259
    def create_image_metadata(self, image_id, key, val):
260
261
262
263
264
265
266
267
268
        """
        :param image_id: integer (str or int)

        :param key: (str) metadatum key

        :param val: (str) metadatum value

        :returns: (dict) updated metadata
        """
269
        req = {'meta': {key: val}}
270
        r = self.images_put(image_id, 'metadata/' + key, json_data=req)
271
        return r.json['meta']
Giorgos Verigakis's avatar
Giorgos Verigakis committed
272
273

    def update_image_metadata(self, image_id, **metadata):
274
275
276
277
278
279
280
        """
        :param image_id: (str)

        :param metadata: dict

        :returns: updated metadata
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
281
        req = {'metadata': metadata}
282
        r = self.images_post(image_id, 'metadata', json_data=req)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
283
        return r.json['metadata']
Giorgos Verigakis's avatar
Giorgos Verigakis committed
284
285

    def delete_image_metadata(self, image_id, key):
286
287
288
289
        """
        :param image_id: (str)

        :param key: (str) metadatum key
290
291

        :returns: (dict) response headers
292
        """
293
        command = path4url('metadata', key)
294
295
        r = self.images_delete(image_id, command)
        return r.headers
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322

    def get_floating_ip_pools(self, tenant_id):
        """
        :param tenant_id: (str)

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

    def get_floating_ips(self, tenant_id):
        """
        :param tenant_id: (str)

        :returns: (dict) {floating_ips:[
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
            ... ]}
        """
        r = self.floating_ips_get(tenant_id)
        return r.json

    def alloc_floating_ip(self, tenant_id, pool=None):
        """
        :param tenant_id: (str)

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

Stavros Sachtouris's avatar
Stavros Sachtouris committed
323
        :returns: (dict) {fixed_ip: . id: . instance_id: . ip: . pool: .}
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
        """
        json_data = dict(pool=pool) if pool else dict()
        r = self.floating_ips_post(tenant_id, json_data)
        return r.json['floating_ip']

    def get_floating_ip(self, tenant_id, fip_id=None):
        """
        :param tenant_id: (str)

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

        :returns: (list) [
            {fixed_ip: ..., id: ..., instance_id: ..., ip: ..., pool: ...},
            ... ]
        """
        r = self.floating_ips_get(tenant_id, fip_id)
        return r.json['floating_ips']

    def delete_floating_ip(self, tenant_id, fip_id=None):
        """
        :param tenant_id: (str)

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

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