3 # Copyright (c) 2018 Orange and others.
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
10 """Ease deploying tenant networks
12 It offers a simple way to create all tenant network resources required by a
13 testcase (including all Functest ones):
15 - TenantNetwork1 selects the user and the project set as env vars
16 - TenantNetwork2 creates a user and project to isolate the same resources
18 This classes could be reused by more complexed scenarios (Single VM)
28 import os_client_config
30 from xtesting.core import testcase
32 from functest.utils import config
33 from functest.utils import env
34 from functest.utils import functest_utils
38 """Ease creating new projects/users"""
39 # pylint: disable=too-many-instance-attributes
41 __logger = logging.getLogger(__name__)
43 def __init__(self, cloud, case_name, guid):
45 self.orig_cloud = cloud
46 self.case_name = case_name
53 self.default_member = env.get('NEW_USER_ROLE')
56 """Create projects/users"""
57 assert self.orig_cloud
59 self.password = ''.join(random.choice(
60 string.ascii_letters + string.digits +
61 '!()*+,-.<=>?@[]^_{|}~') for _ in range(30))
62 self.__logger.debug("password: %s", self.password)
63 self.domain = self.orig_cloud.get_domain(
64 name_or_id=self.orig_cloud.auth.get(
65 "project_domain_name", "Default"))
66 self.project = self.orig_cloud.create_project(
67 name='{}-project_{}'.format(self.case_name[:18], self.guid),
68 description="Created by OPNFV Functest: {}".format(
70 domain_id=self.domain.id)
71 self.__logger.debug("project: %s", self.project)
72 self.user = self.orig_cloud.create_user(
73 name='{}-user_{}'.format(self.case_name, self.guid),
74 password=self.password,
75 domain_id=self.domain.id)
76 self.__logger.debug("user: %s", self.user)
78 if self.orig_cloud.get_role(self.default_member):
79 self.role_name = self.default_member
80 elif self.orig_cloud.get_role(self.default_member.lower()):
81 self.role_name = self.default_member.lower()
83 raise Exception("Cannot detect {}".format(self.default_member))
84 except Exception: # pylint: disable=broad-except
85 self.__logger.info("Creating default role %s", self.default_member)
86 role = self.orig_cloud.create_role(self.default_member)
87 self.role_name = role.name
88 self.__logger.debug("role: %s", role)
89 self.orig_cloud.grant_role(
90 self.role_name, user=self.user.id, project=self.project.id,
91 domain=self.domain.id)
92 osconfig = os_client_config.config.OpenStackConfig()
93 osconfig.cloud_config[
94 'clouds']['envvars']['project_name'] = self.project.name
95 osconfig.cloud_config[
96 'clouds']['envvars']['project_id'] = self.project.id
97 osconfig.cloud_config['clouds']['envvars']['username'] = self.user.name
98 osconfig.cloud_config['clouds']['envvars']['password'] = self.password
99 self.__logger.debug("cloud_config %s", osconfig.cloud_config)
100 self.cloud = shade.OpenStackCloud(
101 cloud_config=osconfig.get_one_cloud())
102 self.__logger.debug("new cloud %s", self.cloud.auth)
104 def get_environ(self):
108 OS_USERNAME=self.user.name,
109 OS_PROJECT_NAME=self.project.name,
110 OS_PROJECT_ID=self.project.id,
111 OS_PASSWORD=self.password)
113 del environ['OS_TENANT_NAME']
114 del environ['OS_TENANT_ID']
115 except Exception: # pylint: disable=broad-except
120 """Remove projects/users"""
122 assert self.orig_cloud
124 self.orig_cloud.delete_user(self.user.id)
126 self.orig_cloud.delete_project(self.project.id)
127 secgroups = self.orig_cloud.list_security_groups(
128 filters={'name': 'default',
129 'project_id': self.project.id})
131 sec_id = secgroups[0].id
132 self.orig_cloud.delete_security_group(sec_id)
133 except Exception: # pylint: disable=broad-except
134 self.__logger.exception("Cannot clean all resources")
137 class TenantNetwork1(testcase.TestCase):
138 # pylint: disable=too-many-instance-attributes
139 """Create a tenant network (scenario1)
141 It creates and configures all tenant network resources required by
142 advanced testcases (subnet, network and router).
144 It ensures that all testcases inheriting from TenantNetwork1 could work
145 without network specific configurations (or at least read the same config
149 __logger = logging.getLogger(__name__)
150 cidr = '192.168.120.0/24'
151 shared_network = False
154 def __init__(self, **kwargs):
155 if "case_name" not in kwargs:
156 kwargs["case_name"] = 'tenantnetwork1'
157 super(TenantNetwork1, self).__init__(**kwargs)
158 self.res_dir = os.path.join(
159 getattr(config.CONF, 'dir_results'), self.case_name)
161 cloud_config = os_client_config.get_config()
162 self.cloud = self.orig_cloud = shade.OpenStackCloud(
163 cloud_config=cloud_config)
164 except Exception: # pylint: disable=broad-except
165 self.cloud = self.orig_cloud = None
167 self.__logger.exception("Cannot connect to Cloud")
169 self.ext_net = self.get_external_network(self.cloud)
170 except Exception: # pylint: disable=broad-except
172 self.__logger.exception("Cannot get the external network")
173 self.guid = str(uuid.uuid4())
179 def get_external_network(cloud):
181 Return the configured external network name or
182 the first retrieved external network name
185 if env.get("EXTERNAL_NETWORK"):
186 network = cloud.get_network(
187 env.get("EXTERNAL_NETWORK"), {"router:external": True})
190 networks = cloud.list_networks({"router:external": True})
196 def get_default_role(cloud, member="Member"):
197 """Get the default role
199 It also tests the role in lowercase to avoid possible conflicts.
201 role = cloud.get_role(member)
203 role = cloud.get_role(member.lower())
207 def get_public_auth_url(cloud):
208 """Get Keystone public endpoint"""
209 keystone_id = functest_utils.search_services(cloud, 'keystone')[0].id
210 endpoint = cloud.search_endpoints(
211 filters={'interface': 'public',
212 'service_id': keystone_id})[0].url
215 def create_network_resources(self):
216 """Create all tenant network resources
218 It creates a router which gateway is the external network detected.
219 The new subnet is attached to that router.
221 Raises: expection on error
224 if not self.allow_no_fip:
227 if hasattr(config.CONF, '{}_network_type'.format(self.case_name)):
228 provider["network_type"] = getattr(
229 config.CONF, '{}_network_type'.format(self.case_name))
230 if hasattr(config.CONF, '{}_physical_network'.format(self.case_name)):
231 provider["physical_network"] = getattr(
232 config.CONF, '{}_physical_network'.format(self.case_name))
233 if hasattr(config.CONF, '{}_segmentation_id'.format(self.case_name)):
234 provider["segmentation_id"] = getattr(
235 config.CONF, '{}_segmentation_id'.format(self.case_name))
236 domain = self.orig_cloud.get_domain(
237 name_or_id=self.orig_cloud.auth.get(
238 "project_domain_name", "Default"))
239 project = self.orig_cloud.get_project(
240 self.cloud.auth['project_name'],
242 self.network = self.orig_cloud.create_network(
243 '{}-net_{}'.format(self.case_name, self.guid),
244 provider=provider, project_id=project.id,
245 shared=self.shared_network)
246 self.__logger.debug("network: %s", self.network)
248 self.subnet = self.cloud.create_subnet(
250 subnet_name='{}-subnet_{}'.format(self.case_name, self.guid),
252 config.CONF, '{}_private_subnet_cidr'.format(self.case_name),
255 dns_nameservers=[env.get('NAMESERVER')])
256 self.__logger.debug("subnet: %s", self.subnet)
258 self.router = self.cloud.create_router(
259 name='{}-router_{}'.format(self.case_name, self.guid),
260 ext_gateway_net_id=self.ext_net.id if self.ext_net else None)
261 self.__logger.debug("router: %s", self.router)
262 self.cloud.add_router_interface(self.router, subnet_id=self.subnet.id)
264 def run(self, **kwargs):
265 status = testcase.TestCase.EX_RUN_ERROR
268 self.start_time = time.time()
269 self.create_network_resources()
271 status = testcase.TestCase.EX_OK
272 except Exception: # pylint: disable=broad-except
273 self.__logger.exception('Cannot run %s', self.case_name)
275 self.stop_time = time.time()
283 self.cloud.remove_router_interface(
284 self.router, self.subnet.id)
285 self.cloud.delete_router(self.router.id)
287 self.cloud.delete_subnet(self.subnet.id)
289 self.cloud.delete_network(self.network.id)
290 except Exception: # pylint: disable=broad-except
291 self.__logger.exception("cannot clean all resources")
294 class TenantNetwork2(TenantNetwork1):
295 """Create a tenant network (scenario2)
297 It creates new user/project before creating and configuring all tenant
298 network resources required by a testcase (subnet, network and router).
300 It ensures that all testcases inheriting from TenantNetwork2 could work
301 without network specific configurations (or at least read the same config
305 __logger = logging.getLogger(__name__)
307 def __init__(self, **kwargs):
308 if "case_name" not in kwargs:
309 kwargs["case_name"] = 'tenantnetwork2'
310 super(TenantNetwork2, self).__init__(**kwargs)
313 self.project = NewProject(
314 self.cloud, self.case_name, self.guid)
315 self.project.create()
316 self.cloud = self.project.cloud
317 except Exception: # pylint: disable=broad-except
318 self.__logger.exception("Cannot create user or project")
324 super(TenantNetwork2, self).clean()
327 except Exception: # pylint: disable=broad-except
328 self.__logger.exception("Cannot clean all resources")