1b2eb9414c6db5d9190a8087b77ae16e5a93822a
[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(object):
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) for _ in range(30))
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 clean(self):
102         """Remove projects/users"""
103         try:
104             assert self.orig_cloud
105             if self.user:
106                 self.orig_cloud.delete_user(self.user.id)
107             if self.project:
108                 self.orig_cloud.delete_project(self.project.id)
109             secgroups = self.orig_cloud.list_security_groups(
110                 filters={'name': 'default',
111                          'project_id': self.project.id})
112             if secgroups:
113                 sec_id = secgroups[0].id
114                 self.orig_cloud.delete_security_group(sec_id)
115         except Exception:  # pylint: disable=broad-except
116             self.__logger.exception("Cannot clean all resources")
117
118
119 class TenantNetwork1(testcase.TestCase):
120     # pylint: disable=too-many-instance-attributes
121     """Create a tenant network (scenario1)
122
123     It creates and configures all tenant network resources required by
124     advanced testcases (subnet, network and router).
125
126     It ensures that all testcases inheriting from TenantNetwork1 could work
127     without network specific configurations (or at least read the same config
128     data).
129     """
130
131     __logger = logging.getLogger(__name__)
132     cidr = '192.168.120.0/24'
133     shared_network = False
134
135     def __init__(self, **kwargs):
136         if "case_name" not in kwargs:
137             kwargs["case_name"] = 'tenantnetwork1'
138         super(TenantNetwork1, self).__init__(**kwargs)
139         self.res_dir = os.path.join(
140             getattr(config.CONF, 'dir_results'), self.case_name)
141         try:
142             cloud_config = os_client_config.get_config()
143             self.cloud = self.orig_cloud = shade.OpenStackCloud(
144                 cloud_config=cloud_config)
145         except Exception:  # pylint: disable=broad-except
146             self.cloud = self.orig_cloud = None
147             self.ext_net = None
148             self.__logger.exception("Cannot connect to Cloud")
149         try:
150             self.ext_net = self.get_external_network(self.cloud)
151         except Exception:  # pylint: disable=broad-except
152             self.__logger.exception("Cannot get the external network")
153         self.guid = str(uuid.uuid4())
154         self.network = None
155         self.subnet = None
156         self.router = None
157
158     @staticmethod
159     def get_external_network(cloud):
160         """
161         Return the configured external network name or
162         the first retrieved external network name
163         """
164         assert cloud
165         if env.get("EXTERNAL_NETWORK"):
166             network = cloud.get_network(
167                 env.get("EXTERNAL_NETWORK"), {"router:external": True})
168             if network:
169                 return network
170         networks = cloud.list_networks({"router:external": True})
171         if networks:
172             return networks[0]
173         return None
174
175     @staticmethod
176     def get_default_role(cloud, member="Member"):
177         """Get the default role
178
179         It also tests the role in lowercase to avoid possible conflicts.
180         """
181         role = cloud.get_role(member)
182         if not role:
183             role = cloud.get_role(member.lower())
184         return role
185
186     @staticmethod
187     def get_public_auth_url(cloud):
188         """Get Keystone public endpoint"""
189         keystone_id = cloud.search_services('keystone')[0].id
190         endpoint = cloud.search_endpoints(
191             filters={'interface': 'public',
192                      'service_id': keystone_id})[0].url
193         return endpoint
194
195     def create_network_resources(self):
196         """Create all tenant network resources
197
198         It creates a router which gateway is the external network detected.
199         The new subnet is attached to that router.
200
201         Raises: expection on error
202         """
203         assert self.cloud
204         assert self.ext_net
205         provider = {}
206         if hasattr(config.CONF, '{}_network_type'.format(self.case_name)):
207             provider["network_type"] = getattr(
208                 config.CONF, '{}_network_type'.format(self.case_name))
209         if hasattr(config.CONF, '{}_physical_network'.format(self.case_name)):
210             provider["physical_network"] = getattr(
211                 config.CONF, '{}_physical_network'.format(self.case_name))
212         if hasattr(config.CONF, '{}_segmentation_id'.format(self.case_name)):
213             provider["segmentation_id"] = getattr(
214                 config.CONF, '{}_segmentation_id'.format(self.case_name))
215         domain = self.orig_cloud.get_domain(
216             name_or_id=self.orig_cloud.auth.get(
217                 "project_domain_name", "Default"))
218         project = self.orig_cloud.get_project(
219             self.cloud.auth['project_name'],
220             domain_id=domain.id)
221         self.network = self.orig_cloud.create_network(
222             '{}-net_{}'.format(self.case_name, self.guid),
223             provider=provider, project_id=project.id,
224             shared=self.shared_network)
225         self.__logger.debug("network: %s", self.network)
226
227         self.subnet = self.cloud.create_subnet(
228             self.network.id,
229             subnet_name='{}-subnet_{}'.format(self.case_name, self.guid),
230             cidr=getattr(
231                 config.CONF, '{}_private_subnet_cidr'.format(self.case_name),
232                 self.cidr),
233             enable_dhcp=True,
234             dns_nameservers=[env.get('NAMESERVER')])
235         self.__logger.debug("subnet: %s", self.subnet)
236
237         self.router = self.cloud.create_router(
238             name='{}-router_{}'.format(self.case_name, self.guid),
239             ext_gateway_net_id=self.ext_net.id)
240         self.__logger.debug("router: %s", self.router)
241         self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id)
242
243     def run(self, **kwargs):
244         status = testcase.TestCase.EX_RUN_ERROR
245         try:
246             assert self.cloud
247             self.start_time = time.time()
248             self.create_network_resources()
249             self.result = 100
250             status = testcase.TestCase.EX_OK
251         except Exception:  # pylint: disable=broad-except
252             self.__logger.exception('Cannot run %s', self.case_name)
253         finally:
254             self.stop_time = time.time()
255         return status
256
257     def clean(self):
258         try:
259             assert self.cloud
260             if self.router:
261                 if self.subnet:
262                     self.cloud.remove_router_interface(
263                         self.router, self.subnet.id)
264                 self.cloud.delete_router(self.router.id)
265             if self.subnet:
266                 self.cloud.delete_subnet(self.subnet.id)
267             if self.network:
268                 self.cloud.delete_network(self.network.id)
269         except Exception:  # pylint: disable=broad-except
270             self.__logger.exception("cannot clean all resources")
271
272
273 class TenantNetwork2(TenantNetwork1):
274     """Create a tenant network (scenario2)
275
276     It creates new user/project before creating and configuring all tenant
277     network resources required by a testcase (subnet, network and router).
278
279     It ensures that all testcases inheriting from TenantNetwork2 could work
280     without network specific configurations (or at least read the same config
281     data).
282     """
283
284     __logger = logging.getLogger(__name__)
285
286     def __init__(self, **kwargs):
287         if "case_name" not in kwargs:
288             kwargs["case_name"] = 'tenantnetwork2'
289         super(TenantNetwork2, self).__init__(**kwargs)
290         try:
291             assert self.cloud
292             self.project = NewProject(
293                 self.cloud, self.case_name, self.guid)
294             self.project.create()
295             self.cloud = self.project.cloud
296         except Exception:  # pylint: disable=broad-except
297             self.__logger.exception("Cannot create user or project")
298             self.cloud = None
299             self.project = None
300
301     def clean(self):
302         try:
303             super(TenantNetwork2, self).clean()
304             assert self.project
305             self.project.clean()
306         except Exception:  # pylint: disable=broad-except
307             self.__logger.exception("Cannot clean all resources")