1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 # and others. All rights reserved.
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:
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 from neutronclient.common.exceptions import NotFound
18 from snaps.openstack.create_network import PortSettings
19 from snaps.openstack.openstack_creator import OpenStackNetworkObject
20 from snaps.openstack.utils import neutron_utils, keystone_utils
22 __author__ = 'spisarski'
24 logger = logging.getLogger('OpenStackNetwork')
27 class OpenStackRouter(OpenStackNetworkObject):
29 Class responsible for managing a router in OpenStack
32 def __init__(self, os_creds, router_settings):
34 Constructor - all parameters are required
35 :param os_creds: The credentials to connect with OpenStack
36 :param router_settings: The settings used to create a router object
37 (must be an instance of the RouterSettings
40 super(self.__class__, self).__init__(os_creds)
42 if not router_settings:
43 raise RouterCreationError('router_settings is required')
45 self.router_settings = router_settings
47 # Attributes instantiated on create()
49 self.__internal_subnets = list()
50 self.__internal_router_interface = None
52 # Dict where the port object is the key and any newly created router
53 # interfaces are the value
58 Loads the existing router.
59 :return: the Router domain object
61 super(self.__class__, self).initialize()
63 self.__router = neutron_utils.get_router(
64 self._neutron, router_settings=self.router_settings)
66 for internal_subnet_name in self.router_settings.internal_subnets:
67 internal_subnet = neutron_utils.get_subnet(
68 self._neutron, subnet_name=internal_subnet_name)
70 self.__internal_subnets.append(internal_subnet)
72 raise RouterCreationError(
73 'Subnet not found with name ' + internal_subnet_name)
75 for port_setting in self.router_settings.port_settings:
76 port = neutron_utils.get_port(
77 self._neutron, port_settings=port_setting)
79 self.__ports.append(port)
85 Responsible for creating the router.
86 :return: the Router domain object
91 self.__router = neutron_utils.create_router(
92 self._neutron, self._os_creds, self.router_settings)
94 for internal_subnet_name in self.router_settings.internal_subnets:
95 internal_subnet = neutron_utils.get_subnet(
96 self._neutron, subnet_name=internal_subnet_name)
98 self.__internal_subnets.append(internal_subnet)
100 logger.debug('Adding router to subnet...')
101 router_intf = neutron_utils.add_interface_router(
102 self._neutron, self.__router,
103 subnet=internal_subnet)
104 self.__internal_router_interface = router_intf
106 raise RouterCreationError(
107 'Subnet not found with name ' + internal_subnet_name)
109 for port_setting in self.router_settings.port_settings:
110 port = neutron_utils.get_port(
111 self._neutron, port_settings=port_setting)
113 'Retrieved port %s for router - %s', port_setting.name,
114 self.router_settings.name)
116 self.__ports.append(port)
119 port = neutron_utils.create_port(
120 self._neutron, self._os_creds, port_setting)
123 'Created port %s for router - %s',
125 self.router_settings.name)
126 self.__ports.append(port)
127 neutron_utils.add_interface_router(self._neutron,
131 raise RouterCreationError(
132 'Error creating port with name - '
135 self.__router = neutron_utils.get_router_by_id(
136 self._neutron, self.__router.id)
141 Removes and deletes all items created in reverse order.
143 for port in self.__ports:
145 'Removing router interface from router %s and port %s',
146 self.router_settings.name, port.name)
148 neutron_utils.remove_interface_router(self._neutron,
149 self.__router, port=port)
152 self.__ports = list()
154 for internal_subnet in self.__internal_subnets:
156 'Removing router interface from router %s and subnet %s',
157 self.router_settings.name, internal_subnet.name)
159 neutron_utils.remove_interface_router(self._neutron,
161 subnet=internal_subnet)
164 self.__internal_subnets = list()
167 logger.info('Removing router ' + self.router_settings.name)
169 neutron_utils.delete_router(self._neutron, self.__router)
174 def get_router(self):
176 Returns the OpenStack router object
181 def get_internal_router_interface(self):
183 Returns the OpenStack internal router interface object
186 return self.__internal_router_interface
189 class RouterCreationError(Exception):
191 Exception to be thrown when an router instance cannot be created
195 class RouterSettings:
197 Class representing a router configuration
200 def __init__(self, **kwargs):
202 Constructor - all parameters are optional
203 :param name: The router name.
204 :param project_name: The name of the project who owns the network. Only
205 administrative users can specify a project ID
206 other than their own. You cannot change this value
207 through authorization policies.
208 :param external_gateway: Name of the external network to which to route
209 :param admin_state_up: The administrative status of the router.
210 True = up / False = down (default True)
211 :param internal_subnets: List of subnet names to which to connect this
212 router for Floating IP purposes
213 :param port_settings: List of PortSettings objects
216 self.name = kwargs.get('name')
217 self.project_name = kwargs.get('project_name')
218 self.external_gateway = kwargs.get('external_gateway')
220 self.admin_state_up = kwargs.get('admin_state_up', True)
221 self.enable_snat = kwargs.get('enable_snat')
222 if kwargs.get('internal_subnets'):
223 self.internal_subnets = kwargs['internal_subnets']
225 self.internal_subnets = list()
227 self.port_settings = list()
228 if kwargs.get('interfaces', kwargs.get('port_settings')):
229 interfaces = kwargs.get('interfaces', kwargs.get('port_settings'))
230 for interface in interfaces:
231 if isinstance(interface, PortSettings):
232 self.port_settings.append(interface)
234 self.port_settings.append(
235 PortSettings(**interface['port']))
238 raise RouterSettingsError('Name is required')
240 def dict_for_neutron(self, neutron, os_creds):
242 Returns a dictionary object representing this object.
243 This is meant to be converted into JSON designed for use by the Neutron
246 TODO - expand automated testing to exercise all parameters
247 :param neutron: The neutron client to retrieve external network
248 information if necessary
249 :param os_creds: The OpenStack credentials
250 :return: the dictionary object
256 out['name'] = self.name
257 if self.project_name:
258 keystone = keystone_utils.keystone_client(os_creds)
259 project = keystone_utils.get_project(
260 keystone=keystone, project_name=self.project_name)
263 project_id = project.id
265 out['tenant_id'] = project_id
267 raise RouterSettingsError(
268 'Could not find project ID for project named - ' +
270 if self.admin_state_up is not None:
271 out['admin_state_up'] = self.admin_state_up
272 if self.external_gateway:
273 ext_net = neutron_utils.get_network(
274 neutron, network_name=self.external_gateway)
276 ext_gw['network_id'] = ext_net.id
277 out['external_gateway_info'] = ext_gw
279 raise RouterSettingsError(
280 'Could not find the external network named - ' +
281 self.external_gateway)
283 return {'router': out}
286 class RouterSettingsError(Exception):
288 Exception to be thrown when router settings attributes are incorrect