Merge "Using flavors from Roles"

This commit is contained in:
Jenkins 2014-03-13 11:09:03 +00:00 committed by Gerrit Code Review
commit 03da0b39df
4 changed files with 114 additions and 27 deletions

View File

@ -30,11 +30,12 @@ LOG = logging.getLogger(__name__)
POC_PARAMS = {'controller': 1, 'compute': 2}
def parse_counts(counts, overcloud_roles):
def parse_counts_and_flavors(counts, overcloud_roles):
"""Helper for parsing the OvercloudRoleCount object
Given a list of OvercloudRoleCount objects return a dict of
(image_name, count) in a format used for building a template.
Given a list of OvercloudRoleCount and dict of OverlcoudRole objects
return a dict of (image_name, count) and (image_name, flavor_id) in a
format used for building a template.
:param counts: List of tuskar.api.controllers.v1.models.OvercloudRoleCount
:type counts: list
@ -43,16 +44,19 @@ def parse_counts(counts, overcloud_roles):
we can access image_name and flavor_id of roles
:type overcloud_roles: dict
:return: Dict of (image_name, count)
:rtype: dict
:return: Tuple of dicts {(image_name, count)}, {(image_name, flavor_id)}
:rtype: two dict objects
"""
parsed_counts = {}
parsed_flavors = {}
for count_obj in counts:
image_name = overcloud_roles[count_obj.overcloud_role_id].image_name
flavor_id = overcloud_roles[count_obj.overcloud_role_id].flavor_id
count = count_obj.num_nodes
parsed_counts[image_name] = count
parsed_flavors[image_name] = flavor_id
return parsed_counts
return parsed_counts, parsed_flavors
def filter_template_attributes(allowed_data, attributes):
@ -86,6 +90,30 @@ def get_overcloud_roles_dict():
pecan.request.dbapi.get_overcloud_roles())
def get_flavor_attributes(parsed_flavors):
"""Helper for building dict of flavor attributes
Given a dict of parsed flavors, it will put a flavor_ids stored in
role into attributes that will be fed to heat stack create/update.
Mapping of image name to flavor_param is stored in template_tools.ROLES.
:param parsed_flavors: Dict of (image_name, flavor_id)
:type parsed_flavors: dict
:return: Dict of (flavor_param, flavor_id) for Heat Template params
:rtype: dict
"""
flavor_attributes = {}
for image_name, flavor_id in parsed_flavors.items():
role = template_tools.ROLES.get(image_name, None)
if role:
flavor_param = role['flavor_param']
flavor_attributes[flavor_param] = flavor_id
return flavor_attributes
def process_stack(attributes, counts, overcloud_roles, create=False):
"""Helper function for processing the stack.
@ -106,35 +134,51 @@ def process_stack(attributes, counts, overcloud_roles, create=False):
:param create: A flag to designate if we are creating or updating the stack
:type create: bool
"""
heat_client = HeatClient()
try:
overcloud = template_tools.merge_templates(
parse_counts(counts, overcloud_roles))
# Get how many of each role we want and what flavor each role uses.
parsed_counts, parsed_flavors = parse_counts_and_flavors(
counts, overcloud_roles)
except Exception as e:
raise exception.ParseCountsAndFlavorsFailed(six.text_type(e))
try:
# Build the template
overcloud = template_tools.merge_templates(parsed_counts)
except Exception as e:
raise exception.HeatTemplateCreateFailed(six.text_type(e))
heat_client = HeatClient()
stack_exists = heat_client.exists_stack()
try:
# Get the parameters that the template accepts and validate
allowed_data = heat_client.validate_template(overcloud)
except Exception as e:
raise exception.HeatTemplateValidateFailed(six.text_type(e))
stack_exists = heat_client.exists_stack()
if stack_exists and create:
raise exception.StackAlreadyCreated()
elif not stack_exists and not create:
raise exception.StackNotFound()
try:
# Put flavors from OverloudRoles into attributes
attributes.update(get_flavor_attributes(parsed_flavors))
# Filter the attributes to allowed only
filtered_attributes = filter_template_attributes(allowed_data,
attributes)
except Exception as e:
raise exception.HeatStackProcessingAttributesFailed(six.text_type(e))
if create:
operation = heat_client.create_stack
else:
operation = heat_client.update_stack
try:
result = operation(
overcloud,
filter_template_attributes(allowed_data, attributes))
result = operation(overcloud, filtered_attributes)
except Exception as e:
if create:
raise exception.HeatStackCreateFailed(six.text_type(e))

View File

@ -114,6 +114,11 @@ class TuskarException(Exception):
return six.text_type(self)
class Invalid(TuskarException):
message = _("Invalid.")
code = 400
class NotAuthorized(TuskarException):
message = _("Not authorized.")
code = 403
@ -153,7 +158,7 @@ class OvercloudRoleExists(DuplicateEntry):
message = _("Overcloud role with name %(name)s already exists.")
class OvercloudRoleInUse(TuskarException):
class OvercloudRoleInUse(Invalid):
message = _('Role %(name)s is in use by an overcloud.')
@ -182,21 +187,29 @@ class StackAlreadyCreated(DuplicateEntry):
message = _("The Stack for this Overcloud already exists.")
class HeatTemplateCreateFailed(TuskarException):
class ParseCountsAndFlavorsFailed(DuplicateEntry):
message = _("Parsing of counts and flavors from roles failed.")
class HeatTemplateCreateFailed(Invalid):
message = _("The Heat template failed to create.")
class HeatTemplateValidateFailed(TuskarException):
class HeatTemplateValidateFailed(Invalid):
message = _("Validation of the Heat template failed.")
class HeatStackUpdateFailed(TuskarException):
class HeatStackProcessingAttributesFailed(Invalid):
message = _("Processing of Heat stack attributes failed")
class HeatStackUpdateFailed(Invalid):
message = _("The Heat stack failed to update.")
class HeatStackCreateFailed(TuskarException):
class HeatStackCreateFailed(Invalid):
message = _("The Heat stack failed to update.")
class HeatStackDeleteFailed(TuskarException):
class HeatStackDeleteFailed(Invalid):
message = _("The Heat stack failed to delete.")

View File

@ -22,13 +22,21 @@ from oslo.config import cfg
from tripleo_heat_merge import merge
# The name of the compute Overcloud role - defined for special case handling
# TODO(lsmola) For now static definition of roles for Icehouse
# we will need to load these associations from somewhere.
OVERCLOUD_CONTROL_ROLE = 'overcloud-control'
OVERCLOUD_COMPUTE_ROLE = 'overcloud-compute'
OVERCLOUD_VOLUME_ROLE = 'overcloud-cinder-volume'
ROLES = {}
ROLES[OVERCLOUD_CONTROL_ROLE] = {'template_param': 'Control',
'flavor_param': 'OvercloudControlFlavor',
'file_name': None}
ROLES[OVERCLOUD_COMPUTE_ROLE] = {'template_param': 'NovaCompute',
'flavor_param': 'OvercloudComputeFlavor',
'file_name': 'overcloud-source.yaml'}
ROLES[OVERCLOUD_VOLUME_ROLE] = {'template_param': 'BlockStorage',
'flavor_param': 'OvercloudBlockStorageFlavor',
'file_name': 'block-storage.yaml'}

View File

@ -74,10 +74,10 @@ class OvercloudTests(base.TestCase):
def test_parse_counts_overcloud_roles_explicit(self):
# Setup
overcloud_role_1 = db_models.OvercloudRole(
image_name='overcloud-compute')
image_name='overcloud-compute', flavor_id='1')
overcloud_role_2 = db_models.OvercloudRole(
image_name='overcloud-block-storage')
image_name='overcloud-cinder-volume', flavor_id='1')
mock_overcloud_roles = {1: overcloud_role_1, 2: overcloud_role_2}
@ -90,12 +90,34 @@ class OvercloudTests(base.TestCase):
mock_counts = [overcloud_role_count_1, overcloud_role_count_2]
# Test
result = overcloud.parse_counts(mock_counts,
overcloud_roles=mock_overcloud_roles)
result = overcloud.parse_counts_and_flavors(
mock_counts,
overcloud_roles=mock_overcloud_roles)
# Verify
self.assertEqual(result, {'overcloud-compute': 5,
'overcloud-block-storage': 9})
self.assertEqual(result,
({'overcloud-compute': 5,
'overcloud-cinder-volume': 9},
{'overcloud-compute': '1',
'overcloud-cinder-volume': '1'}))
def test_get_flavor_attributes(self):
# Setup
parsed_flavors = {'fake': '5',
'overcloud-control': '1',
'overcloud-compute': '2',
'overcloud-cinder-volume': '3'}
# Test
result = overcloud.get_flavor_attributes(parsed_flavors)
# Verify
# The flavors names are stored in template_tools, so this also tests
# it hasn't been changed. This flavor names must match what is int
# the template.
self.assertEqual(result, {'OvercloudControlFlavor': '1',
'OvercloudComputeFlavor': '2',
'OvercloudBlockStorageFlavor': '3'})
def test_filter_template_attributes(self):
# Setup