Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / network / __init__.py @ b6afe2ec

History | View | Annotate | Download (15 kB)

1
# Copyright 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 kamaki.clients import ClientError, Waiter
35
from kamaki.clients.network.rest_api import NetworkRestClient
36

    
37

    
38
class NetworkClient(NetworkRestClient, Waiter):
39
    """OpenStack Network API 2.0 client"""
40

    
41
    def list_networks(self):
42
        r = self.networks_get(success=200)
43
        return r.json['networks']
44

    
45
    def create_network(self, name, admin_state_up=None, shared=None):
46
        req = dict(network=dict(
47
            name=name, admin_state_up=bool(admin_state_up)))
48
        if shared not in (None, ):
49
            req['network']['shared'] = bool(shared)
50
        r = self.networks_post(json_data=req, success=201)
51
        return r.json['network']
52

    
53
    def create_networks(self, networks):
54
        """Atomic operation for batch network creation (all or nothing)
55

56
        :param networks: (list) [
57
            {name: ..(str).., admin_state_up: ..(bool).., shared: ..(bool)..},
58
            {name: ..(str).., admin_state_up: ..(bool).., shared: ..(bool)..}]
59
            name is mandatory, the rest is optional
60
            e.g., create_networks([
61
            {name: 'net1', admin_state_up: True},
62
            {name: 'net2'}])
63
        :returns: (list of dicts) created networks details
64
        :raises ValueError: if networks is misformated
65
        :raises ClientError: if the request failed or didn't return 201
66
        """
67
        try:
68
            msg = 'The networks parameter must be list or tuple'
69
            assert (
70
                isinstance(networks, list) or isinstance(networks, tuple)), msg
71
            for network in networks:
72
                msg = 'Network specification %s is not a dict' % network
73
                assert isinstance(network, dict), msg
74
                err = set(network).difference(
75
                    ('name', 'admin_state_up', 'shared'))
76
                if err:
77
                    raise ValueError(
78
                        'Invalid key(s): %s in network specification %s' % (
79
                            err, network))
80
                msg = 'Name is missing in network specification: %s' % network
81
                assert network.get('name', None), msg
82
                network.setdefault('admin_state_up', False)
83
        except AssertionError as ae:
84
            raise ValueError('%s' % ae)
85

    
86
        req = dict(networks=list(networks))
87
        r = self.networks_post(json_data=req, success=201)
88
        return r.json['networks']
89

    
90
    def get_network_details(self, network_id):
91
        r = self.networks_get(network_id, success=200)
92
        return r.json['network']
93

    
94
    def update_network(
95
            self, network_id, name=None, admin_state_up=None, shared=None):
96
        network = dict()
97
        if name:
98
            network['name'] = name
99
        if admin_state_up not in (None, ):
100
            network['admin_state_up'] = admin_state_up
101
        if shared not in (None, ):
102
            network['shared'] = shared
103
        network = dict(network=network)
104
        r = self.networks_put(network_id, json_data=network, success=200)
105
        return r.json['network']
106

    
107
    def delete_network(self, network_id):
108
        r = self.networks_delete(network_id, success=204)
109
        return r.headers
110

    
111
    def list_subnets(self):
112
        r = self.subnets_get(success=200)
113
        return r.json['subnets']
114

    
115
    def create_subnet(
116
            self, network_id, cidr,
117
            name=None, allocation_pools=None, gateway_ip=None, subnet_id=None,
118
            ipv6=None, enable_dhcp=None):
119
        """
120
        :param network_id: (str)
121
        :param cidr: (str)
122

123
        :param name: (str) The subnet name
124
        :param allocation_pools: (list of dicts) start/end addresses of
125
            allocation pools: [{'start': ..., 'end': ...}, ...]
126
        :param gateway_ip: (str)
127
        :param subnet_id: (str)
128
        :param ipv6: (bool) ip_version == 6 if true else 4 (default)
129
        :param enable_dhcp: (bool)
130
        """
131
        subnet = dict(
132
            network_id=network_id, cidr=cidr, ip_version=6 if ipv6 else 4)
133
        if name:
134
            subnet['name'] = name
135
        if allocation_pools:
136
            subnet['allocation_pools'] = allocation_pools
137
        if gateway_ip:
138
            subnet['gateway_ip'] = gateway_ip
139
        if subnet_id:
140
            subnet['id'] = subnet_id
141
        if enable_dhcp not in (None, ):
142
            subnet['enable_dhcp'] = bool(enable_dhcp)
143
        r = self.subnets_post(json_data=dict(subnet=subnet), success=201)
144
        return r.json['subnet']
145

    
146
    def create_subnets(self, subnets):
147
        """Atomic operation for batch subnet creation (all or nothing)
148

149
        :param subnets: (list of dicts) {key: ...} with all parameters in the
150
            method create_subnet, where method mandatory / optional paramteres
151
            respond to mandatory / optional paramters in subnets items
152
        :returns: (list of dicts) created subnetss details
153
        :raises ValueError: if subnets parameter is incorrectly formated
154
        :raises ClientError: if the request failed or didn't return 201
155
        """
156
        try:
157
            msg = 'The subnets parameter must be list or tuple'
158
            assert (
159
                isinstance(subnets, list) or isinstance(subnets, tuple)), msg
160
            for subnet in subnets:
161
                msg = 'Subnet specification %s is not a dict' % subnet
162
                assert isinstance(subnet, dict), msg
163
                err = set(subnet).difference((
164
                    'network_id', 'cidr', 'name', 'allocation_pools',
165
                    'gateway_ip', 'subnet_id', 'ipv6', 'enable_dhcp'))
166
                if err:
167
                    raise ValueError(
168
                        'Invalid key(s): %s in subnet specification %s' % (
169
                            err, subnet))
170
                msg = 'network_id is missing in subnet spec: %s' % subnet
171
                assert subnet.get('network_id', None), msg
172
                msg = 'cidr is missing in subnet spec: %s' % subnet
173
                assert subnet.get('cidr', None), msg
174
                subnet['ip_version'] = 6 if subnet.pop('ipv6', None) else 4
175
                if 'subnet_id' in subnet:
176
                    subnet['id'] = subnet.pop('subnet_id')
177
        except AssertionError as ae:
178
            raise ValueError('%s' % ae)
179

    
180
        r = self.subnets_post(
181
            json_data=dict(subnets=list(subnets)), success=201)
182
        return r.json['subnets']
183

    
184
    def get_subnet_details(self, subnet_id):
185
        r = self.subnets_get(subnet_id, success=200)
186
        return r.json
187

    
188
    def update_subnet(
189
            self, subnet_id,
190
            name=None, allocation_pools=None, gateway_ip=None, ipv6=None,
191
            enable_dhcp=None):
192
        """
193
        :param subnet_id: (str)
194

195
        :param name: (str) The subnet name
196
        :param allocation_pools: (list of dicts) start/end addresses of
197
            allocation pools: [{'start': ..., 'end': ...}, ...]
198
        :param gateway_ip: (str)
199
        :param ipv6: (bool) ip_version == 6 if true, 4 if false, used as filter
200
        :param enable_dhcp: (bool)
201
        """
202
        subnet = dict()
203
        if name not in (None, ):
204
            subnet['name'] = name
205
        if allocation_pools not in (None, ):
206
            subnet['allocation_pools'] = allocation_pools
207
        if gateway_ip not in (None, ):
208
            subnet['gateway_ip'] = gateway_ip
209
        if ipv6 not in (None, ):
210
            subnet['ip_version'] = 6 if ipv6 else 4
211
        if enable_dhcp not in (None, ):
212
            subnet['enable_dhcp'] = enable_dhcp
213
        r = self.subnets_put(
214
            subnet_id, json=dict(subnet=subnet), success=200)
215
        return r.json['subnet']
216

    
217
    def delete_subnet(self, subnet_id):
218
        r = self.subnets_delete(subnet_id, success=204)
219
        return r.headers
220

    
221
    def list_ports(self):
222
        r = self.ports_get(success=200)
223
        return r.json['ports']
224

    
225
    def create_port(
226
            self, network_id,
227
            name=None, status=None, admin_state_up=None, mac_address=None,
228
            fixed_ips=None, security_groups=None):
229
        """
230
        :param network_id: (str)
231

232
        :param name: (str)
233
        :param status: (str)
234
        :param admin_state_up: (bool) Router administrative status (UP / DOWN)
235
        :param mac_address: (str)
236
        :param fixed_ips: (str)
237
        :param security_groups: (list)
238
        """
239
        port = dict(network_id=network_id)
240
        if name:
241
            port['name'] = name
242
        if status:
243
            port['status'] = status
244
        if admin_state_up not in (None, ):
245
            port['admin_state_up'] = bool(admin_state_up)
246
        if mac_address:
247
            port['mac_address'] = mac_address
248
        if fixed_ips:
249
            port['fixed_ips'] = fixed_ips
250
        if security_groups:
251
            port['security_groups'] = security_groups
252
        r = self.ports_post(json_data=dict(port=port), success=201)
253
        return r.json['port']
254

    
255
    def create_ports(self, ports):
256
        """Atomic operation for batch port creation (all or nothing)
257

258
        :param ports: (list of dicts) {key: ...} with all parameters in the
259
            method create_port, where method mandatory / optional paramteres
260
            respond to mandatory / optional paramters in ports items
261
        :returns: (list of dicts) created portss details
262
        :raises ValueError: if ports parameter is incorrectly formated
263
        :raises ClientError: if the request failed or didn't return 201
264
        """
265
        try:
266
            msg = 'The ports parameter must be list or tuple'
267
            assert (
268
                isinstance(ports, list) or isinstance(ports, tuple)), msg
269
            for port in ports:
270
                msg = 'Subnet specification %s is not a dict' % port
271
                assert isinstance(port, dict), msg
272
                err = set(port).difference((
273
                    'network_id', 'status', 'name', 'admin_state_up',
274
                    'mac_address', 'fixed_ips', 'security_groups'))
275
                if err:
276
                    raise ValueError(
277
                        'Invalid key(s): %s in port specification %s' % (
278
                            err, port))
279
                msg = 'network_id is missing in port spec: %s' % port
280
                assert port.get('network_id', None), msg
281
        except AssertionError as ae:
282
            raise ValueError('%s' % ae)
283
        r = self.ports_post(json_data=dict(ports=list(ports)), success=201)
284
        return r.json['ports']
285

    
286
    def get_port_details(self, port_id):
287
        r = self.ports_get(port_id, success=200)
288
        return r.json['port']
289

    
290
    def delete_port(self, port_id):
291
        r = self.ports_delete(port_id, success=204)
292
        return r.headers
293

    
294
    def update_port(
295
            self, port_id, network_id,
296
            name=None, status=None, admin_state_up=None, mac_address=None,
297
            fixed_ips=None, security_groups=None):
298
        """
299
        :param network_id: (str)
300

301
        :param name: (str)
302
        :param status: (str)
303
        :param admin_state_up: (bool) Router administrative status (UP / DOWN)
304
        :param mac_address: (str)
305
        :param fixed_ips: (str)
306
        :param security_groups: (list)
307
        """
308
        port = dict(network_id=network_id)
309
        if name:
310
            port['name'] = name
311
        if status:
312
            port['status'] = status
313
        if admin_state_up not in (None, ):
314
            port['admin_state_up'] = bool(admin_state_up)
315
        if mac_address:
316
            port['mac_address'] = mac_address
317
        if fixed_ips:
318
            port['fixed_ips'] = fixed_ips
319
        if security_groups:
320
            port['security_groups'] = security_groups
321
        r = self.ports_put(port_id, json_data=dict(port=port), success=200)
322
        return r.json['port']
323

    
324
    def list_floatingips(self):
325
        r = self.floatingips_get(success=200)
326
        return r.json['floatingips']
327

    
328
    def get_floatingip_details(self, floatingip_id):
329
        r = self.floatingips_get(floatingip_id, success=200)
330
        return r.json['floatingip']
331

    
332
    def create_floatingip(
333
            self, floating_network_id,
334
            floating_ip_address='', port_id='', fixed_ip_address='',
335
            args=None):
336
        """Cyclades do not use port_id and fixed_ip_address"""
337
        floatingip = dict(floating_network_id=floating_network_id)
338
        if floating_ip_address:
339
            floatingip['floating_ip_address'] = floating_ip_address
340
        if port_id:
341
            floatingip['port_id'] = port_id
342
        if fixed_ip_address:
343
            floatingip['fixed_ip_address'] = fixed_ip_address
344
        if args is not None:
345
            floatingip.update(args)
346
        r = self.floatingips_post(
347
            json_data=dict(floatingip=floatingip), success=200)
348
        return r.json['floatingip']
349

    
350
    def update_floatingip(
351
            self, floatingip_id,
352
            floating_ip_address='', port_id='', fixed_ip_address=''):
353
        """To nullify something optional, use None"""
354
        floatingip = dict()
355
        if floating_ip_address != '':
356
            floatingip['floating_ip_address'] = floating_ip_address
357
        if port_id != '':
358
            floatingip['port_id'] = port_id
359
        if fixed_ip_address != '':
360
            floatingip['fixed_ip_address'] = fixed_ip_address
361
        r = self.floatingips_put(
362
            floatingip_id, json_data=dict(floatingip=floatingip), success=200)
363
        return r.json['floatingip']
364

    
365
    def delete_floatingip(self, floatingip_id):
366
        r = self.floatingips_delete(floatingip_id, success=204)
367
        return r.headers
368

    
369
    #  Wait methods
370

    
371
    def wait_port(
372
            self, port_id,
373
            current_status='BUILD', delay=1, max_wait=100, wait_cb=None):
374

    
375
        def get_status(self, net_id):
376
            r = self.get_port_details(port_id)
377
            return r['status'], None
378

    
379
        return self._wait(
380
            port_id, current_status, get_status, delay, max_wait, wait_cb)