Statistics
| Branch: | Tag: | Revision:

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)