root / kamaki / clients / cyclades / __init__.py @ 75ae8a08
History | View | Annotate | Download (10 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 | 75ae8a08 | 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 | 75ae8a08 | 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 | 75ae8a08 | Giorgos Korfiatis | :param project: the project where to assign the server
|
68 | 75ae8a08 | 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 | 75ae8a08 | Giorgos Korfiatis | metadata=metadata, personality=personality, networks=networks, |
84 | 75ae8a08 | Giorgos Korfiatis | project=project) |
85 | dbcbf446 | Stavros Sachtouris | |
86 | c75be81a | Stavros Sachtouris | def set_firewall_profile(self, server_id, profile, port_id): |
87 | c75be81a | Stavros Sachtouris | """Set the firewall profile for the public interface of a server
|
88 | c75be81a | Stavros Sachtouris | :param server_id: integer (str or int)
|
89 | c75be81a | Stavros Sachtouris | :param profile: (str) ENABLED | DISABLED | PROTECTED
|
90 | c75be81a | Stavros Sachtouris | :param port_id: (str) This port must connect to a public network
|
91 | c75be81a | Stavros Sachtouris | :returns: (dict) response headers
|
92 | c75be81a | Stavros Sachtouris | """
|
93 | c75be81a | Stavros Sachtouris | req = {'firewallProfile': {'profile': profile, 'nic': port_id}} |
94 | c75be81a | Stavros Sachtouris | r = self.servers_action_post(server_id, json_data=req, success=202) |
95 | c75be81a | Stavros Sachtouris | return r.headers
|
96 | c75be81a | Stavros Sachtouris | |
97 | a1c50326 | Giorgos Verigakis | def start_server(self, server_id): |
98 | f5eac743 | Stavros Sachtouris | """Submit a startup 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 = {'start': {}}
|
105 | ef2e6c9f | Stavros Sachtouris | r = self.servers_action_post(server_id, json_data=req, success=202) |
106 | cd295a1d | Stavros Sachtouris | return r.headers
|
107 | 6f1ec797 | Stavros Sachtouris | |
108 | a1c50326 | Giorgos Verigakis | def shutdown_server(self, server_id): |
109 | f5eac743 | Stavros Sachtouris | """Submit a shutdown request
|
110 | f5eac743 | Stavros Sachtouris |
|
111 | f5eac743 | Stavros Sachtouris | :param server_id: integer (str or int)
|
112 | cd295a1d | Stavros Sachtouris |
|
113 | cd295a1d | Stavros Sachtouris | :returns: (dict) response headers
|
114 | f5eac743 | Stavros Sachtouris | """
|
115 | 6a0b1658 | Giorgos Verigakis | req = {'shutdown': {}}
|
116 | ef2e6c9f | Stavros Sachtouris | r = self.servers_action_post(server_id, json_data=req, success=202) |
117 | cd295a1d | Stavros Sachtouris | return r.headers
|
118 | 3dabe5d2 | Stavros Sachtouris | |
119 | a1c50326 | Giorgos Verigakis | def get_server_console(self, server_id): |
120 | f5eac743 | Stavros Sachtouris | """
|
121 | f5eac743 | Stavros Sachtouris | :param server_id: integer (str or int)
|
122 | f5eac743 | Stavros Sachtouris |
|
123 | 2bd23362 | Stavros Sachtouris | :returns: (dict) info to set a VNC connection to virtual server
|
124 | f5eac743 | Stavros Sachtouris | """
|
125 | 6a0b1658 | Giorgos Verigakis | req = {'console': {'type': 'vnc'}} |
126 | ef2e6c9f | Stavros Sachtouris | r = self.servers_action_post(server_id, json_data=req, success=200) |
127 | 6a0b1658 | Giorgos Verigakis | return r.json['console'] |
128 | 2d67ecbb | Stavros Sachtouris | |
129 | 75ae8a08 | Giorgos Korfiatis | def reassign_server(self, server_id, project): |
130 | 75ae8a08 | Giorgos Korfiatis | req = {'reassign': {'project': project}} |
131 | 75ae8a08 | Giorgos Korfiatis | r = self.servers_action_post(server_id, json_data=req, success=200) |
132 | 75ae8a08 | Giorgos Korfiatis | return r.headers
|
133 | 75ae8a08 | Giorgos Korfiatis | |
134 | a1c50326 | Giorgos Verigakis | def get_server_stats(self, server_id): |
135 | f5eac743 | Stavros Sachtouris | """
|
136 | f5eac743 | Stavros Sachtouris | :param server_id: integer (str or int)
|
137 | f5eac743 | Stavros Sachtouris |
|
138 | f5eac743 | Stavros Sachtouris | :returns: (dict) auto-generated graphs of statistics (urls)
|
139 | f5eac743 | Stavros Sachtouris | """
|
140 | e51c7d5b | Stavros Sachtouris | r = self.servers_stats_get(server_id)
|
141 | 6a0b1658 | Giorgos Verigakis | return r.json['stats'] |
142 | 3dabe5d2 | Stavros Sachtouris | |
143 | b45834eb | Stavros Sachtouris | def get_server_diagnostics(self, server_id): |
144 | b45834eb | Stavros Sachtouris | """
|
145 | b45834eb | Stavros Sachtouris | :param server_id: integer (str or int)
|
146 | b45834eb | Stavros Sachtouris |
|
147 | b45834eb | Stavros Sachtouris | :returns: (list)
|
148 | b45834eb | Stavros Sachtouris | """
|
149 | b45834eb | Stavros Sachtouris | r = self.servers_diagnostics_get(server_id)
|
150 | b45834eb | Stavros Sachtouris | return r.json
|
151 | b45834eb | Stavros Sachtouris | |
152 | 7b2e4bf1 | Stavros Sachtouris | def wait_server( |
153 | 7b2e4bf1 | Stavros Sachtouris | self, server_id,
|
154 | 7b2e4bf1 | Stavros Sachtouris | current_status='BUILD',
|
155 | 7b2e4bf1 | Stavros Sachtouris | delay=1, max_wait=100, wait_cb=None): |
156 | 7b2e4bf1 | Stavros Sachtouris | """Wait for server while its status is current_status
|
157 | 7b2e4bf1 | Stavros Sachtouris |
|
158 | 7b2e4bf1 | Stavros Sachtouris | :param server_id: integer (str or int)
|
159 | 7b2e4bf1 | Stavros Sachtouris |
|
160 | 7b2e4bf1 | Stavros Sachtouris | :param current_status: (str) BUILD|ACTIVE|STOPPED|DELETED|REBOOT
|
161 | 7b2e4bf1 | Stavros Sachtouris |
|
162 | 7b2e4bf1 | Stavros Sachtouris | :param delay: time interval between retries
|
163 | 7b2e4bf1 | Stavros Sachtouris |
|
164 | c788a761 | Stavros Sachtouris | :max_wait: (int) timeout in secconds
|
165 | c788a761 | Stavros Sachtouris |
|
166 | 7b2e4bf1 | Stavros Sachtouris | :param wait_cb: if set a progressbar is used to show progress
|
167 | 7b2e4bf1 | Stavros Sachtouris |
|
168 | 7b2e4bf1 | Stavros Sachtouris | :returns: (str) the new mode if succesfull, (bool) False if timed out
|
169 | 7b2e4bf1 | Stavros Sachtouris | """
|
170 | 7b2e4bf1 | Stavros Sachtouris | |
171 | 7b2e4bf1 | Stavros Sachtouris | def get_status(self, server_id): |
172 | 7b2e4bf1 | Stavros Sachtouris | r = self.get_server_details(server_id)
|
173 | 7b2e4bf1 | Stavros Sachtouris | return r['status'], (r.get('progress', None) if ( |
174 | 7b2e4bf1 | Stavros Sachtouris | current_status in ('BUILD', )) else None) |
175 | 7b2e4bf1 | Stavros Sachtouris | |
176 | 7b2e4bf1 | Stavros Sachtouris | return self._wait( |
177 | 7b2e4bf1 | Stavros Sachtouris | server_id, current_status, get_status, delay, max_wait, wait_cb) |
178 | 7b2e4bf1 | Stavros Sachtouris | |
179 | e864cd9e | Stavros Sachtouris | |
180 | eb647cfe | Stavros Sachtouris | class CycladesNetworkClient(NetworkClient): |
181 | e864cd9e | Stavros Sachtouris | """Cyclades Network API extentions"""
|
182 | e864cd9e | Stavros Sachtouris | |
183 | e864cd9e | Stavros Sachtouris | network_types = ( |
184 | e864cd9e | Stavros Sachtouris | 'CUSTOM', 'MAC_FILTERED', 'IP_LESS_ROUTED', 'PHYSICAL_VLAN') |
185 | e864cd9e | Stavros Sachtouris | |
186 | 0e27687b | Stavros Sachtouris | def list_networks(self, detail=None): |
187 | 0e27687b | Stavros Sachtouris | path = path4url('networks', 'detail' if detail else '') |
188 | 0e27687b | Stavros Sachtouris | r = self.get(path, success=200) |
189 | 0e27687b | Stavros Sachtouris | return r.json['networks'] |
190 | 0e27687b | Stavros Sachtouris | |
191 | 75ae8a08 | Giorgos Korfiatis | def create_network(self, type, name=None, shared=None, project=None): |
192 | e864cd9e | Stavros Sachtouris | req = dict(network=dict(type=type, admin_state_up=True)) |
193 | e864cd9e | Stavros Sachtouris | if name:
|
194 | e864cd9e | Stavros Sachtouris | req['network']['name'] = name |
195 | e864cd9e | Stavros Sachtouris | if shared not in (None, ): |
196 | e864cd9e | Stavros Sachtouris | req['network']['shared'] = bool(shared) |
197 | 75ae8a08 | Giorgos Korfiatis | if project is not None: |
198 | 75ae8a08 | Giorgos Korfiatis | req['network']['project'] = project |
199 | e864cd9e | Stavros Sachtouris | r = self.networks_post(json_data=req, success=201) |
200 | e864cd9e | Stavros Sachtouris | return r.json['network'] |
201 | ccdd1b82 | Stavros Sachtouris | |
202 | 75ae8a08 | Giorgos Korfiatis | def networks_action_post( |
203 | 75ae8a08 | Giorgos Korfiatis | self, network_id='', json_data=None, success=202, **kwargs): |
204 | 75ae8a08 | Giorgos Korfiatis | """POST base_url/networks/<network_id>/action
|
205 | 75ae8a08 | Giorgos Korfiatis |
|
206 | 75ae8a08 | Giorgos Korfiatis | :returns: request response
|
207 | 75ae8a08 | Giorgos Korfiatis | """
|
208 | 75ae8a08 | Giorgos Korfiatis | if json_data:
|
209 | 75ae8a08 | Giorgos Korfiatis | json_data = json.dumps(json_data) |
210 | 75ae8a08 | Giorgos Korfiatis | self.set_header('Content-Type', 'application/json') |
211 | 75ae8a08 | Giorgos Korfiatis | self.set_header('Content-Length', len(json_data)) |
212 | 75ae8a08 | Giorgos Korfiatis | path = path4url('networks', network_id, 'action') |
213 | 75ae8a08 | Giorgos Korfiatis | return self.post(path, data=json_data, success=success, **kwargs) |
214 | 75ae8a08 | Giorgos Korfiatis | |
215 | 75ae8a08 | Giorgos Korfiatis | def reassign_network(self, network_id, project): |
216 | 75ae8a08 | Giorgos Korfiatis | req = {'reassign': {'project': project}} |
217 | 75ae8a08 | Giorgos Korfiatis | r = self.networks_action_post(network_id, json_data=req, success=200) |
218 | 75ae8a08 | Giorgos Korfiatis | return r.headers
|
219 | 75ae8a08 | Giorgos Korfiatis | |
220 | f3740b99 | Stavros Sachtouris | def list_ports(self, detail=None): |
221 | f3740b99 | Stavros Sachtouris | path = path4url('ports', 'detail' if detail else '') |
222 | f3740b99 | Stavros Sachtouris | r = self.get(path, success=200) |
223 | f3740b99 | Stavros Sachtouris | return r.json['ports'] |
224 | f3740b99 | Stavros Sachtouris | |
225 | 737995ed | Stavros Sachtouris | def create_port( |
226 | eb647cfe | Stavros Sachtouris | self, network_id,
|
227 | eb647cfe | Stavros Sachtouris | device_id=None, security_groups=None, name=None, fixed_ips=None): |
228 | b82c93a5 | Stavros Sachtouris | """
|
229 | b82c93a5 | Stavros Sachtouris | :param fixed_ips: (list of dicts) [{"ip_address": IPv4}, ...]
|
230 | b82c93a5 | Stavros Sachtouris | """
|
231 | eb647cfe | Stavros Sachtouris | port = dict(network_id=network_id)
|
232 | eb647cfe | Stavros Sachtouris | if device_id:
|
233 | eb647cfe | Stavros Sachtouris | port['device_id'] = device_id
|
234 | ccdd1b82 | Stavros Sachtouris | if security_groups:
|
235 | ccdd1b82 | Stavros Sachtouris | port['security_groups'] = security_groups
|
236 | 737995ed | Stavros Sachtouris | if name:
|
237 | 737995ed | Stavros Sachtouris | port['name'] = name
|
238 | 56d84a4e | Stavros Sachtouris | if fixed_ips:
|
239 | b82c93a5 | Stavros Sachtouris | for fixed_ip in fixed_ips or []: |
240 | b82c93a5 | Stavros Sachtouris | if not 'ip_address' in fixed_ip: |
241 | 7a3c66e1 | Stavros Sachtouris | raise ValueError('Invalid fixed_ip [%s]' % fixed_ip) |
242 | b7d79306 | Stavros Sachtouris | port['fixed_ips'] = fixed_ips
|
243 | e8ba3e9f | Stavros Sachtouris | r = self.ports_post(json_data=dict(port=port), success=201) |
244 | ccdd1b82 | Stavros Sachtouris | return r.json['port'] |
245 | 6f2b87c1 | Stavros Sachtouris | |
246 | 67377ec3 | Stavros Sachtouris | def create_floatingip( |
247 | 75ae8a08 | Giorgos Korfiatis | self,
|
248 | 75ae8a08 | Giorgos Korfiatis | floating_network_id=None, floating_ip_address='', project_id=None): |
249 | 67377ec3 | Stavros Sachtouris | """
|
250 | 67377ec3 | Stavros Sachtouris | :param floating_network_id: if not provided, it is assigned
|
251 | 67377ec3 | Stavros Sachtouris | automatically by the service
|
252 | 75ae8a08 | Giorgos Korfiatis | :param floating_ip_address: only if the IP is availabel in network pool
|
253 | 75ae8a08 | Giorgos Korfiatis | :param project_id: specific project to get resource quotas from
|
254 | 67377ec3 | Stavros Sachtouris | """
|
255 | 67377ec3 | Stavros Sachtouris | floatingip = {} |
256 | 67377ec3 | Stavros Sachtouris | if floating_network_id:
|
257 | 67377ec3 | Stavros Sachtouris | floatingip['floating_network_id'] = floating_network_id
|
258 | 67377ec3 | Stavros Sachtouris | if floating_ip_address:
|
259 | 67377ec3 | Stavros Sachtouris | floatingip['floating_ip_address'] = floating_ip_address
|
260 | 75ae8a08 | Giorgos Korfiatis | if project_id:
|
261 | 75ae8a08 | Giorgos Korfiatis | floatingip['project'] = project_id
|
262 | 67377ec3 | Stavros Sachtouris | r = self.floatingips_post(
|
263 | 67377ec3 | Stavros Sachtouris | json_data=dict(floatingip=floatingip), success=200) |
264 | 67377ec3 | Stavros Sachtouris | return r.json['floatingip'] |
265 | 75ae8a08 | Giorgos Korfiatis | |
266 | 75ae8a08 | Giorgos Korfiatis | def reassign_floating_ip(self, floating_network_id, project_id): |
267 | 75ae8a08 | Giorgos Korfiatis | """Change the project where this ip is charged"""
|
268 | 75ae8a08 | Giorgos Korfiatis | path = path4url('floatingips', floating_network_id, 'action') |
269 | 75ae8a08 | Giorgos Korfiatis | json_data = dict(reassign=dict(project=project_id)) |
270 | 75ae8a08 | Giorgos Korfiatis | self.post(path, json=json_data, success=202) |