From d28a780d7766b7160d5b6ee262aff5bbb2cc6978 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Sun, 8 Feb 2015 18:50:33 +0000 Subject: [PATCH] Implement update bay node_count It is implemented by updating the Heat stack with the new node_count Partially implements: blueprint update-node-count Change-Id: I2c331043bb6e6fce14fc82932dcd9c3677448598 --- magnum/api/controllers/v1/bay.py | 4 +- magnum/conductor/api.py | 3 ++ magnum/conductor/handlers/bay_k8s_heat.py | 42 ++++++++++++++++++- magnum/tests/api/controllers/v1/test_bay.py | 8 ++++ .../conductor/handlers/test_bay_k8s_heat.py | 31 ++++++++++++++ 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/magnum/api/controllers/v1/bay.py b/magnum/api/controllers/v1/bay.py index 69e564ef2b..ce66b6d035 100644 --- a/magnum/api/controllers/v1/bay.py +++ b/magnum/api/controllers/v1/bay.py @@ -278,8 +278,8 @@ class BaysController(rest.RestController): if rpc_bay[field] != patch_val: rpc_bay[field] = patch_val - rpc_bay.save() - return Bay.convert_with_links(rpc_bay) + res_bay = pecan.request.rpcapi.bay_update(rpc_bay) + return Bay.convert_with_links(res_bay) @wsme_pecan.wsexpose(None, types.uuid, status_code=204) def delete(self, bay_uuid): diff --git a/magnum/conductor/api.py b/magnum/conductor/api.py index 055ce1fd89..960d5ed555 100644 --- a/magnum/conductor/api.py +++ b/magnum/conductor/api.py @@ -59,6 +59,9 @@ class API(rpc_service.API): def bay_show(self, context, uuid): return objects.Bay.get_by_uuid(context, uuid) + def bay_update(self, bay): + return self._call('bay_update', bay=bay) + # Service Operations def service_create(self, service): diff --git a/magnum/conductor/handlers/bay_k8s_heat.py b/magnum/conductor/handlers/bay_k8s_heat.py index e17f267097..1ee8428429 100644 --- a/magnum/conductor/handlers/bay_k8s_heat.py +++ b/magnum/conductor/handlers/bay_k8s_heat.py @@ -17,6 +17,7 @@ from heatclient import exc from oslo_config import cfg from magnum.common import clients +from magnum.common import exception from magnum.common import short_id from magnum import objects from magnum.openstack.common._i18n import _ @@ -87,6 +88,20 @@ def _create_stack(context, osc, bay): return created_stack +def _update_stack(context, osc, bay): + bay_definition = _extract_bay_definition(context, bay) + + tpl_files, template = template_utils.get_template_contents( + cfg.CONF.k8s_heat.template_path) + fields = { + 'parameters': bay_definition, + 'template': template, + 'files': dict(list(tpl_files.items())) + } + + return osc.heat().stacks.update(bay.stack_id, **fields) + + def _parse_stack_outputs(outputs): parsed_outputs = {} @@ -122,6 +137,30 @@ class Handler(object): return bay + def bay_update(self, context, bay): + LOG.debug('k8s_heat bay_update') + + osc = clients.OpenStackClients(context) + stack = osc.heat().stacks.get(bay.stack_id) + if (stack.stack_status != 'CREATE_COMPLETE' and + stack.stack_status != 'UPDATE_COMPLETE'): + raise exception.MagnumException(_( + "Cannot update stack with status: %s") % stack.stack_status) + + delta = bay.obj_what_changed() + if 'node_count' in delta: + delta.remove('node_count') + + _update_stack(context, osc, bay) + self._poll_and_check(osc, bay) + + if delta: + raise exception.InvalidParameterValue(err=( + "cannot change bay property(ies) %s." % ", ".join(delta))) + + bay.save() + return bay + def bay_delete(self, context, uuid): LOG.debug('k8s_heat bay_delete') osc = clients.OpenStackClients(context) @@ -156,7 +195,8 @@ class Handler(object): def poll_and_check(): stack = osc.heat().stacks.get(bay.stack_id) attempts['count'] += 1 - if stack.stack_status == 'CREATE_COMPLETE': + if (stack.stack_status == 'CREATE_COMPLETE' or + stack.stack_status == 'UPDATE_COMPLETE'): parsed_outputs = _parse_stack_outputs(stack.outputs) master_address = parsed_outputs["kube_master"] minion_address = parsed_outputs["kube_minions_external"] diff --git a/magnum/tests/api/controllers/v1/test_bay.py b/magnum/tests/api/controllers/v1/test_bay.py index c26a650aa7..5857315527 100644 --- a/magnum/tests/api/controllers/v1/test_bay.py +++ b/magnum/tests/api/controllers/v1/test_bay.py @@ -129,6 +129,14 @@ class TestPatch(api_base.FunctionalTest): self.bay = obj_utils.create_test_bay(self.context, name='bay_example_A', node_count=3) + p = mock.patch.object(rpcapi.API, 'bay_update') + self.mock_bay_update = p.start() + self.mock_bay_update.side_effect = self._simulate_rpc_bay_update + self.addCleanup(p.stop) + + def _simulate_rpc_bay_update(self, bay): + bay.save() + return bay @mock.patch('oslo.utils.timeutils.utcnow') def test_replace_ok(self, mock_utcnow): diff --git a/magnum/tests/conductor/handlers/test_bay_k8s_heat.py b/magnum/tests/conductor/handlers/test_bay_k8s_heat.py index bbd4b5646f..1600ce211d 100644 --- a/magnum/tests/conductor/handlers/test_bay_k8s_heat.py +++ b/magnum/tests/conductor/handlers/test_bay_k8s_heat.py @@ -230,3 +230,34 @@ class TestBayK8sHeat(base.TestCase): 'files': dict(exptected_files) } mock_heat_client.stacks.create.assert_called_once_with(**expected_args) + + @patch('heatclient.common.template_utils.get_template_contents') + @patch('magnum.conductor.handlers.bay_k8s_heat._extract_bay_definition') + def test_update_stack(self, + mock_extract_bay_definition, + mock_get_template_contents): + + mock_stack_id = 'xx-xx-xx-xx' + expected_template_contents = 'template_contents' + exptected_files = [] + + mock_tpl_files = mock.MagicMock() + mock_tpl_files.items.return_value = exptected_files + mock_get_template_contents.return_value = [ + mock_tpl_files, expected_template_contents] + mock_extract_bay_definition.return_value = {} + mock_heat_client = mock.MagicMock() + mock_osc = mock.MagicMock() + mock_osc.heat.return_value = mock_heat_client + mock_bay = mock.MagicMock() + mock_bay.stack_id = mock_stack_id + + bay_k8s_heat._update_stack({}, mock_osc, mock_bay) + + expected_args = { + 'parameters': {}, + 'template': expected_template_contents, + 'files': dict(exptected_files) + } + mock_heat_client.stacks.update.assert_called_once_with(mock_stack_id, + **expected_args)