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