760dc30e94a0e9a23302352778e6637ebaaeaafd
[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         """Create all tenant network resources
189
190         It creates a router which gateway is the external network detected.
191         The new subnet is attached to that router.
192
193         Raises: expection on error
194         """
195         assert self.cloud
196         assert self.ext_net
197         provider = {}
198         if hasattr(config.CONF, '{}_network_type'.format(self.case_name)):
199             provider["network_type"] = getattr(
200                 config.CONF, '{}_network_type'.format(self.case_name))
201         if hasattr(config.CONF, '{}_physical_network'.format(self.case_name)):
202             provider["physical_network"] = getattr(
203                 config.CONF, '{}_physical_network'.format(self.case_name))
204         if hasattr(config.CONF, '{}_segmentation_id'.format(self.case_name)):
205             provider["segmentation_id"] = getattr(
206                 config.CONF, '{}_segmentation_id'.format(self.case_name))
207         self.network = self.cloud.create_network(
208             '{}-net_{}'.format(self.case_name, self.guid),
209             provider=provider,
210             shared=self.shared_network)
211         self.__logger.debug("network: %s", self.network)
212
213         self.subnet = self.cloud.create_subnet(
214             self.network.id,
215             subnet_name='{}-subnet_{}'.format(self.case_name, self.guid),
216             cidr=getattr(
217                 config.CONF, '{}_private_subnet_cidr'.format(self.case_name),
218                 self.cidr),
219             enable_dhcp=True,
220             dns_nameservers=[env.get('NAMESERVER')])
221         self.__logger.debug("subnet: %s", self.subnet)
222
223         self.router = self.cloud.create_router(
224             name='{}-router_{}'.format(self.case_name, self.guid),
225             ext_gateway_net_id=self.ext_net.id)
226         self.__logger.debug("router: %s", self.router)
227         self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id)
228
229     def run(self, **kwargs):
230         status = testcase.TestCase.EX_RUN_ERROR
231         try:
232             assert self.cloud
233             self.start_time = time.time()
234             self.create_network_resources()
235             self.result = 100
236             status = testcase.TestCase.EX_OK
237         except Exception:  # pylint: disable=broad-except
238             self.__logger.exception('Cannot run %s', self.case_name)
239         finally:
240             self.stop_time = time.time()
241         return status
242
243     def clean(self):
244         try:
245             assert self.cloud
246             if self.router:
247                 if self.subnet:
248                     self.cloud.remove_router_interface(
249                         self.router, self.subnet.id)
250                 self.cloud.delete_router(self.router.id)
251             if self.subnet:
252                 self.cloud.delete_subnet(self.subnet.id)
253             if self.network:
254                 self.cloud.delete_network(self.network.id)
255         except Exception:  # pylint: disable=broad-except
256             self.__logger.exception("cannot clean all ressources")
257
258
259 class TenantNetwork2(TenantNetwork1):
260     """Create a tenant network (scenario2)
261
262     It creates new user/project before creating and configuring all tenant
263     network ressources required by a testcase (subnet, network and router).
264
265     It ensures that all testcases inheriting from TenantNetwork2 could work
266     without network specific configurations (or at least read the same config
267     data).
268     """
269
270     __logger = logging.getLogger(__name__)
271
272     def __init__(self, **kwargs):
273         if "case_name" not in kwargs:
274             kwargs["case_name"] = 'tenantnetwork2'
275         super(TenantNetwork2, self).__init__(**kwargs)
276         try:
277             assert self.cloud
278             self.project = NewProject(
279                 self.cloud, self.case_name, self.guid)
280             self.project.create()
281             self.cloud = self.project.cloud
282         except Exception:  # pylint: disable=broad-except
283             self.__logger.exception("Cannot create user or project")
284             self.cloud = None
285             self.project = None
286
287     def clean(self):
288         try:
289             super(TenantNetwork2, self).clean()
290             assert self.project
291             self.project.clean()
292         except Exception:  # pylint: disable=broad-except
293             self.__logger.exception("Cannot clean all ressources")