blob: 954df2e1f02cc85e31d8c3ff4f5840e70de539fe [file] [log] [blame]
Moshe0bb532c2018-02-26 13:39:57 +02001##############################################################################
2# Copyright 2018 EuropeanSoftwareMarketingLtd.
3# ===================================================================
4# Licensed under the ApacheLicense, Version2.0 (the"License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# software distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and limitations under
12# the License
13##############################################################################
14# vnftest comment: this is a modified copy of
15# yardstick/common/openstack_utils.py
16
17from __future__ import absolute_import
18
19import os
20import time
21import sys
22import logging
23
24from keystoneauth1 import loading
25from keystoneauth1 import session
26from cinderclient import client as cinderclient
27from novaclient import client as novaclient
28from glanceclient import client as glanceclient
29from neutronclient.neutron import client as neutronclient
30
31log = logging.getLogger(__name__)
32
33DEFAULT_HEAT_API_VERSION = '1'
34DEFAULT_API_VERSION = '2'
35
36
37# *********************************************
38# CREDENTIALS
39# *********************************************
40def get_credentials():
41 """Returns a creds dictionary filled with parsed from env"""
42 creds = {}
43
44 keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION')
45
46 if keystone_api_version is None or keystone_api_version == '2':
47 keystone_v3 = False
48 tenant_env = 'OS_TENANT_NAME'
49 tenant = 'tenant_name'
50 else:
51 keystone_v3 = True
52 tenant_env = 'OS_PROJECT_NAME'
53 tenant = 'project_name'
54
55 # The most common way to pass these info to the script is to do it
56 # through environment variables.
57 creds.update({
58 "username": os.environ.get("OS_USERNAME"),
59 "password": os.environ.get("OS_PASSWORD"),
60 "auth_url": os.environ.get("OS_AUTH_URL"),
61 tenant: os.environ.get(tenant_env)
62 })
63
64 if keystone_v3:
65 if os.getenv('OS_USER_DOMAIN_NAME') is not None:
66 creds.update({
67 "user_domain_name": os.getenv('OS_USER_DOMAIN_NAME')
68 })
69 if os.getenv('OS_PROJECT_DOMAIN_NAME') is not None:
70 creds.update({
71 "project_domain_name": os.getenv('OS_PROJECT_DOMAIN_NAME')
72 })
73
74 return creds
75
76
77def get_session_auth():
78 loader = loading.get_plugin_loader('password')
79 creds = get_credentials()
80 auth = loader.load_from_options(**creds)
81 return auth
82
83
84def get_session():
85 auth = get_session_auth()
86 try:
87 cacert = os.environ['OS_CACERT']
88 except KeyError:
89 return session.Session(auth=auth)
90 else:
91 insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
92 cacert = False if insecure else cacert
93 return session.Session(auth=auth, verify=cacert)
94
95
96def get_endpoint(service_type, endpoint_type='publicURL'):
97 auth = get_session_auth()
98 # for multi-region, we need to specify region
99 # when finding the endpoint
100 return get_session().get_endpoint(auth=auth,
101 service_type=service_type,
102 endpoint_type=endpoint_type,
103 region_name=os.environ.get(
104 "OS_REGION_NAME"))
105
106
107# *********************************************
108# CLIENTS
109# *********************************************
110def get_heat_api_version(): # pragma: no cover
111 try:
112 api_version = os.environ['HEAT_API_VERSION']
113 except KeyError:
114 return DEFAULT_HEAT_API_VERSION
115 else:
116 log.info("HEAT_API_VERSION is set in env as '%s'", api_version)
117 return api_version
118
119
120def get_cinder_client_version(): # pragma: no cover
121 try:
122 api_version = os.environ['OS_VOLUME_API_VERSION']
123 except KeyError:
124 return DEFAULT_API_VERSION
125 else:
126 log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
127 return api_version
128
129
130def get_cinder_client(): # pragma: no cover
131 sess = get_session()
132 return cinderclient.Client(get_cinder_client_version(), session=sess)
133
134
135def get_nova_client_version(): # pragma: no cover
136 try:
137 api_version = os.environ['OS_COMPUTE_API_VERSION']
138 except KeyError:
139 return DEFAULT_API_VERSION
140 else:
141 log.info("OS_COMPUTE_API_VERSION is set in env as '%s'", api_version)
142 return api_version
143
144
145def get_nova_client(): # pragma: no cover
146 sess = get_session()
147 return novaclient.Client(get_nova_client_version(), session=sess)
148
149
150def get_neutron_client_version(): # pragma: no cover
151 try:
152 api_version = os.environ['OS_NETWORK_API_VERSION']
153 except KeyError:
154 return DEFAULT_API_VERSION
155 else:
156 log.info("OS_NETWORK_API_VERSION is set in env as '%s'", api_version)
157 return api_version
158
159
160def get_neutron_client(): # pragma: no cover
161 sess = get_session()
162 return neutronclient.Client(get_neutron_client_version(), session=sess)
163
164
165def get_glance_client_version(): # pragma: no cover
166 try:
167 api_version = os.environ['OS_IMAGE_API_VERSION']
168 except KeyError:
169 return DEFAULT_API_VERSION
170 else:
171 log.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
172 return api_version
173
174
175def get_glance_client(): # pragma: no cover
176 sess = get_session()
177 return glanceclient.Client(get_glance_client_version(), session=sess)
178
179
180# *********************************************
181# NOVA
182# *********************************************
183def get_instances(nova_client): # pragma: no cover
184 try:
185 return nova_client.servers.list(search_opts={'all_tenants': 1})
186 except Exception:
187 log.exception("Error [get_instances(nova_client)]")
188
189
190def get_instance_status(nova_client, instance): # pragma: no cover
191 try:
192 return nova_client.servers.get(instance.id).status
193 except Exception:
194 log.exception("Error [get_instance_status(nova_client)]")
195
196
197def get_instance_by_name(nova_client, instance_name): # pragma: no cover
198 try:
199 return nova_client.servers.find(name=instance_name)
200 except Exception:
201 log.exception("Error [get_instance_by_name(nova_client, '%s')]",
202 instance_name)
203
204
205def get_aggregates(nova_client): # pragma: no cover
206 try:
207 return nova_client.aggregates.list()
208 except Exception:
209 log.exception("Error [get_aggregates(nova_client)]")
210
211
212def get_availability_zones(nova_client): # pragma: no cover
213 try:
214 return nova_client.availability_zones.list()
215 except Exception:
216 log.exception("Error [get_availability_zones(nova_client)]")
217
218
219def get_availability_zone_names(nova_client): # pragma: no cover
220 try:
221 return [az.zoneName for az in get_availability_zones(nova_client)]
222 except Exception:
223 log.exception("Error [get_availability_zone_names(nova_client)]")
224
225
226def create_aggregate(nova_client, aggregate_name, av_zone): # pragma: no cover
227 try:
228 nova_client.aggregates.create(aggregate_name, av_zone)
229 except Exception:
230 log.exception("Error [create_aggregate(nova_client, %s, %s)]",
231 aggregate_name, av_zone)
232 return False
233 else:
234 return True
235
236
237def get_aggregate_id(nova_client, aggregate_name): # pragma: no cover
238 try:
239 aggregates = get_aggregates(nova_client)
240 _id = next((ag.id for ag in aggregates if ag.name == aggregate_name))
241 except Exception:
242 log.exception("Error [get_aggregate_id(nova_client, %s)]",
243 aggregate_name)
244 else:
245 return _id
246
247
248def add_host_to_aggregate(nova_client, aggregate_name,
249 compute_host): # pragma: no cover
250 try:
251 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
252 nova_client.aggregates.add_host(aggregate_id, compute_host)
253 except Exception:
254 log.exception("Error [add_host_to_aggregate(nova_client, %s, %s)]",
255 aggregate_name, compute_host)
256 return False
257 else:
258 return True
259
260
261def create_aggregate_with_host(nova_client, aggregate_name, av_zone,
262 compute_host): # pragma: no cover
263 try:
264 create_aggregate(nova_client, aggregate_name, av_zone)
265 add_host_to_aggregate(nova_client, aggregate_name, compute_host)
266 except Exception:
267 log.exception("Error [create_aggregate_with_host("
268 "nova_client, %s, %s, %s)]",
269 aggregate_name, av_zone, compute_host)
270 return False
271 else:
272 return True
273
274
275def create_keypair(nova_client, name, key_path=None): # pragma: no cover
276 try:
277 with open(key_path) as fpubkey:
278 keypair = get_nova_client().keypairs.create(name=name, public_key=fpubkey.read())
279 return keypair
280 except Exception:
281 log.exception("Error [create_keypair(nova_client)]")
282
283
284def create_instance(json_body): # pragma: no cover
285 try:
286 return get_nova_client().servers.create(**json_body)
287 except Exception:
288 log.exception("Error create instance failed")
289 return None
290
291
292def create_instance_and_wait_for_active(json_body): # pragma: no cover
293 SLEEP = 3
294 VM_BOOT_TIMEOUT = 180
295 nova_client = get_nova_client()
296 instance = create_instance(json_body)
297 count = VM_BOOT_TIMEOUT / SLEEP
298 for n in range(count, -1, -1):
299 status = get_instance_status(nova_client, instance)
300 if status.lower() == "active":
301 return instance
302 elif status.lower() == "error":
303 log.error("The instance went to ERROR status.")
304 return None
305 time.sleep(SLEEP)
306 log.error("Timeout booting the instance.")
307 return None
308
309
310def attach_server_volume(server_id, volume_id, device=None): # pragma: no cover
311 try:
312 get_nova_client().volumes.create_server_volume(server_id, volume_id, device)
313 except Exception:
314 log.exception("Error [attach_server_volume(nova_client, '%s', '%s')]",
315 server_id, volume_id)
316 return False
317 else:
318 return True
319
320
321def delete_instance(nova_client, instance_id): # pragma: no cover
322 try:
323 nova_client.servers.force_delete(instance_id)
324 except Exception:
325 log.exception("Error [delete_instance(nova_client, '%s')]",
326 instance_id)
327 return False
328 else:
329 return True
330
331
332def remove_host_from_aggregate(nova_client, aggregate_name,
333 compute_host): # pragma: no cover
334 try:
335 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
336 nova_client.aggregates.remove_host(aggregate_id, compute_host)
337 except Exception:
338 log.exception("Error remove_host_from_aggregate(nova_client, %s, %s)",
339 aggregate_name, compute_host)
340 return False
341 else:
342 return True
343
344
345def remove_hosts_from_aggregate(nova_client,
346 aggregate_name): # pragma: no cover
347 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
348 hosts = nova_client.aggregates.get(aggregate_id).hosts
349 assert(
350 all(remove_host_from_aggregate(nova_client, aggregate_name, host)
351 for host in hosts))
352
353
354def delete_aggregate(nova_client, aggregate_name): # pragma: no cover
355 try:
356 remove_hosts_from_aggregate(nova_client, aggregate_name)
357 nova_client.aggregates.delete(aggregate_name)
358 except Exception:
359 log.exception("Error [delete_aggregate(nova_client, %s)]",
360 aggregate_name)
361 return False
362 else:
363 return True
364
365
366def get_server_by_name(name): # pragma: no cover
367 try:
368 return get_nova_client().servers.list(search_opts={'name': name})[0]
369 except IndexError:
370 log.exception('Failed to get nova client')
371 raise
372
373
374def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover
375 try:
376 return get_nova_client().flavors.create(name, ram, vcpus, disk, **kwargs)
377 except Exception:
378 log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]",
379 name, ram, disk, vcpus, kwargs['is_public'])
380 return None
381
382
383def get_image_by_name(name): # pragma: no cover
384 images = get_nova_client().images.list()
385 try:
386 return next((a for a in images if a.name == name))
387 except StopIteration:
388 log.exception('No image matched')
389
390
391def get_flavor_id(nova_client, flavor_name): # pragma: no cover
392 flavors = nova_client.flavors.list(detailed=True)
393 flavor_id = ''
394 for f in flavors:
395 if f.name == flavor_name:
396 flavor_id = f.id
397 break
398 return flavor_id
399
400
401def get_flavor_by_name(name): # pragma: no cover
402 flavors = get_nova_client().flavors.list()
403 try:
404 return next((a for a in flavors if a.name == name))
405 except StopIteration:
406 log.exception('No flavor matched')
407
408
409def check_status(status, name, iterations, interval): # pragma: no cover
410 for i in range(iterations):
411 try:
412 server = get_server_by_name(name)
413 except IndexError:
414 log.error('Cannot found %s server', name)
415 raise
416
417 if server.status == status:
418 return True
419
420 time.sleep(interval)
421 return False
422
423
424def delete_flavor(flavor_id): # pragma: no cover
425 try:
426 get_nova_client().flavors.delete(flavor_id)
427 except Exception:
428 log.exception("Error [delete_flavor(nova_client, %s)]", flavor_id)
429 return False
430 else:
431 return True
432
433
434def delete_keypair(nova_client, key): # pragma: no cover
435 try:
436 nova_client.keypairs.delete(key=key)
437 return True
438 except Exception:
439 log.exception("Error [delete_keypair(nova_client)]")
440 return False
441
442
443# *********************************************
444# NEUTRON
445# *********************************************
446def get_network_id(neutron_client, network_name): # pragma: no cover
447 networks = neutron_client.list_networks()['networks']
448 return next((n['id'] for n in networks if n['name'] == network_name), None)
449
450
451def get_port_id_by_ip(neutron_client, ip_address): # pragma: no cover
452 ports = neutron_client.list_ports()['ports']
453 return next((i['id'] for i in ports for j in i.get(
454 'fixed_ips') if j['ip_address'] == ip_address), None)
455
456
457def create_neutron_net(neutron_client, json_body): # pragma: no cover
458 try:
459 network = neutron_client.create_network(body=json_body)
460 return network['network']['id']
461 except Exception:
462 log.error("Error [create_neutron_net(neutron_client)]")
463 raise Exception("operation error")
464 return None
465
466
467def delete_neutron_net(neutron_client, network_id): # pragma: no cover
468 try:
469 neutron_client.delete_network(network_id)
470 return True
471 except Exception:
472 log.error("Error [delete_neutron_net(neutron_client, '%s')]" % network_id)
473 return False
474
475
476def create_neutron_subnet(neutron_client, json_body): # pragma: no cover
477 try:
478 subnet = neutron_client.create_subnet(body=json_body)
479 return subnet['subnets'][0]['id']
480 except Exception:
481 log.error("Error [create_neutron_subnet")
482 raise Exception("operation error")
483 return None
484
485
486def create_neutron_router(neutron_client, json_body): # pragma: no cover
487 try:
488 router = neutron_client.create_router(json_body)
489 return router['router']['id']
490 except Exception:
491 log.error("Error [create_neutron_router(neutron_client)]")
492 raise Exception("operation error")
493 return None
494
495
496def delete_neutron_router(neutron_client, router_id): # pragma: no cover
497 try:
498 neutron_client.delete_router(router=router_id)
499 return True
500 except Exception:
501 log.error("Error [delete_neutron_router(neutron_client, '%s')]" % router_id)
502 return False
503
504
505def remove_gateway_router(neutron_client, router_id): # pragma: no cover
506 try:
507 neutron_client.remove_gateway_router(router_id)
508 return True
509 except Exception:
510 log.error("Error [remove_gateway_router(neutron_client, '%s')]" % router_id)
511 return False
512
513
514def remove_interface_router(neutron_client, router_id, subnet_id,
515 **json_body): # pragma: no cover
516 json_body.update({"subnet_id": subnet_id})
517 try:
518 neutron_client.remove_interface_router(router=router_id,
519 body=json_body)
520 return True
521 except Exception:
522 log.error("Error [remove_interface_router(neutron_client, '%s', "
523 "'%s')]" % (router_id, subnet_id))
524 return False
525
526
527def create_floating_ip(neutron_client, extnet_id): # pragma: no cover
528 props = {'floating_network_id': extnet_id}
529 try:
530 ip_json = neutron_client.create_floatingip({'floatingip': props})
531 fip_addr = ip_json['floatingip']['floating_ip_address']
532 fip_id = ip_json['floatingip']['id']
533 except Exception:
534 log.error("Error [create_floating_ip(neutron_client)]")
535 return None
536 return {'fip_addr': fip_addr, 'fip_id': fip_id}
537
538
539def delete_floating_ip(nova_client, floatingip_id): # pragma: no cover
540 try:
541 nova_client.floating_ips.delete(floatingip_id)
542 return True
543 except Exception:
544 log.error("Error [delete_floating_ip(nova_client, '%s')]" % floatingip_id)
545 return False
546
547
548def get_security_groups(neutron_client): # pragma: no cover
549 try:
550 security_groups = neutron_client.list_security_groups()[
551 'security_groups']
552 return security_groups
553 except Exception:
554 log.error("Error [get_security_groups(neutron_client)]")
555 return None
556
557
558def get_security_group_id(neutron_client, sg_name): # pragma: no cover
559 security_groups = get_security_groups(neutron_client)
560 id = ''
561 for sg in security_groups:
562 if sg['name'] == sg_name:
563 id = sg['id']
564 break
565 return id
566
567
568def create_security_group(neutron_client, sg_name, sg_description): # pragma: no cover
569 json_body = {'security_group': {'name': sg_name,
570 'description': sg_description}}
571 try:
572 secgroup = neutron_client.create_security_group(json_body)
573 return secgroup['security_group']
574 except Exception:
575 log.error("Error [create_security_group(neutron_client, '%s', "
576 "'%s')]" % (sg_name, sg_description))
577 return None
578
579
580def create_secgroup_rule(neutron_client, sg_id, direction, protocol,
581 port_range_min=None, port_range_max=None,
582 **json_body): # pragma: no cover
583 # We create a security group in 2 steps
584 # 1 - we check the format and set the json body accordingly
585 # 2 - we call neturon client to create the security group
586
587 # Format check
588 json_body.update({'security_group_rule': {'direction': direction,
589 'security_group_id': sg_id, 'protocol': protocol}})
590 # parameters may be
591 # - both None => we do nothing
592 # - both Not None => we add them to the json description
593 # but one cannot be None is the other is not None
594 if (port_range_min is not None and port_range_max is not None):
595 # add port_range in json description
596 json_body['security_group_rule']['port_range_min'] = port_range_min
597 json_body['security_group_rule']['port_range_max'] = port_range_max
598 log.debug("Security_group format set (port range included)")
599 else:
600 # either both port range are set to None => do nothing
601 # or one is set but not the other => log it and return False
602 if port_range_min is None and port_range_max is None:
603 log.debug("Security_group format set (no port range mentioned)")
604 else:
605 log.error("Bad security group format."
606 "One of the port range is not properly set:"
607 "range min: {},"
608 "range max: {}".format(port_range_min,
609 port_range_max))
610 return False
611
612 # Create security group using neutron client
613 try:
614 neutron_client.create_security_group_rule(json_body)
615 return True
616 except Exception:
617 log.exception("Impossible to create_security_group_rule,"
618 "security group rule probably already exists")
619 return False
620
621
622def create_security_group_full(neutron_client,
623 sg_name, sg_description): # pragma: no cover
624 sg_id = get_security_group_id(neutron_client, sg_name)
625 if sg_id != '':
626 log.info("Using existing security group '%s'..." % sg_name)
627 else:
628 log.info("Creating security group '%s'..." % sg_name)
629 SECGROUP = create_security_group(neutron_client,
630 sg_name,
631 sg_description)
632 if not SECGROUP:
633 log.error("Failed to create the security group...")
634 return None
635
636 sg_id = SECGROUP['id']
637
638 log.debug("Security group '%s' with ID=%s created successfully."
639 % (SECGROUP['name'], sg_id))
640
641 log.debug("Adding ICMP rules in security group '%s'..."
642 % sg_name)
643 if not create_secgroup_rule(neutron_client, sg_id,
644 'ingress', 'icmp'):
645 log.error("Failed to create the security group rule...")
646 return None
647
648 log.debug("Adding SSH rules in security group '%s'..."
649 % sg_name)
650 if not create_secgroup_rule(
651 neutron_client, sg_id, 'ingress', 'tcp', '22', '22'):
652 log.error("Failed to create the security group rule...")
653 return None
654
655 if not create_secgroup_rule(
656 neutron_client, sg_id, 'egress', 'tcp', '22', '22'):
657 log.error("Failed to create the security group rule...")
658 return None
659 return sg_id
660
661
662# *********************************************
663# GLANCE
664# *********************************************
665def get_image_id(glance_client, image_name): # pragma: no cover
666 images = glance_client.images.list()
667 return next((i.id for i in images if i.name == image_name), None)
668
669
670def create_image(glance_client, image_name, file_path, disk_format,
671 container_format, min_disk, min_ram, protected, tag,
672 public, **kwargs): # pragma: no cover
673 if not os.path.isfile(file_path):
674 log.error("Error: file %s does not exist." % file_path)
675 return None
676 try:
677 image_id = get_image_id(glance_client, image_name)
678 if image_id is not None:
679 log.info("Image %s already exists." % image_name)
680 else:
681 log.info("Creating image '%s' from '%s'...", image_name, file_path)
682
683 image = glance_client.images.create(name=image_name,
684 visibility=public,
685 disk_format=disk_format,
686 container_format=container_format,
687 min_disk=min_disk,
688 min_ram=min_ram,
689 tags=tag,
690 protected=protected,
691 **kwargs)
692 image_id = image.id
693 with open(file_path) as image_data:
694 glance_client.images.upload(image_id, image_data)
695 return image_id
696 except Exception:
697 log.error("Error [create_glance_image(glance_client, '%s', '%s', '%s')]",
698 image_name, file_path, public)
699 return None
700
701
702def delete_image(glance_client, image_id): # pragma: no cover
703 try:
704 glance_client.images.delete(image_id)
705
706 except Exception:
707 log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
708 return False
709 else:
710 return True
711
712
713# *********************************************
714# CINDER
715# *********************************************
716def get_volume_id(volume_name): # pragma: no cover
717 volumes = get_cinder_client().volumes.list()
718 return next((v.id for v in volumes if v.name == volume_name), None)
719
720
721def create_volume(cinder_client, volume_name, volume_size,
722 volume_image=False): # pragma: no cover
723 try:
724 if volume_image:
725 volume = cinder_client.volumes.create(name=volume_name,
726 size=volume_size,
727 imageRef=volume_image)
728 else:
729 volume = cinder_client.volumes.create(name=volume_name,
730 size=volume_size)
731 return volume
732 except Exception:
733 log.exception("Error [create_volume(cinder_client, %s)]",
734 (volume_name, volume_size))
735 return None
736
737
738def delete_volume(cinder_client, volume_id, forced=False): # pragma: no cover
739 try:
740 if forced:
741 try:
742 cinder_client.volumes.detach(volume_id)
743 except:
744 log.error(sys.exc_info()[0])
745 cinder_client.volumes.force_delete(volume_id)
746 else:
747 while True:
748 volume = get_cinder_client().volumes.get(volume_id)
749 if volume.status.lower() == 'available':
750 break
751 cinder_client.volumes.delete(volume_id)
752 return True
753 except Exception:
754 log.exception("Error [delete_volume(cinder_client, '%s')]" % volume_id)
755 return False
756
757
758def detach_volume(server_id, volume_id): # pragma: no cover
759 try:
760 get_nova_client().volumes.delete_server_volume(server_id, volume_id)
761 return True
762 except Exception:
763 log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]",
764 server_id, volume_id)
765 return False