Added the ability to give the tests the ability to add in flavor metadata.
[snaps.git] / snaps / openstack / create_router.py
1 # Copyright (c) 2016 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
19 from snaps.openstack.create_network import PortSettings
20 from snaps.openstack.utils import neutron_utils, keystone_utils
21
22 __author__ = 'spisarski'
23
24 logger = logging.getLogger('OpenStackNetwork')
25
26
27 class OpenStackRouter:
28     """
29     Class responsible for creating a router in OpenStack
30     """
31
32     def __init__(self, os_creds, router_settings):
33         """
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
37                                 RouterSettings class)
38         """
39         self.__os_creds = os_creds
40
41         if not router_settings:
42             raise Exception('router_settings is required')
43
44         self.router_settings = router_settings
45         self.__neutron = neutron_utils.neutron_client(os_creds)
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 interfaces are the value
53         self.__ports = list()
54
55     def create(self, cleanup=False):
56         """
57         Responsible for creating the router.
58         :param cleanup: When true, only perform lookups for OpenStack objects.
59         :return: the router object
60         """
61         logger.debug('Creating Router with name - ' + self.router_settings.name)
62         try:
63             existing = False
64             router_inst = neutron_utils.get_router_by_name(self.__neutron, self.router_settings.name)
65             if router_inst:
66                 self.__router = router_inst
67                 existing = True
68             else:
69                 if not cleanup:
70                     self.__router = neutron_utils.create_router(self.__neutron, self.__os_creds, self.router_settings)
71
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)
74                 if internal_subnet:
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)
80                 else:
81                     raise Exception('Subnet not found with name ' + internal_subnet_name)
82
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)
86                 if port:
87                     self.__ports.append(port)
88
89                 if not port and not cleanup and not existing:
90                     port = neutron_utils.create_port(self.__neutron, self.__os_creds, port_setting)
91                     if port:
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)
95                     else:
96                         raise Exception('Error creating port with name - ' + port_setting.name)
97
98             return self.__router
99         except Exception as e:
100             self.clean()
101             raise Exception(e.message)
102
103     def clean(self):
104         """
105         Removes and deletes all items created in reverse order.
106         """
107         for port in self.__ports:
108             logger.info('Removing router interface from router ' + self.router_settings.name +
109                         ' and port ' + port['port']['name'])
110             try:
111                 neutron_utils.remove_interface_router(self.__neutron, self.__router, port=port)
112             except NotFound:
113                 pass
114         self.__ports = list()
115
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'])
119             try:
120                 neutron_utils.remove_interface_router(self.__neutron, self.__router, subnet=internal_subnet)
121             except NotFound:
122                 pass
123         self.__internal_subnets = list()
124
125         if self.__router:
126             logger.info('Removing router ' + self.router_settings.name)
127             try:
128                 neutron_utils.delete_router(self.__neutron, self.__router)
129             except NotFound:
130                 pass
131             self.__router = None
132
133     def get_router(self):
134         """
135         Returns the OpenStack router object
136         :return:
137         """
138         return self.__router
139
140     def get_internal_router_interface(self):
141         """
142         Returns the OpenStack internal router interface object
143         :return:
144         """
145         return self.__internal_router_interface
146
147
148 class RouterSettings:
149     """
150     Class representing a router configuration
151     """
152
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()):
156         """
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
163                              policies.
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
171         :return:
172         """
173         if config:
174             self.name = config.get('name')
175             self.project_name = config.get('project_name')
176             self.external_gateway = config.get('external_gateway')
177
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']
183             else:
184                 self.internal_subnets = internal_subnets
185
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']))
192         else:
193             self.name = name
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
200
201         if not self.name:
202             raise Exception('Name is required')
203
204     def dict_for_neutron(self, neutron, os_creds):
205         """
206         Returns a dictionary object representing this object.
207         This is meant to be converted into JSON designed for use by the Neutron API
208
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
213         """
214         out = dict()
215         ext_gw = dict()
216
217         project_id = None
218
219         if self.name:
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)
224             project_id = None
225             if project:
226                 project_id = project.id
227             if project_id:
228                 out['project_id'] = project_id
229             else:
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)
235             if ext_net:
236                 ext_gw['network_id'] = ext_net['network']['id']
237                 out['external_gateway_info'] = ext_gw
238             else:
239                 raise Exception('Could not find the external network named - ' + self.external_gateway)
240
241         #TODO: Enable SNAT option for Router
242         #TODO: Add external_fixed_ips Tests
243
244         return {'router': out}