__init__.py 9.68 KB
Newer Older
1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
Giorgos Verigakis's avatar
Giorgos Verigakis committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

34
from sys import stdout
35
36
from time import sleep

37
from kamaki.clients.cyclades.rest_api import CycladesRestClient
38
from kamaki.clients import ClientError
Giorgos Verigakis's avatar
Giorgos Verigakis committed
39

40

41
class CycladesClient(CycladesRestClient):
Giorgos Verigakis's avatar
Giorgos Verigakis committed
42
    """GRNet Cyclades API client"""
43

Giorgos Verigakis's avatar
Giorgos Verigakis committed
44
    def start_server(self, server_id):
45
46
47
48
        """Submit a startup request

        :param server_id: integer (str or int)
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
49
        req = {'start': {}}
50
        self.servers_post(server_id, 'action', json_data=req, success=202)
51

Giorgos Verigakis's avatar
Giorgos Verigakis committed
52
    def shutdown_server(self, server_id):
53
54
55
56
        """Submit a shutdown request

        :param server_id: integer (str or int)
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
57
        req = {'shutdown': {}}
58
        self.servers_post(server_id, 'action', json_data=req, success=202)
59

Giorgos Verigakis's avatar
Giorgos Verigakis committed
60
    def get_server_console(self, server_id):
61
62
63
64
65
        """
        :param server_id: integer (str or int)

        :returns: (dict) info to set a VNC connection to VM
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
66
        req = {'console': {'type': 'vnc'}}
67
        r = self.servers_post(server_id, 'action', json_data=req, success=200)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
68
        return r.json['console']
69
70

    def get_firewall_profile(self, server_id):
71
72
73
74
75
76
77
        """
        :param server_id: integer (str or int)

        :returns: (str) ENABLED | DISABLED | PROTECTED

        :raises ClientError: 520 No Firewall Profile
        """
78
79
        r = self.get_server_details(server_id)
        try:
80
            return r['attachments']['values'][0]['firewallProfile']
81
        except KeyError:
82
            raise ClientError(
83
                'No Firewall Profile',
84
                details='Server %s is missing a firewall profile' % server_id)
85

Giorgos Verigakis's avatar
Giorgos Verigakis committed
86
87
    def set_firewall_profile(self, server_id, profile):
        """Set the firewall profile for the public interface of a server
88
89
90
91

        :param server_id: integer (str or int)

        :param profile: (str) ENABLED | DISABLED | PROTECTED
Giorgos Verigakis's avatar
Giorgos Verigakis committed
92
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
93
        req = {'firewallProfile': {'profile': profile}}
94
        self.servers_post(server_id, 'action', json_data=req, success=202)
95

96
97
98
99
100
101
102
103
104
105
106
107
    def list_servers(self, detail=False, changes_since=None):
        """
        :param detail: (bool) append full server details to each item if true

        :param changes_since: (date)

        :returns: list of server ids and names
        """
        detail = 'detail' if detail else ''
        r = self.servers_get(command=detail, changes_since=changes_since)
        return r.json['servers']['values']

108
    def list_server_nics(self, server_id):
109
110
111
112
113
        """
        :param server_id: integer (str or int)

        :returns: (dict) network interface connections
        """
114
        r = self.servers_get(server_id, 'ips')
115
        return r.json['addresses']['values']
116

Giorgos Verigakis's avatar
Giorgos Verigakis committed
117
    def get_server_stats(self, server_id):
118
119
120
121
122
        """
        :param server_id: integer (str or int)

        :returns: (dict) auto-generated graphs of statistics (urls)
        """
123
        r = self.servers_get(server_id, 'stats')
Giorgos Verigakis's avatar
Giorgos Verigakis committed
124
        return r.json['stats']
125

Giorgos Verigakis's avatar
Giorgos Verigakis committed
126
    def list_networks(self, detail=False):
127
128
129
130
131
        """
        :param detail: (bool)

        :returns: (list) id,name if not detail else full info per network
        """
132
133
        detail = 'detail' if detail else ''
        r = self.networks_get(command=detail)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
134
        return r.json['networks']['values']
Giorgos Verigakis's avatar
Giorgos Verigakis committed
135

136
    def list_network_nics(self, network_id):
137
138
139
140
141
        """
        :param network_id: integer (str or int)

        :returns: (list)
        """
142
143
144
        r = self.networks_get(network_id=network_id)
        return r.json['network']['attachments']['values']

145
146
    def create_network(
            self, name,
147
            cidr=None, gateway=None, type=None, dhcp=False):
148
149
150
151
152
153
154
        """
        :param name: (str)

        :param cidr: (str)

        :param geteway: (str)

155
156
        :param type: (str) if None, will use MAC_FILTERED as default
            Valid values: CUSTOM, IP_LESS_ROUTED, MAC_FILTERED, PHYSICAL_VLAN
157

158
        :param dhcp: (bool)
159
160

        :returns: (dict) network detailed info
161
162
163
        """
        net = dict(name=name)
        if cidr:
164
            net['cidr'] = cidr
165
        if gateway:
166
            net['gateway'] = gateway
167
        net['type'] = type or 'MAC_FILTERED'
168
        net['dhcp'] = True if dhcp else False
169
        req = dict(network=net)
170
        r = self.networks_post(json_data=req, success=202)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
171
        return r.json['network']
Giorgos Verigakis's avatar
Giorgos Verigakis committed
172
173

    def get_network_details(self, network_id):
174
175
176
177
178
        """
        :param network_id: integer (str or int)

        :returns: (dict)
        """
179
        r = self.networks_get(network_id=network_id)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
180
        return r.json['network']
Giorgos Verigakis's avatar
Giorgos Verigakis committed
181
182

    def update_network_name(self, network_id, new_name):
183
184
185
186
187
        """
        :param network_id: integer (str or int)

        :param new_name: (str)
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
188
        req = {'network': {'name': new_name}}
189
        self.networks_put(network_id=network_id, json_data=req)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
190
191

    def delete_network(self, network_id):
192
193
194
195
196
        """
        :param network_id: integer (str or int)

        :raises ClientError: 421 Network in use
        """
197
        try:
198
            self.networks_delete(network_id)
199
200
        except ClientError as err:
            if err.status == 421:
201
                err.details = [
202
                    'Network may be still connected to at least one server']
203
            raise
Giorgos Verigakis's avatar
Giorgos Verigakis committed
204
205

    def connect_server(self, server_id, network_id):
206
207
208
209
210
211
        """ Connect a server to a network

        :param server_id: integer (str or int)

        :param network_id: integer (str or int)
        """
Giorgos Verigakis's avatar
Giorgos Verigakis committed
212
        req = {'add': {'serverRef': server_id}}
213
        self.networks_post(network_id, 'action', json_data=req)
Giorgos Verigakis's avatar
Giorgos Verigakis committed
214

215
    def disconnect_server(self, server_id, nic_id):
216
217
218
219
        """
        :param server_id: integer (str or int)

        :param nic_id: (str)
220
221

        :returns: (int) the number of nics disconnected
222
        """
223
        vm_nets = self.list_server_nics(server_id)
224
        num_of_disconnections = 0
225
        for (nic_id, network_id) in [(
226
227
                net['id'],
                net['network_id']) for net in vm_nets if nic_id == net['id']]:
228
            req = {'remove': {'attachment': '%s' % nic_id}}
229
            self.networks_post(network_id, 'action', json_data=req)
230
231
            num_of_disconnections += 1
        return num_of_disconnections
232
233

    def disconnect_network_nics(self, netid):
234
235
236
237
        """
        :param netid: integer (str or int)
        """
        for nic in self.list_network_nics(netid):
238
            req = dict(remove=dict(attachment=nic))
239
            self.networks_post(netid, 'action', json_data=req)
240

241
    def wait_server(
242
243
244
245
246
247
            self,
            server_id,
            current_status='BUILD',
            delay=0.5,
            max_wait=128,
            wait_cb=None):
248
249
250
251
252
253
254
255
256
257
258
        """Wait for server while its status is current_status

        :param server_id: integer (str or int)

        :param current_status: (str) BUILD|ACTIVE|STOPPED|DELETED|REBOOT

        :param delay: time interval between retries

        :param wait_cb: if set a progressbar is used to show progress

        :returns: (str) the new mode if succesfull, (bool) False if timed out
259
260
261
262
        """
        r = self.get_server_details(server_id)
        if r['status'] != current_status:
            return r['status']
263
        old_wait = total_wait = 0
264
265

        if current_status == 'BUILD':
266
            max_wait = 100
267
            wait_gen = wait_cb(max_wait) if wait_cb else None
268
        elif wait_cb:
269
            wait_gen = wait_cb(1 + max_wait // delay)
270
            wait_gen.next()
271
272
273

        while r['status'] == current_status and total_wait <= max_wait:
            if current_status == 'BUILD':
274
                total_wait = int(r['progress'])
275
                if wait_cb:
276
                    for i in range(int(old_wait), int(total_wait)):
277
278
                        wait_gen.next()
                    old_wait = total_wait
279
280
281
                else:
                    stdout.write('.')
                    stdout.flush()
282
283
284
            else:
                if wait_cb:
                    wait_gen.next()
285
286
287
                else:
                    stdout.write('.')
                    stdout.flush()
288
289
290
291
                total_wait += delay
            sleep(delay)
            r = self.get_server_details(server_id)

292
293
294
295
296
297
298
        if r['status'] != current_status:
            if wait_cb:
                try:
                    while True:
                        wait_gen.next()
                except:
                    pass
299
300
            return r['status']
        return False