Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / compute.py @ b3b32add

History | View | Annotate | Download (6.7 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 . import ClientError
37
from .http import HTTPClient
38

    
39

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

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

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

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

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

    
140
    def get_flavor_details(self, flavor_id):
141
        path = '/flavors/%d' % flavor_id
142
        reply = self.http_get(path)
143
        return reply['flavor']
144
    
145
    
146
    def list_images(self, detail=False):
147
        path = '/images/detail' if detail else '/images'
148
        reply = self.http_get(path)
149
        return reply['images']['values']
150
    
151
    def get_image_details(self, image_id):
152
        path = '/images/%s' % image_id
153
        reply = self.http_get(path)
154
        return reply['image']
155
    
156
    def delete_image(self, image_id):
157
        path = '/images/%s' % image_id
158
        self.http_delete(path)
159

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

    
173
    def update_image_metadata(self, image_id, **metadata):
174
        path = '/images/%s/meta' % image_id
175
        body = json.dumps({'metadata': metadata})
176
        reply = self.http_post(path, body, success=201)
177
        return reply['metadata']
178

    
179
    def delete_image_metadata(self, image_id, key):
180
        path = '/images/%s/meta/%s' % (image_id, key)
181
        self.http_delete(path)