Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / cyclades.py @ c59aef27

History | View | Annotate | Download (9.6 kB)

1
# Copyright 2011 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 kamaki.clients.compute import ComputeClient, ClientError
35
from kamaki.clients.utils import path4url
36
import json
37
from time import sleep
38

    
39

    
40
class CycladesClient(ComputeClient):
41
    """GRNet Cyclades API client"""
42

    
43
    def networks_get(self, network_id='', command='', **kwargs):
44
        """GET base_url/networks[/network_id][/command] request
45
        @param network_id or ''
46
        @param command can be 'detail', or ''
47
        """
48
        path = path4url('networks', network_id, command)
49
        success = kwargs.pop('success', (200, 203))
50
        return self.get(path, success=success, **kwargs)
51

    
52
    def networks_delete(self, network_id='', command='', **kwargs):
53
        """DEL ETE base_url/networks[/network_id][/command] request
54
        @param network_id or ''
55
        @param command can be 'detail', or ''
56
        """
57
        path = path4url('networks', network_id, command)
58
        success = kwargs.pop('success', 204)
59
        return self.delete(path, success=success, **kwargs)
60

    
61
    def networks_post(self,
62
        network_id='',
63
        command='',
64
        json_data=None,
65
        **kwargs):
66
        """POST base_url/servers[/server_id]/[command] request
67
        @param server_id or ''
68
        @param command: can be 'action' or ''
69
        @param json_data: a json valid dict that will be send as data
70
        """
71
        data = json_data
72
        if json_data is not None:
73
            data = json.dumps(json_data)
74
            self.set_header('Content-Type', 'application/json')
75
            self.set_header('Content-Length', len(data))
76

    
77
        path = path4url('networks', network_id, command)
78
        success = kwargs.pop('success', 202)
79
        return self.post(path, data=data, success=success, **kwargs)
80

    
81
    def networks_put(self,
82
        network_id='',
83
        command='',
84
        json_data=None,
85
        **kwargs):
86
        """PUT base_url/servers[/server_id]/[command] request
87
        @param server_id or ''
88
        @param command: can be 'action' or ''
89
        @param json_data: a json valid dict that will be send as data
90
        """
91
        data = json_data
92
        if json_data is not None:
93
            data = json.dumps(json_data)
94
            self.set_header('Content-Type', 'application/json')
95
            self.set_header('Content-Length', len(data))
96

    
97
        path = path4url('networks', network_id, command)
98
        success = kwargs.pop('success', 204)
99
        return self.put(path, data=data, success=success, **kwargs)
100

    
101
    def start_server(self, server_id):
102
        """Submit a startup request for a server specified by id"""
103
        req = {'start': {}}
104
        r = self.servers_post(server_id, 'action', json_data=req, success=202)
105
        r.release()
106

    
107
    def shutdown_server(self, server_id):
108
        """Submit a shutdown request for a server specified by id"""
109
        req = {'shutdown': {}}
110
        r = self.servers_post(server_id, 'action', json_data=req, success=202)
111
        r.release()
112

    
113
    def get_server_console(self, server_id):
114
        """Get a VNC connection to the console of a server specified by id"""
115
        req = {'console': {'type': 'vnc'}}
116
        r = self.servers_post(server_id, 'action', json_data=req, success=200)
117
        return r.json['console']
118

    
119
    def get_firewall_profile(self, server_id):
120
        r = self.get_server_details(server_id)
121
        try:
122
            return r['attachments']['values'][0]['firewallProfile']
123
        except KeyError:
124
            raise ClientError('No Firewall Profile', 520,
125
                details='Server %s is missing a firewall profile' % server_id)
126

    
127
    def set_firewall_profile(self, server_id, profile):
128
        """Set the firewall profile for the public interface of a server
129
           The server is specified by id, the profile argument
130
           is one of (ENABLED, DISABLED, PROTECTED).
131
        """
132
        req = {'firewallProfile': {'profile': profile}}
133
        r = self.servers_post(server_id, 'action', json_data=req, success=202)
134
        r.release()
135

    
136
    def list_server_nics(self, server_id):
137
        r = self.servers_get(server_id, 'ips')
138
        return r.json['addresses']['values']
139

    
140
    def get_server_stats(self, server_id):
141
        r = self.servers_get(server_id, 'stats')
142
        return r.json['stats']
143

    
144
    def list_networks(self, detail=False):
145
        detail = 'detail' if detail else ''
146
        r = self.networks_get(command=detail)
147
        return r.json['networks']['values']
148

    
149
    #NEW
150
    def list_network_nics(self, network_id):
151
        r = self.networks_get(network_id=network_id)
152
        return r.json['network']['attachments']['values']
153

    
154
    def create_network(self,
155
        name,
156
        cidr=False,
157
        gateway=False,
158
        type=False,
159
        dhcp=False):
160
        """@params cidr, geteway, type and dhcp should be strings
161
        """
162
        net = dict(name=name)
163
        if cidr:
164
            net['cidr'] = cidr
165
        if gateway:
166
            net['gateway'] = gateway
167
        if type:
168
            net['type'] = type
169
        if dhcp:
170
            net['dhcp'] = dhcp
171
        req = dict(network=net)
172
        r = self.networks_post(json_data=req, success=202)
173
        return r.json['network']
174

    
175
    def get_network_details(self, network_id):
176
        r = self.networks_get(network_id=network_id)
177
        return r.json['network']
178

    
179
    def update_network_name(self, network_id, new_name):
180
        req = {'network': {'name': new_name}}
181
        r = self.networks_put(network_id=network_id, json_data=req)
182
        r.release()
183

    
184
    def delete_network(self, network_id):
185
        try:
186
            r = self.networks_delete(network_id)
187
        except ClientError as err:
188
            if err.status == 421:
189
                err.details =\
190
                'Network may be still connected to at least one server'
191
            raise err
192
        r.release()
193

    
194
    def connect_server(self, server_id, network_id):
195
        req = {'add': {'serverRef': server_id}}
196
        r = self.networks_post(network_id, 'action', json_data=req)
197
        r.release()
198

    
199
    def disconnect_server(self, server_id, nic_id):
200
        server_nets = self.list_server_nics(server_id)
201
        nets = [(net['id'], net['network_id']) for net in server_nets\
202
            if nic_id == net['id']]
203
        if len(nets) == 0:
204
            return
205
        for (nic_id, network_id) in nets:
206
            req = {'remove': {'attachment': unicode(nic_id)}}
207
            r = self.networks_post(network_id, 'action', json_data=req)
208
            r.release()
209

    
210
    #NEW
211
    def disconnect_network_nics(self, netid):
212
        r = self.list_network_nics(netid)
213
        for nic in r:
214
            req = dict(remove=dict(attachment=nic))
215
            r = self.networks_post(netid, 'action', json_data=req)
216

    
217
    def wait_server(self, server_id,
218
        current_status='BUILD',
219
        delay=0.5,
220
        max_wait=128,
221
        wait_cb=None):
222
        """Wait for server to reach a status different from current_status
223
            @return the new mode if succesfull, False if timed out
224
            @server_id
225
            @current_status
226
            @delay time interval between retries
227
            @wait_cb if set a progressbar is used to show progress
228
        """
229
        r = self.get_server_details(server_id)
230
        if r['status'] != current_status:
231
            return r['status']
232
        old_wait = total_wait = 0
233

    
234
        if current_status == 'BUILD':
235
            max_wait = 100 * delay
236

    
237
        if wait_cb:
238
            wait_gen = wait_cb(1 + max_wait // delay)
239
            wait_gen.next()
240

    
241
        while r['status'] == current_status and total_wait <= max_wait:
242
            if current_status == 'BUILD':
243
                total_wait = int(r['progress']) * delay
244
                if wait_cb:
245
                    for i in range(int(old_wait), int(total_wait)):
246
                        wait_gen.next()
247
                    old_wait = total_wait
248
            else:
249
                if wait_cb:
250
                    wait_gen.next()
251
                total_wait += delay
252
            sleep(delay)
253
            r = self.get_server_details(server_id)
254

    
255
        if r['status'] != current_status:
256
            if wait_cb:
257
                try:
258
                    while True:
259
                        wait_gen.next()
260
                except:
261
                    pass
262
            return r['status']
263
        return False