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:
ted chang 2014-04-03 22:49:26 -07:00
parent 3454d2acfd
commit d13ae394c2
2 changed files with 64 additions and 88 deletions

View File

@ -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"
}
},

View File

@ -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",