Merge "Restricted dependency of the novaclient."
[snaps.git] / snaps / openstack / utils / neutron_utils.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16
17 from neutronclient.common.exceptions import NotFound
18 from neutronclient.neutron.client import Client
19 from snaps.openstack.utils import keystone_utils
20
21 __author__ = 'spisarski'
22
23 logger = logging.getLogger('neutron_utils')
24
25 """
26 Utilities for basic neutron API calls
27 """
28
29
30 def neutron_client(os_creds):
31     """
32     Instantiates and returns a client for communications with OpenStack's Neutron server
33     :param os_creds: the credentials for connecting to the OpenStack remote API
34     :return: the client object
35     """
36     return Client(api_version=os_creds.network_api_version, session=keystone_utils.keystone_session(os_creds))
37
38
39 def create_network(neutron, os_creds, network_settings):
40     """
41     Creates a network for OpenStack
42     :param neutron: the client
43     :param os_creds: the OpenStack credentials
44     :param network_settings: A dictionary containing the network configuration and is responsible for creating the
45                             network request JSON body
46     :return: the network object
47     """
48     if neutron and network_settings:
49         logger.info('Creating network with name ' + network_settings.name)
50         json_body = network_settings.dict_for_neutron(os_creds)
51         return neutron.create_network(body=json_body)
52     else:
53         logger.error("Failed to create network")
54         raise Exception
55
56
57 def delete_network(neutron, network):
58     """
59     Deletes a network for OpenStack
60     :param neutron: the client
61     :param network: the network object
62     """
63     if neutron and network:
64         logger.info('Deleting network with name ' + network['network']['name'])
65         neutron.delete_network(network['network']['id'])
66
67
68 def get_network(neutron, network_name, project_id=None):
69     """
70     Returns an object (dictionary) of the first network found with a given name and project_id (if included)
71     :param neutron: the client
72     :param network_name: the name of the network to retrieve
73     :param project_id: the id of the network's project
74     :return:
75     """
76     net_filter = dict()
77     if network_name:
78         net_filter['name'] = network_name
79     if project_id:
80         net_filter['project_id'] = project_id
81
82     networks = neutron.list_networks(**net_filter)
83     for network, netInsts in networks.items():
84         for inst in netInsts:
85             if inst.get('name') == network_name:
86                 if project_id and inst.get('project_id') == project_id:
87                     return {'network': inst}
88                 else:
89                     return {'network': inst}
90     return None
91
92
93 def get_network_by_id(neutron, network_id):
94     """
95     Returns the network object (dictionary) with the given ID
96     :param neutron: the client
97     :param network_id: the id of the network to retrieve
98     :return:
99     """
100     networks = neutron.list_networks(**{'id': network_id})
101     for network, netInsts in networks.items():
102         for inst in netInsts:
103             if inst.get('id') == network_id:
104                 return {'network': inst}
105     return None
106
107
108 def create_subnet(neutron, subnet_settings, os_creds, network=None):
109     """
110     Creates a network subnet for OpenStack
111     :param neutron: the client
112     :param network: the network object
113     :param subnet_settings: A dictionary containing the subnet configuration and is responsible for creating the subnet
114                             request JSON body
115     :param os_creds: the OpenStack credentials
116     :return: the subnet object
117     """
118     if neutron and network and subnet_settings:
119         json_body = {'subnets': [subnet_settings.dict_for_neutron(os_creds, network=network)]}
120         logger.info('Creating subnet with name ' + subnet_settings.name)
121         subnets = neutron.create_subnet(body=json_body)
122         return {'subnet': subnets['subnets'][0]}
123     else:
124         logger.error("Failed to create subnet.")
125         raise Exception
126
127
128 def delete_subnet(neutron, subnet):
129     """
130     Deletes a network subnet for OpenStack
131     :param neutron: the client
132     :param subnet: the subnet object
133     """
134     if neutron and subnet:
135         logger.info('Deleting subnet with name ' + subnet['subnet']['name'])
136         neutron.delete_subnet(subnet['subnet']['id'])
137
138
139 def get_subnet_by_name(neutron, subnet_name):
140     """
141     Returns the first subnet object (dictionary) found with a given name
142     :param neutron: the client
143     :param subnet_name: the name of the network to retrieve
144     :return:
145     """
146     subnets = neutron.list_subnets(**{'name': subnet_name})
147     for subnet, subnetInst in subnets.items():
148         for inst in subnetInst:
149             if inst.get('name') == subnet_name:
150                 return {'subnet': inst}
151     return None
152
153
154 def create_router(neutron, os_creds, router_settings):
155     """
156     Creates a router for OpenStack
157     :param neutron: the client
158     :param os_creds: the OpenStack credentials
159     :param router_settings: A dictionary containing the router configuration and is responsible for creating the subnet
160                             request JSON body
161     :return: the router object
162     """
163     if neutron:
164         json_body = router_settings.dict_for_neutron(neutron, os_creds)
165         logger.info('Creating router with name - ' + router_settings.name)
166         return neutron.create_router(json_body)
167     else:
168         logger.error("Failed to create router.")
169         raise Exception
170
171
172 def delete_router(neutron, router):
173     """
174     Deletes a router for OpenStack
175     :param neutron: the client
176     :param router: the router object
177     """
178     if neutron and router:
179         logger.info('Deleting router with name - ' + router['router']['name'])
180         neutron.delete_router(router=router['router']['id'])
181         return True
182
183
184 def get_router_by_name(neutron, router_name):
185     """
186     Returns the first router object (dictionary) found with a given name
187     :param neutron: the client
188     :param router_name: the name of the network to retrieve
189     :return:
190     """
191     routers = neutron.list_routers(**{'name': router_name})
192     for router, routerInst in routers.items():
193         for inst in routerInst:
194             if inst.get('name') == router_name:
195                 return {'router': inst}
196     return None
197
198
199 def add_interface_router(neutron, router, subnet=None, port=None):
200     """
201     Adds an interface router for OpenStack for either a subnet or port. Exception will be raised if requesting for both.
202     :param neutron: the client
203     :param router: the router object
204     :param subnet: the subnet object
205     :param port: the port object
206     :return: the interface router object
207     """
208     if subnet and port:
209         raise Exception('Cannot add interface to the router. Both subnet and port were sent in. Either or please.')
210
211     if neutron and router and (router or subnet):
212         logger.info('Adding interface to router with name ' + router['router']['name'])
213         return neutron.add_interface_router(router=router['router']['id'], body=__create_port_json_body(subnet, port))
214     else:
215         raise Exception("Unable to create interface router as neutron client, router or subnet were not created")
216
217
218 def remove_interface_router(neutron, router, subnet=None, port=None):
219     """
220     Removes an interface router for OpenStack
221     :param neutron: the client
222     :param router: the router object
223     :param subnet: the subnet object (either subnet or port, not both)
224     :param port: the port object
225     """
226     if router:
227         try:
228             logger.info('Removing router interface from router named ' + router['router']['name'])
229             neutron.remove_interface_router(router=router['router']['id'], body=__create_port_json_body(subnet, port))
230         except NotFound as e:
231             logger.warning('Could not remove router interface. NotFound - ' + str(e))
232             pass
233     else:
234         logger.warning('Could not remove router interface, No router object')
235
236
237 def __create_port_json_body(subnet=None, port=None):
238     """
239     Returns the dictionary required for creating and deleting router interfaces. Will only work on a subnet or port
240     object. Will throw and exception if parameters contain both or neither
241     :param subnet: the subnet object
242     :param port: the port object
243     :return: the dict
244     """
245     if subnet and port:
246         raise Exception('Cannot create JSON body with both subnet and port')
247     if not subnet and not port:
248         raise Exception('Cannot create JSON body without subnet or port')
249
250     if subnet:
251         return {"subnet_id": subnet['subnet']['id']}
252     else:
253         return {"port_id": port['port']['id']}
254
255
256 def create_port(neutron, os_creds, port_settings):
257     """
258     Creates a port for OpenStack
259     :param neutron: the client
260     :param os_creds: the OpenStack credentials
261     :param port_settings: the settings object for port configuration
262     :return: the port object
263     """
264     json_body = port_settings.dict_for_neutron(neutron, os_creds)
265     logger.info('Creating port for network with name - ' + port_settings.network_name)
266     return neutron.create_port(body=json_body)
267
268
269 def delete_port(neutron, port):
270     """
271     Removes an OpenStack port
272     :param neutron: the client
273     :param port: the port object
274     :return:
275     """
276     logger.info('Deleting port with name ' + port['port']['name'])
277     neutron.delete_port(port['port']['id'])
278
279
280 def get_port_by_name(neutron, port_name):
281     """
282     Returns the first port object (dictionary) found with a given name
283     :param neutron: the client
284     :param port_name: the name of the port to retrieve
285     :return:
286     """
287     ports = neutron.list_ports(**{'name': port_name})
288     for port in ports['ports']:
289         if port['name'] == port_name:
290             return {'port': port}
291     return None
292
293
294 def create_security_group(neutron, keystone, sec_grp_settings):
295     """
296     Creates a security group object in OpenStack
297     :param neutron: the Neutron client
298     :param keystone: the Keystone client
299     :param sec_grp_settings: the security group settings
300     :return: the security group object
301     """
302     logger.info('Creating security group with name - ' + sec_grp_settings.name)
303     return neutron.create_security_group(sec_grp_settings.dict_for_neutron(keystone))
304
305
306 def delete_security_group(neutron, sec_grp):
307     """
308     Deletes a security group object from OpenStack
309     :param neutron: the client
310     :param sec_grp: the security group object to delete
311     """
312     logger.info('Deleting security group with name - ' + sec_grp['security_group']['name'])
313     return neutron.delete_security_group(sec_grp['security_group']['id'])
314
315
316 def get_security_group(neutron, name):
317     """
318     Returns the first security group object of the given name else None
319     :param neutron: the client
320     :param name: the name of security group object to retrieve
321     """
322     logger.info('Retrieving security group with name - ' + name)
323
324     groups = neutron.list_security_groups(**{'name': name})
325     for group in groups['security_groups']:
326         if group['name'] == name:
327             return {'security_group': group}
328     return None
329
330
331 def get_security_group_by_id(neutron, sec_grp_id):
332     """
333     Returns the first security group object of the given name else None
334     :param neutron: the client
335     :param sec_grp_id: the id of the security group to retrieve
336     """
337     logger.info('Retrieving security group with ID - ' + sec_grp_id)
338
339     groups = neutron.list_security_groups(**{'sec_grp_id': sec_grp_id})
340     for group in groups['security_groups']:
341         return {'security_group': group}
342     return None
343
344
345 def create_security_group_rule(neutron, sec_grp_rule_settings):
346     """
347     Creates a security group object in OpenStack
348     :param neutron: the client
349     :param sec_grp_rule_settings: the security group rule settings
350     :return: the security group object
351     """
352     logger.info('Creating security group to security group - ' + sec_grp_rule_settings.sec_grp_name)
353     return neutron.create_security_group_rule(sec_grp_rule_settings.dict_for_neutron(neutron))
354
355
356 def delete_security_group_rule(neutron, sec_grp_rule):
357     """
358     Deletes a security group object from OpenStack
359     :param neutron: the client
360     :param sec_grp_rule: the security group rule object to delete
361     """
362     logger.info('Deleting security group rule with ID - ' + sec_grp_rule['security_group_rule']['id'])
363     neutron.delete_security_group_rule(sec_grp_rule['security_group_rule']['id'])
364
365
366 def get_rules_by_security_group(neutron, sec_grp):
367     """
368     Retrieves all of the rules for a given security group
369     :param neutron: the client
370     :param sec_grp: the security group object
371     """
372     logger.info('Retrieving security group rules associate with the security group - ' +
373                 sec_grp['security_group']['name'])
374     out = list()
375     rules = neutron.list_security_group_rules(**{'security_group_id': sec_grp['security_group']['id']})
376     for rule in rules['security_group_rules']:
377         if rule['security_group_id'] == sec_grp['security_group']['id']:
378             out.append({'security_group_rule': rule})
379     return out
380
381
382 def get_rule_by_id(neutron, sec_grp, rule_id):
383     """
384     Deletes a security group object from OpenStack
385     :param neutron: the client
386     :param sec_grp: the security group object
387     :param rule_id: the rule's ID
388     """
389     rules = neutron.list_security_group_rules(**{'security_group_id': sec_grp['security_group']['id']})
390     for rule in rules['security_group_rules']:
391         if rule['id'] == rule_id:
392             return {'security_group_rule': rule}
393     return None
394
395
396 def get_external_networks(neutron):
397     """
398     Returns a list of external OpenStack network object/dict for all external networks
399     :param neutron: the client
400     :return: a list of external networks (empty list if none configured)
401     """
402     out = list()
403     for network in neutron.list_networks(**{'router:external': True})['networks']:
404         out.append({'network': network})
405     return out