Update to Alpine 3.14
[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
150     def __init__(self, **kwargs):
151         if "case_name" not in kwargs:
152             kwargs["case_name"] = 'tenantnetwork1'
153         super().__init__(**kwargs)
154         self.dir_results = os.path.join(getattr(config.CONF, 'dir_results'))
155         self.res_dir = os.path.join(self.dir_results, self.case_name)
156         self.output_log_name = 'functest.log'
157         self.output_debug_log_name = 'functest.debug.log'
158         self.ext_net = None
159         try:
160             cloud_config = os_client_config.get_config()
161             self.cloud = self.orig_cloud = shade.OpenStackCloud(
162                 cloud_config=cloud_config)
163         except Exception:  # pylint: disable=broad-except
164             self.cloud = self.orig_cloud = None
165             self.__logger.exception("Cannot connect to Cloud")
166         if env.get('NO_TENANT_NETWORK').lower() != 'true':
167             try:
168                 self.ext_net = self.get_external_network(self.cloud)
169             except Exception:  # pylint: disable=broad-except
170                 self.__logger.exception("Cannot get the external network")
171         self.guid = str(uuid.uuid4())
172         self.network = None
173         self.subnet = None
174         self.router = None
175
176     @staticmethod
177     def get_external_network(cloud):
178         """
179         Return the configured external network name or
180         the first retrieved external network name
181         """
182         assert cloud
183         if env.get("EXTERNAL_NETWORK"):
184             network = cloud.get_network(
185                 env.get("EXTERNAL_NETWORK"), {"router:external": True})
186             if network:
187                 return network
188         networks = cloud.list_networks({"router:external": True})
189         if networks:
190             return networks[0]
191         return None
192
193     @staticmethod
194     def get_default_role(cloud, member="Member"):
195         """Get the default role
196
197         It also tests the role in lowercase to avoid possible conflicts.
198         """
199         role = cloud.get_role(member)
200         if not role:
201             role = cloud.get_role(member.lower())
202         return role
203
204     @staticmethod
205     def get_public_auth_url(cloud):
206         """Get Keystone public endpoint"""
207         keystone_id = functest_utils.search_services(cloud, 'keystone')[0].id
208         endpoint = cloud.search_endpoints(
209             filters={'interface': 'public',
210                      'service_id': keystone_id})[0].url
211         return endpoint
212
213     def create_network_resources(self):
214         """Create all tenant network resources
215
216         It creates a router which gateway is the external network detected.
217         The new subnet is attached to that router.
218
219         Raises: expection on error
220         """
221         assert self.cloud
222         if env.get('NO_TENANT_NETWORK').lower() != 'true':
223             assert self.ext_net
224         provider = {}
225         if hasattr(config.CONF, '{}_network_type'.format(self.case_name)):
226             provider["network_type"] = getattr(
227                 config.CONF, '{}_network_type'.format(self.case_name))
228         if hasattr(config.CONF, '{}_physical_network'.format(self.case_name)):
229             provider["physical_network"] = getattr(
230                 config.CONF, '{}_physical_network'.format(self.case_name))
231         if hasattr(config.CONF, '{}_segmentation_id'.format(self.case_name)):
232             provider["segmentation_id"] = getattr(
233                 config.CONF, '{}_segmentation_id'.format(self.case_name))
234         domain = self.orig_cloud.get_domain(
235             name_or_id=self.orig_cloud.auth.get(
236                 "project_domain_name", "Default"))
237         project = self.orig_cloud.get_project(
238             self.cloud.auth['project_name'],
239             domain_id=domain.id)
240         self.network = self.orig_cloud.create_network(
241             '{}-net_{}'.format(self.case_name, self.guid),
242             provider=provider, project_id=project.id,
243             shared=self.shared_network)
244         self.__logger.debug("network: %s", self.network)
245
246         self.subnet = self.cloud.create_subnet(
247             self.network.id,
248             subnet_name='{}-subnet_{}'.format(self.case_name, self.guid),
249             cidr=getattr(
250                 config.CONF, '{}_private_subnet_cidr'.format(self.case_name),
251                 self.cidr),
252             enable_dhcp=True,
253             dns_nameservers=[env.get('NAMESERVER')])
254         self.__logger.debug("subnet: %s", self.subnet)
255
256         self.router = self.cloud.create_router(
257             name='{}-router_{}'.format(self.case_name, self.guid),
258             ext_gateway_net_id=self.ext_net.id if self.ext_net else None)
259         self.__logger.debug("router: %s", self.router)
260         self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id)
261
262     def run(self, **kwargs):
263         status = testcase.TestCase.EX_RUN_ERROR
264         try:
265             assert self.cloud
266             self.start_time = time.time()
267             if env.get('NO_TENANT_NETWORK').lower() != 'true':
268                 self.create_network_resources()
269             self.result = 100
270             status = testcase.TestCase.EX_OK
271         except Exception:  # pylint: disable=broad-except
272             self.__logger.exception('Cannot run %s', self.case_name)
273         finally:
274             self.stop_time = time.time()
275         return status
276
277     def clean(self):
278         try:
279             assert self.cloud
280             if self.router:
281                 if self.subnet:
282                     self.cloud.remove_router_interface(
283                         self.router, self.subnet.id)
284                 self.cloud.delete_router(self.router.id)
285             if self.subnet:
286                 self.cloud.delete_subnet(self.subnet.id)
287             if self.network:
288                 self.cloud.delete_network(self.network.id)
289         except Exception:  # pylint: disable=broad-except
290             self.__logger.exception("cannot clean all resources")
291
292
293 class TenantNetwork2(TenantNetwork1):
294     """Create a tenant network (scenario2)
295
296     It creates new user/project before creating and configuring all tenant
297     network resources required by a testcase (subnet, network and router).
298
299     It ensures that all testcases inheriting from TenantNetwork2 could work
300     without network specific configurations (or at least read the same config
301     data).
302     """
303
304     __logger = logging.getLogger(__name__)
305
306     def __init__(self, **kwargs):
307         if "case_name" not in kwargs:
308             kwargs["case_name"] = 'tenantnetwork2'
309         super().__init__(**kwargs)
310         try:
311             assert self.cloud
312             self.project = NewProject(
313                 self.cloud, self.case_name, self.guid)
314             self.project.create()
315             self.cloud = self.project.cloud
316         except Exception:  # pylint: disable=broad-except
317             self.__logger.exception("Cannot create user or project")
318             self.cloud = None
319             self.project = None
320
321     def clean(self):
322         try:
323             super().clean()
324             assert self.project
325             self.project.clean()
326         except Exception:  # pylint: disable=broad-except
327             self.__logger.exception("Cannot clean all resources")