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 commit be7c90e30c
)
This commit is contained in:
parent
ace00a3296
commit
56c0fd5caf
|
@ -125,20 +125,37 @@ class ScaleDownAction(base.TripleOAction):
|
|||
|
||||
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
|
||||
|
|
|
@ -73,7 +73,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',
|
||||
|
@ -130,7 +130,7 @@ class ScaleDownActionTest(base.TestCase):
|
|||
|
||||
# Test
|
||||
action = scale.ScaleDownAction(
|
||||
constants.STACK_TIMEOUT_DEFAULT, ['resource_id'], 'stack')
|
||||
constants.STACK_TIMEOUT_DEFAULT, ['124'], 'stack')
|
||||
result = action.run(mock_ctx)
|
||||
|
||||
heatclient.stacks.validate.assert_called_once_with(
|
||||
|
@ -203,3 +203,119 @@ class ScaleDownActionTest(base.TestCase):
|
|||
result = action.run(mock_ctx)
|
||||
|
||||
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