Rearange client packages 4 uniformity in testing
[kamaki] / kamaki / clients / cyclades / __init__.py
1 # Copyright 2011-2013 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 from time import sleep
35
36 from kamaki.clients.cyclades_rest_api import CycladesClientApi
37 from kamaki.clients import ClientError
38 from sys import stdout
39
40
41 class CycladesClient(CycladesClientApi):
42     """GRNet Cyclades API client"""
43
44     def start_server(self, server_id):
45         """Submit a startup request
46
47         :param server_id: integer (str or int)
48         """
49         req = {'start': {}}
50         r = self.servers_post(server_id, 'action', json_data=req, success=202)
51         r.release()
52
53     def shutdown_server(self, server_id):
54         """Submit a shutdown request
55
56         :param server_id: integer (str or int)
57         """
58         req = {'shutdown': {}}
59         r = self.servers_post(server_id, 'action', json_data=req, success=202)
60         r.release()
61
62     def get_server_console(self, server_id):
63         """
64         :param server_id: integer (str or int)
65
66         :returns: (dict) info to set a VNC connection to VM
67         """
68         req = {'console': {'type': 'vnc'}}
69         r = self.servers_post(server_id, 'action', json_data=req, success=200)
70         return r.json['console']
71
72     def get_firewall_profile(self, server_id):
73         """
74         :param server_id: integer (str or int)
75
76         :returns: (str) ENABLED | DISABLED | PROTECTED
77
78         :raises ClientError: 520 No Firewall Profile
79         """
80         r = self.get_server_details(server_id)
81         try:
82             return r['attachments']['values'][0]['firewallProfile']
83         except KeyError:
84             raise ClientError(
85                 'No Firewall Profile',
86                 details='Server %s is missing a firewall profile' % server_id)
87
88     def set_firewall_profile(self, server_id, profile):
89         """Set the firewall profile for the public interface of a server
90
91         :param server_id: integer (str or int)
92
93         :param profile: (str) ENABLED | DISABLED | PROTECTED
94         """
95         req = {'firewallProfile': {'profile': profile}}
96         r = self.servers_post(server_id, 'action', json_data=req, success=202)
97         r.release()
98
99     def list_servers(self, detail=False, changes_since=None):
100         """
101         :param detail: (bool) append full server details to each item if true
102
103         :param changes_since: (date)
104
105         :returns: list of server ids and names
106         """
107         detail = 'detail' if detail else ''
108         r = self.servers_get(command=detail, changes_since=changes_since)
109         return r.json['servers']['values']
110
111     def list_server_nics(self, server_id):
112         """
113         :param server_id: integer (str or int)
114
115         :returns: (dict) network interface connections
116         """
117         r = self.servers_get(server_id, 'ips')
118         return r.json['addresses']['values']
119
120     def get_server_stats(self, server_id):
121         """
122         :param server_id: integer (str or int)
123
124         :returns: (dict) auto-generated graphs of statistics (urls)
125         """
126         r = self.servers_get(server_id, 'stats')
127         return r.json['stats']
128
129     def list_networks(self, detail=False):
130         """
131         :param detail: (bool)
132
133         :returns: (list) id,name if not detail else full info per network
134         """
135         detail = 'detail' if detail else ''
136         r = self.networks_get(command=detail)
137         return r.json['networks']['values']
138
139     def list_network_nics(self, network_id):
140         """
141         :param network_id: integer (str or int)
142
143         :returns: (list)
144         """
145         r = self.networks_get(network_id=network_id)
146         return r.json['network']['attachments']['values']
147
148     def create_network(
149             self, name,
150             cidr=None, gateway=None, type=None, dhcp=False):
151         """
152         :param name: (str)
153
154         :param cidr: (str)
155
156         :param geteway: (str)
157
158         :param type: (str)
159
160         :param dhcp: (bool)
161
162         :returns: (dict) network detailed info
163         """
164         net = dict(name=name)
165         if cidr:
166             net['cidr'] = cidr
167         if gateway:
168             net['gateway'] = gateway
169         if type:
170             net['type'] = type
171         net['dhcp'] = True if dhcp else False
172         req = dict(network=net)
173         r = self.networks_post(json_data=req, success=202)
174         return r.json['network']
175
176     def get_network_details(self, network_id):
177         """
178         :param network_id: integer (str or int)
179
180         :returns: (dict)
181         """
182         r = self.networks_get(network_id=network_id)
183         return r.json['network']
184
185     def update_network_name(self, network_id, new_name):
186         """
187         :param network_id: integer (str or int)
188
189         :param new_name: (str)
190         """
191         req = {'network': {'name': new_name}}
192         r = self.networks_put(network_id=network_id, json_data=req)
193         r.release()
194
195     def delete_network(self, network_id):
196         """
197         :param network_id: integer (str or int)
198
199         :raises ClientError: 421 Network in use
200         """
201         try:
202             r = self.networks_delete(network_id)
203         except ClientError as err:
204             if err.status == 421:
205                 err.details = [
206                     'Network may be still connected to at least one server']
207             raise err
208         r.release()
209
210     def connect_server(self, server_id, network_id):
211         """ Connect a server to a network
212
213         :param server_id: integer (str or int)
214
215         :param network_id: integer (str or int)
216         """
217         req = {'add': {'serverRef': server_id}}
218         r = self.networks_post(network_id, 'action', json_data=req)
219         r.release()
220
221     def disconnect_server(self, server_id, nic_id):
222         """
223         :param server_id: integer (str or int)
224
225         :param nic_id: (str)
226         """
227         vm_nets = self.list_server_nics(server_id)
228         num_of_disconnections = 0
229         for (nic_id, network_id) in [(
230                 net['id'],
231                 net['network_id']) for net in vm_nets if nic_id == net['id']]:
232             req = {'remove': {'attachment': '%s' % nic_id}}
233             r = self.networks_post(network_id, 'action', json_data=req)
234             r.release()
235             num_of_disconnections += 1
236         return num_of_disconnections
237
238     def disconnect_network_nics(self, netid):
239         """
240         :param netid: integer (str or int)
241         """
242         for nic in self.list_network_nics(netid):
243             req = dict(remove=dict(attachment=nic))
244             r = self.networks_post(netid, 'action', json_data=req)
245             r.release()
246
247     def wait_server(
248             self,
249             server_id,
250             current_status='BUILD',
251             delay=0.5,
252             max_wait=128,
253             wait_cb=None):
254         """Wait for server while its status is current_status
255
256         :param server_id: integer (str or int)
257
258         :param current_status: (str) BUILD|ACTIVE|STOPPED|DELETED|REBOOT
259
260         :param delay: time interval between retries
261
262         :param wait_cb: if set a progressbar is used to show progress
263
264         :returns: (str) the new mode if succesfull, (bool) False if timed out
265         """
266         r = self.get_server_details(server_id)
267         if r['status'] != current_status:
268             return r['status']
269         old_wait = total_wait = 0
270
271         if current_status == 'BUILD':
272             max_wait = 100
273             wait_gen = wait_cb(max_wait) if wait_cb else None
274         elif wait_cb:
275             wait_gen = wait_cb(1 + max_wait // delay)
276             wait_gen.next()
277
278         while r['status'] == current_status and total_wait <= max_wait:
279             if current_status == 'BUILD':
280                 total_wait = int(r['progress'])
281                 if wait_cb:
282                     for i in range(int(old_wait), int(total_wait)):
283                         wait_gen.next()
284                     old_wait = total_wait
285                 else:
286                     stdout.write('.')
287                     stdout.flush()
288             else:
289                 if wait_cb:
290                     wait_gen.next()
291                 else:
292                     stdout.write('.')
293                     stdout.flush()
294                 total_wait += delay
295             sleep(delay)
296             r = self.get_server_details(server_id)
297
298         if r['status'] != current_status:
299             if wait_cb:
300                 try:
301                     while True:
302                         wait_gen.next()
303                 except:
304                     pass
305             return r['status']
306         return False