Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute.py @ d2cea1e2

History | View | Annotate | Download (6.9 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
import json
35

    
36
from .http import HTTPClient
37

    
38

    
39
class ComputeClient(HTTPClient):
40
    """OpenStack Compute API 1.1 client"""
41
    
42
    @property
43
    def url(self):
44
        url = self.config.get('compute_url') or self.config.get('url')
45
        if not url:
46
            raise ClientError('No URL was given')
47
        return url
48
    
49
    @property
50
    def token(self):
51
        token = self.config.get('compute_token') or self.config.get('token')
52
        if not token:
53
            raise ClientError('No token was given')
54
        return token
55
    
56
    def list_servers(self, detail=False):
57
        """List servers, returned detailed output if detailed is True"""
58
        path = '/servers/detail' if detail else '/servers'
59
        reply = self.http_get(path)
60
        return reply['servers']['values']
61
    
62
    def get_server_details(self, server_id):
63
        """Return detailed output on a server specified by its id"""
64
        path = '/servers/%d' % server_id
65
        reply = self.http_get(path)
66
        return reply['server']
67
    
68
    def create_server(self, name, flavor_id, image_id, personality=None):
69
        """Submit request to create a new server
70

71
        The flavor_id specifies the hardware configuration to use,
72
        the image_id specifies the OS Image to be deployed inside the new
73
        server.
74

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

78
        The call returns a dictionary describing the newly created server.
79
        """
80
        req = {'name': name, 'flavorRef': flavor_id, 'imageRef': image_id}
81
        if personality:
82
            req['personality'] = personality
83
        
84
        body = json.dumps({'server': req})
85
        reply = self.http_post('/servers', body)
86
        return reply['server']
87
    
88
    def update_server_name(self, server_id, new_name):
89
        """Update the name of the server as reported by the API.
90

91
        This call does not modify the hostname actually used by the server
92
        internally.
93
        """
94
        path = '/servers/%d' % server_id
95
        body = json.dumps({'server': {'name': new_name}})
96
        self.http_put(path, body)
97
    
98
    def delete_server(self, server_id):
99
        """Submit a deletion request for a server specified by id"""
100
        path = '/servers/%d' % server_id
101
        self.http_delete(path)
102
    
103
    def reboot_server(self, server_id, hard=False):
104
        """Submit a reboot request for a server specified by id"""
105
        path = '/servers/%d/action' % server_id
106
        type = 'HARD' if hard else 'SOFT'
107
        body = json.dumps({'reboot': {'type': type}})
108
        self.http_post(path, body)
109
        
110
    def get_server_metadata(self, server_id, key=None):
111
        path = '/servers/%d/meta' % server_id
112
        if key:
113
            path += '/%s' % key
114
        reply = self.http_get(path)
115
        return reply['meta'] if key else reply['metadata']['values']
116
    
117
    def create_server_metadata(self, server_id, key, val):
118
        path = '/servers/%d/meta/%s' % (server_id, key)
119
        body = json.dumps({'meta': {key: val}})
120
        reply = self.http_put(path, body, success=201)
121
        return reply['meta']
122
    
123
    def update_server_metadata(self, server_id, **metadata):
124
        path = '/servers/%d/meta' % server_id
125
        body = json.dumps({'metadata': metadata})
126
        reply = self.http_post(path, body, success=201)
127
        return reply['metadata']
128
    
129
    def delete_server_metadata(self, server_id, key):
130
        path = '/servers/%d/meta/%s' % (server_id, key)
131
        reply = self.http_delete(path)
132
        
133
        
134
    def list_flavors(self, detail=False):
135
        path = '/flavors/detail' if detail else '/flavors'
136
        reply = self.http_get(path)
137
        return reply['flavors']['values']
138

    
139
    def get_flavor_details(self, flavor_id):
140
        path = '/flavors/%d' % flavor_id
141
        reply = self.http_get(path)
142
        return reply['flavor']
143
    
144
    
145
    def list_images(self, detail=False):
146
        path = '/images/detail' if detail else '/images'
147
        reply = self.http_get(path)
148
        return reply['images']['values']
149

    
150
    def get_image_details(self, image_id):
151
        path = '/images/%d' % image_id
152
        reply = self.http_get(path)
153
        return reply['image']
154

    
155
    def create_image(self, server_id, name):
156
        req = {'name': name, 'serverRef': server_id}
157
        body = json.dumps({'image': req})
158
        reply = self.http_post('/images', body)
159
        return reply['image']
160

    
161
    def delete_image(self, image_id):
162
        path = '/images/%d' % image_id
163
        self.http_delete(path)
164

    
165
    def get_image_metadata(self, image_id, key=None):
166
        path = '/images/%d/meta' % image_id
167
        if key:
168
            path += '/%s' % key
169
        reply = self.http_get(path)
170
        return reply['meta'] if key else reply['metadata']['values']
171
    
172
    def create_image_metadata(self, image_id, key, val):
173
        path = '/images/%d/meta/%s' % (image_id, key)
174
        body = json.dumps({'meta': {key: val}})
175
        reply = self.http_put(path, body, success=201)
176
        reply['meta']
177

    
178
    def update_image_metadata(self, image_id, **metadata):
179
        path = '/images/%d/meta' % image_id
180
        body = json.dumps({'metadata': metadata})
181
        reply = self.http_post(path, body, success=201)
182
        return reply['metadata']
183

    
184
    def delete_image_metadata(self, image_id, key):
185
        path = '/images/%d/meta/%s' % (image_id, key)
186
        reply = self.http_delete(path)