Refactored neutron_utils#get_router_by_name() to get_router()
[snaps.git] / snaps / openstack / create_router.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 snaps.openstack.create_network import PortSettings
19 from snaps.openstack.utils import neutron_utils, keystone_utils
20
21 __author__ = 'spisarski'
22
23 logger = logging.getLogger('OpenStackNetwork')
24
25
26 class OpenStackRouter:
27     """
28     Class responsible for creating a router in OpenStack
29     """
30
31     def __init__(self, os_creds, router_settings):
32         """
33         Constructor - all parameters are required
34         :param os_creds: The credentials to connect with OpenStack
35         :param router_settings: The settings used to create a router object
36                                 (must be an instance of the RouterSettings
37                                 class)
38         """
39         self.__os_creds = os_creds
40
41         if not router_settings:
42             raise RouterCreationError('router_settings is required')
43
44         self.router_settings = router_settings
45         self.__neutron = None
46
47         # Attributes instantiated on create()
48         self.__router = None
49         self.__internal_subnets = list()
50         self.__internal_router_interface = None
51
52         # Dict where the port object is the key and any newly created router
53         # interfaces are the value
54         self.__ports = list()
55
56     def create(self, cleanup=False):
57         """
58         Responsible for creating the router.
59         :param cleanup: When true, only perform lookups for OpenStack objects.
60         :return: the router object
61         """
62         self.__neutron = neutron_utils.neutron_client(self.__os_creds)
63
64         logger.debug(
65             'Creating Router with name - ' + self.router_settings.name)
66         existing = False
67         router_inst = neutron_utils.get_router(
68             self.__neutron, router_settings=self.router_settings)
69         if router_inst:
70             self.__router = router_inst
71             existing = True
72         else:
73             if not cleanup:
74                 self.__router = neutron_utils.create_router(
75                     self.__neutron, self.__os_creds, self.router_settings)
76
77         for internal_subnet_name in self.router_settings.internal_subnets:
78             internal_subnet = neutron_utils.get_subnet(
79                 self.__neutron, subnet_name=internal_subnet_name)
80             if internal_subnet:
81                 self.__internal_subnets.append(internal_subnet)
82                 if internal_subnet and not cleanup and not existing:
83                     logger.debug('Adding router to subnet...')
84                     router_intf = neutron_utils.add_interface_router(
85                         self.__neutron, self.__router, subnet=internal_subnet)
86                     self.__internal_router_interface = router_intf
87             else:
88                 raise RouterCreationError(
89                     'Subnet not found with name ' + internal_subnet_name)
90
91         for port_setting in self.router_settings.port_settings:
92             port = neutron_utils.get_port(
93                 self.__neutron, port_settings=port_setting)
94             logger.info(
95                 'Retrieved port %s for router - %s', port_setting.name,
96                 self.router_settings.name)
97             if port:
98                 self.__ports.append(port)
99
100             if not port and not cleanup and not existing:
101                 port = neutron_utils.create_port(self.__neutron,
102                                                  self.__os_creds, port_setting)
103                 if port:
104                     logger.info(
105                         'Created port %s for router - %s', port_setting.name,
106                         self.router_settings.name)
107                     self.__ports.append(port)
108                     neutron_utils.add_interface_router(self.__neutron,
109                                                        self.__router,
110                                                        port=port)
111                 else:
112                     raise RouterCreationError(
113                         'Error creating port with name - ' + port_setting.name)
114
115         return self.__router
116
117     def clean(self):
118         """
119         Removes and deletes all items created in reverse order.
120         """
121         for port in self.__ports:
122             logger.info(
123                 'Removing router interface from router %s and port %s',
124                 self.router_settings.name, port.name)
125             try:
126                 neutron_utils.remove_interface_router(self.__neutron,
127                                                       self.__router, port=port)
128             except NotFound:
129                 pass
130         self.__ports = list()
131
132         for internal_subnet in self.__internal_subnets:
133             logger.info(
134                 'Removing router interface from router %s and subnet %s',
135                 self.router_settings.name, internal_subnet.name)
136             try:
137                 neutron_utils.remove_interface_router(self.__neutron,
138                                                       self.__router,
139                                                       subnet=internal_subnet)
140             except NotFound:
141                 pass
142         self.__internal_subnets = list()
143
144         if self.__router:
145             logger.info('Removing router ' + self.router_settings.name)
146             try:
147                 neutron_utils.delete_router(self.__neutron, self.__router)
148             except NotFound:
149                 pass
150             self.__router = None
151
152     def get_router(self):
153         """
154         Returns the OpenStack router object
155         :return:
156         """
157         return self.__router
158
159     def get_internal_router_interface(self):
160         """
161         Returns the OpenStack internal router interface object
162         :return:
163         """
164         return self.__internal_router_interface
165
166
167 class RouterCreationError(Exception):
168     """
169     Exception to be thrown when an router instance cannot be created
170     """
171
172
173 class RouterSettings:
174     """
175     Class representing a router configuration
176     """
177
178     def __init__(self, **kwargs):
179         """
180         Constructor - all parameters are optional
181         :param name: The router name.
182         :param project_name: The name of the project who owns the network. Only
183                              administrative users can specify a project ID
184                              other than their own. You cannot change this value
185                              through authorization policies.
186         :param external_gateway: Name of the external network to which to route
187         :param admin_state_up: The administrative status of the router.
188                                True = up / False = down (default True)
189         :param external_fixed_ips: Dictionary containing the IP address
190                                    parameters.
191         :param internal_subnets: List of subnet names to which to connect this
192                                  router for Floating IP purposes
193         :param port_settings: List of PortSettings objects
194         :return:
195         """
196         self.name = kwargs.get('name')
197         self.project_name = kwargs.get('project_name')
198         self.external_gateway = kwargs.get('external_gateway')
199
200         self.admin_state_up = kwargs.get('admin_state_up')
201         self.enable_snat = kwargs.get('enable_snat')
202         self.external_fixed_ips = kwargs.get('external_fixed_ips')
203         if kwargs.get('internal_subnets'):
204             self.internal_subnets = kwargs['internal_subnets']
205         else:
206             self.internal_subnets = list()
207
208         self.port_settings = list()
209         if kwargs.get('interfaces', kwargs.get('port_settings')):
210             interfaces = kwargs.get('interfaces', kwargs.get('port_settings'))
211             for interface in interfaces:
212                 if isinstance(interface, PortSettings):
213                     self.port_settings.append(interface)
214                 else:
215                     self.port_settings.append(
216                         PortSettings(**interface['port']))
217
218         if not self.name:
219             raise RouterSettingsError('Name is required')
220
221     def dict_for_neutron(self, neutron, os_creds):
222         """
223         Returns a dictionary object representing this object.
224         This is meant to be converted into JSON designed for use by the Neutron
225         API
226
227         TODO - expand automated testing to exercise all parameters
228         :param neutron: The neutron client to retrieve external network
229                         information if necessary
230         :param os_creds: The OpenStack credentials
231         :return: the dictionary object
232         """
233         out = dict()
234         ext_gw = dict()
235
236         if self.name:
237             out['name'] = self.name
238         if self.project_name:
239             keystone = keystone_utils.keystone_client(os_creds)
240             project = keystone_utils.get_project(
241                 keystone=keystone, project_name=self.project_name)
242             project_id = None
243             if project:
244                 project_id = project.id
245             if project_id:
246                 out['tenant_id'] = project_id
247             else:
248                 raise RouterSettingsError(
249                     'Could not find project ID for project named - ' +
250                     self.project_name)
251         if self.admin_state_up is not None:
252             out['admin_state_up'] = self.admin_state_up
253         if self.external_gateway:
254             ext_net = neutron_utils.get_network(
255                 neutron, network_name=self.external_gateway)
256             if ext_net:
257                 ext_gw['network_id'] = ext_net.id
258                 out['external_gateway_info'] = ext_gw
259             else:
260                 raise RouterSettingsError(
261                     'Could not find the external network named - ' +
262                     self.external_gateway)
263
264         return {'router': out}
265
266
267 class RouterSettingsError(Exception):
268     """
269     Exception to be thrown when router settings attributes are incorrect
270     """