Switch to Tempest password generator
[functest.git] / functest / core / tenantnetwork.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2018 Orange and others.
4 #
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9
10 """Ease deploying tenant networks
11
12 It offers a simple way to create all tenant network resources required by a
13 testcase (including all Functest ones):
14
15   - TenantNetwork1 selects the user and the project set as env vars
16   - TenantNetwork2 creates a user and project to isolate the same resources
17
18 This classes could be reused by more complexed scenarios (Single VM)
19 """
20
21 import logging
22 import os
23 import time
24 import uuid
25
26 import os_client_config
27 import shade
28 from tempest.lib.common.utils import data_utils
29 from xtesting.core import testcase
30
31 from functest.utils import config
32 from functest.utils import env
33 from functest.utils import functest_utils
34
35
36 class NewProject():
37     """Ease creating new projects/users"""
38     # pylint: disable=too-many-instance-attributes
39
40     __logger = logging.getLogger(__name__)
41
42     def __init__(self, cloud, case_name, guid):
43         self.cloud = None
44         self.orig_cloud = cloud
45         self.case_name = case_name
46         self.guid = guid
47         self.project = None
48         self.user = None
49         self.password = None
50         self.domain = None
51         self.role_name = None
52         self.default_member = env.get('NEW_USER_ROLE')
53
54     def create(self):
55         """Create projects/users"""
56         assert self.orig_cloud
57         assert self.case_name
58         self.password = data_utils.rand_password().replace('%', '!')
59         self.__logger.debug("password: %s", self.password)
60         self.domain = self.orig_cloud.get_domain(
61             name_or_id=self.orig_cloud.auth.get(
62                 "project_domain_name", "Default"))
63         self.project = self.orig_cloud.create_project(
64             name='{}-project_{}'.format(self.case_name[:18], self.guid),
65             description="Created by OPNFV Functest: {}".format(
66                 self.case_name),
67             domain_id=self.domain.id)
68         self.__logger.debug("project: %s", self.project)
69         self.user = self.orig_cloud.create_user(
70             name='{}-user_{}'.format(self.case_name, self.guid),
71             password=self.password,
72             domain_id=self.domain.id)
73         self.__logger.debug("user: %s", self.user)
74         try:
75             if self.orig_cloud.get_role(self.default_member):
76                 self.role_name = self.default_member
77             elif self.orig_cloud.get_role(self.default_member.lower()):
78                 self.role_name = self.default_member.lower()
79             else:
80                 raise Exception("Cannot detect {}".format(self.default_member))
81         except Exception:  # pylint: disable=broad-except
82             self.__logger.info("Creating default role %s", self.default_member)
83             role = self.orig_cloud.create_role(self.default_member)
84             self.role_name = role.name
85             self.__logger.debug("role: %s", role)
86         self.orig_cloud.grant_role(
87             self.role_name, user=self.user.id, project=self.project.id,
88             domain=self.domain.id)
89         osconfig = os_client_config.config.OpenStackConfig()
90         osconfig.cloud_config[
91             'clouds']['envvars']['project_name'] = self.project.name
92         osconfig.cloud_config[
93             'clouds']['envvars']['project_id'] = self.project.id
94         osconfig.cloud_config['clouds']['envvars']['username'] = self.user.name
95         osconfig.cloud_config['clouds']['envvars']['password'] = self.password
96         self.__logger.debug("cloud_config %s", osconfig.cloud_config)
97         self.cloud = shade.OpenStackCloud(
98             cloud_config=osconfig.get_one_cloud())
99         self.__logger.debug("new cloud %s", self.cloud.auth)
100
101     def get_environ(self):
102         "Get new environ"
103         environ = dict(
104             os.environ,
105             OS_USERNAME=self.user.name,
106             OS_PROJECT_NAME=self.project.name,
107             OS_PROJECT_ID=self.project.id,
108             OS_PASSWORD=self.password)
109         try:
110             del environ['OS_TENANT_NAME']
111             del environ['OS_TENANT_ID']
112         except Exception:  # pylint: disable=broad-except
113             pass
114         return environ
115
116     def clean(self):
117         """Remove projects/users"""
118         try:
119             assert self.orig_cloud
120             if self.user:
121                 self.orig_cloud.delete_user(self.user.id)
122             if self.project:
123                 self.orig_cloud.delete_project(self.project.id)
124             secgroups = self.orig_cloud.list_security_groups(
125                 filters={'name': 'default',
126                          'project_id': self.project.id})
127             if secgroups:
128                 sec_id = secgroups[0].id
129                 self.orig_cloud.delete_security_group(sec_id)
130         except Exception:  # pylint: disable=broad-except
131             self.__logger.exception("Cannot clean all resources")
132
133
134 class TenantNetwork1(testcase.TestCase):
135     # pylint: disable=too-many-instance-attributes
136     """Create a tenant network (scenario1)
137
138     It creates and configures all tenant network resources required by
139     advanced testcases (subnet, network and router).
140
141     It ensures that all testcases inheriting from TenantNetwork1 could work
142     without network specific configurations (or at least read the same config
143     data).
144     """
145
146     __logger = logging.getLogger(__name__)
147     cidr = '192.168.120.0/24'
148     shared_network = False
149     allow_no_fip = False
150
151     def __init__(self, **kwargs):
152         if "case_name" not in kwargs:
153             kwargs["case_name"] = 'tenantnetwork1'
154         super(TenantNetwork1, self).__init__(**kwargs)
155         self.res_dir = os.path.join(
156             getattr(config.CONF, 'dir_results'), self.case_name)
157         try:
158             cloud_config = os_client_config.get_config()
159             self.cloud = self.orig_cloud = shade.OpenStackCloud(
160                 cloud_config=cloud_config)
161         except Exception:  # pylint: disable=broad-except
162             self.cloud = self.orig_cloud = None
163             self.ext_net = None
164             self.__logger.exception("Cannot connect to Cloud")
165         try:
166             self.ext_net = self.get_external_network(self.cloud)
167         except Exception:  # pylint: disable=broad-except
168             self.ext_net = None
169             self.__logger.exception("Cannot get the external network")
170         self.guid = str(uuid.uuid4())
171         self.network = None
172         self.subnet = None
173         self.router = None
174
175     @staticmethod
176     def get_external_network(cloud):
177         """
178         Return the configured external network name or
179         the first retrieved external network name
180         """
181         assert cloud
182         if env.get("EXTERNAL_NETWORK"):
183             network = cloud.get_network(
184                 env.get("EXTERNAL_NETWORK"), {"router:external": True})
185             if network:
186                 return network
187         networks = cloud.list_networks({"router:external": True})
188         if networks:
189             return networks[0]
190         return None
191
192     @staticmethod
193     def get_default_role(cloud, member="Member"):
194         """Get the default role
195
196         It also tests the role in lowercase to avoid possible conflicts.
197         """
198         role = cloud.get_role(member)
199         if not role:
200             role = cloud.get_role(member.lower())
201         return role
202
203     @staticmethod
204     def get_public_auth_url(cloud):
205         """Get Keystone public endpoint"""
206         keystone_id = functest_utils.search_services(cloud, 'keystone')[0].id
207         endpoint = cloud.search_endpoints(
208             filters={'interface': 'public',
209                      'service_id': keystone_id})[0].url
210         return endpoint
211
212     def create_network_resources(self):
213         """Create all tenant network resources
214
215         It creates a router which gateway is the external network detected.
216         The new subnet is attached to that router.
217
218         Raises: expection on error
219         """
220         assert self.cloud
221         if not self.allow_no_fip:
222             assert self.ext_net
223         provider = {}
224         if hasattr(config.CONF, '{}_network_type'.format(self.case_name)):
225             provider["network_type"] = getattr(
226                 config.CONF, '{}_network_type'.format(self.case_name))
227         if hasattr(config.CONF, '{}_physical_network'.format(self.case_name)):
228             provider["physical_network"] = getattr(
229                 config.CONF, '{}_physical_network'.format(self.case_name))
230         if hasattr(config.CONF, '{}_segmentation_id'.format(self.case_name)):
231             provider["segmentation_id"] = getattr(
232                 config.CONF, '{}_segmentation_id'.format(self.case_name))
233         domain = self.orig_cloud.get_domain(
234             name_or_id=self.orig_cloud.auth.get(
235                 "project_domain_name", "Default"))
236         project = self.orig_cloud.get_project(
237             self.cloud.auth['project_name'],
238             domain_id=domain.id)
239         self.network = self.orig_cloud.create_network(
240             '{}-net_{}'.format(self.case_name, self.guid),
241             provider=provider, project_id=project.id,
242             shared=self.shared_network)
243         self.__logger.debug("network: %s", self.network)
244
245         self.subnet = self.cloud.create_subnet(
246             self.network.id,
247             subnet_name='{}-subnet_{}'.format(self.case_name, self.guid),
248             cidr=getattr(
249                 config.CONF, '{}_private_subnet_cidr'.format(self.case_name),
250                 self.cidr),
251             enable_dhcp=True,
252             dns_nameservers=[env.get('NAMESERVER')])
253         self.__logger.debug("subnet: %s", self.subnet)
254
255         self.router = self.cloud.create_router(
256             name='{}-router_{}'.format(self.case_name, self.guid),
257             ext_gateway_net_id=self.ext_net.id if self.ext_net else None)
258         self.__logger.debug("router: %s", self.router)
259         self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id)
260
261     def run(self, **kwargs):
262         status = testcase.TestCase.EX_RUN_ERROR
263         try:
264             assert self.cloud
265             self.start_time = time.time()
266             self.create_network_resources()
267             self.result = 100
268             status = testcase.TestCase.EX_OK
269         except Exception:  # pylint: disable=broad-except
270             self.__logger.exception('Cannot run %s', self.case_name)
271         finally:
272             self.stop_time = time.time()
273         return status
274
275     def clean(self):
276         try:
277             assert self.cloud
278             if self.router:
279                 if self.subnet:
280                     self.cloud.remove_router_interface(
281                         self.router, self.subnet.id)
282                 self.cloud.delete_router(self.router.id)
283             if self.subnet:
284                 self.cloud.delete_subnet(self.subnet.id)
285             if self.network:
286                 self.cloud.delete_network(self.network.id)
287         except Exception:  # pylint: disable=broad-except
288             self.__logger.exception("cannot clean all resources")
289
290
291 class TenantNetwork2(TenantNetwork1):
292     """Create a tenant network (scenario2)
293
294     It creates new user/project before creating and configuring all tenant
295     network resources required by a testcase (subnet, network and router).
296
297     It ensures that all testcases inheriting from TenantNetwork2 could work
298     without network specific configurations (or at least read the same config
299     data).
300     """
301
302     __logger = logging.getLogger(__name__)
303
304     def __init__(self, **kwargs):
305         if "case_name" not in kwargs:
306             kwargs["case_name"] = 'tenantnetwork2'
307         super(TenantNetwork2, self).__init__(**kwargs)
308         try:
309             assert self.cloud
310             self.project = NewProject(
311                 self.cloud, self.case_name, self.guid)
312             self.project.create()
313             self.cloud = self.project.cloud
314         except Exception:  # pylint: disable=broad-except
315             self.__logger.exception("Cannot create user or project")
316             self.cloud = None
317             self.project = None
318
319     def clean(self):
320         try:
321             super(TenantNetwork2, self).clean()
322             assert self.project
323             self.project.clean()
324         except Exception:  # pylint: disable=broad-except
325             self.__logger.exception("Cannot clean all resources")