Add flavor discovery
Added methods to discover the id of the smallest flavor. It's no longer required to set flavor_ref_alt and flavor_ref in the conf.json Change-Id: I65a9eba32ea1b2d10d1c836cad9efc0a4cf2115a Partially-implements: blueprint discover-cloud-artifacts
This commit is contained in:
parent
3454d2acfd
commit
d13ae394c2
|
@ -11,9 +11,7 @@
|
|||
"compute":
|
||||
{
|
||||
"image_ref": "a8d70acb-f1c4-4171-b0ce-d73e5de21a9d",
|
||||
"image_ref_alt": "6182b1da-e64d-4440-b0ef-c0afa4d77abb",
|
||||
"flavor_ref": "1",
|
||||
"flavor_ref_alt": "1"
|
||||
"image_ref_alt": "6182b1da-e64d-4440-b0ef-c0afa4d77abb"
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ import argparse
|
|||
import ConfigParser
|
||||
import fnmatch
|
||||
import json
|
||||
import keystoneclient.v2_0.client as ksclient
|
||||
import logging
|
||||
from operator import itemgetter
|
||||
import os
|
||||
import requests
|
||||
import subprocess
|
||||
|
@ -73,13 +75,13 @@ class Test:
|
|||
self.sample_conf_parser.read(self.sample_conf_file)
|
||||
|
||||
def gen_config(self):
|
||||
'''Merge mini config, extra config, tempest.conf.sample
|
||||
and write to tempest.config.
|
||||
'''Merge mini config, extra config, and discovered config
|
||||
to tempest.conf.sample and write to tempest.config.
|
||||
'''
|
||||
self.logger.info('Generating tempest.config')
|
||||
self.merge_to_sample_conf(self.mini_conf_dict)
|
||||
self.merge_to_sample_conf(self.extra_conf_dict)
|
||||
# discovered config will not overwrite the value in the
|
||||
# discovery will not discover the user provided values in the
|
||||
# mini_conf_dict and extra_conf_dict
|
||||
discovered_conf_dict = self._build_discovered_dict_conf()
|
||||
self.merge_to_sample_conf(discovered_conf_dict)
|
||||
|
@ -212,11 +214,13 @@ class Test:
|
|||
{"identity": {"region": self.get_identity_region,
|
||||
"admin_tenant_name": self.get_admin_tenant_name,
|
||||
"tenant_name": self.get_tenant_name,
|
||||
"alt_tenant_name": self.get_alt_tenant_name}
|
||||
"alt_tenant_name": self.get_alt_tenant_name},
|
||||
"compute": {"flavor_ref": self.find_smallest_flavor,
|
||||
"flavor_ref_alt": self.find_smallest_flavor}
|
||||
}
|
||||
|
||||
# Remove the configs from the default discovery
|
||||
# for those that caller choose to overwrite.
|
||||
# for those that caller has provided.
|
||||
self._subtract_dictionaries(discovery_conf_dict, self.mini_conf_dict)
|
||||
self._subtract_dictionaries(discovery_conf_dict, self.extra_conf_dict)
|
||||
|
||||
|
@ -228,58 +232,18 @@ class Test:
|
|||
self.logger.info("Discovered configs: %s" % discovery_conf_dict)
|
||||
return discovery_conf_dict
|
||||
|
||||
def get_keystone_token(self, url, user, password, tenant=""):
|
||||
''' Returns the json response from keystone tokens API call.'''
|
||||
parameter = {"auth": {"tenantName": tenant,
|
||||
"passwordCredentials":
|
||||
{"username": user,
|
||||
"password": password}
|
||||
}
|
||||
}
|
||||
header = {"Content-type": "application/json"}
|
||||
try:
|
||||
req = requests.post(url, data=json.dumps(parameter),
|
||||
headers=header)
|
||||
if req.status_code is not requests.codes.ok:
|
||||
req.raise_for_status()
|
||||
except:
|
||||
self.logger.critical("Failed to get a Keystone token. "
|
||||
"Please verify your keystone endpoint url,"
|
||||
"username or password.\n"
|
||||
"url: \"%s\"\nheader: %s\nparameter: %s\n"
|
||||
"response %s" %
|
||||
(url, header, parameter, req.content))
|
||||
raise
|
||||
|
||||
return req.content
|
||||
|
||||
def get_tenants(self, token_id):
|
||||
'''Return a list of tenants of a token_id.'''
|
||||
keystone_url = self.sample_conf_parser.get("identity", "uri")
|
||||
headers = {"Content-type": "application/json",
|
||||
"X-Auth-Token": token_id}
|
||||
try:
|
||||
req = requests.get(keystone_url + "/tenants", headers=headers)
|
||||
except:
|
||||
self.logger.critical("failed to get tenant for token id %s"
|
||||
"from %s" % (token_id, keystone_url))
|
||||
raise
|
||||
return json.loads(req.content)["tenants"]
|
||||
|
||||
def get_alt_tenant_name(self):
|
||||
'''Return the alt_tenant_name
|
||||
'''
|
||||
keystone_url = self.sample_conf_parser.get("identity", "uri")
|
||||
alt_user = self.sample_conf_parser.get("identity", "alt_username")
|
||||
alt_pw = self.sample_conf_parser.get("identity", "alt_password")
|
||||
token_id = json.loads(self.get_keystone_token(url=keystone_url +
|
||||
"/tokens",
|
||||
user=alt_user,
|
||||
password=alt_pw)
|
||||
)["access"]["token"]["id"]
|
||||
'''TODO: Assuming the user only belongs to one tenant'''
|
||||
keystone = ksclient.Client(auth_url=keystone_url, username=alt_user,
|
||||
password=alt_pw)
|
||||
|
||||
try:
|
||||
alt_tenant = self.get_tenants(token_id)[0]["name"]
|
||||
# In case multiple tenants are found return the fist one.
|
||||
alt_tenant = keystone.tenants.list()[0].name
|
||||
except:
|
||||
self.logger.critical("failed to get the tenant for alt_username %s"
|
||||
"from %s" % (alt_user, keystone_url))
|
||||
|
@ -292,34 +256,26 @@ class Test:
|
|||
keystone_url = self.sample_conf_parser.get("identity", "uri")
|
||||
user = self.sample_conf_parser.get("identity", "username")
|
||||
pw = self.sample_conf_parser.get("identity", "password")
|
||||
token_id = json.loads(self.get_keystone_token(url=keystone_url +
|
||||
"/tokens",
|
||||
user=user,
|
||||
password=pw)
|
||||
)["access"]["token"]["id"]
|
||||
'''TODO: Assuming the user only belongs to one tenant'''
|
||||
keystone = ksclient.Client(auth_url=keystone_url, username=user,
|
||||
password=pw)
|
||||
try:
|
||||
tenant = self.get_tenants(token_id)[0]["name"]
|
||||
# In case multiple tenants are found return the fist one.
|
||||
tenant_name = keystone.tenants.list()[0].name
|
||||
except:
|
||||
self.logger.critical("failed to get the tenant for username %s"
|
||||
"from %s" % (user, keystone_url))
|
||||
raise
|
||||
return tenant
|
||||
return tenant_name
|
||||
|
||||
def get_admin_tenant_name(self):
|
||||
'''
|
||||
Return the admin_tenant_name.
|
||||
TODO: save admin tenant as an attribute so get_identity_region()
|
||||
method can directly use it.
|
||||
'''
|
||||
keystone_url = self.sample_conf_parser.get("identity", "uri")
|
||||
admin_user = self.sample_conf_parser.get("identity", "admin_username")
|
||||
admin_pw = self.sample_conf_parser.get("identity", "admin_password")
|
||||
token_id = json.loads(self.get_keystone_token(url=keystone_url +
|
||||
"/tokens",
|
||||
user=admin_user,
|
||||
password=admin_pw)
|
||||
)["access"]["token"]["id"]
|
||||
keystone = ksclient.Client(auth_url=keystone_url, username=admin_user,
|
||||
password=admin_pw)
|
||||
|
||||
'''TODO: Authenticate as "admin" (public URL) against each tenant found
|
||||
in tanantList until a tenant is found on which "admin" has
|
||||
|
@ -328,13 +284,14 @@ class Test:
|
|||
tempest.sample.conf.identiy.admin_role
|
||||
'''
|
||||
try:
|
||||
tenant = self.get_tenants(token_id)[0]["name"]
|
||||
# In case multiple tenants are found return the fist one.
|
||||
admin_tenant_name = keystone.tenants.list()[0].name
|
||||
except:
|
||||
self.logger.critical("failed to get the tenant for"
|
||||
"admin_username %s from %s" %
|
||||
(admin_user, keystone_url))
|
||||
raise
|
||||
return tenant
|
||||
return admin_tenant_name
|
||||
|
||||
def get_identity_region(self):
|
||||
'''Return the identity region.
|
||||
|
@ -343,20 +300,14 @@ class Test:
|
|||
admin_user = self.sample_conf_parser.get("identity", "admin_username")
|
||||
admin_pw = self.sample_conf_parser.get("identity", "admin_password")
|
||||
admin_tenant = self.get_admin_tenant_name()
|
||||
'''
|
||||
TODO: Preserve the admin token id as an attribute because
|
||||
the admin_token will be used to for image discovery
|
||||
'''
|
||||
admin_token = json.loads(self.get_keystone_token
|
||||
(url=keystone_url + "/tokens",
|
||||
user=admin_user,
|
||||
password=admin_pw,
|
||||
tenant=admin_tenant))
|
||||
'''TODO: assume there is only one identity endpoint'''
|
||||
keystone = ksclient.Client(auth_url=keystone_url, username=admin_user,
|
||||
password=admin_pw, tenant_name=admin_tenant)
|
||||
|
||||
'''Find the region that has the user provided endpoint uri'''
|
||||
identity_region =\
|
||||
[service["endpoints"][0]["region"]
|
||||
for service in admin_token["access"]["serviceCatalog"]
|
||||
if service["type"] == "identity"][0]
|
||||
[ep["region"] for ep in keystone.service_catalog.
|
||||
get_endpoints(service_type="identity")["identity"]
|
||||
if keystone_url in ep.values()][0]
|
||||
|
||||
return identity_region
|
||||
|
||||
|
@ -368,9 +319,36 @@ class Test:
|
|||
pass
|
||||
|
||||
def find_smallest_flavor(self):
|
||||
'''Find the smallest flavor by sorting by memory size.
|
||||
'''Return the id of the smallest flavor sorted by
|
||||
Ram, Disk, and then vcpus.
|
||||
'''
|
||||
pass
|
||||
keystone_url = self.sample_conf_parser.get("identity", "uri")
|
||||
admin_user = self.sample_conf_parser.get("identity", "admin_username")
|
||||
admin_pw = self.sample_conf_parser.get("identity", "admin_password")
|
||||
admin_tenant = self.get_admin_tenant_name()
|
||||
keystone = ksclient.Client(auth_url=keystone_url, username=admin_user,
|
||||
password=admin_pw, tenant_name=admin_tenant)
|
||||
|
||||
# In case of multiple compute endpoint, pick the first one
|
||||
compute_admin_url =\
|
||||
keystone.service_catalog.\
|
||||
get_endpoints(service_type="compute")["compute"][0]["adminURL"]
|
||||
token_id = keystone.auth_token
|
||||
headers = {"Content-type": "application/json",
|
||||
"X-Auth-Token": token_id}
|
||||
uri = "/flavors/detail?minRam=512&minDisk=1"
|
||||
try:
|
||||
resp = requests.get(compute_admin_url + uri,
|
||||
headers=headers, timeout=10)
|
||||
if resp.status_code is not requests.codes.ok:
|
||||
resp.raise_for_status()
|
||||
except:
|
||||
self.logger.critical("Failed to find smallest flavor. "
|
||||
"url: \"%s\"\n" %
|
||||
(compute_admin_url + uri))
|
||||
flavors_dict = json.loads(resp.content)["flavors"]
|
||||
return sorted(flavors_dict,
|
||||
key=itemgetter('ram', 'disk', 'vcpus'))[0]["id"]
|
||||
|
||||
def delete_image(self):
|
||||
'''Delete a image.
|
||||
|
@ -379,9 +357,9 @@ class Test:
|
|||
|
||||
if __name__ == '__main__':
|
||||
''' Generate tempest.conf from a tempest.conf.sample and then run test.'''
|
||||
parser = argparse.ArgumentParser(description='Starts a tempest test',
|
||||
formatter_class=argparse.
|
||||
ArgumentDefaultsHelpFormatter)
|
||||
parser = argparse.ArgumentParser(description='A wrapper for executing '
|
||||
'tempest test.',
|
||||
formatter_class=argparse.HelpFormatter)
|
||||
conflict_group = parser.add_mutually_exclusive_group()
|
||||
|
||||
conflict_group.add_argument("--callback",
|
||||
|
|
Loading…
Reference in New Issue