Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute.py @ a517ff50

History | View | Annotate | Download (9.1 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 ClientError
35
from kamaki.clients.compute_rest_api import ComputeClientApi
36
from kamaki.clients.utils import path4url
37
import json
38

    
39

    
40
class ComputeClient(ComputeClientApi):
41
    """OpenStack Compute API 1.1 client"""
42

    
43
    def list_servers(self, detail=False):
44
        """
45
        :param detail: if true, append full server details to each item
46

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

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

56
        :param server_id: integer (int or str)
57

58
        :returns: dict with server details
59
        """
60
        r = self.servers_get(server_id, **kwargs)
61
        return r.json['server']
62

    
63
    def create_server(self, name, flavor_id, image_id, personality=None):
64
        """Submit request to create a new server
65

66
        :param name: (str)
67

68
        :param flavor_id: integer id denoting a preset hardware configuration
69

70
        :param image_id: (str) id denoting the OS image to run on the VM
71

72
        :param personality: a list of (file path, file contents) tuples,
73
            describing files to be injected into VM upon creation.
74

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

77
        :raises ClientError: wraps request errors
78
        """
79
        req = {'server': {'name': name,
80
                          'flavorRef': flavor_id,
81
                          'imageRef': image_id}}
82

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

    
94
        if personality:
95
            req['server']['personality'] = personality
96

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

    
113
    def update_server_name(self, server_id, new_name):
114
        """Update the name of the server as reported by the API (does not
115
            modify the hostname used inside the VM)
116

117
        :param server_id: integer (str or int)
118

119
        :param new_name: (str)
120
        """
121
        req = {'server': {'name': new_name}}
122
        r = self.servers_put(server_id, json_data=req)
123
        r.release()
124

    
125
    def delete_server(self, server_id):
126
        """Submit a deletion request for a server specified by id
127

128
        :param server_id: integer (str or int)
129
        """
130
        r = self.servers_delete(server_id)
131
        r.release()
132

    
133
    def reboot_server(self, server_id, hard=False):
134
        """
135
        :param server_id: integer (str or int)
136

137
        :param hard: perform a hard reboot if true, soft reboot otherwise
138
        """
139
        type = 'HARD' if hard else 'SOFT'
140
        req = {'reboot': {'type': type}}
141
        r = self.servers_post(server_id, 'action', json_data=req)
142
        r.release()
143

    
144
    def get_server_metadata(self, server_id, key=''):
145
        """
146
        :param server_id: integer (str or int)
147

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

150
        :returns: a key:val dict of requests metadata
151
        """
152
        command = path4url('meta', key)
153
        r = self.servers_get(server_id, command)
154
        return r.json['meta'] if key != '' else r.json['metadata']['values']
155

    
156
    def create_server_metadata(self, server_id, key, val):
157
        """
158
        :param server_id: integer (str or int)
159

160
        :param key: (str)
161

162
        :param val: (str)
163

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

    
174
    def update_server_metadata(self, server_id, **metadata):
175
        """
176
        :param server_id: integer (str or int)
177

178
        :param metadata: dict of key:val metadata
179

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

    
186
    def delete_server_metadata(self, server_id, key):
187
        """
188
        :param server_id: integer (str or int)
189

190
        :param key: (str) the meta key
191
        """
192
        r = self.servers_delete(server_id, 'meta/' + key)
193
        r.release()
194

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

199
        :returns: (dict) flavor info
200
        """
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
        """
207
        :param flavor_id: integer (str or int)
208

209
        :returns: dict
210
        """
211
        r = self.flavors_get(flavor_id)
212
        return r.json['flavor']
213

    
214
    def list_images(self, detail=False):
215
        """
216
        :param detail: (bool) detailed info if set, short if not
217

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

    
224
    def get_image_details(self, image_id, **kwargs):
225
        """
226
        :param image_id: integer (str or int)
227

228
        :returns: dict
229

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

    
239
    def delete_image(self, image_id):
240
        """
241
        :param image_id: (str)
242
        """
243
        r = self.images_delete(image_id)
244
        r.release()
245

    
246
    def get_image_metadata(self, image_id, key=''):
247
        """
248
        :param image_id: (str)
249

250
        :param key: (str) the metadatum key
251

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

    
258
    def create_image_metadata(self, image_id, key, val):
259
        """
260
        :param image_id: integer (str or int)
261

262
        :param key: (str) metadatum key
263

264
        :param val: (str) metadatum value
265

266
        :returns: (dict) updated metadata
267
        """
268
        req = {'meta': {key: val}}
269
        r = self.images_put(image_id, 'meta/' + key, json_data=req)
270
        return r.json['meta']
271

    
272
    def update_image_metadata(self, image_id, **metadata):
273
        """
274
        :param image_id: (str)
275

276
        :param metadata: dict
277

278
        :returns: updated metadata
279
        """
280
        req = {'metadata': metadata}
281
        r = self.images_post(image_id, 'meta', json_data=req)
282
        return r.json['metadata']
283

    
284
    def delete_image_metadata(self, image_id, key):
285
        """
286
        :param image_id: (str)
287

288
        :param key: (str) metadatum key
289
        """
290
        command = path4url('meta', key)
291
        r = self.images_delete(image_id, command)
292
        r.release()