Merge "Update cirros image to latest version"
[functest.git] / functest / utils / openstack_utils.py
index 6f0c5fb..3093cb5 100755 (executable)
 
 import os
 import os.path
-import subprocess
+import re
 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
@@ -37,134 +42,203 @@ class MissingEnvVar(Exception):
         return str.format("Please set the mandatory env var: {}", self.var)
 
 
+def is_keystone_v3():
+    keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION')
+    if (keystone_api_version is None or
+            keystone_api_version == '2'):
+        return False
+    else:
+        return True
+
+
+def get_rc_env_vars():
+    env_vars = ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD']
+    if is_keystone_v3():
+        env_vars.extend(['OS_PROJECT_NAME',
+                         'OS_USER_DOMAIN_NAME',
+                         'OS_PROJECT_DOMAIN_NAME'])
+    else:
+        env_vars.extend(['OS_TENANT_NAME'])
+    return env_vars
+
+
 def check_credentials():
     """
     Check if the OpenStack credentials (openrc) are sourced
     """
-    env_vars = ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD', 'OS_TENANT_NAME']
+    env_vars = get_rc_env_vars()
     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_env_cred_dict():
+    env_cred_dict = {
+        'OS_USERNAME': 'username',
+        'OS_PASSWORD': 'password',
+        'OS_AUTH_URL': 'auth_url',
+        'OS_TENANT_NAME': 'tenant_name',
+        'OS_USER_DOMAIN_NAME': 'user_domain_name',
+        'OS_PROJECT_DOMAIN_NAME': 'project_domain_name',
+        'OS_PROJECT_NAME': 'project_name',
+        'OS_ENDPOINT_TYPE': 'endpoint_type',
+        'OS_REGION_NAME': 'region_name'
+    }
+    return env_cred_dict
+
+
+def get_credentials(other_creds={}):
+    """Returns a creds dictionary filled with parsed from env
     """
     creds = {}
+    env_vars = get_rc_env_vars()
+    env_cred_dict = get_env_cred_dict()
 
-    # Check that the env vars exists:
-    envvars = ('OS_USERNAME', 'OS_PASSWORD', 'OS_AUTH_URL', 'OS_TENANT_NAME')
-    for envvar in envvars:
+    for envvar in env_vars:
         if os.getenv(envvar) is None:
             raise MissingEnvVar(envvar)
+        else:
+            creds_key = env_cred_dict.get(envvar)
+            creds.update({creds_key: os.getenv(envvar)})
+
+    if 'tenant' in other_creds.keys():
+        if is_keystone_v3():
+            tenant = 'project_name'
+        else:
+            tenant = 'tenant_name'
+        other_creds[tenant] = other_creds.pop('tenant')
+
+    creds.update(other_creds)
 
-    # 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"),
-        "auth_url": os.environ.get("OS_AUTH_URL"),
-        tenant: os.environ.get("OS_TENANT_NAME")
-    })
-    if os.getenv('OS_ENDPOINT_TYPE') is not None:
-        creds.update({
-            "endpoint_type": os.environ.get("OS_ENDPOINT_TYPE")
-        })
-    if os.getenv('OS_REGION_NAME') is not None:
-        creds.update({
-            "region_name": os.environ.get("OS_REGION_NAME")
-        })
-    cacert = os.environ.get("OS_CACERT")
-    if cacert is not None:
-        # each openstack client uses differnt kwargs for this
-        creds.update({"cacert": cacert,
-                      "ca_cert": cacert,
-                      "https_ca_cert": cacert,
-                      "https_cacert": cacert,
-                      "ca_file": cacert})
-        creds.update({"insecure": "True", "https_insecure": "True"})
-        if not os.path.isfile(cacert):
-            logger.info("WARNING: The 'OS_CACERT' environment variable is "
-                        "set to %s but the file does not exist." % cacert)
     return creds
 
 
 def source_credentials(rc_file):
-    pipe = subprocess.Popen(". %s; env" % rc_file, stdout=subprocess.PIPE,
-                            shell=True)
-    output = pipe.communicate()[0]
-    env = dict((line.split("=", 1) for line in output.splitlines()))
-    os.environ.update(env)
-    return env
+    with open(rc_file, "r") as f:
+        for line in f:
+            var = line.rstrip('"\n').replace('export ', '').split("=")
+            # The two next lines should be modified as soon as rc_file
+            # conforms with common rules. Be aware that it could induce
+            # issues if value starts with '
+            key = re.sub(r'^["\' ]*|[ \'"]*$', '', var[0])
+            value = re.sub(r'^["\' ]*|[ \'"]*$', '', "".join(var[1:]))
+            os.environ[key] = value
 
 
 def get_credentials_for_rally():
-    creds = get_credentials("keystone")
-    admin_keys = ['username', 'tenant_name', 'password']
-    endpoint_types = [('internalURL', 'internal'),
-                      ('publicURL', 'public'), ('adminURL', 'admin')]
-    if 'endpoint_type' in creds.keys():
-        for k, v in endpoint_types:
-            if creds['endpoint_type'] == k:
-                creds['endpoint_type'] = v
+    creds = get_credentials()
+    env_cred_dict = get_env_cred_dict()
     rally_conf = {"type": "ExistingCloud", "admin": {}}
     for key in creds:
-        if key in admin_keys:
-            rally_conf['admin'][key] = creds[key]
-        else:
+        if key == 'auth_url':
             rally_conf[key] = creds[key]
+        else:
+            rally_conf['admin'][key] = creds[key]
+
+    endpoint_types = [('internalURL', 'internal'),
+                      ('publicURL', 'public'), ('adminURL', 'admin')]
+
+    endpoint_type = os.getenv('OS_ENDPOINT_TYPE')
+    if endpoint_type is not None:
+        cred_key = env_cred_dict.get('OS_ENDPOINT_TYPE')
+        for k, v in endpoint_types:
+            if endpoint_type == k:
+                rally_conf[cred_key] = v
+
+    region_name = os.getenv('OS_REGION_NAME')
+    if region_name is not None:
+        cred_key = env_cred_dict.get('OS_REGION_NAME')
+        rally_conf[cred_key] = region_name
     return rally_conf
 
 
+def get_session_auth(other_creds={}):
+    loader = loading.get_plugin_loader('password')
+    creds = get_credentials(other_creds)
+    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(other_creds={}):
+    auth = get_session_auth(other_creds)
+    return session.Session(auth=auth)
+
+
 # *********************************************
 #   CLIENTS
 # *********************************************
-def get_keystone_client():
-    creds_keystone = get_credentials("keystone")
-    return keystoneclient.Client(**creds_keystone)
+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(other_creds={}):
+    sess = get_session(other_creds)
+    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(other_creds={}):
+    sess = get_session(other_creds)
+    return novaclient.Client(get_nova_client_version(), session=sess)
 
-def get_nova_client():
-    creds_nova = get_credentials("nova")
-    return novaclient.Client('2', **creds_nova)
 
+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)
 
+def get_cinder_client(other_creds={}):
+    sess = get_session(other_creds)
+    return cinderclient.Client(get_cinder_client_version(), session=sess)
 
-def get_neutron_client():
-    creds_neutron = get_credentials("neutron")
-    return neutronclient.Client(**creds_neutron)
 
+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_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)
+
+def get_neutron_client(other_creds={}):
+    sess = get_session(other_creds)
+    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(other_creds={}):
+    sess = get_session(other_creds)
+    return glanceclient.Client(get_glance_client_version(), session=sess)
 
 
 # *********************************************
@@ -446,13 +520,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
 
 
@@ -1043,38 +1117,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
 
 
@@ -1191,7 +1256,10 @@ def delete_volume_type(cinder_client, volume_type):
 # *********************************************
 def get_tenants(keystone_client):
     try:
-        tenants = keystone_client.tenants.list()
+        if is_keystone_v3():
+            tenants = keystone_client.projects.list()
+        else:
+            tenants = keystone_client.tenants.list()
         return tenants
     except Exception, e:
         logger.error("Error [get_tenants(keystone_client)]: %s" % e)
@@ -1208,7 +1276,7 @@ def get_users(keystone_client):
 
 
 def get_tenant_id(keystone_client, tenant_name):
-    tenants = keystone_client.tenants.list()
+    tenants = get_tenants(keystone_client)
     id = ''
     for t in tenants:
         if t.name == tenant_name:
@@ -1218,7 +1286,7 @@ def get_tenant_id(keystone_client, tenant_name):
 
 
 def get_user_id(keystone_client, user_name):
-    users = keystone_client.users.list()
+    users = get_users(keystone_client)
     id = ''
     for u in users:
         if u.name == user_name:
@@ -1239,9 +1307,16 @@ def get_role_id(keystone_client, role_name):
 
 def create_tenant(keystone_client, tenant_name, tenant_description):
     try:
-        tenant = keystone_client.tenants.create(tenant_name,
-                                                tenant_description,
-                                                enabled=True)
+        if is_keystone_v3():
+            tenant = keystone_client.projects.create(
+                name=tenant_name,
+                description=tenant_description,
+                domain="default",
+                enabled=True)
+        else:
+            tenant = keystone_client.tenants.create(tenant_name,
+                                                    tenant_description,
+                                                    enabled=True)
         return tenant.id
     except Exception, e:
         logger.error("Error [create_tenant(keystone_client, '%s', '%s')]: %s"
@@ -1252,9 +1327,18 @@ def create_tenant(keystone_client, tenant_name, tenant_description):
 def create_user(keystone_client, user_name, user_password,
                 user_email, tenant_id):
     try:
-        user = keystone_client.users.create(user_name, user_password,
-                                            user_email, tenant_id,
-                                            enabled=True)
+        if is_keystone_v3():
+            user = keystone_client.users.create(name=user_name,
+                                                password=user_password,
+                                                email=user_email,
+                                                project_id=tenant_id,
+                                                enabled=True)
+        else:
+            user = keystone_client.users.create(user_name,
+                                                user_password,
+                                                user_email,
+                                                tenant_id,
+                                                enabled=True)
         return user.id
     except Exception, e:
         logger.error("Error [create_user(keystone_client, '%s', '%s', '%s'"
@@ -1265,7 +1349,12 @@ def create_user(keystone_client, user_name, user_password,
 
 def add_role_user(keystone_client, user_id, role_id, tenant_id):
     try:
-        keystone_client.roles.add_user_role(user_id, role_id, tenant_id)
+        if is_keystone_v3():
+            keystone_client.roles.grant(role=role_id,
+                                        user=user_id,
+                                        project=tenant_id)
+        else:
+            keystone_client.roles.add_user_role(user_id, role_id, tenant_id)
         return True
     except Exception, e:
         logger.error("Error [add_role_user(keystone_client, '%s', '%s'"
@@ -1275,7 +1364,10 @@ def add_role_user(keystone_client, user_id, role_id, tenant_id):
 
 def delete_tenant(keystone_client, tenant_id):
     try:
-        keystone_client.tenants.delete(tenant_id)
+        if is_keystone_v3():
+            keystone_client.projects.delete(tenant_id)
+        else:
+            keystone_client.tenants.delete(tenant_id)
         return True
     except Exception, e:
         logger.error("Error [delete_tenant(keystone_client, '%s')]: %s"