1 ##############################################################################
2 # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
15 from keystoneauth1 import loading
16 from keystoneauth1 import session
19 from cinderclient import client as cinderclient
20 from novaclient import client as novaclient
21 from glanceclient import client as glanceclient
22 from neutronclient.neutron import client as neutronclient
25 log = logging.getLogger(__name__)
27 DEFAULT_HEAT_API_VERSION = '1'
28 DEFAULT_API_VERSION = '2'
31 # *********************************************
33 # *********************************************
34 def get_credentials():
35 """Returns a creds dictionary filled with parsed from env"""
38 keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION')
40 if keystone_api_version is None or keystone_api_version == '2':
42 tenant_env = 'OS_TENANT_NAME'
43 tenant = 'tenant_name'
46 tenant_env = 'OS_PROJECT_NAME'
47 tenant = 'project_name'
49 # The most common way to pass these info to the script is to do it
50 # through environment variables.
52 "username": os.environ.get("OS_USERNAME"),
53 "password": os.environ.get("OS_PASSWORD"),
54 "auth_url": os.environ.get("OS_AUTH_URL"),
55 tenant: os.environ.get(tenant_env)
59 if os.getenv('OS_USER_DOMAIN_NAME') is not None:
61 "user_domain_name": os.getenv('OS_USER_DOMAIN_NAME')
63 if os.getenv('OS_PROJECT_DOMAIN_NAME') is not None:
65 "project_domain_name": os.getenv('OS_PROJECT_DOMAIN_NAME')
71 def get_session_auth():
72 loader = loading.get_plugin_loader('password')
73 creds = get_credentials()
74 auth = loader.load_from_options(**creds)
79 auth = get_session_auth()
81 cacert = os.environ['OS_CACERT']
83 return session.Session(auth=auth)
85 insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
86 cacert = False if insecure else cacert
87 return session.Session(auth=auth, verify=cacert)
90 def get_endpoint(service_type, endpoint_type='publicURL'):
91 auth = get_session_auth()
92 # for multi-region, we need to specify region
93 # when finding the endpoint
94 return get_session().get_endpoint(auth=auth,
95 service_type=service_type,
96 endpoint_type=endpoint_type,
97 region_name=os.environ.get(
101 # *********************************************
103 # *********************************************
104 def get_heat_api_version(): # pragma: no cover
106 api_version = os.environ['HEAT_API_VERSION']
108 return DEFAULT_HEAT_API_VERSION
110 log.info("HEAT_API_VERSION is set in env as '%s'", api_version)
114 def get_cinder_client_version(): # pragma: no cover
116 api_version = os.environ['OS_VOLUME_API_VERSION']
118 return DEFAULT_API_VERSION
120 log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
124 def get_cinder_client(): # pragma: no cover
126 return cinderclient.Client(get_cinder_client_version(), session=sess)
129 def get_nova_client_version(): # pragma: no cover
131 api_version = os.environ['OS_COMPUTE_API_VERSION']
133 return DEFAULT_API_VERSION
135 log.info("OS_COMPUTE_API_VERSION is set in env as '%s'", api_version)
139 def get_nova_client(): # pragma: no cover
141 return novaclient.Client(get_nova_client_version(), session=sess)
144 def get_neutron_client_version(): # pragma: no cover
146 api_version = os.environ['OS_NETWORK_API_VERSION']
148 return DEFAULT_API_VERSION
150 log.info("OS_NETWORK_API_VERSION is set in env as '%s'", api_version)
154 def get_neutron_client(): # pragma: no cover
156 return neutronclient.Client(get_neutron_client_version(), session=sess)
159 def get_glance_client_version(): # pragma: no cover
161 api_version = os.environ['OS_IMAGE_API_VERSION']
163 return DEFAULT_API_VERSION
165 log.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
169 def get_glance_client(): # pragma: no cover
171 return glanceclient.Client(get_glance_client_version(), session=sess)
174 def get_shade_client():
175 return shade.openstack_cloud()
177 # *********************************************
179 # *********************************************
180 def get_instances(nova_client):
182 return nova_client.servers.list(search_opts={'all_tenants': 1})
183 except Exception: # pylint: disable=broad-except
184 log.exception("Error [get_instances(nova_client)]")
187 def get_instance_status(nova_client, instance): # pragma: no cover
189 return nova_client.servers.get(instance.id).status
190 except Exception: # pylint: disable=broad-except
191 log.exception("Error [get_instance_status(nova_client)]")
194 def get_instance_by_name(nova_client, instance_name): # pragma: no cover
196 return nova_client.servers.find(name=instance_name)
197 except Exception: # pylint: disable=broad-except
198 log.exception("Error [get_instance_by_name(nova_client, '%s')]",
202 def get_aggregates(nova_client): # pragma: no cover
204 return nova_client.aggregates.list()
205 except Exception: # pylint: disable=broad-except
206 log.exception("Error [get_aggregates(nova_client)]")
209 def get_availability_zones(nova_client): # pragma: no cover
211 return nova_client.availability_zones.list()
212 except Exception: # pylint: disable=broad-except
213 log.exception("Error [get_availability_zones(nova_client)]")
216 def get_availability_zone_names(nova_client): # pragma: no cover
218 return [az.zoneName for az in get_availability_zones(nova_client)]
219 except Exception: # pylint: disable=broad-except
220 log.exception("Error [get_availability_zone_names(nova_client)]")
223 def create_aggregate(nova_client, aggregate_name, av_zone): # pragma: no cover
225 nova_client.aggregates.create(aggregate_name, av_zone)
226 except Exception: # pylint: disable=broad-except
227 log.exception("Error [create_aggregate(nova_client, %s, %s)]",
228 aggregate_name, av_zone)
234 def get_aggregate_id(nova_client, aggregate_name): # pragma: no cover
236 aggregates = get_aggregates(nova_client)
237 _id = next((ag.id for ag in aggregates if ag.name == aggregate_name))
238 except Exception: # pylint: disable=broad-except
239 log.exception("Error [get_aggregate_id(nova_client, %s)]",
245 def add_host_to_aggregate(nova_client, aggregate_name,
246 compute_host): # pragma: no cover
248 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
249 nova_client.aggregates.add_host(aggregate_id, compute_host)
250 except Exception: # pylint: disable=broad-except
251 log.exception("Error [add_host_to_aggregate(nova_client, %s, %s)]",
252 aggregate_name, compute_host)
258 def create_aggregate_with_host(nova_client, aggregate_name, av_zone,
259 compute_host): # pragma: no cover
261 create_aggregate(nova_client, aggregate_name, av_zone)
262 add_host_to_aggregate(nova_client, aggregate_name, compute_host)
263 except Exception: # pylint: disable=broad-except
264 log.exception("Error [create_aggregate_with_host("
265 "nova_client, %s, %s, %s)]",
266 aggregate_name, av_zone, compute_host)
272 def create_keypair(name, key_path=None): # pragma: no cover
274 with open(key_path) as fpubkey:
275 keypair = get_nova_client().keypairs.create(name=name, public_key=fpubkey.read())
277 except Exception: # pylint: disable=broad-except
278 log.exception("Error [create_keypair(nova_client)]")
281 def create_instance(json_body): # pragma: no cover
283 return get_nova_client().servers.create(**json_body)
284 except Exception: # pylint: disable=broad-except
285 log.exception("Error create instance failed")
289 def create_instance_and_wait_for_active(json_body): # pragma: no cover
291 VM_BOOT_TIMEOUT = 180
292 nova_client = get_nova_client()
293 instance = create_instance(json_body)
294 count = VM_BOOT_TIMEOUT / SLEEP
295 for _ in range(count, -1, -1):
296 status = get_instance_status(nova_client, instance)
297 if status.lower() == "active":
299 elif status.lower() == "error":
300 log.error("The instance went to ERROR status.")
303 log.error("Timeout booting the instance.")
307 def attach_server_volume(server_id, volume_id, device=None): # pragma: no cover
309 get_nova_client().volumes.create_server_volume(server_id, volume_id, device)
310 except Exception: # pylint: disable=broad-except
311 log.exception("Error [attach_server_volume(nova_client, '%s', '%s')]",
312 server_id, volume_id)
318 def delete_instance(nova_client, instance_id): # pragma: no cover
320 nova_client.servers.force_delete(instance_id)
321 except Exception: # pylint: disable=broad-except
322 log.exception("Error [delete_instance(nova_client, '%s')]",
329 def remove_host_from_aggregate(nova_client, aggregate_name,
330 compute_host): # pragma: no cover
332 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
333 nova_client.aggregates.remove_host(aggregate_id, compute_host)
334 except Exception: # pylint: disable=broad-except
335 log.exception("Error remove_host_from_aggregate(nova_client, %s, %s)",
336 aggregate_name, compute_host)
342 def remove_hosts_from_aggregate(nova_client,
343 aggregate_name): # pragma: no cover
344 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
345 hosts = nova_client.aggregates.get(aggregate_id).hosts
347 all(remove_host_from_aggregate(nova_client, aggregate_name, host)
351 def delete_aggregate(nova_client, aggregate_name): # pragma: no cover
353 remove_hosts_from_aggregate(nova_client, aggregate_name)
354 nova_client.aggregates.delete(aggregate_name)
355 except Exception: # pylint: disable=broad-except
356 log.exception("Error [delete_aggregate(nova_client, %s)]",
363 def get_server_by_name(name): # pragma: no cover
365 return get_nova_client().servers.list(search_opts={'name': name})[0]
367 log.exception('Failed to get nova client')
371 def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover
373 return get_nova_client().flavors.create(name, ram, vcpus, disk, **kwargs)
374 except Exception: # pylint: disable=broad-except
375 log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]",
376 name, ram, disk, vcpus, kwargs['is_public'])
380 def get_image_by_name(name): # pragma: no cover
381 images = get_nova_client().images.list()
383 return next((a for a in images if a.name == name))
384 except StopIteration:
385 log.exception('No image matched')
388 def get_flavor_id(nova_client, flavor_name): # pragma: no cover
389 flavors = nova_client.flavors.list(detailed=True)
392 if f.name == flavor_name:
398 def get_flavor_by_name(name): # pragma: no cover
399 flavors = get_nova_client().flavors.list()
401 return next((a for a in flavors if a.name == name))
402 except StopIteration:
403 log.exception('No flavor matched')
406 def check_status(status, name, iterations, interval): # pragma: no cover
407 for _ in range(iterations):
409 server = get_server_by_name(name)
411 log.error('Cannot found %s server', name)
414 if server.status == status:
421 def delete_flavor(flavor_id): # pragma: no cover
423 get_nova_client().flavors.delete(flavor_id)
424 except Exception: # pylint: disable=broad-except
425 log.exception("Error [delete_flavor(nova_client, %s)]", flavor_id)
431 def delete_keypair(nova_client, key): # pragma: no cover
433 nova_client.keypairs.delete(key=key)
435 except Exception: # pylint: disable=broad-except
436 log.exception("Error [delete_keypair(nova_client)]")
440 # *********************************************
442 # *********************************************
443 def get_network_id(shade_client, network_name):
444 networks = shade_client.list_networks({'name': network_name})
446 return networks[0]['id']
449 def create_neutron_net(neutron_client, json_body): # pragma: no cover
451 network = neutron_client.create_network(body=json_body)
452 return network['network']['id']
453 except Exception: # pylint: disable=broad-except
454 log.error("Error [create_neutron_net(neutron_client)]")
455 raise Exception("operation error")
458 def delete_neutron_net(neutron_client, network_id): # pragma: no cover
460 neutron_client.delete_network(network_id)
462 except Exception: # pylint: disable=broad-except
463 log.error("Error [delete_neutron_net(neutron_client, '%s')]",
468 def create_neutron_subnet(neutron_client, json_body): # pragma: no cover
470 subnet = neutron_client.create_subnet(body=json_body)
471 return subnet['subnets'][0]['id']
472 except Exception: # pylint: disable=broad-except
473 log.error("Error [create_neutron_subnet")
474 raise Exception("operation error")
477 def create_neutron_router(neutron_client, json_body): # pragma: no cover
479 router = neutron_client.create_router(json_body)
480 return router['router']['id']
481 except Exception: # pylint: disable=broad-except
482 log.error("Error [create_neutron_router(neutron_client)]")
483 raise Exception("operation error")
486 def delete_neutron_router(neutron_client, router_id): # pragma: no cover
488 neutron_client.delete_router(router=router_id)
490 except Exception: # pylint: disable=broad-except
491 log.error("Error [delete_neutron_router(neutron_client, '%s')]",
496 def remove_gateway_router(neutron_client, router_id): # pragma: no cover
498 neutron_client.remove_gateway_router(router_id)
500 except Exception: # pylint: disable=broad-except
501 log.error("Error [remove_gateway_router(neutron_client, '%s')]",
506 def remove_interface_router(neutron_client, router_id, subnet_id,
507 **json_body): # pragma: no cover
508 json_body.update({"subnet_id": subnet_id})
510 neutron_client.remove_interface_router(router=router_id,
513 except Exception: # pylint: disable=broad-except
514 log.error("Error [remove_interface_router(neutron_client, '%s', "
515 "'%s')]", router_id, subnet_id)
519 def create_floating_ip(neutron_client, extnet_id): # pragma: no cover
520 props = {'floating_network_id': extnet_id}
522 ip_json = neutron_client.create_floatingip({'floatingip': props})
523 fip_addr = ip_json['floatingip']['floating_ip_address']
524 fip_id = ip_json['floatingip']['id']
525 except Exception: # pylint: disable=broad-except
526 log.error("Error [create_floating_ip(neutron_client)]")
528 return {'fip_addr': fip_addr, 'fip_id': fip_id}
531 def delete_floating_ip(nova_client, floatingip_id): # pragma: no cover
533 nova_client.floating_ips.delete(floatingip_id)
535 except Exception: # pylint: disable=broad-except
536 log.error("Error [delete_floating_ip(nova_client, '%s')]",
541 def get_security_groups(neutron_client): # pragma: no cover
543 security_groups = neutron_client.list_security_groups()[
545 return security_groups
546 except Exception: # pylint: disable=broad-except
547 log.error("Error [get_security_groups(neutron_client)]")
551 def get_security_group_id(neutron_client, sg_name): # pragma: no cover
552 security_groups = get_security_groups(neutron_client)
554 for sg in security_groups:
555 if sg['name'] == sg_name:
561 def create_security_group(neutron_client, sg_name, sg_description): # pragma: no cover
562 json_body = {'security_group': {'name': sg_name,
563 'description': sg_description}}
565 secgroup = neutron_client.create_security_group(json_body)
566 return secgroup['security_group']
567 except Exception: # pylint: disable=broad-except
568 log.error("Error [create_security_group(neutron_client, '%s', "
569 "'%s')]", sg_name, sg_description)
573 def create_secgroup_rule(neutron_client, sg_id, direction, protocol,
574 port_range_min=None, port_range_max=None,
575 **json_body): # pragma: no cover
576 # We create a security group in 2 steps
577 # 1 - we check the format and set the json body accordingly
578 # 2 - we call neturon client to create the security group
581 json_body.update({'security_group_rule': {'direction': direction,
582 'security_group_id': sg_id, 'protocol': protocol}})
584 # - both None => we do nothing
585 # - both Not None => we add them to the json description
586 # but one cannot be None is the other is not None
587 if (port_range_min is not None and port_range_max is not None):
588 # add port_range in json description
589 json_body['security_group_rule']['port_range_min'] = port_range_min
590 json_body['security_group_rule']['port_range_max'] = port_range_max
591 log.debug("Security_group format set (port range included)")
593 # either both port range are set to None => do nothing
594 # or one is set but not the other => log it and return False
595 if port_range_min is None and port_range_max is None:
596 log.debug("Security_group format set (no port range mentioned)")
598 log.error("Bad security group format."
599 "One of the port range is not properly set:"
600 "range min: %s, range max: %s", port_range_min,
604 # Create security group using neutron client
606 neutron_client.create_security_group_rule(json_body)
608 except Exception: # pylint: disable=broad-except
609 log.exception("Impossible to create_security_group_rule,"
610 "security group rule probably already exists")
614 def create_security_group_full(neutron_client,
615 sg_name, sg_description): # pragma: no cover
616 sg_id = get_security_group_id(neutron_client, sg_name)
618 log.info("Using existing security group '%s'...", sg_name)
620 log.info("Creating security group '%s'...", sg_name)
621 SECGROUP = create_security_group(neutron_client,
625 log.error("Failed to create the security group...")
628 sg_id = SECGROUP['id']
630 log.debug("Security group '%s' with ID=%s created successfully.",
631 SECGROUP['name'], sg_id)
633 log.debug("Adding ICMP rules in security group '%s'...", sg_name)
634 if not create_secgroup_rule(neutron_client, sg_id,
636 log.error("Failed to create the security group rule...")
639 log.debug("Adding SSH rules in security group '%s'...", sg_name)
640 if not create_secgroup_rule(
641 neutron_client, sg_id, 'ingress', 'tcp', '22', '22'):
642 log.error("Failed to create the security group rule...")
645 if not create_secgroup_rule(
646 neutron_client, sg_id, 'egress', 'tcp', '22', '22'):
647 log.error("Failed to create the security group rule...")
652 # *********************************************
654 # *********************************************
655 def get_image_id(glance_client, image_name): # pragma: no cover
656 images = glance_client.images.list()
657 return next((i.id for i in images if i.name == image_name), None)
660 def create_image(glance_client, image_name, file_path, disk_format,
661 container_format, min_disk, min_ram, protected, tag,
662 public, **kwargs): # pragma: no cover
663 if not os.path.isfile(file_path):
664 log.error("Error: file %s does not exist.", file_path)
667 image_id = get_image_id(glance_client, image_name)
668 if image_id is not None:
669 log.info("Image %s already exists.", image_name)
671 log.info("Creating image '%s' from '%s'...", image_name, file_path)
673 image = glance_client.images.create(name=image_name,
675 disk_format=disk_format,
676 container_format=container_format,
683 with open(file_path) as image_data:
684 glance_client.images.upload(image_id, image_data)
686 except Exception: # pylint: disable=broad-except
687 log.error("Error [create_glance_image(glance_client, '%s', '%s', '%s')]",
688 image_name, file_path, public)
692 def delete_image(glance_client, image_id): # pragma: no cover
694 glance_client.images.delete(image_id)
696 except Exception: # pylint: disable=broad-except
697 log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
703 # *********************************************
705 # *********************************************
706 def get_volume_id(volume_name): # pragma: no cover
707 volumes = get_cinder_client().volumes.list()
708 return next((v.id for v in volumes if v.name == volume_name), None)
711 def create_volume(cinder_client, volume_name, volume_size,
712 volume_image=False): # pragma: no cover
715 volume = cinder_client.volumes.create(name=volume_name,
717 imageRef=volume_image)
719 volume = cinder_client.volumes.create(name=volume_name,
722 except Exception: # pylint: disable=broad-except
723 log.exception("Error [create_volume(cinder_client, %s)]",
724 (volume_name, volume_size))
728 def delete_volume(cinder_client, volume_id, forced=False): # pragma: no cover
732 cinder_client.volumes.detach(volume_id)
733 except Exception: # pylint: disable=broad-except
734 log.error(sys.exc_info()[0])
735 cinder_client.volumes.force_delete(volume_id)
738 volume = get_cinder_client().volumes.get(volume_id)
739 if volume.status.lower() == 'available':
741 cinder_client.volumes.delete(volume_id)
743 except Exception: # pylint: disable=broad-except
744 log.exception("Error [delete_volume(cinder_client, '%s')]", volume_id)
748 def detach_volume(server_id, volume_id): # pragma: no cover
750 get_nova_client().volumes.delete_server_volume(server_id, volume_id)
752 except Exception: # pylint: disable=broad-except
753 log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]",
754 server_id, volume_id)