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
Depends-On: https://review.opendev.org/727696
Change-Id: I3066689facc169125ade29156190cb98910f32b8
(cherry picked from commit be7c90e30c)
(cherry picked from commit 56c0fd5caf)
This commit is contained in:
Rabi Mishra 2020-03-26 12:45:40 +05:30
parent c782d68884
commit 927583ded3
2 changed files with 140 additions and 7 deletions

View File

@ -120,20 +120,37 @@ class ScaleDownAction(templates.ProcessTemplatesAction):
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):
heatclient = self.get_orchestration_client(context)
resources = heatclient.resources.list(self.container, nested_depth=5)
resources_by_role = collections.defaultdict(list)
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(
x['href'] for x in res.links if
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
# of nova server)
role_resource = next(x for x in resources if

View File

@ -72,7 +72,7 @@ class ScaleDownActionTest(base.TestCase):
'a959ac7d6a4a475daf2428df315c41ef/'
'stacks/overcloud/124'}],
logical_resource_id='node0',
physical_resource_id='123',
physical_resource_id='124',
resource_type='OS::TripleO::Compute',
parent_resource='Compute',
resource_name='node0',
@ -129,7 +129,7 @@ class ScaleDownActionTest(base.TestCase):
# Test
action = scale.ScaleDownAction(
constants.STACK_TIMEOUT_DEFAULT, ['resource_id'], 'stack')
constants.STACK_TIMEOUT_DEFAULT, ['124'], 'stack')
action.run(mock_ctx)
heatclient.stacks.validate.assert_called_once_with(
@ -152,3 +152,119 @@ class ScaleDownActionTest(base.TestCase):
"stack",
"tripleo.parameters.get"
)
@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)