Merge "Conform SFC test case to FeatureBase"
[functest.git] / functest / utils / openstack_utils.py
index df6fb5d..711a62f 100755 (executable)
@@ -14,16 +14,21 @@ import subprocess
 import sys
 import time
 
+from keystoneauth1 import loading
+from keystoneauth1 import session
 from cinderclient import client as cinderclient
-import functest.utils.functest_logger as ft_logger
-import functest.utils.functest_utils as ft_utils
 from glanceclient import client as glanceclient
-from keystoneclient.v2_0 import client as keystoneclient
-from neutronclient.v2_0 import client as neutronclient
 from novaclient import client as novaclient
+from keystoneclient import client as keystoneclient
+from neutronclient.neutron import client as neutronclient
+
+import functest.utils.functest_logger as ft_logger
+import functest.utils.functest_utils as ft_utils
 
 logger = ft_logger.Logger("openstack_utils").getLogger()
 
+DEFAULT_API_VERSION = '2'
+
 
 # *********************************************
 #   CREDENTIALS
@@ -45,40 +50,46 @@ def check_credentials():
     return all(map(lambda v: v in os.environ and os.environ[v], env_vars))
 
 
-def get_credentials(service):
-    """Returns a creds dictionary filled with the following keys:
-    * username
-    * password/api_key (depending on the service)
-    * tenant_name/project_id (depending on the service)
-    * auth_url
-    :param service: a string indicating the name of the service
-                    requesting the credentials.
+def get_credentials():
+    """Returns a creds dictionary filled with parsed from env
     """
     creds = {}
 
+    keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION')
+    if (keystone_api_version is None or
+            keystone_api_version == '2'):
+        keystone_v3 = False
+        tenant_env = 'OS_TENANT_NAME'
+        tenant = 'tenant_name'
+    else:
+        keystone_v3 = True
+        tenant_env = 'OS_PROJECT_NAME'
+        tenant = 'project_name'
+
     # Check that the env vars exists:
-    envvars = ('OS_USERNAME', 'OS_PASSWORD', 'OS_AUTH_URL', 'OS_TENANT_NAME')
+    envvars = ('OS_USERNAME', 'OS_PASSWORD', 'OS_AUTH_URL', tenant_env)
     for envvar in envvars:
         if os.getenv(envvar) is None:
             raise MissingEnvVar(envvar)
 
-    # Unfortunately, each of the OpenStack client will request slightly
-    # different entries in their credentials dict.
-    if service.lower() in ("nova", "cinder"):
-        password = "api_key"
-        tenant = "project_id"
-    else:
-        password = "password"
-        tenant = "tenant_name"
-
     # The most common way to pass these info to the script is to do it through
     # environment variables.
     creds.update({
         "username": os.environ.get("OS_USERNAME"),
-        password: os.environ.get("OS_PASSWORD"),
+        "password": os.environ.get("OS_PASSWORD"),
         "auth_url": os.environ.get("OS_AUTH_URL"),
-        tenant: os.environ.get("OS_TENANT_NAME")
+        tenant: os.environ.get(tenant_env)
     })
+    if keystone_v3:
+        if os.getenv('OS_USER_DOMAIN_NAME') is not None:
+            creds.update({
+                "user_domain_name": os.getenv('OS_USER_DOMAIN_NAME')
+            })
+        if os.getenv('OS_PROJECT_DOMAIN_NAME') is not None:
+            creds.update({
+                "project_domain_name": os.getenv('OS_PROJECT_DOMAIN_NAME')
+            })
+
     if os.getenv('OS_ENDPOINT_TYPE') is not None:
         creds.update({
             "endpoint_type": os.environ.get("OS_ENDPOINT_TYPE")
@@ -112,8 +123,15 @@ def source_credentials(rc_file):
 
 
 def get_credentials_for_rally():
-    creds = get_credentials("keystone")
-    admin_keys = ['username', 'tenant_name', 'password']
+    creds = get_credentials()
+    keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION')
+    if (keystone_api_version is None or
+            keystone_api_version == '2'):
+        admin_keys = ['username', 'tenant_name', 'password']
+    else:
+        admin_keys = ['username', 'password', 'user_domain_name',
+                      'project_name', 'project_domain_name']
+
     endpoint_types = [('internalURL', 'internal'),
                       ('publicURL', 'public'), ('adminURL', 'admin')]
     if 'endpoint_type' in creds.keys():
@@ -129,42 +147,95 @@ def get_credentials_for_rally():
     return rally_conf
 
 
+def get_session_auth():
+    loader = loading.get_plugin_loader('password')
+    creds = get_credentials()
+    auth = loader.load_from_options(**creds)
+    return auth
+
+
+def get_endpoint(service_type, endpoint_type='publicURL'):
+    auth = get_session_auth()
+    return get_session().get_endpoint(auth=auth,
+                                      service_type=service_type,
+                                      endpoint_type=endpoint_type)
+
+
+def get_session():
+    auth = get_session_auth()
+    return session.Session(auth=auth)
+
+
 # *********************************************
 #   CLIENTS
 # *********************************************
+def get_keystone_client_version():
+    api_version = os.getenv('OS_IDENTITY_API_VERSION')
+    if api_version is not None:
+        logger.info("OS_IDENTITY_API_VERSION is set in env as '%s'",
+                    api_version)
+        return api_version
+    return DEFAULT_API_VERSION
+
+
 def get_keystone_client():
-    creds_keystone = get_credentials("keystone")
-    return keystoneclient.Client(**creds_keystone)
+    sess = get_session()
+    return keystoneclient.Client(get_keystone_client_version(), session=sess)
+
+
+def get_nova_client_version():
+    api_version = os.getenv('OS_COMPUTE_API_VERSION')
+    if api_version is not None:
+        logger.info("OS_COMPUTE_API_VERSION is set in env as '%s'",
+                    api_version)
+        return api_version
+    return DEFAULT_API_VERSION
 
 
 def get_nova_client():
-    creds_nova = get_credentials("nova")
-    return novaclient.Client('2', **creds_nova)
+    sess = get_session()
+    return novaclient.Client(get_nova_client_version(), session=sess)
+
+
+def get_cinder_client_version():
+    api_version = os.getenv('OS_VOLUME_API_VERSION')
+    if api_version is not None:
+        logger.info("OS_VOLUME_API_VERSION is set in env as '%s'",
+                    api_version)
+        return api_version
+    return DEFAULT_API_VERSION
 
 
 def get_cinder_client():
-    creds_cinder = get_credentials("cinder")
-    creds_cinder.update({
-        "service_type": "volume"
-    })
-    return cinderclient.Client('2', **creds_cinder)
+    sess = get_session()
+    return cinderclient.Client(get_cinder_client_version(), session=sess)
+
+
+def get_neutron_client_version():
+    api_version = os.getenv('OS_NETWORK_API_VERSION')
+    if api_version is not None:
+        logger.info("OS_NETWORK_API_VERSION is set in env as '%s'",
+                    api_version)
+        return api_version
+    return DEFAULT_API_VERSION
 
 
 def get_neutron_client():
-    creds_neutron = get_credentials("neutron")
-    return neutronclient.Client(**creds_neutron)
+    sess = get_session()
+    return neutronclient.Client(get_neutron_client_version(), session=sess)
+
+
+def get_glance_client_version():
+    api_version = os.getenv('OS_IMAGE_API_VERSION')
+    if api_version is not None:
+        logger.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version)
+        return api_version
+    return DEFAULT_API_VERSION
 
 
 def get_glance_client():
-    keystone_client = get_keystone_client()
-    glance_endpoint_type = 'publicURL'
-    os_endpoint_type = os.getenv('OS_ENDPOINT_TYPE')
-    if os_endpoint_type is not None:
-        glance_endpoint_type = os_endpoint_type
-    glance_endpoint = keystone_client.service_catalog.url_for(
-        service_type='image', endpoint_type=glance_endpoint_type)
-    return glanceclient.Client(1, glance_endpoint,
-                               token=keystone_client.auth_token)
+    sess = get_session()
+    return glanceclient.Client(get_glance_client_version(), session=sess)
 
 
 # *********************************************
@@ -218,6 +289,45 @@ def get_flavor_id_by_ram_range(nova_client, min_ram, max_ram):
     return id
 
 
+def get_aggregates(nova_client):
+    try:
+        aggregates = nova_client.aggregates.list()
+        return aggregates
+    except Exception, e:
+        logger.error("Error [get_aggregates(nova_client)]: %s" % e)
+        return None
+
+
+def get_aggregate_id(nova_client, aggregate_name):
+    try:
+        aggregates = get_aggregates(nova_client)
+        _id = [ag.id for ag in aggregates if ag.name == aggregate_name][0]
+        return _id
+    except Exception, e:
+        logger.error("Error [get_aggregate_id(nova_client, %s)]:"
+                     " %s" % (aggregate_name, e))
+        return None
+
+
+def get_availability_zones(nova_client):
+    try:
+        availability_zones = nova_client.availability_zones.list()
+        return availability_zones
+    except Exception, e:
+        logger.error("Error [get_availability_zones(nova_client)]: %s" % e)
+        return None
+
+
+def get_availability_zone_names(nova_client):
+    try:
+        az_names = [az.zoneName for az in get_availability_zones(nova_client)]
+        return az_names
+    except Exception, e:
+        logger.error("Error [get_availability_zone_names(nova_client)]:"
+                     " %s" % e)
+        return None
+
+
 def create_flavor(nova_client, flavor_name, ram, disk, vcpus, public=True):
     try:
         flavor = nova_client.flavors.create(
@@ -281,6 +391,40 @@ def get_hypervisors(nova_client):
         return None
 
 
+def create_aggregate(nova_client, aggregate_name, av_zone):
+    try:
+        nova_client.aggregates.create(aggregate_name, av_zone)
+        return True
+    except Exception, e:
+        logger.error("Error [create_aggregate(nova_client, %s, %s)]: %s"
+                     % (aggregate_name, av_zone, e))
+        return None
+
+
+def add_host_to_aggregate(nova_client, aggregate_name, compute_host):
+    try:
+        aggregate_id = get_aggregate_id(nova_client, aggregate_name)
+        nova_client.aggregates.add_host(aggregate_id, compute_host)
+        return True
+    except Exception, e:
+        logger.error("Error [add_host_to_aggregate(nova_client, %s, %s)]: %s"
+                     % (aggregate_name, compute_host, e))
+        return None
+
+
+def create_aggregate_with_host(
+        nova_client, aggregate_name, av_zone, compute_host):
+    try:
+        create_aggregate(nova_client, aggregate_name, av_zone)
+        add_host_to_aggregate(nova_client, aggregate_name, compute_host)
+        return True
+    except Exception, e:
+        logger.error("Error [create_aggregate_with_host("
+                     "nova_client, %s, %s, %s)]: %s"
+                     % (aggregate_name, av_zone, compute_host, e))
+        return None
+
+
 def create_instance(flavor_name,
                     image_id,
                     network_id,
@@ -373,13 +517,13 @@ def create_floating_ip(neutron_client):
     return {'fip_addr': fip_addr, 'fip_id': fip_id}
 
 
-def add_floating_ip(nova_client, server_id, floatingip_id):
+def add_floating_ip(nova_client, server_id, floatingip_addr):
     try:
-        nova_client.servers.add_floating_ip(server_id, floatingip_id)
+        nova_client.servers.add_floating_ip(server_id, floatingip_addr)
         return True
     except Exception, e:
         logger.error("Error [add_floating_ip(nova_client, '%s', '%s')]: %s"
-                     % (server_id, floatingip_id, e))
+                     % (server_id, floatingip_addr, e))
         return False
 
 
@@ -403,6 +547,36 @@ def delete_floating_ip(nova_client, floatingip_id):
         return False
 
 
+def remove_host_from_aggregate(nova_client, aggregate_name, compute_host):
+    try:
+        aggregate_id = get_aggregate_id(nova_client, aggregate_name)
+        nova_client.aggregates.remove_host(aggregate_id, compute_host)
+        return True
+    except Exception, e:
+        logger.error("Error [remove_host_from_aggregate(nova_client, %s, %s)]:"
+                     " %s" % (aggregate_name, compute_host, e))
+        return False
+
+
+def remove_hosts_from_aggregate(nova_client, aggregate_name):
+    aggregate_id = get_aggregate_id(nova_client, aggregate_name)
+    hosts = nova_client.aggregates.get(aggregate_id).hosts
+    assert(
+        all(remove_host_from_aggregate(nova_client, aggregate_name, host)
+            for host in hosts))
+
+
+def delete_aggregate(nova_client, aggregate_name):
+    try:
+        remove_hosts_from_aggregate(nova_client, aggregate_name)
+        nova_client.aggregates.delete(aggregate_name)
+        return True
+    except Exception, e:
+        logger.error("Error [delete_aggregate(nova_client, %s)]: %s"
+                     % (aggregate_name, e))
+        return False
+
+
 # *********************************************
 #   NEUTRON
 # *********************************************
@@ -940,38 +1114,29 @@ def get_image_id(glance_client, image_name):
 
 
 def create_glance_image(glance_client, image_name, file_path, disk="qcow2",
-                        container="bare", public=True):
+                        container="bare", public="public"):
     if not os.path.isfile(file_path):
         logger.error("Error: file %s does not exist." % file_path)
         return None
     try:
         image_id = get_image_id(glance_client, image_name)
         if image_id != '':
-            if logger:
-                logger.info("Image %s already exists." % image_name)
+            logger.info("Image %s already exists." % image_name)
         else:
-            if logger:
-                logger.info("Creating image '%s' from '%s'..." % (image_name,
-                                                                  file_path))
-            try:
-                properties = ft_utils.get_functest_config(
-                    'general.image_properties')
-            except ValueError:
-                # image properties are not configured
-                # therefore don't add any properties
-                properties = {}
-            with open(file_path) as fimage:
-                image = glance_client.images.create(name=image_name,
-                                                    is_public=public,
-                                                    disk_format=disk,
-                                                    container_format=container,
-                                                    properties=properties,
-                                                    data=fimage)
+            logger.info("Creating image '%s' from '%s'..." % (image_name,
+                                                              file_path))
+
+            image = glance_client.images.create(name=image_name,
+                                                visibility=public,
+                                                disk_format=disk,
+                                                container_format=container)
             image_id = image.id
+            with open(file_path) as image_data:
+                glance_client.images.upload(image_id, image_data)
         return image_id
     except Exception, e:
         logger.error("Error [create_glance_image(glance_client, '%s', '%s', "
-                     "'%s')]: %s" % (image_name, file_path, str(public), e))
+                     "'%s')]: %s" % (image_name, file_path, public, e))
         return None