diff --git a/tuskar/api/controllers/v1/overcloud.py b/tuskar/api/controllers/v1/overcloud.py index a3c301a5..45c3b527 100644 --- a/tuskar/api/controllers/v1/overcloud.py +++ b/tuskar/api/controllers/v1/overcloud.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) POC_PARAMS = {'controller': 1, 'compute': 2} -def parse_counts(counts): +def parse_counts(counts, overcloud_roles): """Helper for parsing the OvercloudRoleCount object Given a list of OvercloudRoleCount objects return a dict of @@ -39,12 +39,16 @@ def parse_counts(counts): :param counts: List of tuskar.api.controllers.v1.models.OvercloudRoleCount :type counts: list + :param overcloud_roles: Dict of (overcloud_role_id, overcloud_role) so + we can access image_name and flavor_id of roles + :type overcloud_roles: dict + :return: Dict of (image_name, count) :rtype: dict """ parsed_counts = {} for count_obj in counts: - image_name = count_obj.overcloud_role.image_name + image_name = overcloud_roles[count_obj.overcloud_role_id].image_name count = count_obj.num_nodes parsed_counts[image_name] = count @@ -76,7 +80,13 @@ def filter_template_attributes(allowed_data, attributes): return filtered_data -def process_stack(attributes, counts, create=False): +def get_overcloud_roles_dict(): + return dict((overcloud_role.id, overcloud_role) + for overcloud_role in + pecan.request.dbapi.get_overcloud_roles()) + + +def process_stack(attributes, counts, overcloud_roles, create=False): """Helper function for processing the stack. Given a params dict containing the Overcloud Roles and initialization @@ -89,11 +99,16 @@ def process_stack(attributes, counts, create=False): :param counts: Dictionary of counts of roles to be deployed :type counts: dict + :param overcloud_roles: Dict of (overcloud_role_id, overcloud_role) so + we can access image_name and flavor_id of roles + :type overcloud_roles: dict + :param create: A flag to designate if we are creating or updating the stack :type create: bool """ try: - overcloud = template_tools.merge_templates(parse_counts(counts)) + overcloud = template_tools.merge_templates( + parse_counts(counts, overcloud_roles)) except Exception as e: raise exception.HeatTemplateCreateFailed(six.text_type(e)) @@ -117,14 +132,17 @@ def process_stack(attributes, counts, create=False): operation = heat_client.update_stack try: - operation(overcloud, - filter_template_attributes(allowed_data, attributes)) + result = operation( + overcloud, + filter_template_attributes(allowed_data, attributes)) except Exception as e: if create: raise exception.HeatStackCreateFailed(six.text_type(e)) else: raise exception.HeatStackUpdateFailed(six.text_type(e)) + return result + class OvercloudsController(rest.RestController): """REST controller for the Overcloud class.""" @@ -156,7 +174,19 @@ class OvercloudsController(rest.RestController): """ LOG.debug('Creating overcloud: %s' % transfer_overcloud) + # FIXME(lsmola) This is just POC of creating a stack + # this has to be done properly with proper Work-flow abstraction of: + # step 1- build template and start stack-create + # step 2- put the right stack_id to the overcloud + # step 3- initialize the stack + # step 4- set the correct overcloud status + stack = process_stack(transfer_overcloud.attributes, + transfer_overcloud.counts, + get_overcloud_roles_dict(), + create=True) + # Persist to the database + transfer_overcloud.stack_id = stack['stack']['id'] db_overcloud = transfer_overcloud.to_db_model() result = pecan.request.dbapi.create_overcloud(db_overcloud) @@ -164,15 +194,6 @@ class OvercloudsController(rest.RestController): saved_overcloud =\ models.Overcloud.from_db_model(result) - # FIXME(lsmola) This is just POC of creating a stack - # this has to be done properly with proper Work-flow abstraction of: - # step one- build template and start stack-create - # step 2- put the right stack_id to the overcloud - # step 3- initialize the stack - # step 4- set the correct overcloud status - process_stack(saved_overcloud.attributes, result.counts, - create=True) - return saved_overcloud @wsme.validate(models.Overcloud) @@ -210,10 +231,11 @@ class OvercloudsController(rest.RestController): # FIXME(lsmola) This is just POC of updating a stack # this probably should also have workflow - # step one- build template and stack-update + # step 1- build template and stack-update # step 2- set the correct overcloud status + process_stack(updated_overcloud.attributes, updated_overcloud.counts, - create=True) + get_overcloud_roles_dict(), create=True) return updated_overcloud @@ -231,7 +253,7 @@ class OvercloudsController(rest.RestController): # FIXME(lsmola) this should always try to delete both overcloud # and stack. So it requires some exception catch over below. # FIXME(lsmola) there is also a workflow needed - # step one- delete stack and set status deleting in progress to + # step 1- delete stack and set status deleting in progress to # overcloud # step 2 - once stack is deleted, delete the overcloud LOG.debug('Deleting overcloud with ID: %s' % overcloud_id) diff --git a/tuskar/tests/api/controllers/v1/test_overcloud.py b/tuskar/tests/api/controllers/v1/test_overcloud.py index 23914494..6ceebe5f 100644 --- a/tuskar/tests/api/controllers/v1/test_overcloud.py +++ b/tuskar/tests/api/controllers/v1/test_overcloud.py @@ -71,7 +71,7 @@ class OvercloudTests(base.TestCase): mock_db_get.assert_called_once_with(12345) - def test_parse_counts(self): + def test_parse_counts_overcloud_roles_explicit(self): # Setup overcloud_role_1 = db_models.OvercloudRole( image_name='overcloud-compute') @@ -79,16 +79,19 @@ class OvercloudTests(base.TestCase): overcloud_role_2 = db_models.OvercloudRole( image_name='overcloud-block-storage') + mock_overcloud_roles = {1: overcloud_role_1, 2: overcloud_role_2} + overcloud_role_count_1 = db_models.OvercloudRoleCount( - overcloud_role_id=2, num_nodes=5, overcloud_role=overcloud_role_1) + overcloud_role_id=1, num_nodes=5) overcloud_role_count_2 = db_models.OvercloudRoleCount( - overcloud_role_id=2, num_nodes=9, overcloud_role=overcloud_role_2) + overcloud_role_id=2, num_nodes=9) mock_counts = [overcloud_role_count_1, overcloud_role_count_2] # Test - result = overcloud.parse_counts(mock_counts) + result = overcloud.parse_counts(mock_counts, + overcloud_roles=mock_overcloud_roles) # Verify self.assertEqual(result, {'overcloud-compute': 5, @@ -121,7 +124,7 @@ class OvercloudTests(base.TestCase): 'tuskar.heat.client.HeatClient.__new__', return_value=mock.Mock(**{ 'validate_template.return_value': {}, 'exists_stack.return_value': False, - 'create_stack.return_value': True, + 'create_stack.return_value': {'stack': {'id': '1'}}, }) ) def test_create_stack(self, mock_heat_client, mock_heat_merge_templates): @@ -129,10 +132,10 @@ class OvercloudTests(base.TestCase): mock_heat_merge_templates.return_value = None # Test - response = overcloud.process_stack({}, {}, create=True) + response = overcloud.process_stack({}, {}, {}, create=True) # Verify - self.assertEqual(response, None) + self.assertEqual(response, {'stack': {'id': '1'}}) @mock.patch('tuskar.heat.template_tools.merge_templates') @mock.patch( @@ -150,7 +153,7 @@ class OvercloudTests(base.TestCase): # Test and Verify self.assertRaises( exception.HeatStackCreateFailed, - overcloud.process_stack, {}, {}, True) + overcloud.process_stack, {}, {}, {}, create=True) @mock.patch('tuskar.heat.template_tools.merge_templates') @mock.patch( @@ -167,8 +170,8 @@ class OvercloudTests(base.TestCase): # Test and Verify self.assertRaises( - exception.StackAlreadyCreated, overcloud.process_stack, {}, {}, - True) + exception.StackAlreadyCreated, overcloud.process_stack, {}, {}, {}, + create=True) @mock.patch('tuskar.heat.template_tools.merge_templates') @mock.patch( @@ -186,18 +189,22 @@ class OvercloudTests(base.TestCase): # Test and Verify self.assertRaises( exception.HeatTemplateValidateFailed, overcloud.process_stack, - {}, {}, - True) + {}, {}, {}, create=True) + @mock.patch('tuskar.db.sqlalchemy.api.Connection.get_overcloud_roles') @mock.patch('tuskar.api.controllers.v1.overcloud.process_stack') @mock.patch('tuskar.db.sqlalchemy.api.Connection.create_overcloud') - def test_post(self, mock_db_create, mock_process_stack): + def test_post(self, mock_db_create, mock_process_stack, + mock_get_overcloud_roles): # Setup create_me = {'name': 'new'} fake_created = db_models.Overcloud(name='created') mock_db_create.return_value = fake_created - mock_process_stack.return_value = None + mock_process_stack.return_value = {'stack': {'id': '1'}} + mock_get_overcloud_roles.return_value = [ + mock.Mock(**{'id.return_value': 1, }), + mock.Mock(**{'id.return_value': 2, })] # Test response = self.app.post_json(URL_OVERCLOUDS, params=create_me) @@ -218,7 +225,7 @@ class OvercloudTests(base.TestCase): 'tuskar.heat.client.HeatClient.__new__', return_value=mock.Mock(**{ 'validate_template.return_value': {}, 'exists_stack.return_value': True, - 'create_stack.return_value': True, + 'update_stack.return_value': {'stack': {'id': '1'}}, }) ) def test_update_stack(self, mock_heat_client, mock_heat_merge_templates): @@ -226,10 +233,10 @@ class OvercloudTests(base.TestCase): mock_heat_merge_templates.return_value = None # Test - response = overcloud.process_stack({}, {}) + response = overcloud.process_stack({}, {}, {}) # Verify - self.assertEqual(response, None) + self.assertEqual(response, {'stack': {'id': '1'}}) @mock.patch('tuskar.heat.template_tools.merge_templates') @mock.patch( @@ -247,7 +254,7 @@ class OvercloudTests(base.TestCase): # Test and Verify self.assertRaises( exception.HeatStackUpdateFailed, overcloud.process_stack, {}, - {}) + {}, {}) @mock.patch('tuskar.heat.template_tools.merge_templates') @mock.patch( @@ -264,7 +271,7 @@ class OvercloudTests(base.TestCase): # Test and Verify self.assertRaises( - exception.StackNotFound, overcloud.process_stack, {}, {}) + exception.StackNotFound, overcloud.process_stack, {}, {}, {}) @mock.patch('tuskar.heat.template_tools.merge_templates') @mock.patch( @@ -282,7 +289,7 @@ class OvercloudTests(base.TestCase): # Test and Verify self.assertRaises( exception.HeatTemplateValidateFailed, overcloud.process_stack, - {}, {}) + {}, {}, {}) @mock.patch('tuskar.api.controllers.v1.overcloud.process_stack') @mock.patch('tuskar.db.sqlalchemy.api.Connection.update_overcloud')