root / kamaki / clients / cyclades / __init__.py @ 89a1c636
History | View | Annotate | Download (9.7 kB)
1 | 24851aa5 | Stavros Sachtouris | # Copyright 2011-2013 GRNET S.A. All rights reserved.
|
---|---|---|---|
2 | a1c50326 | Giorgos Verigakis | #
|
3 | a1c50326 | Giorgos Verigakis | # Redistribution and use in source and binary forms, with or
|
4 | a1c50326 | Giorgos Verigakis | # without modification, are permitted provided that the following
|
5 | a1c50326 | Giorgos Verigakis | # conditions are met:
|
6 | a1c50326 | Giorgos Verigakis | #
|
7 | a1c50326 | Giorgos Verigakis | # 1. Redistributions of source code must retain the above
|
8 | a1c50326 | Giorgos Verigakis | # copyright notice, this list of conditions and the following
|
9 | a1c50326 | Giorgos Verigakis | # disclaimer.
|
10 | a1c50326 | Giorgos Verigakis | #
|
11 | a1c50326 | Giorgos Verigakis | # 2. Redistributions in binary form must reproduce the above
|
12 | a1c50326 | Giorgos Verigakis | # copyright notice, this list of conditions and the following
|
13 | a1c50326 | Giorgos Verigakis | # disclaimer in the documentation and/or other materials
|
14 | a1c50326 | Giorgos Verigakis | # provided with the distribution.
|
15 | a1c50326 | Giorgos Verigakis | #
|
16 | a1c50326 | Giorgos Verigakis | # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 | a1c50326 | Giorgos Verigakis | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 | a1c50326 | Giorgos Verigakis | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 | a1c50326 | Giorgos Verigakis | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 | a1c50326 | Giorgos Verigakis | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 | a1c50326 | Giorgos Verigakis | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 | a1c50326 | Giorgos Verigakis | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 | a1c50326 | Giorgos Verigakis | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 | a1c50326 | Giorgos Verigakis | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 | a1c50326 | Giorgos Verigakis | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 | a1c50326 | Giorgos Verigakis | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 | a1c50326 | Giorgos Verigakis | # POSSIBILITY OF SUCH DAMAGE.
|
28 | a1c50326 | Giorgos Verigakis | #
|
29 | a1c50326 | Giorgos Verigakis | # The views and conclusions contained in the software and
|
30 | a1c50326 | Giorgos Verigakis | # documentation are those of the authors and should not be
|
31 | a1c50326 | Giorgos Verigakis | # interpreted as representing official policies, either expressed
|
32 | a1c50326 | Giorgos Verigakis | # or implied, of GRNET S.A.
|
33 | a1c50326 | Giorgos Verigakis | |
34 | 55faa0bc | Stavros Sachtouris | from kamaki.clients.cyclades.rest_api import CycladesRestClient |
35 | e864cd9e | Stavros Sachtouris | from kamaki.clients.network import NetworkClient |
36 | 0e27687b | Stavros Sachtouris | from kamaki.clients.utils import path4url |
37 | 6f2b87c1 | Stavros Sachtouris | from kamaki.clients import ClientError, Waiter |
38 | 89a1c636 | Giorgos Korfiatis | import json |
39 | 3dabe5d2 | Stavros Sachtouris | |
40 | 6f2b87c1 | Stavros Sachtouris | class CycladesClient(CycladesRestClient, Waiter): |
41 | 76e7661e | Stavros Sachtouris | """Synnefo Cyclades Compute API client"""
|
42 | 2f749e6e | Stavros Sachtouris | |
43 | dbcbf446 | Stavros Sachtouris | def create_server( |
44 | dbcbf446 | Stavros Sachtouris | self, name, flavor_id, image_id,
|
45 | 89a1c636 | Giorgos Korfiatis | metadata=None, personality=None, networks=None, project=None): |
46 | dbcbf446 | Stavros Sachtouris | """Submit request to create a new server
|
47 | dbcbf446 | Stavros Sachtouris |
|
48 | dbcbf446 | Stavros Sachtouris | :param name: (str)
|
49 | dbcbf446 | Stavros Sachtouris |
|
50 | dbcbf446 | Stavros Sachtouris | :param flavor_id: integer id denoting a preset hardware configuration
|
51 | dbcbf446 | Stavros Sachtouris |
|
52 | 2bd23362 | Stavros Sachtouris | :param image_id: (str) id denoting the OS image to run on virt. server
|
53 | dbcbf446 | Stavros Sachtouris |
|
54 | dbcbf446 | Stavros Sachtouris | :param metadata: (dict) vm metadata updated by os/users image metadata
|
55 | dbcbf446 | Stavros Sachtouris |
|
56 | dbcbf446 | Stavros Sachtouris | :param personality: a list of (file path, file contents) tuples,
|
57 | 2bd23362 | Stavros Sachtouris | describing files to be injected into virtual server upon creation
|
58 | dbcbf446 | Stavros Sachtouris |
|
59 | 264a13f7 | Stavros Sachtouris | :param networks: (list of dicts) Networks to connect to, list this:
|
60 | 264a13f7 | Stavros Sachtouris | "networks": [
|
61 | de329b4c | Stavros Sachtouris | {"uuid": <network_uuid>},
|
62 | de329b4c | Stavros Sachtouris | {"uuid": <network_uuid>, "fixed_ip": address},
|
63 | de329b4c | Stavros Sachtouris | {"port": <port_id>}, ...]
|
64 | eb647cfe | Stavros Sachtouris | ATTENTION: Empty list is different to None. None means ' do not
|
65 | eb647cfe | Stavros Sachtouris | mention it', empty list means 'automatically get an ip'
|
66 | 264a13f7 | Stavros Sachtouris |
|
67 | 89a1c636 | Giorgos Korfiatis | :param project: the project where to assign the server
|
68 | 89a1c636 | Giorgos Korfiatis |
|
69 | 2bd23362 | Stavros Sachtouris | :returns: a dict with the new virtual server details
|
70 | dbcbf446 | Stavros Sachtouris |
|
71 | dbcbf446 | Stavros Sachtouris | :raises ClientError: wraps request errors
|
72 | dbcbf446 | Stavros Sachtouris | """
|
73 | dbcbf446 | Stavros Sachtouris | image = self.get_image_details(image_id)
|
74 | dbcbf446 | Stavros Sachtouris | metadata = metadata or dict() |
75 | dbcbf446 | Stavros Sachtouris | for key in ('os', 'users'): |
76 | dbcbf446 | Stavros Sachtouris | try:
|
77 | dbcbf446 | Stavros Sachtouris | metadata[key] = image['metadata'][key]
|
78 | dbcbf446 | Stavros Sachtouris | except KeyError: |
79 | dbcbf446 | Stavros Sachtouris | pass
|
80 | dbcbf446 | Stavros Sachtouris | |
81 | 65a8b1da | Stavros Sachtouris | return super(CycladesClient, self).create_server( |
82 | 65a8b1da | Stavros Sachtouris | name, flavor_id, image_id, |
83 | 89a1c636 | Giorgos Korfiatis | metadata=metadata, personality=personality, networks=networks, |
84 | 89a1c636 | Giorgos Korfiatis | project=project) |
85 | dbcbf446 | Stavros Sachtouris | |
86 | a1c50326 | Giorgos Verigakis | def start_server(self, server_id): |
87 | f5eac743 | Stavros Sachtouris | """Submit a startup request
|
88 | f5eac743 | Stavros Sachtouris |
|
89 | f5eac743 | Stavros Sachtouris | :param server_id: integer (str or int)
|
90 | cd295a1d | Stavros Sachtouris |
|
91 | cd295a1d | Stavros Sachtouris | :returns: (dict) response headers
|
92 | f5eac743 | Stavros Sachtouris | """
|
93 | 6a0b1658 | Giorgos Verigakis | req = {'start': {}}
|
94 | ef2e6c9f | Stavros Sachtouris | r = self.servers_action_post(server_id, json_data=req, success=202) |
95 | cd295a1d | Stavros Sachtouris | return r.headers
|
96 | 6f1ec797 | Stavros Sachtouris | |
97 | a1c50326 | Giorgos Verigakis | def shutdown_server(self, server_id): |
98 | f5eac743 | Stavros Sachtouris | """Submit a shutdown request
|
99 | f5eac743 | Stavros Sachtouris |
|
100 | f5eac743 | Stavros Sachtouris | :param server_id: integer (str or int)
|
101 | cd295a1d | Stavros Sachtouris |
|
102 | cd295a1d | Stavros Sachtouris | :returns: (dict) response headers
|
103 | f5eac743 | Stavros Sachtouris | """
|
104 | 6a0b1658 | Giorgos Verigakis | req = {'shutdown': {}}
|
105 | ef2e6c9f | Stavros Sachtouris | r = self.servers_action_post(server_id, json_data=req, success=202) |
106 | cd295a1d | Stavros Sachtouris | return r.headers
|
107 | 3dabe5d2 | Stavros Sachtouris | |
108 | a1c50326 | Giorgos Verigakis | def get_server_console(self, server_id): |
109 | f5eac743 | Stavros Sachtouris | """
|
110 | f5eac743 | Stavros Sachtouris | :param server_id: integer (str or int)
|
111 | f5eac743 | Stavros Sachtouris |
|
112 | 2bd23362 | Stavros Sachtouris | :returns: (dict) info to set a VNC connection to virtual server
|
113 | f5eac743 | Stavros Sachtouris | """
|
114 | 6a0b1658 | Giorgos Verigakis | req = {'console': {'type': 'vnc'}} |
115 | ef2e6c9f | Stavros Sachtouris | r = self.servers_action_post(server_id, json_data=req, success=200) |
116 | 6a0b1658 | Giorgos Verigakis | return r.json['console'] |
117 | 2d67ecbb | Stavros Sachtouris | |
118 | 89a1c636 | Giorgos Korfiatis | def reassign_server(self, server_id, project): |
119 | 89a1c636 | Giorgos Korfiatis | req = {'reassign': {'project': project}} |
120 | 89a1c636 | Giorgos Korfiatis | r = self.servers_action_post(server_id, json_data=req, success=200) |
121 | 89a1c636 | Giorgos Korfiatis | return r.headers
|
122 | 89a1c636 | Giorgos Korfiatis | |
123 | a1c50326 | Giorgos Verigakis | def get_server_stats(self, server_id): |
124 | f5eac743 | Stavros Sachtouris | """
|
125 | f5eac743 | Stavros Sachtouris | :param server_id: integer (str or int)
|
126 | f5eac743 | Stavros Sachtouris |
|
127 | f5eac743 | Stavros Sachtouris | :returns: (dict) auto-generated graphs of statistics (urls)
|
128 | f5eac743 | Stavros Sachtouris | """
|
129 | e51c7d5b | Stavros Sachtouris | r = self.servers_stats_get(server_id)
|
130 | 6a0b1658 | Giorgos Verigakis | return r.json['stats'] |
131 | 3dabe5d2 | Stavros Sachtouris | |
132 | b45834eb | Stavros Sachtouris | def get_server_diagnostics(self, server_id): |
133 | b45834eb | Stavros Sachtouris | """
|
134 | b45834eb | Stavros Sachtouris | :param server_id: integer (str or int)
|
135 | b45834eb | Stavros Sachtouris |
|
136 | b45834eb | Stavros Sachtouris | :returns: (list)
|
137 | b45834eb | Stavros Sachtouris | """
|
138 | b45834eb | Stavros Sachtouris | r = self.servers_diagnostics_get(server_id)
|
139 | b45834eb | Stavros Sachtouris | return r.json
|
140 | b45834eb | Stavros Sachtouris | |
141 | 7b2e4bf1 | Stavros Sachtouris | def wait_server( |
142 | 7b2e4bf1 | Stavros Sachtouris | self, server_id,
|
143 | 7b2e4bf1 | Stavros Sachtouris | current_status='BUILD',
|
144 | 7b2e4bf1 | Stavros Sachtouris | delay=1, max_wait=100, wait_cb=None): |
145 | 7b2e4bf1 | Stavros Sachtouris | """Wait for server while its status is current_status
|
146 | 7b2e4bf1 | Stavros Sachtouris |
|
147 | 7b2e4bf1 | Stavros Sachtouris | :param server_id: integer (str or int)
|
148 | 7b2e4bf1 | Stavros Sachtouris |
|
149 | 7b2e4bf1 | Stavros Sachtouris | :param current_status: (str) BUILD|ACTIVE|STOPPED|DELETED|REBOOT
|
150 | 7b2e4bf1 | Stavros Sachtouris |
|
151 | 7b2e4bf1 | Stavros Sachtouris | :param delay: time interval between retries
|
152 | 7b2e4bf1 | Stavros Sachtouris |
|
153 | c788a761 | Stavros Sachtouris | :max_wait: (int) timeout in secconds
|
154 | c788a761 | Stavros Sachtouris |
|
155 | 7b2e4bf1 | Stavros Sachtouris | :param wait_cb: if set a progressbar is used to show progress
|
156 | 7b2e4bf1 | Stavros Sachtouris |
|
157 | 7b2e4bf1 | Stavros Sachtouris | :returns: (str) the new mode if succesfull, (bool) False if timed out
|
158 | 7b2e4bf1 | Stavros Sachtouris | """
|
159 | 7b2e4bf1 | Stavros Sachtouris | |
160 | 7b2e4bf1 | Stavros Sachtouris | def get_status(self, server_id): |
161 | 7b2e4bf1 | Stavros Sachtouris | r = self.get_server_details(server_id)
|
162 | 7b2e4bf1 | Stavros Sachtouris | return r['status'], (r.get('progress', None) if ( |
163 | 7b2e4bf1 | Stavros Sachtouris | current_status in ('BUILD', )) else None) |
164 | 7b2e4bf1 | Stavros Sachtouris | |
165 | 7b2e4bf1 | Stavros Sachtouris | return self._wait( |
166 | 7b2e4bf1 | Stavros Sachtouris | server_id, current_status, get_status, delay, max_wait, wait_cb) |
167 | 7b2e4bf1 | Stavros Sachtouris | |
168 | e864cd9e | Stavros Sachtouris | |
169 | eb647cfe | Stavros Sachtouris | class CycladesNetworkClient(NetworkClient): |
170 | e864cd9e | Stavros Sachtouris | """Cyclades Network API extentions"""
|
171 | e864cd9e | Stavros Sachtouris | |
172 | e864cd9e | Stavros Sachtouris | network_types = ( |
173 | e864cd9e | Stavros Sachtouris | 'CUSTOM', 'MAC_FILTERED', 'IP_LESS_ROUTED', 'PHYSICAL_VLAN') |
174 | e864cd9e | Stavros Sachtouris | |
175 | 0e27687b | Stavros Sachtouris | def list_networks(self, detail=None): |
176 | 0e27687b | Stavros Sachtouris | path = path4url('networks', 'detail' if detail else '') |
177 | 0e27687b | Stavros Sachtouris | r = self.get(path, success=200) |
178 | 0e27687b | Stavros Sachtouris | return r.json['networks'] |
179 | 0e27687b | Stavros Sachtouris | |
180 | 89a1c636 | Giorgos Korfiatis | def create_network(self, type, name=None, shared=None, project=None): |
181 | e864cd9e | Stavros Sachtouris | req = dict(network=dict(type=type, admin_state_up=True)) |
182 | e864cd9e | Stavros Sachtouris | if name:
|
183 | e864cd9e | Stavros Sachtouris | req['network']['name'] = name |
184 | e864cd9e | Stavros Sachtouris | if shared not in (None, ): |
185 | e864cd9e | Stavros Sachtouris | req['network']['shared'] = bool(shared) |
186 | 89a1c636 | Giorgos Korfiatis | if project is not None: |
187 | 89a1c636 | Giorgos Korfiatis | req['network']['project'] = project |
188 | e864cd9e | Stavros Sachtouris | r = self.networks_post(json_data=req, success=201) |
189 | e864cd9e | Stavros Sachtouris | return r.json['network'] |
190 | ccdd1b82 | Stavros Sachtouris | |
191 | 89a1c636 | Giorgos Korfiatis | def networks_action_post( |
192 | 89a1c636 | Giorgos Korfiatis | self, network_id='', json_data=None, success=202, **kwargs): |
193 | 89a1c636 | Giorgos Korfiatis | """POST base_url/networks/<network_id>/action
|
194 | 89a1c636 | Giorgos Korfiatis |
|
195 | 89a1c636 | Giorgos Korfiatis | :returns: request response
|
196 | 89a1c636 | Giorgos Korfiatis | """
|
197 | 89a1c636 | Giorgos Korfiatis | if json_data:
|
198 | 89a1c636 | Giorgos Korfiatis | json_data = json.dumps(json_data) |
199 | 89a1c636 | Giorgos Korfiatis | self.set_header('Content-Type', 'application/json') |
200 | 89a1c636 | Giorgos Korfiatis | self.set_header('Content-Length', len(json_data)) |
201 | 89a1c636 | Giorgos Korfiatis | path = path4url('networks', network_id, 'action') |
202 | 89a1c636 | Giorgos Korfiatis | return self.post(path, data=json_data, success=success, **kwargs) |
203 | 89a1c636 | Giorgos Korfiatis | |
204 | 89a1c636 | Giorgos Korfiatis | def reassign_network(self, network_id, project): |
205 | 89a1c636 | Giorgos Korfiatis | req = {'reassign': {'project': project}} |
206 | 89a1c636 | Giorgos Korfiatis | r = self.networks_action_post(network_id, json_data=req, success=200) |
207 | 89a1c636 | Giorgos Korfiatis | return r.headers
|
208 | 89a1c636 | Giorgos Korfiatis | |
209 | f3740b99 | Stavros Sachtouris | def list_ports(self, detail=None): |
210 | f3740b99 | Stavros Sachtouris | path = path4url('ports', 'detail' if detail else '') |
211 | f3740b99 | Stavros Sachtouris | r = self.get(path, success=200) |
212 | f3740b99 | Stavros Sachtouris | return r.json['ports'] |
213 | f3740b99 | Stavros Sachtouris | |
214 | 737995ed | Stavros Sachtouris | def create_port( |
215 | eb647cfe | Stavros Sachtouris | self, network_id,
|
216 | eb647cfe | Stavros Sachtouris | device_id=None, security_groups=None, name=None, fixed_ips=None): |
217 | b82c93a5 | Stavros Sachtouris | """
|
218 | b82c93a5 | Stavros Sachtouris | :param fixed_ips: (list of dicts) [{"ip_address": IPv4}, ...]
|
219 | b82c93a5 | Stavros Sachtouris | """
|
220 | eb647cfe | Stavros Sachtouris | port = dict(network_id=network_id)
|
221 | eb647cfe | Stavros Sachtouris | if device_id:
|
222 | eb647cfe | Stavros Sachtouris | port['device_id'] = device_id
|
223 | ccdd1b82 | Stavros Sachtouris | if security_groups:
|
224 | ccdd1b82 | Stavros Sachtouris | port['security_groups'] = security_groups
|
225 | 737995ed | Stavros Sachtouris | if name:
|
226 | 737995ed | Stavros Sachtouris | port['name'] = name
|
227 | 56d84a4e | Stavros Sachtouris | if fixed_ips:
|
228 | b82c93a5 | Stavros Sachtouris | for fixed_ip in fixed_ips or []: |
229 | b82c93a5 | Stavros Sachtouris | if not 'ip_address' in fixed_ip: |
230 | 7a3c66e1 | Stavros Sachtouris | raise ValueError('Invalid fixed_ip [%s]' % fixed_ip) |
231 | b7d79306 | Stavros Sachtouris | port['fixed_ips'] = fixed_ips
|
232 | e8ba3e9f | Stavros Sachtouris | r = self.ports_post(json_data=dict(port=port), success=201) |
233 | ccdd1b82 | Stavros Sachtouris | return r.json['port'] |
234 | 6f2b87c1 | Stavros Sachtouris | |
235 | 89a1c636 | Giorgos Korfiatis | def create_floatingip(self, floating_network_id, floating_ip_address='', |
236 | 89a1c636 | Giorgos Korfiatis | project=None):
|
237 | 89a1c636 | Giorgos Korfiatis | args = {"project": project}
|
238 | eb647cfe | Stavros Sachtouris | return super(CycladesNetworkClient, self).create_floatingip( |
239 | 89a1c636 | Giorgos Korfiatis | floating_network_id, floating_ip_address=floating_ip_address, |
240 | 89a1c636 | Giorgos Korfiatis | args=args) |
241 | 6f2b87c1 | Stavros Sachtouris | |
242 | eb647cfe | Stavros Sachtouris | def update_floatingip(self, floating_network_id, floating_ip_address=''): |
243 | eb647cfe | Stavros Sachtouris | """To nullify something optional, use None"""
|
244 | eb647cfe | Stavros Sachtouris | return super(CycladesNetworkClient, self).update_floatingip( |
245 | eb647cfe | Stavros Sachtouris | floating_network_id, floating_ip_address=floating_ip_address) |
246 | 89a1c636 | Giorgos Korfiatis | |
247 | 89a1c636 | Giorgos Korfiatis | def floating_ip_action_post( |
248 | 89a1c636 | Giorgos Korfiatis | self, fip_id, json_data=None, success=202, **kwargs): |
249 | 89a1c636 | Giorgos Korfiatis | """POST base_url/floatingips/<fip_id>/action
|
250 | 89a1c636 | Giorgos Korfiatis |
|
251 | 89a1c636 | Giorgos Korfiatis | :returns: request response
|
252 | 89a1c636 | Giorgos Korfiatis | """
|
253 | 89a1c636 | Giorgos Korfiatis | if json_data:
|
254 | 89a1c636 | Giorgos Korfiatis | json_data = json.dumps(json_data) |
255 | 89a1c636 | Giorgos Korfiatis | self.set_header('Content-Type', 'application/json') |
256 | 89a1c636 | Giorgos Korfiatis | self.set_header('Content-Length', len(json_data)) |
257 | 89a1c636 | Giorgos Korfiatis | path = path4url('floatingips', fip_id, 'action') |
258 | 89a1c636 | Giorgos Korfiatis | return self.post(path, data=json_data, success=success, **kwargs) |
259 | 89a1c636 | Giorgos Korfiatis | |
260 | 89a1c636 | Giorgos Korfiatis | def reassign_floating_ip(self, floating_network_id, project): |
261 | 89a1c636 | Giorgos Korfiatis | req = {'reassign': {'project': project}} |
262 | 89a1c636 | Giorgos Korfiatis | r = self.floating_ip_action_post(floating_network_id, json_data=req) |