root / kamaki / client.py @ eb3ca8ca
History | View | Annotate | Download (12.1 kB)
1 | 5d1d131b | Giorgos Verigakis | # Copyright 2011 GRNET S.A. All rights reserved.
|
---|---|---|---|
2 | 5d1d131b | Giorgos Verigakis | #
|
3 | 5d1d131b | Giorgos Verigakis | # Redistribution and use in source and binary forms, with or
|
4 | 5d1d131b | Giorgos Verigakis | # without modification, are permitted provided that the following
|
5 | 5d1d131b | Giorgos Verigakis | # conditions are met:
|
6 | 5d1d131b | Giorgos Verigakis | #
|
7 | 5d1d131b | Giorgos Verigakis | # 1. Redistributions of source code must retain the above
|
8 | 5d1d131b | Giorgos Verigakis | # copyright notice, this list of conditions and the following
|
9 | 5d1d131b | Giorgos Verigakis | # disclaimer.
|
10 | 5d1d131b | Giorgos Verigakis | #
|
11 | 5d1d131b | Giorgos Verigakis | # 2. Redistributions in binary form must reproduce the above
|
12 | 5d1d131b | Giorgos Verigakis | # copyright notice, this list of conditions and the following
|
13 | 5d1d131b | Giorgos Verigakis | # disclaimer in the documentation and/or other materials
|
14 | 5d1d131b | Giorgos Verigakis | # provided with the distribution.
|
15 | 5d1d131b | Giorgos Verigakis | #
|
16 | 5d1d131b | Giorgos Verigakis | # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 | 5d1d131b | Giorgos Verigakis | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 | 5d1d131b | Giorgos Verigakis | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 | 5d1d131b | Giorgos Verigakis | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 | 5d1d131b | Giorgos Verigakis | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 | 5d1d131b | Giorgos Verigakis | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 | 5d1d131b | Giorgos Verigakis | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 | 5d1d131b | Giorgos Verigakis | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 | 5d1d131b | Giorgos Verigakis | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 | 5d1d131b | Giorgos Verigakis | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 | 5d1d131b | Giorgos Verigakis | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 | 5d1d131b | Giorgos Verigakis | # POSSIBILITY OF SUCH DAMAGE.
|
28 | 5d1d131b | Giorgos Verigakis | #
|
29 | 5d1d131b | Giorgos Verigakis | # The views and conclusions contained in the software and
|
30 | 5d1d131b | Giorgos Verigakis | # documentation are those of the authors and should not be
|
31 | 5d1d131b | Giorgos Verigakis | # interpreted as representing official policies, either expressed
|
32 | 5d1d131b | Giorgos Verigakis | # or implied, of GRNET S.A.
|
33 | 5d1d131b | Giorgos Verigakis | |
34 | 5d1d131b | Giorgos Verigakis | import json |
35 | 5d1d131b | Giorgos Verigakis | import logging |
36 | 5d1d131b | Giorgos Verigakis | |
37 | 5d1d131b | Giorgos Verigakis | from httplib import HTTPConnection, HTTPSConnection |
38 | 5d1d131b | Giorgos Verigakis | from urlparse import urlparse |
39 | 5d1d131b | Giorgos Verigakis | |
40 | 5d1d131b | Giorgos Verigakis | |
41 | 653b0597 | Giorgos Verigakis | log = logging.getLogger('kamaki.client')
|
42 | 653b0597 | Giorgos Verigakis | |
43 | 653b0597 | Giorgos Verigakis | |
44 | 5d1d131b | Giorgos Verigakis | class ClientError(Exception): |
45 | 443c1f01 | Vangelis Koukis | def __init__(self, message, status=0, details=''): |
46 | 5d1d131b | Giorgos Verigakis | self.message = message
|
47 | 443c1f01 | Vangelis Koukis | self.status = status
|
48 | 5d1d131b | Giorgos Verigakis | self.details = details
|
49 | 5d1d131b | Giorgos Verigakis | |
50 | 443c1f01 | Vangelis Koukis | def __int__(self): |
51 | 443c1f01 | Vangelis Koukis | return int(self.status) |
52 | 443c1f01 | Vangelis Koukis | |
53 | 443c1f01 | Vangelis Koukis | def __str__(self): |
54 | 443c1f01 | Vangelis Koukis | r = self.message
|
55 | 443c1f01 | Vangelis Koukis | if self.status: |
56 | 443c1f01 | Vangelis Koukis | r += "\nHTTP Status: %d" % self.status |
57 | 443c1f01 | Vangelis Koukis | if self.details: |
58 | 443c1f01 | Vangelis Koukis | r += "\nDetails: \n%s" % self.details |
59 | 443c1f01 | Vangelis Koukis | return r
|
60 | 443c1f01 | Vangelis Koukis | |
61 | 5d1d131b | Giorgos Verigakis | |
62 | 5d1d131b | Giorgos Verigakis | class Client(object): |
63 | 5d1d131b | Giorgos Verigakis | def __init__(self, url, token=''): |
64 | 5d1d131b | Giorgos Verigakis | self.url = url
|
65 | 5d1d131b | Giorgos Verigakis | self.token = token
|
66 | 5d1d131b | Giorgos Verigakis | |
67 | 5d1d131b | Giorgos Verigakis | def _cmd(self, method, path, body=None, success=200): |
68 | 5d1d131b | Giorgos Verigakis | p = urlparse(self.url)
|
69 | 5d1d131b | Giorgos Verigakis | path = p.path + path |
70 | 5d1d131b | Giorgos Verigakis | if p.scheme == 'http': |
71 | 5d1d131b | Giorgos Verigakis | conn = HTTPConnection(p.netloc) |
72 | 5d1d131b | Giorgos Verigakis | elif p.scheme == 'https': |
73 | 5d1d131b | Giorgos Verigakis | conn = HTTPSConnection(p.netloc) |
74 | 5d1d131b | Giorgos Verigakis | else:
|
75 | 443c1f01 | Vangelis Koukis | raise ClientError('Unknown URL scheme') |
76 | 5d1d131b | Giorgos Verigakis | |
77 | 5d1d131b | Giorgos Verigakis | headers = {'X-Auth-Token': self.token} |
78 | 5d1d131b | Giorgos Verigakis | if body:
|
79 | 5d1d131b | Giorgos Verigakis | headers['Content-Type'] = 'application/json' |
80 | 5d1d131b | Giorgos Verigakis | headers['Content-Length'] = len(body) |
81 | 5d1d131b | Giorgos Verigakis | |
82 | 054e60c0 | Giorgos Verigakis | log.debug('>' * 50) |
83 | 653b0597 | Giorgos Verigakis | log.debug('%s %s', method, path)
|
84 | 5d1d131b | Giorgos Verigakis | for key, val in headers.items(): |
85 | 653b0597 | Giorgos Verigakis | log.debug('%s: %s', key, val)
|
86 | 5d1d131b | Giorgos Verigakis | if body:
|
87 | 653b0597 | Giorgos Verigakis | log.debug('')
|
88 | 054e60c0 | Giorgos Verigakis | log.debug(body) |
89 | 5d1d131b | Giorgos Verigakis | |
90 | 5d1d131b | Giorgos Verigakis | conn.request(method, path, body, headers) |
91 | 054e60c0 | Giorgos Verigakis | |
92 | 5d1d131b | Giorgos Verigakis | resp = conn.getresponse() |
93 | 054e60c0 | Giorgos Verigakis | buf = resp.read() |
94 | 054e60c0 | Giorgos Verigakis | |
95 | 054e60c0 | Giorgos Verigakis | log.debug('<' * 50) |
96 | 653b0597 | Giorgos Verigakis | log.info('%d %s', resp.status, resp.reason)
|
97 | 5d1d131b | Giorgos Verigakis | for key, val in resp.getheaders(): |
98 | 653b0597 | Giorgos Verigakis | log.info('%s: %s', key.capitalize(), val)
|
99 | 653b0597 | Giorgos Verigakis | log.info('')
|
100 | 054e60c0 | Giorgos Verigakis | log.debug(buf) |
101 | 054e60c0 | Giorgos Verigakis | log.debug('-' * 50) |
102 | 5d1d131b | Giorgos Verigakis | |
103 | 5d1d131b | Giorgos Verigakis | try:
|
104 | 5d1d131b | Giorgos Verigakis | reply = json.loads(buf) if buf else {} |
105 | 5d1d131b | Giorgos Verigakis | except ValueError: |
106 | 6d604f07 | Vangelis Koukis | raise ClientError('Did not receive valid JSON reply', |
107 | 6d604f07 | Vangelis Koukis | resp.status, buf) |
108 | 5d1d131b | Giorgos Verigakis | |
109 | 5d1d131b | Giorgos Verigakis | if resp.status != success:
|
110 | 5d1d131b | Giorgos Verigakis | if len(reply) == 1: |
111 | 5d1d131b | Giorgos Verigakis | key = reply.keys()[0]
|
112 | 5d1d131b | Giorgos Verigakis | val = reply[key] |
113 | 5d1d131b | Giorgos Verigakis | message = '%s: %s' % (key, val.get('message', '')) |
114 | 5d1d131b | Giorgos Verigakis | details = val.get('details', '') |
115 | 443c1f01 | Vangelis Koukis | raise ClientError(message, resp.status, details)
|
116 | 5d1d131b | Giorgos Verigakis | else:
|
117 | 5d1d131b | Giorgos Verigakis | raise ClientError('Invalid response from the server') |
118 | 5d1d131b | Giorgos Verigakis | |
119 | 5d1d131b | Giorgos Verigakis | return reply
|
120 | 5d1d131b | Giorgos Verigakis | |
121 | 5d1d131b | Giorgos Verigakis | def _get(self, path, success=200): |
122 | 5d1d131b | Giorgos Verigakis | return self._cmd('GET', path, None, success) |
123 | 5d1d131b | Giorgos Verigakis | |
124 | 5d1d131b | Giorgos Verigakis | def _post(self, path, body, success=202): |
125 | 5d1d131b | Giorgos Verigakis | return self._cmd('POST', path, body, success) |
126 | 5d1d131b | Giorgos Verigakis | |
127 | 5d1d131b | Giorgos Verigakis | def _put(self, path, body, success=204): |
128 | 5d1d131b | Giorgos Verigakis | return self._cmd('PUT', path, body, success) |
129 | 5d1d131b | Giorgos Verigakis | |
130 | 5d1d131b | Giorgos Verigakis | def _delete(self, path, success=204): |
131 | 5d1d131b | Giorgos Verigakis | return self._cmd('DELETE', path, None, success) |
132 | eb3ca8ca | Giorgos Verigakis | |
133 | eb3ca8ca | Giorgos Verigakis | |
134 | eb3ca8ca | Giorgos Verigakis | class ComputeClient(Client): |
135 | 5d1d131b | Giorgos Verigakis | # Servers
|
136 | 5d1d131b | Giorgos Verigakis | |
137 | 5d1d131b | Giorgos Verigakis | def list_servers(self, detail=False): |
138 | f108622a | Vangelis Koukis | """List servers, returned detailed output if detailed is True"""
|
139 | 5d1d131b | Giorgos Verigakis | path = '/servers/detail' if detail else '/servers' |
140 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
141 | 5d1d131b | Giorgos Verigakis | return reply['servers']['values'] |
142 | 5d1d131b | Giorgos Verigakis | |
143 | 5d1d131b | Giorgos Verigakis | def get_server_details(self, server_id): |
144 | f108622a | Vangelis Koukis | """Return detailed output on a server specified by its id"""
|
145 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d' % server_id
|
146 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
147 | 5d1d131b | Giorgos Verigakis | return reply['server'] |
148 | 5d1d131b | Giorgos Verigakis | |
149 | f108622a | Vangelis Koukis | def create_server(self, name, flavor_id, image_id, personality=None): |
150 | f108622a | Vangelis Koukis | """Submit request to create a new server
|
151 | f108622a | Vangelis Koukis |
|
152 | f108622a | Vangelis Koukis | The flavor_id specifies the hardware configuration to use,
|
153 | f108622a | Vangelis Koukis | the image_id specifies the OS Image to be deployed inside the new
|
154 | f108622a | Vangelis Koukis | server.
|
155 | f108622a | Vangelis Koukis |
|
156 | f108622a | Vangelis Koukis | The personality argument is a list of (file path, file contents)
|
157 | f108622a | Vangelis Koukis | tuples, describing files to be injected into the server upon creation.
|
158 | f108622a | Vangelis Koukis |
|
159 | f108622a | Vangelis Koukis | The call returns a dictionary describing the newly created server.
|
160 | f108622a | Vangelis Koukis |
|
161 | f108622a | Vangelis Koukis | """
|
162 | 76f01c50 | Giorgos Verigakis | |
163 | f108622a | Vangelis Koukis | req = {'name': name, 'flavorRef': flavor_id, 'imageRef': image_id} |
164 | 76f01c50 | Giorgos Verigakis | if personality:
|
165 | 57b8dd5a | Giorgos Verigakis | req['personality'] = personality
|
166 | 76f01c50 | Giorgos Verigakis | |
167 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'server': req})
|
168 | 5d1d131b | Giorgos Verigakis | reply = self._post('/servers', body) |
169 | 5d1d131b | Giorgos Verigakis | return reply['server'] |
170 | 5d1d131b | Giorgos Verigakis | |
171 | 5d1d131b | Giorgos Verigakis | def update_server_name(self, server_id, new_name): |
172 | f108622a | Vangelis Koukis | """Update the name of the server as reported by the API.
|
173 | f108622a | Vangelis Koukis |
|
174 | f108622a | Vangelis Koukis | This call does not modify the hostname actually used by the server
|
175 | f108622a | Vangelis Koukis | internally.
|
176 | f108622a | Vangelis Koukis |
|
177 | f108622a | Vangelis Koukis | """
|
178 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d' % server_id
|
179 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'server': {'name': new_name}}) |
180 | 5d1d131b | Giorgos Verigakis | self._put(path, body)
|
181 | 5d1d131b | Giorgos Verigakis | |
182 | 5d1d131b | Giorgos Verigakis | def delete_server(self, server_id): |
183 | f108622a | Vangelis Koukis | """Submit a deletion request for a server specified by id"""
|
184 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d' % server_id
|
185 | 5d1d131b | Giorgos Verigakis | self._delete(path)
|
186 | 5d1d131b | Giorgos Verigakis | |
187 | 5d1d131b | Giorgos Verigakis | def reboot_server(self, server_id, hard=False): |
188 | f108622a | Vangelis Koukis | """Submit a reboot request for a server specified by id"""
|
189 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/action' % server_id
|
190 | 5d1d131b | Giorgos Verigakis | type = 'HARD' if hard else 'SOFT' |
191 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'reboot': {'type': type}}) |
192 | 5d1d131b | Giorgos Verigakis | self._post(path, body)
|
193 | 5d1d131b | Giorgos Verigakis | |
194 | 5d1d131b | Giorgos Verigakis | def start_server(self, server_id): |
195 | f108622a | Vangelis Koukis | """Submit a startup request for a server specified by id"""
|
196 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/action' % server_id
|
197 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'start': {}})
|
198 | 5d1d131b | Giorgos Verigakis | self._post(path, body)
|
199 | 5d1d131b | Giorgos Verigakis | |
200 | 5d1d131b | Giorgos Verigakis | def shutdown_server(self, server_id): |
201 | f108622a | Vangelis Koukis | """Submit a shutdown request for a server specified by id"""
|
202 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/action' % server_id
|
203 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'shutdown': {}})
|
204 | 5d1d131b | Giorgos Verigakis | self._post(path, body)
|
205 | 5d1d131b | Giorgos Verigakis | |
206 | 5d1d131b | Giorgos Verigakis | def get_server_console(self, server_id): |
207 | f108622a | Vangelis Koukis | """Get a VNC connection to the console of a server specified by id"""
|
208 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/action' % server_id
|
209 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'console': {'type': 'vnc'}}) |
210 | 9a3f4ffc | Giorgos Verigakis | reply = self._post(path, body, 200) |
211 | 5d1d131b | Giorgos Verigakis | return reply['console'] |
212 | 5d1d131b | Giorgos Verigakis | |
213 | 5d1d131b | Giorgos Verigakis | def set_firewall_profile(self, server_id, profile): |
214 | f108622a | Vangelis Koukis | """Set the firewall profile for the public interface of a server
|
215 | f108622a | Vangelis Koukis |
|
216 | f108622a | Vangelis Koukis | The server is specified by id, the profile argument
|
217 | f108622a | Vangelis Koukis | is one of (ENABLED, DISABLED, PROTECTED).
|
218 | f108622a | Vangelis Koukis |
|
219 | f108622a | Vangelis Koukis | """
|
220 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/action' % server_id
|
221 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'firewallProfile': {'profile': profile}}) |
222 | 9a3f4ffc | Giorgos Verigakis | self._post(path, body, 202) |
223 | 5d1d131b | Giorgos Verigakis | |
224 | 5d1d131b | Giorgos Verigakis | def list_server_addresses(self, server_id, network=None): |
225 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/ips' % server_id
|
226 | 5d1d131b | Giorgos Verigakis | if network:
|
227 | 5d1d131b | Giorgos Verigakis | path += '/%s' % network
|
228 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
229 | 5d1d131b | Giorgos Verigakis | return [reply['network']] if network else reply['addresses']['values'] |
230 | 5d1d131b | Giorgos Verigakis | |
231 | 5d1d131b | Giorgos Verigakis | def get_server_metadata(self, server_id, key=None): |
232 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/meta' % server_id
|
233 | 5d1d131b | Giorgos Verigakis | if key:
|
234 | 5d1d131b | Giorgos Verigakis | path += '/%s' % key
|
235 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
236 | 5d1d131b | Giorgos Verigakis | return reply['meta'] if key else reply['metadata']['values'] |
237 | 5d1d131b | Giorgos Verigakis | |
238 | 5d1d131b | Giorgos Verigakis | def create_server_metadata(self, server_id, key, val): |
239 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/meta/%s' % (server_id, key)
|
240 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'meta': {key: val}})
|
241 | 5d1d131b | Giorgos Verigakis | reply = self._put(path, body, 201) |
242 | 5d1d131b | Giorgos Verigakis | return reply['meta'] |
243 | 5d1d131b | Giorgos Verigakis | |
244 | ec52784d | Giorgos Verigakis | def update_server_metadata(self, server_id, **metadata): |
245 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/meta' % server_id
|
246 | ec52784d | Giorgos Verigakis | body = json.dumps({'metadata': metadata})
|
247 | 5d1d131b | Giorgos Verigakis | reply = self._post(path, body, 201) |
248 | 5d1d131b | Giorgos Verigakis | return reply['metadata'] |
249 | 5d1d131b | Giorgos Verigakis | |
250 | 5d1d131b | Giorgos Verigakis | def delete_server_metadata(self, server_id, key): |
251 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/meta/%s' % (server_id, key)
|
252 | 5d1d131b | Giorgos Verigakis | reply = self._delete(path)
|
253 | 5d1d131b | Giorgos Verigakis | |
254 | 5d1d131b | Giorgos Verigakis | def get_server_stats(self, server_id): |
255 | 5d1d131b | Giorgos Verigakis | path = '/servers/%d/stats' % server_id
|
256 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
257 | 5d1d131b | Giorgos Verigakis | return reply['stats'] |
258 | 5d1d131b | Giorgos Verigakis | |
259 | 5d1d131b | Giorgos Verigakis | |
260 | 5d1d131b | Giorgos Verigakis | # Flavors
|
261 | 5d1d131b | Giorgos Verigakis | |
262 | 5d1d131b | Giorgos Verigakis | def list_flavors(self, detail=False): |
263 | 5d1d131b | Giorgos Verigakis | path = '/flavors/detail' if detail else '/flavors' |
264 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
265 | 5d1d131b | Giorgos Verigakis | return reply['flavors']['values'] |
266 | 5d1d131b | Giorgos Verigakis | |
267 | 5d1d131b | Giorgos Verigakis | def get_flavor_details(self, flavor_id): |
268 | 5d1d131b | Giorgos Verigakis | path = '/flavors/%d' % flavor_id
|
269 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
270 | 5d1d131b | Giorgos Verigakis | return reply['flavor'] |
271 | 5d1d131b | Giorgos Verigakis | |
272 | 5d1d131b | Giorgos Verigakis | |
273 | 5d1d131b | Giorgos Verigakis | # Images
|
274 | 5d1d131b | Giorgos Verigakis | |
275 | 5d1d131b | Giorgos Verigakis | def list_images(self, detail=False): |
276 | 5d1d131b | Giorgos Verigakis | path = '/images/detail' if detail else '/images' |
277 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
278 | 5d1d131b | Giorgos Verigakis | return reply['images']['values'] |
279 | 5d1d131b | Giorgos Verigakis | |
280 | 5d1d131b | Giorgos Verigakis | def get_image_details(self, image_id): |
281 | 5d1d131b | Giorgos Verigakis | path = '/images/%d' % image_id
|
282 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
283 | 5d1d131b | Giorgos Verigakis | return reply['image'] |
284 | 5d1d131b | Giorgos Verigakis | |
285 | 5d1d131b | Giorgos Verigakis | def create_image(self, server_id, name): |
286 | 5d1d131b | Giorgos Verigakis | req = {'name': name, 'serverRef': server_id} |
287 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'image': req})
|
288 | 5d1d131b | Giorgos Verigakis | reply = self._post('/images', body) |
289 | 5d1d131b | Giorgos Verigakis | return reply['image'] |
290 | 5d1d131b | Giorgos Verigakis | |
291 | 5d1d131b | Giorgos Verigakis | def delete_image(self, image_id): |
292 | 5d1d131b | Giorgos Verigakis | path = '/images/%d' % image_id
|
293 | 5d1d131b | Giorgos Verigakis | self._delete(path)
|
294 | 5d1d131b | Giorgos Verigakis | |
295 | 5d1d131b | Giorgos Verigakis | def get_image_metadata(self, image_id, key=None): |
296 | 5d1d131b | Giorgos Verigakis | path = '/images/%d/meta' % image_id
|
297 | 5d1d131b | Giorgos Verigakis | if key:
|
298 | 5d1d131b | Giorgos Verigakis | path += '/%s' % key
|
299 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
300 | 5d1d131b | Giorgos Verigakis | return reply['meta'] if key else reply['metadata']['values'] |
301 | 5d1d131b | Giorgos Verigakis | |
302 | 5d1d131b | Giorgos Verigakis | def create_image_metadata(self, image_id, key, val): |
303 | 5d1d131b | Giorgos Verigakis | path = '/images/%d/meta/%s' % (image_id, key)
|
304 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'meta': {key: val}})
|
305 | 5d1d131b | Giorgos Verigakis | reply = self._put(path, body, 201) |
306 | 5d1d131b | Giorgos Verigakis | reply['meta']
|
307 | 5d1d131b | Giorgos Verigakis | |
308 | ec52784d | Giorgos Verigakis | def update_image_metadata(self, image_id, **metadata): |
309 | 5d1d131b | Giorgos Verigakis | path = '/images/%d/meta' % image_id
|
310 | ec52784d | Giorgos Verigakis | body = json.dumps({'metadata': metadata})
|
311 | 5d1d131b | Giorgos Verigakis | reply = self._post(path, body, 201) |
312 | 5d1d131b | Giorgos Verigakis | return reply['metadata'] |
313 | 5d1d131b | Giorgos Verigakis | |
314 | 5d1d131b | Giorgos Verigakis | def delete_image_metadata(self, image_id, key): |
315 | 5d1d131b | Giorgos Verigakis | path = '/images/%d/meta/%s' % (image_id, key)
|
316 | 5d1d131b | Giorgos Verigakis | reply = self._delete(path)
|
317 | 5d1d131b | Giorgos Verigakis | |
318 | 5d1d131b | Giorgos Verigakis | |
319 | 5d1d131b | Giorgos Verigakis | # Networks
|
320 | 5d1d131b | Giorgos Verigakis | |
321 | 5d1d131b | Giorgos Verigakis | def list_networks(self, detail=False): |
322 | 5d1d131b | Giorgos Verigakis | path = '/networks/detail' if detail else '/networks' |
323 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
324 | 5d1d131b | Giorgos Verigakis | return reply['networks']['values'] |
325 | 5d1d131b | Giorgos Verigakis | |
326 | 5d1d131b | Giorgos Verigakis | def create_network(self, name): |
327 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'network': {'name': name}}) |
328 | 5d1d131b | Giorgos Verigakis | reply = self._post('/networks', body) |
329 | 5d1d131b | Giorgos Verigakis | return reply['network'] |
330 | 5d1d131b | Giorgos Verigakis | |
331 | 5d1d131b | Giorgos Verigakis | def get_network_details(self, network_id): |
332 | 5d1d131b | Giorgos Verigakis | path = '/networks/%s' % network_id
|
333 | 5d1d131b | Giorgos Verigakis | reply = self._get(path)
|
334 | 5d1d131b | Giorgos Verigakis | return reply['network'] |
335 | 5d1d131b | Giorgos Verigakis | |
336 | 5d1d131b | Giorgos Verigakis | def update_network_name(self, network_id, new_name): |
337 | 5d1d131b | Giorgos Verigakis | path = '/networks/%s' % network_id
|
338 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'network': {'name': new_name}}) |
339 | 5d1d131b | Giorgos Verigakis | self._put(path, body)
|
340 | 5d1d131b | Giorgos Verigakis | |
341 | 5d1d131b | Giorgos Verigakis | def delete_network(self, network_id): |
342 | 5d1d131b | Giorgos Verigakis | path = '/networks/%s' % network_id
|
343 | 5d1d131b | Giorgos Verigakis | self._delete(path)
|
344 | 5d1d131b | Giorgos Verigakis | |
345 | 5d1d131b | Giorgos Verigakis | def connect_server(self, server_id, network_id): |
346 | 5d1d131b | Giorgos Verigakis | path = '/networks/%s/action' % network_id
|
347 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'add': {'serverRef': server_id}}) |
348 | 5d1d131b | Giorgos Verigakis | self._post(path, body)
|
349 | 5d1d131b | Giorgos Verigakis | |
350 | 5d1d131b | Giorgos Verigakis | def disconnect_server(self, server_id, network_id): |
351 | 5d1d131b | Giorgos Verigakis | path = '/networks/%s/action' % network_id
|
352 | 5d1d131b | Giorgos Verigakis | body = json.dumps({'remove': {'serverRef': server_id}}) |
353 | 5d1d131b | Giorgos Verigakis | self._post(path, body)
|
354 | eb3ca8ca | Giorgos Verigakis | |
355 | eb3ca8ca | Giorgos Verigakis | |
356 | eb3ca8ca | Giorgos Verigakis | class ImagesClient(Client): |
357 | eb3ca8ca | Giorgos Verigakis | def list_public(self, detail=False): |
358 | eb3ca8ca | Giorgos Verigakis | path = '/images/detail' if detail else '/images/' |
359 | eb3ca8ca | Giorgos Verigakis | return self._get(path) |