Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute.py @ 3dabe5d2

History | View | Annotate | Download (11.3 kB)

1
# Copyright 2011 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 Client, ClientError
35
from kamaki.clients.utils import path4url
36
import json
37

    
38

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

    
42
    def raise_for_status(self, r):
43
        try:
44
            d = r.json
45
            key = d.keys()[0]
46
            val = d[key]
47
            message = '%s: %s' % (key, val.get('message', ''))
48
            details = val.get('details', '')
49
        except AttributeError:
50
            message = 'Request responded with error code %s' %\
51
                unicode(r.status_code)
52
            details = '%s %s' %\
53
                (unicode(r.request.method), unicode(r.request.url))
54
        raise ClientError(message, r.status_code, details)
55

    
56
    def servers_get(self, server_id='', command='', **kwargs):
57
        """GET base_url/servers[/server_id][/command] request
58
        @param server_id or ''
59
        @param command: can be 'ips', 'stats', or ''
60
        """
61
        path = path4url('servers', server_id, command)
62
        success = kwargs.pop('success', 200)
63
        return self.get(path, success=success, **kwargs)
64

    
65
    def servers_delete(self, server_id='', command='', **kwargs):
66
        """DEL ETE base_url/servers[/server_id][/command] request
67
        @param server_id or ''
68
        @param command: can be 'ips', 'stats', or ''
69
        """
70
        path = path4url('servers', server_id, command)
71
        success = kwargs.pop('success', 204)
72
        return self.delete(path, success=success, **kwargs)
73

    
74
    def servers_post(self, server_id='', command='', json_data=None, **kwargs):
75
        """POST base_url/servers[/server_id]/[command] request
76
        @param server_id or ''
77
        @param command: can be 'action' or ''
78
        @param json_data: a json valid dict that will be send as data
79
        """
80
        data = json_data
81
        if json_data is not None:
82
            data = json.dumps(json_data)
83
            self.set_header('Content-Type', 'application/json')
84
            self.set_header('Content-Length', len(data))
85

    
86
        path = path4url('servers', server_id, command)
87
        success = kwargs.pop('success', 202)
88
        return self.post(path, data=data, success=success, **kwargs)
89

    
90
    def servers_put(self, server_id='', command='', json_data=None, **kwargs):
91
        """PUT base_url/servers[/server_id]/[command] request
92
        @param server_id or ''
93
        @param command: can be 'action' or ''
94
        @param json_data: a json valid dict that will be send as data
95
        """
96
        data = json_data
97
        if json_data is not None:
98
            data = json.dumps(json_data)
99
            self.set_header('Content-Type', 'application/json')
100
            self.set_header('Content-Length', len(data))
101

    
102
        path = path4url('servers', server_id, command)
103
        success = kwargs.pop('success', 204)
104
        return self.put(path, data=data, success=success, **kwargs)
105

    
106
    def list_servers(self, detail=False):
107
        """List servers, returned detailed output if detailed is True"""
108
        detail = 'detail' if detail else ''
109
        r = self.servers_get(command=detail)
110
        return r.json['servers']['values']
111

    
112
    def get_server_details(self, server_id, **kwargs):
113
        """Return detailed output on a server specified by its id"""
114
        r = self.servers_get(server_id, **kwargs)
115
        return r.json['server']
116

    
117
    def create_server(self, name, flavor_id, image_id, personality=None):
118
        """Submit request to create a new server
119

120
        The flavor_id specifies the hardware configuration to use,
121
        the image_id specifies the OS Image to be deployed inside the new
122
        server.
123

124
        The personality argument is a list of (file path, file contents)
125
        tuples, describing files to be injected into the server upon creation.
126

127
        The call returns a dictionary describing the newly created server.
128
        """
129
        req = {'server': {'name': name,
130
                          'flavorRef': flavor_id,
131
                          'imageRef': image_id}}
132
        if personality:
133
            req['server']['personality'] = personality
134

    
135
        try:
136
            r = self.servers_post(json_data=req)
137
        except ClientError as err:
138
            try:
139
                tmp_err = err.details.split(',')
140
                tmp_err = tmp_err[0].split(':')
141
                tmp_err = tmp_err[2].split('"')
142
                err.message = tmp_err[1]
143
            finally:
144
                raise err
145
        return r.json['server']
146

    
147
    def update_server_name(self, server_id, new_name):
148
        """Update the name of the server as reported by the API.
149

150
        This call does not modify the hostname actually used by the server
151
        internally.
152
        """
153
        req = {'server': {'name': new_name}}
154
        r = self.servers_put(server_id, json_data=req)
155
        r.release()
156

    
157
    def delete_server(self, server_id):
158
        """Submit a deletion request for a server specified by id"""
159
        r = self.servers_delete(server_id)
160
        r.release()
161

    
162
    def reboot_server(self, server_id, hard=False):
163
        """Submit a reboot request for a server specified by id"""
164
        type = 'HARD' if hard else 'SOFT'
165
        req = {'reboot': {'type': type}}
166
        r = self.servers_post(server_id, 'action', json_data=req)
167
        r.release()
168

    
169
    def get_server_metadata(self, server_id, key=''):
170
        command = path4url('meta', key)
171
        r = self.servers_get(server_id, command)
172
        return r.json['meta'] if key != '' else r.json['metadata']['values']
173

    
174
    def create_server_metadata(self, server_id, key, val):
175
        req = {'meta': {key: val}}
176
        r = self.servers_put(server_id,
177
            'meta/' + key,
178
            json_data=req,
179
            success=201)
180
        return r.json['meta']
181

    
182
    def update_server_metadata(self, server_id, **metadata):
183
        req = {'metadata': metadata}
184
        r = self.servers_post(server_id, 'meta', json_data=req, success=201)
185
        return r.json['metadata']
186

    
187
    def delete_server_metadata(self, server_id, key):
188
        r = self.servers_delete(server_id, 'meta/' + key)
189
        r.release()
190

    
191
    def flavors_get(self, flavor_id='', command='', **kwargs):
192
        """GET base_url[/flavor_id][/command]
193
        @param flavor_id
194
        @param command
195
        """
196
        path = path4url('flavors', flavor_id, command)
197
        success = kwargs.pop('success', 200)
198
        return self.get(path, success=success, **kwargs)
199

    
200
    def list_flavors(self, detail=False):
201
        detail = 'detail' if detail else ''
202
        r = self.flavors_get(command='detail')
203
        return r.json['flavors']['values']
204

    
205
    def get_flavor_details(self, flavor_id):
206
        r = self.flavors_get(flavor_id)
207
        return r.json['flavor']
208

    
209
    def images_get(self, image_id='', command='', **kwargs):
210
        """GET base_url[/image_id][/command]
211
        @param image_id
212
        @param command
213
        """
214
        path = path4url('images', image_id, command)
215
        success = kwargs.pop('success', 200)
216
        return self.get(path, success=success, **kwargs)
217

    
218
    def images_delete(self, image_id='', command='', **kwargs):
219
        """DEL ETE base_url[/image_id][/command]
220
        @param image_id
221
        @param command
222
        """
223
        path = path4url('images', image_id, command)
224
        success = kwargs.pop('success', 204)
225
        return self.delete(path, success=success, **kwargs)
226

    
227
    def images_post(self, image_id='', command='', json_data=None, **kwargs):
228
        """POST base_url/images[/image_id]/[command] request
229
        @param image_id or ''
230
        @param command: can be 'action' or ''
231
        @param json_data: a json valid dict that will be send as data
232
        """
233
        data = json_data
234
        if json_data is not None:
235
            data = json.dumps(json_data)
236
            self.set_header('Content-Type', 'application/json')
237
            self.set_header('Content-Length', len(data))
238

    
239
        path = path4url('images', image_id, command)
240
        success = kwargs.pop('success', 201)
241
        return self.post(path, data=data, success=success, **kwargs)
242

    
243
    def images_put(self, image_id='', command='', json_data=None, **kwargs):
244
        """PUT base_url/images[/image_id]/[command] request
245
        @param image_id or ''
246
        @param command: can be 'action' or ''
247
        @param json_data: a json valid dict that will be send as data
248
        """
249
        data = json_data
250
        if json_data is not None:
251
            data = json.dumps(json_data)
252
            self.set_header('Content-Type', 'application/json')
253
            self.set_header('Content-Length', len(data))
254

    
255
        path = path4url('images', image_id, command)
256
        success = kwargs.pop('success', 201)
257
        return self.put(path, data=data, success=success, **kwargs)
258

    
259
    def list_images(self, detail=False):
260
        detail = 'detail' if detail else ''
261
        r = self.images_get(command=detail)
262
        return r.json['images']['values']
263

    
264
    def get_image_details(self, image_id, **kwargs):
265
        r = self.images_get(image_id, **kwargs)
266
        try:
267
            return r.json['image']
268
        except KeyError:
269
            raise ClientError('Image not available', 404,
270
                details='Image %d not found or not accessible')
271

    
272
    def delete_image(self, image_id):
273
        r = self.images_delete(image_id)
274
        r.release()
275

    
276
    def get_image_metadata(self, image_id, key=''):
277
        command = path4url('meta', key)
278
        r = self.images_get(image_id, command)
279
        return r.json['meta'] if key != '' else r.json['metadata']['values']
280

    
281
    def create_image_metadata(self, image_id, key, val):
282
        req = {'meta': {key: val}}
283
        r = self.images_put(image_id, 'meta/' + key, json_data=req)
284
        return r.json['meta']
285

    
286
    def update_image_metadata(self, image_id, **metadata):
287
        req = {'metadata': metadata}
288
        r = self.images_post(image_id, 'meta', json_data=req)
289
        return r.json['metadata']
290

    
291
    def delete_image_metadata(self, image_id, key):
292
        command = path4url('meta', key)
293
        r = self.images_delete(image_id, command)
294
        r.release()