5c3af22602724a6fbec4091b3c6968aa5ffe8ae8
[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 ressources required by a
13 testcase (including all Functest ones):
14   - TenantNetwork1 selects the user and the project set as env vars
15   - TenantNetwork2 creates a user and project to isolate the same ressources
16
17 This classes could be reused by more complexed scenarios (Single VM)
18 """
19
20 import logging
21 import os
22 import time
23 import uuid
24
25 import os_client_config
26 import shade
27 from xtesting.core import testcase
28
29 from functest.utils import config
30 from functest.utils import env
31
32
33 class NewProject(object):
34     """Ease creating new projects/users"""
35     # pylint: disable=too-many-instance-attributes
36
37     __logger = logging.getLogger(__name__)
38
39     def __init__(self, cloud, case_name, guid):
40         self.cloud = None
41         self.orig_cloud = cloud
42         self.case_name = case_name
43         self.guid = guid
44         self.project = None
45         self.user = None
46         self.password = None
47         self.domain = None
48         self.role = None
49         self.role_name = None
50         self.default_member = env.get('NEW_USER_ROLE')
51
52     def create(self):
53         """Create projects/users"""
54         assert self.orig_cloud
55         assert self.case_name
56         self.password = str(uuid.uuid4())
57         self.domain = self.orig_cloud.get_domain(
58             name_or_id=self.orig_cloud.auth.get(
59                 "project_domain_name", "Default"))
60         self.project = self.orig_cloud.create_project(
61             name='{}-project_{}'.format(self.case_name, self.guid),
62             description="Created by OPNFV Functest: {}".format(
63                 self.case_name),
64             domain_id=self.domain.id)
65         self.__logger.debug("project: %s", self.project)
66         self.user = self.orig_cloud.create_user(
67             name='{}-user_{}'.format(self.case_name, self.guid),
68             password=self.password,
69             domain_id=self.domain.id)
70         self.__logger.debug("user: %s", self.user)
71         try:
72             if self.orig_cloud.get_role(self.default_member):
73                 self.role_name = self.default_member
74             elif self.orig_cloud.get_role(self.default_member.lower()):
75                 self.role_name = self.default_member.lower()
76             else:
77                 raise Exception("Cannot detect {}".format(self.default_member))
78         except Exception:  # pylint: disable=broad-except
79             self.__logger.info("Creating default role %s", self.default_member)
80             self.role = self.orig_cloud.create_role(self.default_member)
81             self.role_name = self.role.name
82             self.__logger.debug("role: %s", self.role)
83         self.orig_cloud.grant_role(
84             self.role_name, user=self.user.id, project=self.project.id,
85             domain=self.domain.id)
86         osconfig = os_client_config.config.OpenStackConfig()
87         osconfig.cloud_config[
88             'clouds']['envvars']['project_name'] = self.project.name
89         osconfig.cloud_config[
90             'clouds']['envvars']['project_id'] = self.project.id
91         osconfig.cloud_config['clouds']['envvars']['username'] = self.user.name
92         osconfig.cloud_config['clouds']['envvars']['password'] = self.password
93         self.__logger.debug("cloud_config %s", osconfig.cloud_config)
94         self.cloud = shade.OpenStackCloud(
95             cloud_config=osconfig.get_one_cloud())
96         self.__logger.debug("new cloud %s", self.cloud.auth)
97
98     def clean(self):
99         """Remove projects/users"""
100         try:
101             assert self.orig_cloud
102             if self.user:
103                 self.orig_cloud.delete_user(self.user.id)
104             if self.project:
105                 self.orig_cloud.delete_project(self.project.id)
106             if self.role:
107                 self.orig_cloud.delete_role(self.role.id)
108         except Exception:  # pylint: disable=broad-except
109             self.__logger.exception("Cannot clean all ressources")
110
111
112 class TenantNetwork1(testcase.TestCase):
113     # pylint: disable=too-many-instance-attributes
114     """Create a tenant network (scenario1)
115
116     It creates and configures all tenant network ressources required by
117     advanced testcases (subnet, network and router).
118
119     It ensures that all testcases inheriting from TenantNetwork1 could work
120     without network specific configurations (or at least read the same config
121     data).
122     """
123
124     __logger = logging.getLogger(__name__)
125     cidr = '192.168.120.0/24'
126     shared_network = False
127
128     def __init__(self, **kwargs):
129         if "case_name" not in kwargs:
130             kwargs["case_name"] = 'tenantnetwork1'
131         super(TenantNetwork1, self).__init__(**kwargs)
132         self.res_dir = os.path.join(
133             getattr(config.CONF, 'dir_results'), self.case_name)
134         try:
135             cloud_config = os_client_config.get_config()
136             self.cloud = shade.OpenStackCloud(cloud_config=cloud_config)
137         except Exception:  # pylint: disable=broad-except
138             self.cloud = None
139             self.ext_net = None
140             self.__logger.exception("Cannot connect to Cloud")
141         try:
142             self.ext_net = self.get_external_network(self.cloud)
143         except Exception:  # pylint: disable=broad-except
144             self.__logger.exception("Cannot get the external network")
145         self.guid = str(uuid.uuid4())
146         self.network = None
147         self.subnet = None
148         self.router = None
149
150     @staticmethod
151     def get_external_network(cloud):
152         """
153         Return the configured external network name or
154         the first retrieved external network name
155         """
156         assert cloud
157         if env.get("EXTERNAL_NETWORK"):
158             network = cloud.get_network(
159                 env.get("EXTERNAL_NETWORK"), {"router:external": True})
160             if network:
161                 return network
162         networks = cloud.list_networks({"router:external": True})
163         if networks:
164             return networks[0]
165         return None
166
167     @staticmethod
168     def get_default_role(cloud, member="Member"):
169         """Get the default role
170
171         It also tests the role in lowercase to avoid possible conflicts.
172         """
173         role = cloud.get_role(member)
174         if not role:
175             role = cloud.get_role(member.lower())
176         return role
177
178     @staticmethod
179     def get_public_auth_url(cloud):
180         """Get Keystone public endpoint"""
181         keystone_id = cloud.search_services('keystone')[0].id
182         endpoint = cloud.search_endpoints(
183             filters={'interface': 'public',
184                      'service_id': keystone_id})[0].url
185         return endpoint
186
187     def _create_network_resources(self):
188         assert self.cloud
189         assert self.ext_net
190         provider = {}
191         if hasattr(config.CONF, '{}_network_type'.format(self.case_name)):
192             provider["network_type"] = getattr(
193                 config.CONF, '{}_network_type'.format(self.case_name))
194         if hasattr(config.CONF, '{}_physical_network'.format(self.case_name)):
195             provider["physical_network"] = getattr(
196                 config.CONF, '{}_physical_network'.format(self.case_name))
197         if hasattr(config.CONF, '{}_segmentation_id'.format(self.case_name)):
198             provider["segmentation_id"] = getattr(
199                 config.CONF, '{}_segmentation_id'.format(self.case_name))
200         self.network = self.cloud.create_network(
201             '{}-net_{}'.format(self.case_name, self.guid),
202             provider=provider,
203             shared=self.shared_network)
204         self.__logger.debug("network: %s", self.network)
205
206         self.subnet = self.cloud.create_subnet(
207             self.network.id,
208             subnet_name='{}-subnet_{}'.format(self.case_name, self.guid),
209             cidr=getattr(
210                 config.CONF, '{}_private_subnet_cidr'.format(self.case_name),
211                 self.cidr),
212             enable_dhcp=True,
213             dns_nameservers=[env.get('NAMESERVER')])
214         self.__logger.debug("subnet: %s", self.subnet)
215
216         self.router = self.cloud.create_router(
217             name='{}-router_{}'.format(self.case_name, self.guid),
218             ext_gateway_net_id=self.ext_net.id)
219         self.__logger.debug("router: %s", self.router)
220         self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id)
221
222     def run(self, **kwargs):
223         status = testcase.TestCase.EX_RUN_ERROR
224         try:
225             assert self.cloud
226             self.start_time = time.time()
227             self._create_network_resources()
228             self.result = 100
229             status = testcase.TestCase.EX_OK
230         except Exception:  # pylint: disable=broad-except
231             self.__logger.exception('Cannot run %s', self.case_name)
232         finally:
233             self.stop_time = time.time()
234         return status
235
236     def clean(self):
237         try:
238             assert self.cloud
239             if self.router:
240                 if self.subnet:
241                     self.cloud.remove_router_interface(
242                         self.router, self.subnet.id)
243                 self.cloud.delete_router(self.router.id)
244             if self.subnet:
245                 self.cloud.delete_subnet(self.subnet.id)
246             if self.network:
247                 self.cloud.delete_network(self.network.id)
248         except Exception:  # pylint: disable=broad-except
249             self.__logger.exception("cannot clean all ressources")
250
251
252 class TenantNetwork2(TenantNetwork1):
253     """Create a tenant network (scenario2)
254
255     It creates new user/project before creating and configuring all tenant
256     network ressources required by a testcase (subnet, network and router).
257
258     It ensures that all testcases inheriting from TenantNetwork2 could work
259     without network specific configurations (or at least read the same config
260     data).
261     """
262
263     __logger = logging.getLogger(__name__)
264
265     def __init__(self, **kwargs):
266         if "case_name" not in kwargs:
267             kwargs["case_name"] = 'tenantnetwork2'
268         super(TenantNetwork2, self).__init__(**kwargs)
269         try:
270             assert self.cloud
271             self.project = NewProject(
272                 self.cloud, self.case_name, self.guid)
273             self.project.create()
274             self.cloud = self.project.cloud
275         except Exception:  # pylint: disable=broad-except
276             self.__logger.exception("Cannot create user or project")
277             self.cloud = None
278             self.project = None
279
280     def clean(self):
281         try:
282             super(TenantNetwork2, self).clean()
283             assert self.project
284             self.project.clean()
285         except Exception:  # pylint: disable=broad-except
286             self.__logger.exception("Cannot clean all ressources")