1 # Copyright (c) 2016 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
19 from snaps.openstack.create_network import PortSettings
20 from snaps.openstack.utils import neutron_utils, keystone_utils
22 __author__ = 'spisarski'
24 logger = logging.getLogger('OpenStackNetwork')
27 class OpenStackRouter:
29 Class responsible for creating 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 (must be an instance of the
39 self.__os_creds = os_creds
41 if not router_settings:
42 raise Exception('router_settings is required')
44 self.router_settings = router_settings
45 self.__neutron = neutron_utils.neutron_client(os_creds)
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 interfaces are the value
55 def create(self, cleanup=False):
57 Responsible for creating the router.
58 :param cleanup: When true, only perform lookups for OpenStack objects.
59 :return: the router object
61 logger.debug('Creating Router with name - ' + self.router_settings.name)
64 router_inst = neutron_utils.get_router_by_name(self.__neutron, self.router_settings.name)
66 self.__router = router_inst
70 self.__router = neutron_utils.create_router(self.__neutron, self.__os_creds, self.router_settings)
72 for internal_subnet_name in self.router_settings.internal_subnets:
73 internal_subnet = neutron_utils.get_subnet_by_name(self.__neutron, internal_subnet_name)
75 self.__internal_subnets.append(internal_subnet)
76 if internal_subnet and not cleanup and not existing:
77 logger.debug('Adding router to subnet...')
78 self.__internal_router_interface = neutron_utils.add_interface_router(
79 self.__neutron, self.__router, subnet=internal_subnet)
81 raise Exception('Subnet not found with name ' + internal_subnet_name)
83 for port_setting in self.router_settings.port_settings:
84 port = neutron_utils.get_port_by_name(self.__neutron, port_setting.name)
85 logger.info('Retrieved port ' + port_setting.name + ' for router - ' + self.router_settings.name)
87 self.__ports.append(port)
89 if not port and not cleanup and not existing:
90 port = neutron_utils.create_port(self.__neutron, self.__os_creds, port_setting)
92 logger.info('Created port ' + port_setting.name + ' for router - ' + self.router_settings.name)
93 self.__ports.append(port)
94 neutron_utils.add_interface_router(self.__neutron, self.__router, port=port)
96 raise Exception('Error creating port with name - ' + port_setting.name)
99 except Exception as e:
101 raise Exception(e.message)
105 Removes and deletes all items created in reverse order.
107 for port in self.__ports:
108 logger.info('Removing router interface from router ' + self.router_settings.name +
109 ' and port ' + port['port']['name'])
111 neutron_utils.remove_interface_router(self.__neutron, self.__router, port=port)
114 self.__ports = list()
116 for internal_subnet in self.__internal_subnets:
117 logger.info('Removing router interface from router ' + self.router_settings.name +
118 ' and subnet ' + internal_subnet['subnet']['name'])
120 neutron_utils.remove_interface_router(self.__neutron, self.__router, subnet=internal_subnet)
123 self.__internal_subnets = list()
126 logger.info('Removing router ' + self.router_settings.name)
128 neutron_utils.delete_router(self.__neutron, self.__router)
133 def get_router(self):
135 Returns the OpenStack router object
140 def get_internal_router_interface(self):
142 Returns the OpenStack internal router interface object
145 return self.__internal_router_interface
148 class RouterSettings:
150 Class representing a router configuration
153 def __init__(self, config=None, name=None, project_name=None, external_gateway=None,
154 admin_state_up=True, external_fixed_ips=None, internal_subnets=list(),
155 port_settings=list()):
157 Constructor - all parameters are optional
158 :param config: Should be a dict object containing the configuration settings using the attribute names below
159 as each member's the key and overrides any of the other parameters.
160 :param name: The router name.
161 :param project_name: The name of the project who owns the network. Only administrative users can specify a
162 project ID other than their own. You cannot change this value through authorization
164 :param external_gateway: Name of the external network to which to route
165 :param admin_state_up: The administrative status of the router. True = up / False = down (default True)
166 :param enable_snat: Boolean value. Enable Source NAT (SNAT) attribute. Default is True. To persist this
167 attribute value, set the enable_snat_by_default option in the neutron.conf file.
168 :param external_fixed_ips: Dictionary containing the IP address parameters.
169 :param internal_subnets: List of subnet names to which to connect this router for Floating IP purposes
170 :param port_settings: List of PortSettings objects
174 self.name = config.get('name')
175 self.project_name = config.get('project_name')
176 self.external_gateway = config.get('external_gateway')
178 self.admin_state_up = config.get('admin_state_up')
179 self.enable_snat = config.get('enable_snat')
180 self.external_fixed_ips = config.get('external_fixed_ips')
181 if config.get('internal_subnets'):
182 self.internal_subnets = config['internal_subnets']
184 self.internal_subnets = internal_subnets
186 self.port_settings = list()
187 if config.get('interfaces'):
188 interfaces = config['interfaces']
189 for interface in interfaces:
190 if interface.get('port'):
191 self.port_settings.append(PortSettings(config=interface['port']))
194 self.project_name = project_name
195 self.external_gateway = external_gateway
196 self.admin_state_up = admin_state_up
197 self.external_fixed_ips = external_fixed_ips
198 self.internal_subnets = internal_subnets
199 self.port_settings = port_settings
202 raise Exception('Name is required')
204 def dict_for_neutron(self, neutron, os_creds):
206 Returns a dictionary object representing this object.
207 This is meant to be converted into JSON designed for use by the Neutron API
209 TODO - expand automated testing to exercise all parameters
210 :param neutron: The neutron client to retrieve external network information if necessary
211 :param os_creds: The OpenStack credentials
212 :return: the dictionary object
220 out['name'] = self.name
221 if self.project_name:
222 keystone = keystone_utils.keystone_client(os_creds)
223 project = keystone_utils.get_project(keystone, self.project_name)
226 project_id = project.id
228 out['project_id'] = project_id
230 raise Exception('Could not find project ID for project named - ' + self.project_name)
231 if self.admin_state_up is not None:
232 out['admin_state_up'] = self.admin_state_up
233 if self.external_gateway:
234 ext_net = neutron_utils.get_network(neutron, self.external_gateway, project_id)
236 ext_gw['network_id'] = ext_net['network']['id']
237 out['external_gateway_info'] = ext_gw
239 raise Exception('Could not find the external network named - ' + self.external_gateway)
241 #TODO: Enable SNAT option for Router
242 #TODO: Add external_fixed_ips Tests
244 return {'router': out}