Add punctuations in 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 random
24 import string
25 import time
26 import uuid
27
28 import os_client_config
29 import shade
30 from xtesting.core import testcase
31
32 from functest.utils import config
33 from functest.utils import env
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 = ''.join(random.choice(
59             string.ascii_letters + string.digits +
60             '!()*+,-.<=>?@[]^_{|}~') for _ in range(30))
61         self.__logger.debug("password: %s", self.password)
62         self.domain = self.orig_cloud.get_domain(
63             name_or_id=self.orig_cloud.auth.get(
64                 "project_domain_name", "Default"))
65         self.project = self.orig_cloud.create_project(
66             name='{}-project_{}'.format(self.case_name[:18], self.guid),
67             description="Created by OPNFV Functest: {}".format(
68                 self.case_name),
69             domain_id=self.domain.id)
70         self.__logger.debug("project: %s", self.project)
71         self.user = self.orig_cloud.create_user(
72             name='{}-user_{}'.format(self.case_name, self.guid),
73             password=self.password,
74             domain_id=self.domain.id)
75         self.__logger.debug("user: %s", self.user)
76         try:
77             if self.orig_cloud.get_role(self.default_member):
78                 self.role_name = self.default_member
79             elif self.orig_cloud.get_role(self.default_member.lower()):
80                 self.role_name = self.default_member.lower()
81             else:
82                 raise Exception("Cannot detect {}".format(self.default_member))
83         except Exception:  # pylint: disable=broad-except
84             self.__logger.info("Creating default role %s", self.default_member)
85             role = self.orig_cloud.create_role(self.default_member)
86             self.role_name = role.name
87             self.__logger.debug("role: %s", role)
88         self.orig_cloud.grant_role(
89             self.role_name, user=self.user.id, project=self.project.id,
90             domain=self.domain.id)
91         osconfig = os_client_config.config.OpenStackConfig()
92         osconfig.cloud_config[
93             'clouds']['envvars']['project_name'] = self.project.name
94         osconfig.cloud_config[
95             'clouds']['envvars']['project_id'] = self.project.id
96         osconfig.cloud_config['clouds']['envvars']['username'] = self.user.name
97         osconfig.cloud_config['clouds']['envvars']['password'] = self.password
98         self.__logger.debug("cloud_config %s", osconfig.cloud_config)
99         self.cloud = shade.OpenStackCloud(
100             cloud_config=osconfig.get_one_cloud())
101         self.__logger.debug("new cloud %s", self.cloud.auth)
102
103     def get_environ(self):
104         "Get new environ"
105         environ = dict(
106             os.environ,
107             OS_USERNAME=self.user.name,
108             OS_PROJECT_NAME=self.project.name,
109             OS_PROJECT_ID=self.project.id,
110             OS_PASSWORD=self.password)
111         try:
112             del environ['OS_TENANT_NAME']
113             del environ['OS_TENANT_ID']
114         except Exception:  # pylint: disable=broad-except
115             pass
116         return environ
117
118     def clean(self):
119         """Remove projects/users"""
120         try:
121             assert self.orig_cloud
122             if self.user:
123                 self.orig_cloud.delete_user(self.user.id)
124             if self.project:
125                 self.orig_cloud.delete_project(self.project.id)
126             secgroups = self.orig_cloud.list_security_groups(
127                 filters={'name': 'default',
128                          'project_id': self.project.id})
129             if secgroups:
130                 sec_id = secgroups[0].id
131                 self.orig_cloud.delete_security_group(sec_id)
132         except Exception:  # pylint: disable=broad-except
133             self.__logger.exception("Cannot clean all resources")
134
135
136 class TenantNetwork1(testcase.TestCase):
137     # pylint: disable=too-many-instance-attributes
138     """Create a tenant network (scenario1)
139
140     It creates and configures all tenant network resources required by
141     advanced testcases (subnet, network and router).
142
143     It ensures that all testcases inheriting from TenantNetwork1 could work
144     without network specific configurations (or at least read the same config
145     data).
146     """
147
148     __logger = logging.getLogger(__name__)
149     cidr = '192.168.120.0/24'
150     shared_network = False
151     allow_no_fip = False
152
153     def __init__(self, **kwargs):
154         if "case_name" not in kwargs:
155             kwargs["case_name"] = 'tenantnetwork1'
156         super(TenantNetwork1, self).__init__(**kwargs)
157         self.res_dir = os.path.join(
158             getattr(config.CONF, 'dir_results'), self.case_name)
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.ext_net = None
166             self.__logger.exception("Cannot connect to Cloud")
167         try:
168             self.ext_net = self.get_external_network(self.cloud)
169         except Exception:  # pylint: disable=broad-except
170             self.ext_net = None
171             self.__logger.exception("Cannot get the external network")
172         self.guid = str(uuid.uuid4())
173         self.network = None
174         self.subnet = None
175         self.router = None
176
177     @staticmethod
178     def get_external_network(cloud):
179         """
180         Return the configured external network name or
181         the first retrieved external network name
182         """
183         assert cloud
184         if env.get("EXTERNAL_NETWORK"):
185             network = cloud.get_network(
186                 env.get("EXTERNAL_NETWORK"), {"router:external": True})
187             if network:
188                 return network
189         networks = cloud.list_networks({"router:external": True})
190         if networks:
191             return networks[0]
192         return None
193
194     @staticmethod
195     def get_default_role(cloud, member="Member"):
196         """Get the default role
197
198         It also tests the role in lowercase to avoid possible conflicts.
199         """
200         role = cloud.get_role(member)
201         if not role:
202             role = cloud.get_role(member.lower())
203         return role
204
205     @staticmethod
206     def get_public_auth_url(cloud):
207         """Get Keystone public endpoint"""
208         keystone_id = cloud.search_services('keystone')[0].id
209         endpoint = cloud.search_endpoints(
210             filters={'interface': 'public',
211                      'service_id': keystone_id})[0].url
212         return endpoint
213
214     def create_network_resources(self):
215         """Create all tenant network resources
216
217         It creates a router which gateway is the external network detected.
218         The new subnet is attached to that router.
219
220         Raises: expection on error
221         """
222         assert self.cloud
223         if not self.allow_no_fip:
224             assert self.ext_net
225         provider = {}
226         if hasattr(config.CONF, '{}_network_type'.format(self.case_name)):
227             provider["network_type"] = getattr(
228                 config.CONF, '{}_network_type'.format(self.case_name))
229         if hasattr(config.CONF, '{}_physical_network'.format(self.case_name)):
230             provider["physical_network"] = getattr(
231                 config.CONF, '{}_physical_network'.format(self.case_name))
232         if hasattr(config.CONF, '{}_segmentation_id'.format(self.case_name)):
233             provider["segmentation_id"] = getattr(
234                 config.CONF, '{}_segmentation_id'.format(self.case_name))
235         domain = self.orig_cloud.get_domain(
236             name_or_id=self.orig_cloud.auth.get(
237                 "project_domain_name", "Default"))
238         project = self.orig_cloud.get_project(
239             self.cloud.auth['project_name'],
240             domain_id=domain.id)
241         self.network = self.orig_cloud.create_network(
242             '{}-net_{}'.format(self.case_name, self.guid),
243             provider=provider, project_id=project.id,
244             shared=self.shared_network)
245         self.__logger.debug("network: %s", self.network)
246
247         self.subnet = self.cloud.create_subnet(
248             self.network.id,
249             subnet_name='{}-subnet_{}'.format(self.case_name, self.guid),
250             cidr=getattr(
251                 config.CONF, '{}_private_subnet_cidr'.format(self.case_name),
252                 self.cidr),
253             enable_dhcp=True,
254             dns_nameservers=[env.get('NAMESERVER')])
255         self.__logger.debug("subnet: %s", self.subnet)
256
257         self.router = self.cloud.create_router(
258             name='{}-router_{}'.format(self.case_name, self.guid),
259             ext_gateway_net_id=self.ext_net.id if self.ext_net else None)
260         self.__logger.debug("router: %s", self.router)
261         self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id)
262
263     def run(self, **kwargs):
264         status = testcase.TestCase.EX_RUN_ERROR
265         try:
266             assert self.cloud
267             self.start_time = time.time()
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(TenantNetwork2, self).__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(TenantNetwork2, self).clean()
324             assert self.project
325             self.project.clean()
326         except Exception:  # pylint: disable=broad-except
327             self.__logger.exception("Cannot clean all resources")