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 create_neutron_subnet(neutron_client, json_body): # pragma: no cover
462 subnet = neutron_client.create_subnet(body=json_body)
463 return subnet['subnets'][0]['id']
465 log.error("Error [create_neutron_subnet")
466 raise Exception("operation error")
470 def create_neutron_router(neutron_client, json_body): # pragma: no cover
472 router = neutron_client.create_router(json_body)
473 return router['router']['id']
475 log.error("Error [create_neutron_router(neutron_client)]")
476 raise Exception("operation error")
480 def create_floating_ip(neutron_client, extnet_id): # pragma: no cover
481 props = {'floating_network_id': extnet_id}
483 ip_json = neutron_client.create_floatingip({'floatingip': props})
484 fip_addr = ip_json['floatingip']['floating_ip_address']
485 fip_id = ip_json['floatingip']['id']
487 log.error("Error [create_floating_ip(neutron_client)]")
489 return {'fip_addr': fip_addr, 'fip_id': fip_id}
492 def delete_floating_ip(nova_client, floatingip_id): # pragma: no cover
494 nova_client.floating_ips.delete(floatingip_id)
497 log.error("Error [delete_floating_ip(nova_client, '%s')]" % floatingip_id)
501 def get_security_groups(neutron_client): # pragma: no cover
503 security_groups = neutron_client.list_security_groups()[
505 return security_groups
507 log.error("Error [get_security_groups(neutron_client)]")
511 def get_security_group_id(neutron_client, sg_name): # pragma: no cover
512 security_groups = get_security_groups(neutron_client)
514 for sg in security_groups:
515 if sg['name'] == sg_name:
521 def create_security_group(neutron_client, sg_name, sg_description): # pragma: no cover
522 json_body = {'security_group': {'name': sg_name,
523 'description': sg_description}}
525 secgroup = neutron_client.create_security_group(json_body)
526 return secgroup['security_group']
528 log.error("Error [create_security_group(neutron_client, '%s', "
529 "'%s')]" % (sg_name, sg_description))
533 def create_secgroup_rule(neutron_client, sg_id, direction, protocol,
534 port_range_min=None, port_range_max=None,
535 **json_body): # pragma: no cover
536 # We create a security group in 2 steps
537 # 1 - we check the format and set the json body accordingly
538 # 2 - we call neturon client to create the security group
541 json_body.update({'security_group_rule': {'direction': direction,
542 'security_group_id': sg_id, 'protocol': protocol}})
544 # - both None => we do nothing
545 # - both Not None => we add them to the json description
546 # but one cannot be None is the other is not None
547 if (port_range_min is not None and port_range_max is not None):
548 # add port_range in json description
549 json_body['security_group_rule']['port_range_min'] = port_range_min
550 json_body['security_group_rule']['port_range_max'] = port_range_max
551 log.debug("Security_group format set (port range included)")
553 # either both port range are set to None => do nothing
554 # or one is set but not the other => log it and return False
555 if port_range_min is None and port_range_max is None:
556 log.debug("Security_group format set (no port range mentioned)")
558 log.error("Bad security group format."
559 "One of the port range is not properly set:"
561 "range max: {}".format(port_range_min,
565 # Create security group using neutron client
567 neutron_client.create_security_group_rule(json_body)
570 log.exception("Impossible to create_security_group_rule,"
571 "security group rule probably already exists")
575 def create_security_group_full(neutron_client,
576 sg_name, sg_description): # pragma: no cover
577 sg_id = get_security_group_id(neutron_client, sg_name)
579 log.info("Using existing security group '%s'..." % sg_name)
581 log.info("Creating security group '%s'..." % sg_name)
582 SECGROUP = create_security_group(neutron_client,
586 log.error("Failed to create the security group...")
589 sg_id = SECGROUP['id']
591 log.debug("Security group '%s' with ID=%s created successfully."
592 % (SECGROUP['name'], sg_id))
594 log.debug("Adding ICMP rules in security group '%s'..."
596 if not create_secgroup_rule(neutron_client, sg_id,
598 log.error("Failed to create the security group rule...")
601 log.debug("Adding SSH rules in security group '%s'..."
603 if not create_secgroup_rule(
604 neutron_client, sg_id, 'ingress', 'tcp', '22', '22'):
605 log.error("Failed to create the security group rule...")
608 if not create_secgroup_rule(
609 neutron_client, sg_id, 'egress', 'tcp', '22', '22'):
610 log.error("Failed to create the security group rule...")
615 # *********************************************
617 # *********************************************
618 def get_image_id(glance_client, image_name): # pragma: no cover
619 images = glance_client.images.list()
620 return next((i.id for i in images if i.name == image_name), None)
623 def create_image(glance_client, image_name, file_path, disk_format,
624 container_format, min_disk, min_ram, protected, tag,
625 public, **kwargs): # pragma: no cover
626 if not os.path.isfile(file_path):
627 log.error("Error: file %s does not exist." % file_path)
630 image_id = get_image_id(glance_client, image_name)
631 if image_id is not None:
632 log.info("Image %s already exists." % image_name)
634 log.info("Creating image '%s' from '%s'...", image_name, file_path)
636 image = glance_client.images.create(name=image_name,
638 disk_format=disk_format,
639 container_format=container_format,
646 with open(file_path) as image_data:
647 glance_client.images.upload(image_id, image_data)
650 log.error("Error [create_glance_image(glance_client, '%s', '%s', '%s')]",
651 image_name, file_path, public)
655 def delete_image(glance_client, image_id): # pragma: no cover
657 glance_client.images.delete(image_id)
660 log.exception("Error [delete_flavor(glance_client, %s)]", image_id)
666 # *********************************************
668 # *********************************************
669 def get_volume_id(volume_name): # pragma: no cover
670 volumes = get_cinder_client().volumes.list()
671 return next((v.id for v in volumes if v.name == volume_name), None)
674 def create_volume(cinder_client, volume_name, volume_size,
675 volume_image=False): # pragma: no cover
678 volume = cinder_client.volumes.create(name=volume_name,
680 imageRef=volume_image)
682 volume = cinder_client.volumes.create(name=volume_name,
686 log.exception("Error [create_volume(cinder_client, %s)]",
687 (volume_name, volume_size))
691 def delete_volume(cinder_client, volume_id, forced=False): # pragma: no cover
695 cinder_client.volumes.detach(volume_id)
697 log.error(sys.exc_info()[0])
698 cinder_client.volumes.force_delete(volume_id)
701 volume = get_cinder_client().volumes.get(volume_id)
702 if volume.status.lower() == 'available':
704 cinder_client.volumes.delete(volume_id)
707 log.exception("Error [delete_volume(cinder_client, '%s')]" % volume_id)
711 def detach_volume(server_id, volume_id): # pragma: no cover
713 get_nova_client().volumes.delete_server_volume(server_id, volume_id)
716 log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]",
717 server_id, volume_id)