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 ##############################################################################
10 from __future__ import absolute_import
17 from keystoneauth1 import loading
18 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
24 log = logging.getLogger(__name__)
26 DEFAULT_HEAT_API_VERSION = '1'
27 DEFAULT_API_VERSION = '2'
30 # *********************************************
32 # *********************************************
33 def get_credentials():
34 """Returns a creds dictionary filled with parsed from env"""
37 keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION')
39 if keystone_api_version is None or keystone_api_version == '2':
41 tenant_env = 'OS_TENANT_NAME'
42 tenant = 'tenant_name'
45 tenant_env = 'OS_PROJECT_NAME'
46 tenant = 'project_name'
48 # The most common way to pass these info to the script is to do it
49 # through environment variables.
51 "username": os.environ.get("OS_USERNAME"),
52 "password": os.environ.get("OS_PASSWORD"),
53 "auth_url": os.environ.get("OS_AUTH_URL"),
54 tenant: os.environ.get(tenant_env)
58 if os.getenv('OS_USER_DOMAIN_NAME') is not None:
60 "user_domain_name": os.getenv('OS_USER_DOMAIN_NAME')
62 if os.getenv('OS_PROJECT_DOMAIN_NAME') is not None:
64 "project_domain_name": os.getenv('OS_PROJECT_DOMAIN_NAME')
70 def get_session_auth():
71 loader = loading.get_plugin_loader('password')
72 creds = get_credentials()
73 auth = loader.load_from_options(**creds)
78 auth = get_session_auth()
80 cacert = os.environ['OS_CACERT']
82 return session.Session(auth=auth)
84 insecure = os.getenv('OS_INSECURE', '').lower() == 'true'
85 cacert = False if insecure else cacert
86 return session.Session(auth=auth, verify=cacert)
89 def get_endpoint(service_type, endpoint_type='publicURL'):
90 auth = get_session_auth()
91 # for multi-region, we need to specify region
92 # when finding the endpoint
93 return get_session().get_endpoint(auth=auth,
94 service_type=service_type,
95 endpoint_type=endpoint_type,
96 region_name=os.environ.get(
100 # *********************************************
102 # *********************************************
103 def get_heat_api_version(): # pragma: no cover
105 api_version = os.environ['HEAT_API_VERSION']
107 return DEFAULT_HEAT_API_VERSION
109 log.info("HEAT_API_VERSION is set in env as '%s'", api_version)
113 def get_cinder_client_version(): # pragma: no cover
115 api_version = os.environ['OS_VOLUME_API_VERSION']
117 return DEFAULT_API_VERSION
119 log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
123 def get_cinder_client(): # pragma: no cover
125 return cinderclient.Client(get_cinder_client_version(), session=sess)
128 def get_nova_client_version(): # pragma: no cover
130 api_version = os.environ['OS_COMPUTE_API_VERSION']
132 return DEFAULT_API_VERSION
134 log.info("OS_COMPUTE_API_VERSION is set in env as '%s'", api_version)
138 def get_nova_client(): # pragma: no cover
140 return novaclient.Client(get_nova_client_version(), session=sess)
143 def get_neutron_client_version(): # pragma: no cover
145 api_version = os.environ['OS_NETWORK_API_VERSION']
147 return DEFAULT_API_VERSION
149 log.info("OS_NETWORK_API_VERSION is set in env as '%s'", api_version)
153 def get_neutron_client(): # pragma: no cover
155 return neutronclient.Client(get_neutron_client_version(), session=sess)
158 def get_glance_client_version(): # pragma: no cover
160 api_version = os.environ['OS_IMAGE_API_VERSION']
162 return DEFAULT_API_VERSION
164 log.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
168 def get_glance_client(): # pragma: no cover
170 return glanceclient.Client(get_glance_client_version(), session=sess)
173 # *********************************************
175 # *********************************************
176 def get_instances(nova_client): # pragma: no cover
178 return nova_client.servers.list(search_opts={'all_tenants': 1})
180 log.exception("Error [get_instances(nova_client)]")
183 def get_instance_status(nova_client, instance): # pragma: no cover
185 return nova_client.servers.get(instance.id).status
187 log.exception("Error [get_instance_status(nova_client)]")
190 def get_instance_by_name(nova_client, instance_name): # pragma: no cover
192 return nova_client.servers.find(name=instance_name)
194 log.exception("Error [get_instance_by_name(nova_client, '%s')]",
198 def get_aggregates(nova_client): # pragma: no cover
200 return nova_client.aggregates.list()
202 log.exception("Error [get_aggregates(nova_client)]")
205 def get_availability_zones(nova_client): # pragma: no cover
207 return nova_client.availability_zones.list()
209 log.exception("Error [get_availability_zones(nova_client)]")
212 def get_availability_zone_names(nova_client): # pragma: no cover
214 return [az.zoneName for az in get_availability_zones(nova_client)]
216 log.exception("Error [get_availability_zone_names(nova_client)]")
219 def create_aggregate(nova_client, aggregate_name, av_zone): # pragma: no cover
221 nova_client.aggregates.create(aggregate_name, av_zone)
223 log.exception("Error [create_aggregate(nova_client, %s, %s)]",
224 aggregate_name, av_zone)
230 def get_aggregate_id(nova_client, aggregate_name): # pragma: no cover
232 aggregates = get_aggregates(nova_client)
233 _id = next((ag.id for ag in aggregates if ag.name == aggregate_name))
235 log.exception("Error [get_aggregate_id(nova_client, %s)]",
241 def add_host_to_aggregate(nova_client, aggregate_name,
242 compute_host): # pragma: no cover
244 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
245 nova_client.aggregates.add_host(aggregate_id, compute_host)
247 log.exception("Error [add_host_to_aggregate(nova_client, %s, %s)]",
248 aggregate_name, compute_host)
254 def create_aggregate_with_host(nova_client, aggregate_name, av_zone,
255 compute_host): # pragma: no cover
257 create_aggregate(nova_client, aggregate_name, av_zone)
258 add_host_to_aggregate(nova_client, aggregate_name, compute_host)
260 log.exception("Error [create_aggregate_with_host("
261 "nova_client, %s, %s, %s)]",
262 aggregate_name, av_zone, compute_host)
268 def create_keypair(nova_client, name, key_path=None): # pragma: no cover
270 with open(key_path) as fpubkey:
271 keypair = get_nova_client().keypairs.create(name=name, public_key=fpubkey.read())
274 log.exception("Error [create_keypair(nova_client)]")
277 def create_instance(json_body): # pragma: no cover
279 return get_nova_client().servers.create(**json_body)
281 log.exception("Error create instance failed")
285 def create_instance_and_wait_for_active(json_body): # pragma: no cover
287 VM_BOOT_TIMEOUT = 180
288 nova_client = get_nova_client()
289 instance = create_instance(json_body)
290 count = VM_BOOT_TIMEOUT / SLEEP
291 for n in range(count, -1, -1):
292 status = get_instance_status(nova_client, instance)
293 if status.lower() == "active":
295 elif status.lower() == "error":
296 log.error("The instance went to ERROR status.")
299 log.error("Timeout booting the instance.")
303 def attach_server_volume(server_id, volume_id, device=None): # pragma: no cover
305 get_nova_client().volumes.create_server_volume(server_id, volume_id, device)
307 log.exception("Error [attach_server_volume(nova_client, '%s', '%s')]",
308 server_id, volume_id)
314 def delete_instance(nova_client, instance_id): # pragma: no cover
316 nova_client.servers.force_delete(instance_id)
318 log.exception("Error [delete_instance(nova_client, '%s')]",
325 def remove_host_from_aggregate(nova_client, aggregate_name,
326 compute_host): # pragma: no cover
328 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
329 nova_client.aggregates.remove_host(aggregate_id, compute_host)
331 log.exception("Error remove_host_from_aggregate(nova_client, %s, %s)",
332 aggregate_name, compute_host)
338 def remove_hosts_from_aggregate(nova_client,
339 aggregate_name): # pragma: no cover
340 aggregate_id = get_aggregate_id(nova_client, aggregate_name)
341 hosts = nova_client.aggregates.get(aggregate_id).hosts
343 all(remove_host_from_aggregate(nova_client, aggregate_name, host)
347 def delete_aggregate(nova_client, aggregate_name): # pragma: no cover
349 remove_hosts_from_aggregate(nova_client, aggregate_name)
350 nova_client.aggregates.delete(aggregate_name)
352 log.exception("Error [delete_aggregate(nova_client, %s)]",
359 def get_server_by_name(name): # pragma: no cover
361 return get_nova_client().servers.list(search_opts={'name': name})[0]
363 log.exception('Failed to get nova client')
367 def create_flavor(name, ram, vcpus, disk, **kwargs): # pragma: no cover
369 return get_nova_client().flavors.create(name, ram, vcpus, disk, **kwargs)
371 log.exception("Error [create_flavor(nova_client, %s, %s, %s, %s, %s)]",
372 name, ram, disk, vcpus, kwargs['is_public'])
376 def get_image_by_name(name): # pragma: no cover
377 images = get_nova_client().images.list()
379 return next((a for a in images if a.name == name))
380 except StopIteration:
381 log.exception('No image matched')
384 def get_flavor_id(nova_client, flavor_name): # pragma: no cover
385 flavors = nova_client.flavors.list(detailed=True)
388 if f.name == flavor_name:
394 def get_flavor_by_name(name): # pragma: no cover
395 flavors = get_nova_client().flavors.list()
397 return next((a for a in flavors if a.name == name))
398 except StopIteration:
399 log.exception('No flavor matched')
402 def check_status(status, name, iterations, interval): # pragma: no cover
403 for i in range(iterations):
405 server = get_server_by_name(name)
407 log.error('Cannot found %s server', name)
410 if server.status == status:
417 def delete_flavor(flavor_id): # pragma: no cover
419 get_nova_client().flavors.delete(flavor_id)
421 log.exception("Error [delete_flavor(nova_client, %s)]", flavor_id)
427 def delete_keypair(nova_client, key): # pragma: no cover
429 nova_client.keypairs.delete(key=key)
432 log.exception("Error [delete_keypair(nova_client)]")
436 # *********************************************
438 # *********************************************
439 def get_network_id(neutron_client, network_name): # pragma: no cover
440 networks = neutron_client.list_networks()['networks']
441 return next((n['id'] for n in networks if n['name'] == network_name), None)
444 def get_port_id_by_ip(neutron_client, ip_address): # pragma: no cover
445 ports = neutron_client.list_ports()['ports']
446 return next((i['id'] for i in ports for j in i.get(
447 'fixed_ips') if j['ip_address'] == ip_address), None)
450 def create_neutron_net(neutron_client, json_body): # pragma: no cover
452 network = neutron_client.create_network(body=json_body)
453 return network['network']['id']
455 log.error("Error [create_neutron_net(neutron_client)]")
456 raise Exception("operation error")
460 def delete_neutron_net(neutron_client, network_id): # pragma: no cover
462 neutron_client.delete_network(network_id)
465 log.error("Error [delete_neutron_net(neutron_client, '%s')]" % network_id)
469 def create_neutron_subnet(neutron_client, json_body): # pragma: no cover
471 subnet = neutron_client.create_subnet(body=json_body)
472 return subnet['subnets'][0]['id']
474 log.error("Error [create_neutron_subnet")
475 raise Exception("operation error")
479 def create_neutron_router(neutron_client, json_body): # pragma: no cover
481 router = neutron_client.create_router(json_body)
482 return router['router']['id']
484 log.error("Error [create_neutron_router(neutron_client)]")
485 raise Exception("operation error")
489 def delete_neutron_router(neutron_client, router_id): # pragma: no cover
491 neutron_client.delete_router(router=router_id)
494 log.error("Error [delete_neutron_router(neutron_client, '%s')]" % router_id)
498 def remove_gateway_router(neutron_client, router_id): # pragma: no cover
500 neutron_client.remove_gateway_router(router_id)
503 log.error("Error [remove_gateway_router(neutron_client, '%s')]" % router_id)
507 def remove_interface_router(neutron_client, router_id, subnet_id,
508 **json_body): # pragma: no cover
509 json_body.update({"subnet_id": subnet_id})
511 neutron_client.remove_interface_router(router=router_id,
515 log.error("Error [remove_interface_router(neutron_client, '%s', "
516 "'%s')]" % (router_id, subnet_id))
520 def create_floating_ip(neutron_client, extnet_id): # pragma: no cover
521 props = {'floating_network_id': extnet_id}
523 ip_json = neutron_client.create_floatingip({'floatingip': props})
524 fip_addr = ip_json['floatingip']['floating_ip_address']
525 fip_id = ip_json['floatingip']['id']
527 log.error("Error [create_floating_ip(neutron_client)]")
529 return {'fip_addr': fip_addr, 'fip_id': fip_id}
532 def delete_floating_ip(nova_client, floatingip_id): # pragma: no cover
534 nova_client.floating_ips.delete(floatingip_id)
537 log.error("Error [delete_floating_ip(nova_client, '%s')]" % floatingip_id)
541 def get_security_groups(neutron_client): # pragma: no cover
543 security_groups = neutron_client.list_security_groups()[
545 return security_groups
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']
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:"
601 "range max: {}".format(port_range_min,
605 # Create security group using neutron client
607 neutron_client.create_security_group_rule(json_body)
610 log.exception("Impossible to create_security_group_rule,"
611 "security group rule probably already exists")
615 def create_security_group_full(neutron_client,
616 sg_name, sg_description): # pragma: no cover
617 sg_id = get_security_group_id(neutron_client, sg_name)
619 log.info("Using existing security group '%s'..." % sg_name)
621 log.info("Creating security group '%s'..." % sg_name)
622 SECGROUP = create_security_group(neutron_client,
626 log.error("Failed to create the security group...")
629 sg_id = SECGROUP['id']
631 log.debug("Security group '%s' with ID=%s created successfully."
632 % (SECGROUP['name'], sg_id))
634 log.debug("Adding ICMP rules in security group '%s'..."
636 if not create_secgroup_rule(neutron_client, sg_id,
638 log.error("Failed to create the security group rule...")
641 log.debug("Adding SSH rules in security group '%s'..."
643 if not create_secgroup_rule(
644 neutron_client, sg_id, 'ingress', 'tcp', '22', '22'):
645 log.error("Failed to create the security group rule...")
648 if not create_secgroup_rule(
649 neutron_client, sg_id, 'egress', 'tcp', '22', '22'):
650 log.error("Failed to create the security group rule...")
655 # *********************************************
657 # *********************************************
658 def get_image_id(glance_client, image_name): # pragma: no cover
659 images = glance_client.images.list()
660 return next((i.id for i in images if i.name == image_name), None)
663 def create_image(glance_client, image_name, file_path, disk_format,
664 container_format, min_disk, min_ram, protected, tag,
665 public, **kwargs): # pragma: no cover
666 if not os.path.isfile(file_path):
667 log.error("Error: file %s does not exist." % file_path)
670 image_id = get_image_id(glance_client, image_name)
671 if image_id is not None:
672 log.info("Image %s already exists." % image_name)
674 log.info("Creating image '%s' from '%s'...", image_name, file_path)
676 image = glance_client.images.create(name=image_name,
678 disk_format=disk_format,
679 container_format=container_format,
686 with open(file_path) as image_data:
687 glance_client.images.upload(image_id, image_data)
690 log.error("Error [create_glance_image(glance_client, '%s', '%s', '%s')]",
691 image_name, file_path, public)
695 def delete_image(glance_client, image_id): # pragma: no cover
697 glance_client.images.delete(image_id)
700 log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
706 # *********************************************
708 # *********************************************
709 def get_volume_id(volume_name): # pragma: no cover
710 volumes = get_cinder_client().volumes.list()
711 return next((v.id for v in volumes if v.name == volume_name), None)
714 def create_volume(cinder_client, volume_name, volume_size,
715 volume_image=False): # pragma: no cover
718 volume = cinder_client.volumes.create(name=volume_name,
720 imageRef=volume_image)
722 volume = cinder_client.volumes.create(name=volume_name,
726 log.exception("Error [create_volume(cinder_client, %s)]",
727 (volume_name, volume_size))
731 def delete_volume(cinder_client, volume_id, forced=False): # pragma: no cover
735 cinder_client.volumes.detach(volume_id)
737 log.error(sys.exc_info()[0])
738 cinder_client.volumes.force_delete(volume_id)
741 volume = get_cinder_client().volumes.get(volume_id)
742 if volume.status.lower() == 'available':
744 cinder_client.volumes.delete(volume_id)
747 log.exception("Error [delete_volume(cinder_client, '%s')]" % volume_id)
751 def detach_volume(server_id, volume_id): # pragma: no cover
753 get_nova_client().volumes.delete_server_volume(server_id, volume_id)
756 log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]",
757 server_id, volume_id)