Match hostname when doing node delete
``overcloud node delete`` at present matches the provided nova instance ids with existing severs to build the heat blacklist. This patch would allow users to also use hostnames when deleting nodes. This will work with deployed servers and scale_playbook.yaml that matches host pattern. Partial-Bug: #1869089 Change-Id: I3066689facc169125ade29156190cb98910f32b8 (cherry picked from commitbe7c90e30c
) (cherry picked from commit56c0fd5caf
)
This commit is contained in:
parent
a3f3367dce
commit
ee6358f9e8
|
@ -120,20 +120,37 @@ class ScaleDownAction(templates.ProcessTemplatesAction):
|
||||||
|
|
||||||
return stack_params
|
return stack_params
|
||||||
|
|
||||||
|
def _match_hostname(self, heatclient, instance_list, res, stack_name):
|
||||||
|
type_patterns = ['DeployedServer', 'Server']
|
||||||
|
if any(res.resource_type.endswith(x) for x in type_patterns):
|
||||||
|
res_details = heatclient.resources.get(
|
||||||
|
stack_name, res.resource_name)
|
||||||
|
if 'name' in res_details.attributes:
|
||||||
|
try:
|
||||||
|
instance_list.remove(res_details.attributes['name'])
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
def run(self, context):
|
def run(self, context):
|
||||||
heatclient = self.get_orchestration_client(context)
|
heatclient = self.get_orchestration_client(context)
|
||||||
resources = heatclient.resources.list(self.container, nested_depth=5)
|
resources = heatclient.resources.list(self.container, nested_depth=5)
|
||||||
resources_by_role = collections.defaultdict(list)
|
resources_by_role = collections.defaultdict(list)
|
||||||
instance_list = list(self.nodes)
|
instance_list = list(self.nodes)
|
||||||
for res in resources:
|
|
||||||
try:
|
|
||||||
instance_list.remove(res.physical_resource_id)
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
for res in resources:
|
||||||
stack_name, stack_id = next(
|
stack_name, stack_id = next(
|
||||||
x['href'] for x in res.links if
|
x['href'] for x in res.links if
|
||||||
x['rel'] == 'stack').rsplit('/', 2)[1:]
|
x['rel'] == 'stack').rsplit('/', 2)[1:]
|
||||||
|
|
||||||
|
try:
|
||||||
|
instance_list.remove(res.physical_resource_id)
|
||||||
|
except ValueError:
|
||||||
|
if not self._match_hostname(
|
||||||
|
heatclient, instance_list, res, stack_name):
|
||||||
|
continue
|
||||||
|
|
||||||
# get resource to remove from resource group (it's parent resource
|
# get resource to remove from resource group (it's parent resource
|
||||||
# of nova server)
|
# of nova server)
|
||||||
role_resource = next(x for x in resources if
|
role_resource = next(x for x in resources if
|
||||||
|
|
|
@ -73,7 +73,7 @@ class ScaleDownActionTest(base.TestCase):
|
||||||
'a959ac7d6a4a475daf2428df315c41ef/'
|
'a959ac7d6a4a475daf2428df315c41ef/'
|
||||||
'stacks/overcloud/124'}],
|
'stacks/overcloud/124'}],
|
||||||
logical_resource_id='node0',
|
logical_resource_id='node0',
|
||||||
physical_resource_id='123',
|
physical_resource_id='124',
|
||||||
resource_type='OS::TripleO::Compute',
|
resource_type='OS::TripleO::Compute',
|
||||||
parent_resource='Compute',
|
parent_resource='Compute',
|
||||||
resource_name='node0',
|
resource_name='node0',
|
||||||
|
@ -130,7 +130,7 @@ class ScaleDownActionTest(base.TestCase):
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
action = scale.ScaleDownAction(
|
action = scale.ScaleDownAction(
|
||||||
constants.STACK_TIMEOUT_DEFAULT, ['resource_id'], 'stack')
|
constants.STACK_TIMEOUT_DEFAULT, ['124'], 'stack')
|
||||||
result = action.run(mock_ctx)
|
result = action.run(mock_ctx)
|
||||||
|
|
||||||
heatclient.stacks.validate.assert_called_once_with(
|
heatclient.stacks.validate.assert_called_once_with(
|
||||||
|
@ -203,3 +203,119 @@ class ScaleDownActionTest(base.TestCase):
|
||||||
result = action.run(mock_ctx)
|
result = action.run(mock_ctx)
|
||||||
|
|
||||||
self.assertEqual(actions.Result(error='Update error'), result)
|
self.assertEqual(actions.Result(error='Update error'), result)
|
||||||
|
|
||||||
|
@mock.patch('tripleo_common.actions.base.TripleOAction.'
|
||||||
|
'cache_delete')
|
||||||
|
@mock.patch('tripleo_common.actions.base.TripleOAction.'
|
||||||
|
'get_orchestration_client')
|
||||||
|
@mock.patch('heatclient.common.template_utils.'
|
||||||
|
'process_multiple_environments_and_files')
|
||||||
|
@mock.patch('heatclient.common.template_utils.get_template_contents')
|
||||||
|
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
|
||||||
|
def test_run_with_hostmatch(self, mock_get_object_client,
|
||||||
|
mock_get_template_contents, mock_env_files,
|
||||||
|
mock_get_heat_client, mock_cache):
|
||||||
|
|
||||||
|
mock_env_files.return_value = ({}, {})
|
||||||
|
heatclient = mock.MagicMock()
|
||||||
|
heatclient.resources.list.return_value = [
|
||||||
|
mock.MagicMock(
|
||||||
|
links=[{'rel': 'stack',
|
||||||
|
'href': 'http://192.0.2.1:8004/v1/'
|
||||||
|
'a959ac7d6a4a475daf2428df315c41ef/'
|
||||||
|
'stacks/overcloud/123'}],
|
||||||
|
logical_resource_id='logical_id',
|
||||||
|
physical_resource_id='resource_id',
|
||||||
|
resource_type='OS::Heat::ResourceGroup',
|
||||||
|
resource_name='Compute'
|
||||||
|
),
|
||||||
|
mock.MagicMock(
|
||||||
|
links=[{'rel': 'stack',
|
||||||
|
'href': 'http://192.0.2.1:8004/v1/'
|
||||||
|
'a959ac7d6a4a475daf2428df315c41ef/'
|
||||||
|
'stacks/overcloud/124'}],
|
||||||
|
logical_resource_id='node0',
|
||||||
|
physical_resource_id='124',
|
||||||
|
resource_type='OS::TripleO::ComputeServer',
|
||||||
|
parent_resource='Compute',
|
||||||
|
resource_name='node0',
|
||||||
|
)
|
||||||
|
]
|
||||||
|
heatclient.resources.get.return_value = mock.MagicMock(
|
||||||
|
attributes={'name': 'node0'})
|
||||||
|
heatclient.stacks.get.return_value = mock_stack()
|
||||||
|
heatclient.stacks.validate.return_value = {}
|
||||||
|
mock_get_heat_client.return_value = heatclient
|
||||||
|
|
||||||
|
mock_ctx = mock.MagicMock()
|
||||||
|
swift = mock.MagicMock(url="http://test.com")
|
||||||
|
mock_env = yaml.safe_dump({
|
||||||
|
'name': 'overcloud',
|
||||||
|
'temp_environment': 'temp_environment',
|
||||||
|
'template': 'template',
|
||||||
|
'environments': [{u'path': u'environments/test.yaml'}]
|
||||||
|
}, default_flow_style=False)
|
||||||
|
mock_roles = yaml.safe_dump([{"name": "foo"}])
|
||||||
|
mock_network = yaml.safe_dump([{'enabled': False}])
|
||||||
|
mock_exclude = yaml.safe_dump({"name": "foo"})
|
||||||
|
swift.get_object.side_effect = (
|
||||||
|
({}, mock_env),
|
||||||
|
({}, mock_env),
|
||||||
|
({}, mock_roles),
|
||||||
|
({}, mock_network),
|
||||||
|
({}, mock_exclude),
|
||||||
|
({}, mock_env),
|
||||||
|
({}, mock_env),
|
||||||
|
({}, mock_env),
|
||||||
|
({}, mock_roles),
|
||||||
|
({}, mock_network),
|
||||||
|
({}, mock_exclude),
|
||||||
|
({}, mock_env),
|
||||||
|
({}, mock_env),
|
||||||
|
swiftexceptions.ClientException('atest2')
|
||||||
|
)
|
||||||
|
|
||||||
|
def return_container_files(*args):
|
||||||
|
return ('headers', [{'name': 'foo.role.j2.yaml'}])
|
||||||
|
|
||||||
|
swift.get_container = mock.MagicMock(
|
||||||
|
side_effect=return_container_files)
|
||||||
|
mock_get_object_client.return_value = swift
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'resource_registry': {
|
||||||
|
'resources': {'*': {'*': {'UpdateDeployment': {'hooks': []}}}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_get_template_contents.return_value = ({}, {
|
||||||
|
'heat_template_version': '2016-04-30'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Test
|
||||||
|
action = scale.ScaleDownAction(
|
||||||
|
constants.STACK_TIMEOUT_DEFAULT, ['node0'], 'stack')
|
||||||
|
result = action.run(mock_ctx)
|
||||||
|
|
||||||
|
heatclient.stacks.validate.assert_called_once_with(
|
||||||
|
environment=env,
|
||||||
|
files={},
|
||||||
|
show_nested=True,
|
||||||
|
template={'heat_template_version': '2016-04-30'}
|
||||||
|
)
|
||||||
|
|
||||||
|
clear_list = list(['ComputeCount', 'ComputeRemovalPolicies',
|
||||||
|
'ComputeRemovalPoliciesMode'])
|
||||||
|
_, kwargs = heatclient.stacks.update.call_args
|
||||||
|
self.assertEqual(set(kwargs['clear_parameters']), set(clear_list))
|
||||||
|
self.assertEqual(kwargs['environment'], env)
|
||||||
|
self.assertEqual(kwargs['existing'], True)
|
||||||
|
self.assertEqual(kwargs['files'], {})
|
||||||
|
|
||||||
|
mock_cache.assert_called_with(
|
||||||
|
mock_ctx,
|
||||||
|
"stack",
|
||||||
|
"tripleo.parameters.get"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(None, result)
|
||||||
|
|
Loading…
Reference in New Issue