Merge "Using flavors from Roles"
This commit is contained in:
commit
03da0b39df
@ -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))
|
||||
|
@ -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.")
|
||||
|
@ -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'}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user