Browse Source

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)
(cherry picked from commit 56c0fd5caf)
changes/08/725608/1
Rabi Mishra 3 months ago
parent
commit
f6e3041291
2 changed files with 139 additions and 6 deletions
  1. +21
    -4
      tripleo_common/actions/scale.py
  2. +118
    -2
      tripleo_common/tests/actions/test_scale.py

+ 21
- 4
tripleo_common/actions/scale.py 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:
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:
continue
if not self._match_hostname(
heatclient, instance_list, res, stack_name):
continue

stack_name, stack_id = next(
x['href'] for x in res.links if
x['rel'] == 'stack').rsplit('/', 2)[1:]
# get resource to remove from resource group (it's parent resource
# of nova server)
role_resource = next(x for x in resources if


+ 118
- 2
tripleo_common/tests/actions/test_scale.py View File

@@ -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…
Cancel
Save