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.utils import neutron_utils, keystone_utils
21 __author__ = 'spisarski'
23 logger = logging.getLogger('OpenStackNetwork')
26 class OpenStackRouter:
28 Class responsible for creating a router in OpenStack
31 def __init__(self, os_creds, router_settings):
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
39 self.__os_creds = os_creds
41 if not router_settings:
42 raise RouterCreationError('router_settings is required')
44 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
56 def create(self, cleanup=False):
58 Responsible for creating the router.
59 :param cleanup: When true, only perform lookups for OpenStack objects.
60 :return: the router object
62 self.__neutron = neutron_utils.neutron_client(self.__os_creds)
65 'Creating Router with name - ' + self.router_settings.name)
67 router_inst = neutron_utils.get_router_by_name(
68 self.__neutron, self.router_settings.name)
70 self.__router = router_inst
74 self.__router = neutron_utils.create_router(
75 self.__neutron, self.__os_creds, self.router_settings)
77 for internal_subnet_name in self.router_settings.internal_subnets:
78 internal_subnet = neutron_utils.get_subnet_by_name(
79 self.__neutron, internal_subnet_name)
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 self.__internal_router_interface = neutron_utils.add_interface_router(
85 self.__neutron, self.__router, subnet=internal_subnet)
87 raise RouterCreationError(
88 'Subnet not found with name ' + internal_subnet_name)
90 for port_setting in self.router_settings.port_settings:
91 port = neutron_utils.get_port_by_name(self.__neutron,
94 'Retrieved port %s for router - %s', port_setting.name,
95 self.router_settings.name)
97 self.__ports.append(port)
99 if not port and not cleanup and not existing:
100 port = neutron_utils.create_port(self.__neutron,
101 self.__os_creds, port_setting)
104 'Created port %s for router - %s', port_setting.name,
105 self.router_settings.name)
106 self.__ports.append(port)
107 neutron_utils.add_interface_router(self.__neutron,
111 raise RouterCreationError(
112 'Error creating port with name - ' + port_setting.name)
118 Removes and deletes all items created in reverse order.
120 for port in self.__ports:
122 'Removing router interface from router %s and port %s',
123 self.router_settings.name, port.name)
125 neutron_utils.remove_interface_router(self.__neutron,
126 self.__router, port=port)
129 self.__ports = list()
131 for internal_subnet in self.__internal_subnets:
133 'Removing router interface from router %s and subnet %s',
134 self.router_settings.name, internal_subnet.name)
136 neutron_utils.remove_interface_router(self.__neutron,
138 subnet=internal_subnet)
141 self.__internal_subnets = list()
144 logger.info('Removing router ' + self.router_settings.name)
146 neutron_utils.delete_router(self.__neutron, self.__router)
151 def get_router(self):
153 Returns the OpenStack router object
158 def get_internal_router_interface(self):
160 Returns the OpenStack internal router interface object
163 return self.__internal_router_interface
166 class RouterCreationError(Exception):
168 Exception to be thrown when an router instance cannot be created
172 class RouterSettings:
174 Class representing a router configuration
177 def __init__(self, **kwargs):
179 Constructor - all parameters are optional
180 :param name: The router name.
181 :param project_name: The name of the project who owns the network. Only
182 administrative users can specify a project ID
183 other than their own. You cannot change this value
184 through authorization policies.
185 :param external_gateway: Name of the external network to which to route
186 :param admin_state_up: The administrative status of the router.
187 True = up / False = down (default True)
188 :param external_fixed_ips: Dictionary containing the IP address
190 :param internal_subnets: List of subnet names to which to connect this
191 router for Floating IP purposes
192 :param port_settings: List of PortSettings objects
195 self.name = kwargs.get('name')
196 self.project_name = kwargs.get('project_name')
197 self.external_gateway = kwargs.get('external_gateway')
199 self.admin_state_up = kwargs.get('admin_state_up')
200 self.enable_snat = kwargs.get('enable_snat')
201 self.external_fixed_ips = kwargs.get('external_fixed_ips')
202 if kwargs.get('internal_subnets'):
203 self.internal_subnets = kwargs['internal_subnets']
205 self.internal_subnets = list()
207 self.port_settings = list()
208 if kwargs.get('interfaces', kwargs.get('port_settings')):
209 interfaces = kwargs.get('interfaces', kwargs.get('port_settings'))
210 for interface in interfaces:
211 if isinstance(interface, PortSettings):
212 self.port_settings.append(interface)
214 self.port_settings.append(
215 PortSettings(**interface['port']))
218 raise RouterSettingsError('Name is required')
220 def dict_for_neutron(self, neutron, os_creds):
222 Returns a dictionary object representing this object.
223 This is meant to be converted into JSON designed for use by the Neutron
226 TODO - expand automated testing to exercise all parameters
227 :param neutron: The neutron client to retrieve external network
228 information if necessary
229 :param os_creds: The OpenStack credentials
230 :return: the dictionary object
238 out['name'] = self.name
239 if self.project_name:
240 keystone = keystone_utils.keystone_client(os_creds)
241 project = keystone_utils.get_project(keystone, self.project_name)
244 project_id = project.id
246 out['project_id'] = project_id
248 raise RouterSettingsError(
249 'Could not find project ID for project named - ' +
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(neutron, self.external_gateway,
257 ext_gw['network_id'] = ext_net.id
258 out['external_gateway_info'] = ext_gw
260 raise RouterSettingsError(
261 'Could not find the external network named - ' +
262 self.external_gateway)
264 # TODO: Enable SNAT option for Router
265 # TODO: Add external_fixed_ips Tests
267 return {'router': out}
270 class RouterSettingsError(Exception):
272 Exception to be thrown when router settings attributes are incorrect