1 # Copyright 2013 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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.
34 from kamaki.clients import ClientError
35 from kamaki.clients.network.rest_api import NetworkRestClient
38 class NetworkClient(NetworkRestClient):
39 """OpenStack Network API 2.0 client"""
41 def list_networks(self):
42 r = self.networks_get(success=200)
43 return r.json['networks']
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']
53 def create_networks(self, networks):
54 """Atomic operation for batch network creation (all or nothing)
55 :param networks: (list) [
56 {name: ..(str).., admin_state_up: ..(bool).., shared: ..(bool)..},
57 {name: ..(str).., admin_state_up: ..(bool).., shared: ..(bool)..}]
58 name is mandatory, the rest is optional
59 e.g., create_networks([
60 {name: 'net1', admin_state_up: True},
62 :returns: (list of dicts) created networks details
63 :raises ValueError: if networks is misformated
64 :raises ClientError: if the request failed or didn't return 201
67 msg = 'The networks parameter must be list or tuple'
69 isinstance(networks, list) or isinstance(networks, tuple)), msg
70 for network in networks:
71 msg = 'Network specification %s is not a dict' % network
72 assert isinstance(network, dict), msg
73 err = set(network).difference(
74 ('name', 'admin_state_up', 'shared'))
77 'Invalid key(s): %s in network specification %s' % (
79 msg = 'Name is missing in network specification: %s' % network
80 assert network.get('name', None), msg
81 network.setdefault('admin_state_up', False)
82 except AssertionError as ae:
83 raise ValueError('%s' % ae)
85 req = dict(networks=list(networks))
86 r = self.networks_post(json_data=req, success=201)
87 return r.json['networks']
89 def get_network_details(self, network_id):
90 r = self.networks_get(network_id, success=200)
91 return r.json['network']
94 self, network_id, name=None, admin_state_up=None, shared=None):
97 network['name'] = name
98 if admin_state_up not in (None, ):
99 network['admin_state_up'] = admin_state_up
100 if shared not in (None, ):
101 network['shared'] = shared
102 network = dict(network=network)
103 r = self.networks_put(network_id, json_data=network, success=200)
104 return r.json['network']
106 def delete_network(self, network_id):
107 r = self.networks_delete(network_id, success=204)
110 def list_subnets(self):
111 r = self.subnets_get(success=200)
112 return r.json['subnets']
115 self, network_id, cidr,
116 name=None, allocation_pools=None, gateway_ip=None, subnet_id=None,
117 ipv6=None, enable_dhcp=None):
119 :param network_id: (str)
122 :param name: (str) The subnet name
123 :param allocation_pools: (list of dicts) start/end addresses of
124 allocation pools: [{'start': ..., 'end': ...}, ...]
125 :param gateway_ip: (str)
126 :param subnet_id: (str)
127 :param ipv6: (bool) ip_version == 6 if true else 4 (default)
128 :param enable_dhcp: (bool)
131 network_id=network_id, cidr=cidr, ip_version=6 if ipv6 else 4)
133 subnet['name'] = name
135 subnet['allocation_pools'] = allocation_pools
137 subnet['gateway_ip'] = gateway_ip
139 subnet['id'] = subnet_id
140 if enable_dhcp not in (None, ):
141 subnet['enable_dhcp'] = bool(enable_dhcp)
142 r = self.subnets_post(json_data=dict(subnet=subnet), success=201)
143 return r.json['subnet']
145 def create_subnets(self, subnets):
146 """Atomic operation for batch subnet creation (all or nothing)
147 :param subnets: (list of dicts) {key: ...} with all parameters in the
148 method create_subnet, where method mandatory / optional paramteres
149 respond to mandatory / optional paramters in subnets items
150 :returns: (list of dicts) created subnetss details
151 :raises ValueError: if subnets parameter is incorrectly formated
152 :raises ClientError: if the request failed or didn't return 201
155 msg = 'The subnets parameter must be list or tuple'
157 isinstance(subnets, list) or isinstance(subnets, tuple)), msg
158 for subnet in subnets:
159 msg = 'Subnet specification %s is not a dict' % subnet
160 assert isinstance(subnet, dict), msg
161 err = set(subnet).difference((
162 'network_id', 'cidr', 'name', 'allocation_pools',
163 'gateway_ip', 'subnet_id', 'ipv6', 'enable_dhcp'))
166 'Invalid key(s): %s in subnet specification %s' % (
168 msg = 'network_id is missing in subnet spec: %s' % subnet
169 assert subnet.get('network_id', None), msg
170 msg = 'cidr is missing in subnet spec: %s' % subnet
171 assert subnet.get('cidr', None), msg
172 subnet['ip_version'] = 6 if subnet.pop('ipv6', None) else 4
173 if 'subnet_id' in subnet:
174 subnet['id'] = subnet.pop('subnet_id')
175 except AssertionError as ae:
176 raise ValueError('%s' % ae)
178 r = self.subnets_post(
179 json_data=dict(subnets=list(subnets)), success=201)
180 return r.json['subnets']
182 def get_subnet_details(self, subnet_id):
183 r = self.subnets_get(subnet_id, success=201)
187 self, network_id, cidr,
188 name=None, allocation_pools=None, gateway_ip=None, subnet_id=None,
189 ipv6=None, enable_dhcp=None):
191 :param network_id: (str) used as filter
192 :param cidr: (str) used as filter
194 :param name: (str) The subnet name
195 :param allocation_pools: (list of dicts) start/end addresses of
196 allocation pools: [{'start': ..., 'end': ...}, ...]
197 :param gateway_ip: (str)
198 :param subnet_id: (str)
199 :param ipv6: (bool) ip_version == 6 if true, 4 if false, used as filter
200 :param enable_dhcp: (bool)
202 subnet = dict(network_id=network_id, cidr=cidr)
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 subnet_id not in (None, ):
210 subnet['id'] = subnet_id
211 if ipv6 not in (None, ):
212 subnet['ip_version'] = 6 if ipv6 else 4
213 if enable_dhcp not in (None, ):
214 subnet['enable_dhcp'] = enable_dhcp
215 r = self.subnets_put(json_data=dict(subnet=subnet), success=201)
216 return r.json['subnet']
218 def delete_subnet(self, subnet_id):
219 r = self.subnets_delete(subnet_id, success=204)
222 def list_ports(self):
223 r = self.ports_get(success=200)
224 return r.json['ports']
228 name=None, status=None, admin_state_up=None, mac_address=None,
229 fixed_ips=None, security_groups=None):
231 :param network_id: (str)
235 :param admin_state_up: (bool) Router administrative status (UP / DOWN)
236 :param mac_address: (str)
237 :param fixed_ips: (str)
238 :param security_groups: (list)
240 port = dict(network_id=network_id)
244 port['status'] = status
245 if admin_state_up not in (None, ):
246 port['admin_state_up'] = bool(admin_state_up)
248 port['mac_address'] = mac_address
250 port['fixed_ips'] = fixed_ips
252 port['security_groups'] = security_groups
253 r = self.ports_post(json_data=dict(port=port), success=201)
254 return r.json['port']
256 def create_ports(self, ports):
257 """Atomic operation for batch port creation (all or nothing)
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
266 msg = 'The ports parameter must be list or tuple'
268 isinstance(ports, list) or isinstance(ports, tuple)), msg
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'))
277 'Invalid key(s): %s in port specification %s' % (
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']
286 def get_port_details(self, port_id):
287 r = self.ports_get(port_id, success=201)
288 return r.json['port']
290 def delete_port(self, port_id):
291 r = self.ports_delete(port_id, success=204)
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):
299 :param network_id: (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)
308 port = dict(network_id=network_id)
312 port['status'] = status
313 if admin_state_up not in (None, ):
314 port['admin_state_up'] = bool(admin_state_up)
316 port['mac_address'] = mac_address
318 port['fixed_ips'] = fixed_ips
320 port['security_groups'] = security_groups
321 r = self.ports_put(port_id, json_data=dict(port=port), success=201)
322 return r.json['port']