blob: 8ef55de15200aee0a093c7bacf885243c0053b80 [file] [log] [blame]
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +02001from cinderclient import client as cinder_client
2from glanceclient import client as glance_client
3from keystoneauth1 import identity as keystone_identity
4from keystoneauth1 import session as keystone_session
5from keystoneclient.v3 import client as keystone_client
6from neutronclient.v2_0 import client as neutron_client
7from novaclient import client as novaclient
8
9import logging
10import os
11import random
12import time
13
14import utils
15
16logger = logging.getLogger(__name__)
17
18
19class OfficialClientManager(object):
20 """Manager that provides access to the official python clients for
21 calling various OpenStack APIs.
22 """
23
24 CINDERCLIENT_VERSION = 3
25 GLANCECLIENT_VERSION = 2
26 KEYSTONECLIENT_VERSION = 3
27 NEUTRONCLIENT_VERSION = 2
28 NOVACLIENT_VERSION = 2
29 INTERFACE = 'admin'
30 if "OS_ENDPOINT_TYPE" in list(os.environ.keys()):
31 INTERFACE = os.environ["OS_ENDPOINT_TYPE"]
32
33 def __init__(self, username=None, password=None,
34 tenant_name=None, auth_url=None, endpoint_type="internalURL",
35 cert=False, domain="Default", **kwargs):
36 self.traceback = ""
37
38 self.client_attr_names = [
39 "auth",
40 "compute",
41 "network",
42 "volume",
43 "image",
44 ]
45 self.username = username
46 self.password = password
47 self.tenant_name = tenant_name
48 self.project_name = tenant_name
49 self.auth_url = auth_url
50 self.endpoint_type = endpoint_type
51 self.cert = cert
52 self.domain = domain
53 self.kwargs = kwargs
54
55 # Lazy clients
56 self._auth = None
57 self._compute = None
58 self._network = None
59 self._volume = None
60 self._image = None
61
62 @classmethod
63 def _get_auth_session(cls, username=None, password=None,
64 tenant_name=None, auth_url=None, cert=None,
65 domain='Default'):
66 if None in (username, password, tenant_name):
67 print((username, password, tenant_name))
68 msg = ("Missing required credentials for identity client. "
69 "username: {username}, password: {password}, "
70 "tenant_name: {tenant_name}").format(
71 username=username,
72 password=password,
73 tenant_name=tenant_name
74 )
75 raise msg
76
77 if cert and "https" not in auth_url:
78 auth_url = auth_url.replace("http", "https")
79
80 if "v2" in auth_url:
81 raise BaseException("Keystone v2 is deprecated since OpenStack"
82 "Queens release. So current OS_AUTH_URL {} "
83 "is not valid. Please use Keystone v3."
84 "".format(auth_url))
85 else:
86 auth_url = auth_url if ("v3" in auth_url) else "{}{}".format(
87 auth_url, "/v3")
88 auth = keystone_identity.v3.Password(
89 auth_url=auth_url,
90 user_domain_name=domain,
91 username=username,
92 password=password,
93 project_domain_name=domain,
94 project_name=tenant_name)
95
96 auth_session = keystone_session.Session(auth=auth, verify=cert)
97 # auth_session.get_auth_headers()
98 return auth_session
99
100 @classmethod
101 def get_auth_client(cls, username=None, password=None,
102 tenant_name=None, auth_url=None, cert=None,
103 domain='Default', **kwargs):
104 session = cls._get_auth_session(
105 username=username,
106 password=password,
107 tenant_name=tenant_name,
108 auth_url=auth_url,
109 cert=cert,
110 domain=domain)
111 keystone = keystone_client.Client(version=cls.KEYSTONECLIENT_VERSION,
112 session=session, **kwargs)
113 keystone.management_url = auth_url
114 return keystone
115
116 @classmethod
117 def get_compute_client(cls, username=None, password=None,
118 tenant_name=None, auth_url=None, cert=None,
119 domain='Default', **kwargs):
120 session = cls._get_auth_session(
121 username=username, password=password, tenant_name=tenant_name,
122 auth_url=auth_url, cert=cert, domain=domain)
123 service_type = 'compute'
124 compute_client = novaclient.Client(
125 version=cls.NOVACLIENT_VERSION, session=session,
126 service_type=service_type, os_cache=False, **kwargs)
127 return compute_client
128
129 @classmethod
130 def get_network_client(cls, username=None, password=None,
131 tenant_name=None, auth_url=None, cert=None,
132 domain='Default', **kwargs):
133 session = cls._get_auth_session(
134 username=username, password=password, tenant_name=tenant_name,
135 auth_url=auth_url, cert=cert, domain=domain)
136 service_type = 'network'
137 return neutron_client.Client(
138 service_type=service_type, session=session,
139 interface=cls.INTERFACE, **kwargs)
140
141 @classmethod
142 def get_volume_client(cls, username=None, password=None,
143 tenant_name=None, auth_url=None, cert=None,
144 domain='Default', **kwargs):
145 session = cls._get_auth_session(
146 username=username, password=password, tenant_name=tenant_name,
147 auth_url=auth_url, cert=cert, domain=domain)
148 service_type = 'volume'
149 return cinder_client.Client(
150 version=cls.CINDERCLIENT_VERSION,
151 service_type=service_type,
152 interface=cls.INTERFACE,
153 session=session, **kwargs)
154
155 @classmethod
156 def get_image_client(cls, username=None, password=None,
157 tenant_name=None, auth_url=None, cert=None,
158 domain='Default', **kwargs):
159 session = cls._get_auth_session(
160 username=username, password=password, tenant_name=tenant_name,
161 auth_url=auth_url, cert=cert, domain=domain)
162 service_type = 'image'
163 return glance_client.Client(
164 version=cls.GLANCECLIENT_VERSION,
165 service_type=service_type,
166 session=session, interface=cls.INTERFACE,
167 **kwargs)
168
169 @property
170 def auth(self):
171 if self._auth is None:
172 self._auth = self.get_auth_client(
173 self.username, self.password, self.tenant_name, self.auth_url,
174 self.cert, self.domain, endpoint_type=self.endpoint_type
175 )
176 return self._auth
177
178 @property
179 def compute(self):
180 if self._compute is None:
181 self._compute = self.get_compute_client(
182 self.username, self.password, self.tenant_name, self.auth_url,
183 self.cert, self.domain, endpoint_type=self.endpoint_type
184 )
185 return self._compute
186
187 @property
188 def network(self):
189 if self._network is None:
190 self._network = self.get_network_client(
191 self.username, self.password, self.tenant_name, self.auth_url,
192 self.cert, self.domain, endpoint_type=self.endpoint_type
193 )
194 return self._network
195
196 @property
197 def volume(self):
198 if self._volume is None:
199 self._volume = self.get_volume_client(
200 self.username, self.password, self.tenant_name, self.auth_url,
201 self.cert, self.domain, endpoint_type=self.endpoint_type
202 )
203 return self._volume
204
205 @property
206 def image(self):
207
208 if self._image is None:
209 self._image = self.get_image_client(
210 self.username, self.password, self.tenant_name, self.auth_url,
211 self.cert, self.domain
212 )
213 return self._image
214
215
216class OSCliActions(object):
217 def __init__(self, os_clients):
218 self.os_clients = os_clients
219 self.create_fake_ext_net = False
220
221 def get_admin_tenant(self):
222 # TODO Keystone v3 doesnt have tenants attribute
223 return self.os_clients.auth.projects.find(name="admin")
224
225 def get_internal_network(self):
226 networks = [
227 net for net in self.os_clients.network.list_networks()["networks"]
228 if net["admin_state_up"] and not net["router:external"] and
229 len(net["subnets"])
230 ]
231 if networks:
232 net = networks[0]
233 else:
234 net = self.create_network_resources()
235 return net
236
237 def create_fake_external_network(self):
238 logger.info(
239 "Could not find any external network, creating a fake one...")
240 net_name = "spt-ext-net-{}".format(random.randrange(100, 999))
241 net_body = {"network": {"name": net_name,
242 "router:external": True,
243 "provider:network_type": "local"}}
244 try:
245 ext_net = \
246 self.os_clients.network.create_network(net_body)['network']
247 logger.info("Created a fake external net {}".format(net_name))
248 except Exception as e:
249 # in case 'local' net type is absent, create with default type
250 net_body["network"].pop('provider:network_type', None)
251 ext_net = \
252 self.os_clients.network.create_network(net_body)['network']
253 subnet_name = "spt-ext-subnet-{}".format(random.randrange(100, 999))
254 subnet_body = {
255 "subnet": {
256 "name": subnet_name,
257 "network_id": ext_net["id"],
258 "ip_version": 4,
259 "cidr": "10.255.255.0/24",
260 "allocation_pools": [{"start": "10.255.255.100",
261 "end": "10.255.255.200"}]
262 }
263 }
264 self.os_clients.network.create_subnet(subnet_body)
265 self.create_fake_ext_net = True
266 return ext_net
267
268 def get_external_network(self):
269 config = utils.get_configuration()
270 ext_net = config.get('external_network') or ''
271 if not ext_net:
272 networks = [
273 net for net in
274 self.os_clients.network.list_networks()["networks"]
275 if net["admin_state_up"] and net["router:external"] and
276 len(net["subnets"])
277 ]
278 else:
279 networks = [net for net in
280 self.os_clients.network.list_networks()["networks"]
281 if net["name"] == ext_net]
282
283 if networks:
284 ext_net = networks[0]
285 logger.info("Using external net '{}'.".format(ext_net["name"]))
286 else:
287 ext_net = self.create_fake_external_network()
288 return ext_net
289
290 def create_flavor(self, name, ram=256, vcpus=1, disk=2):
291 logger.info("Creating a flavor {}".format(name))
292 return self.os_clients.compute.flavors.create(name, ram, vcpus, disk)
293
294 def create_sec_group(self, rulesets=None):
295 if rulesets is None:
296 rulesets = [
297 {
298 # ssh
299 'ip_protocol': 'tcp',
300 'from_port': 22,
301 'to_port': 22,
302 'cidr': '0.0.0.0/0',
303 },
304 {
305 # iperf3
306 'ip_protocol': 'tcp',
307 'from_port': 5201,
308 'to_port': 5201,
309 'cidr': '0.0.0.0/0',
310 },
311 {
312 # ping
313 'ip_protocol': 'icmp',
314 'from_port': -1,
315 'to_port': -1,
316 'cidr': '0.0.0.0/0',
317 }
318 ]
319 sg_name = "spt-test-secgroup-{}".format(random.randrange(100, 999))
320 sg_desc = sg_name + " SPT"
321 secgroup = self.os_clients.compute.security_groups.create(
322 sg_name, sg_desc)
323 for ruleset in rulesets:
324 self.os_clients.compute.security_group_rules.create(
325 secgroup.id, **ruleset)
326 logger.info("Created a security group {}".format(sg_name))
327 return secgroup
328
329 def create_basic_server(self, image=None, flavor=None, net=None,
330 availability_zone=None, sec_groups=(),
331 keypair=None):
332 os_conn = self.os_clients
333 net = net or self.get_internal_network()
334 kwargs = {}
335 if sec_groups:
336 kwargs['security_groups'] = sec_groups
337 server = os_conn.compute.servers.create(
338 "spt-test-server-{}".format(random.randrange(100, 999)),
339 image, flavor, nics=[{"net-id": net["id"]}],
340 availability_zone=availability_zone, key_name=keypair, **kwargs)
341
342 return server
343
344 def get_vm(self, vm_id):
345 os_conn = self.os_clients
346 try:
347 vm = os_conn.compute.servers.find(id=vm_id)
348 except Exception as e:
349 raise Exception(
350 "{}. Could not get the VM \"{}\": {}".format(
351 vm_id, e))
352 return vm
353
354 def check_vm_is_active(self, vm_uuid, retry_delay=5, timeout=500):
355 vm = None
356 timeout_reached = False
357 start_time = time.time()
358 expected_state = 'ACTIVE'
359 while not timeout_reached:
360 vm = self.get_vm(vm_uuid)
361 if vm.status == expected_state:
362 logger.info(
363 "VM {} is in {} status.".format(vm_uuid, vm.status))
364 break
365 if vm.status == 'ERROR':
366 break
367 time.sleep(retry_delay)
368 timeout_reached = (time.time() - start_time) > timeout
369 if vm.status != expected_state:
370 logger.info("VM {} is in {} status.".format(vm_uuid, vm.status))
371 raise TimeoutError(
372 "VM {vm_uuid} on is expected to be in '{expected_state}' "
373 "state, but is in '{actual}' state instead.".format(
374 vm_uuid=vm_uuid, expected_state=expected_state,
375 actual=vm.status))
376
377 def create_network(self, tenant_id):
378 net_name = "spt-test-net-{}".format(random.randrange(100, 999))
Ievgeniia Zadorozhnafa13ba62022-06-18 02:03:09 +0300379 config = utils.get_configuration()
380 mtu = config.get('custom_mtu') or 'default'
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200381 net_body = {
382 'network': {
383 'name': net_name,
384 'tenant_id': tenant_id
385 }
386 }
Ievgeniia Zadorozhnafa13ba62022-06-18 02:03:09 +0300387 if mtu != 'default':
388 try:
389 net_body['network']['mtu'] = int(mtu)
390 except ValueError as e:
391 raise ValueError("MTU value '{}' is not correct. "
392 "Must be an integer at 'custom_mtu' in "
393 "global_config.yaml.\n{}".format(mtu, e))
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200394 net = self.os_clients.network.create_network(net_body)['network']
395 logger.info("Created internal network {}".format(net_name))
396 return net
397
398 def create_subnet(self, net, tenant_id, cidr=None):
399 subnet_name = "spt-test-subnet-{}".format(random.randrange(100, 999))
400 subnet_body = {
401 'subnet': {
402 "name": subnet_name,
403 'network_id': net['id'],
404 'ip_version': 4,
405 'cidr': cidr if cidr else '10.1.7.0/24',
406 'tenant_id': tenant_id
407 }
408 }
409 subnet = self.os_clients.network.create_subnet(subnet_body)['subnet']
410 logger.info("Created subnet {}".format(subnet_name))
411 return subnet
412
413 def create_router(self, ext_net, tenant_id):
414 name = 'spt-test-router-{}'.format(random.randrange(100, 999))
415 router_body = {
416 'router': {
417 'name': name,
418 'external_gateway_info': {
419 'network_id': ext_net['id']
420 },
421 'tenant_id': tenant_id
422 }
423 }
424 logger.info("Created a router {}".format(name))
425 router = self.os_clients.network.create_router(router_body)['router']
426 return router
427
428 def create_network_resources(self):
429 tenant_id = self.get_admin_tenant().id
430 self.get_external_network()
431 net = self.create_network(tenant_id)
432 self.create_subnet(net, tenant_id)
433 return net
434
435 def list_nova_computes(self):
436 nova_services = self.os_clients.compute.hosts.list()
437 computes_list = [h for h in nova_services if h.service == "compute"]
438 return computes_list