ExtractProvisionedNode - include removed_rsrc
Get the removed_rsrc_list for each ResourceGroup in the stack. When building the instances list, add entries with 'provisioned': false if the current index is in removed_rsrc_list and no replacement using the same hostname exist. We no longer sort entries from AnsibleHostVarsMap, I think sorting might actually be problematic in a corner case where HostnameMap was used to give nodes non-sort friendly names like: overcloud-controller-0: non overcloud-controller-1: sort overcloud-controller-2: friendly Sorting ^^ would result: overcloud-controller-0: friendly overcloud-controller-1: non overcloud-controller-2: sort Resolves: RHBZ#2238349 Change-Id: I30d5ac4921af584b2a234880aafa20bbcaea052d
This commit is contained in:
@@ -771,14 +771,14 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
}, {
|
}, {
|
||||||
'output_key': 'AnsibleHostVarsMap',
|
'output_key': 'AnsibleHostVarsMap',
|
||||||
'output_value': {
|
'output_value': {
|
||||||
'Compute': [
|
'Compute': {
|
||||||
'overcloud-novacompute-0'
|
'overcloud-novacompute-0': {},
|
||||||
],
|
},
|
||||||
'Controller': [
|
'Controller': {
|
||||||
'overcloud-controller-0',
|
'overcloud-controller-0': {},
|
||||||
'overcloud-controller-1',
|
'overcloud-controller-1': {},
|
||||||
'overcloud-controller-2'
|
'overcloud-controller-2': {},
|
||||||
],
|
},
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'output_key': 'RoleNetIpMap',
|
'output_key': 'RoleNetIpMap',
|
||||||
@@ -802,6 +802,10 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.resource = mock.Mock()
|
||||||
|
self.resource.attributes = dict()
|
||||||
|
self.resource.attributes['removed_rsrc_list'] = []
|
||||||
|
|
||||||
self.nodes = [
|
self.nodes = [
|
||||||
mock.Mock(),
|
mock.Mock(),
|
||||||
mock.Mock(),
|
mock.Mock(),
|
||||||
@@ -925,9 +929,11 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
|
|
||||||
def test_extract(self):
|
def test_extract(self):
|
||||||
stack = mock.Mock()
|
stack = mock.Mock()
|
||||||
|
stack.stack_name = 'overcloud'
|
||||||
stack.to_dict.return_value = self.stack_dict
|
stack.to_dict.return_value = self.stack_dict
|
||||||
stack.environment.return_value = {}
|
stack.environment.return_value = {}
|
||||||
self.orchestration.stacks.get.return_value = stack
|
self.orchestration.stacks.get.return_value = stack
|
||||||
|
self.orchestration.resources.get.return_value = self.resource
|
||||||
|
|
||||||
self.baremetal.node.list.return_value = self.nodes
|
self.baremetal.node.list.return_value = self.nodes
|
||||||
argslist = ['--output', self.extract_file.name,
|
argslist = ['--output', self.extract_file.name,
|
||||||
@@ -1001,6 +1007,7 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
|
|
||||||
def test_extract_ips_from_pool(self):
|
def test_extract_ips_from_pool(self):
|
||||||
stack = mock.Mock()
|
stack = mock.Mock()
|
||||||
|
stack.stack_name = 'overcloud'
|
||||||
stack.to_dict.return_value = self.stack_dict
|
stack.to_dict.return_value = self.stack_dict
|
||||||
stack.environment.return_value = {
|
stack.environment.return_value = {
|
||||||
'parameter_defaults': {
|
'parameter_defaults': {
|
||||||
@@ -1011,6 +1018,7 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.orchestration.stacks.get.return_value = stack
|
self.orchestration.stacks.get.return_value = stack
|
||||||
|
self.orchestration.resources.get.return_value = self.resource
|
||||||
|
|
||||||
self.baremetal.node.list.return_value = self.nodes
|
self.baremetal.node.list.return_value = self.nodes
|
||||||
argslist = ['--roles-file', self.roles_file.name,
|
argslist = ['--roles-file', self.roles_file.name,
|
||||||
@@ -1131,6 +1139,7 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.orchestration.stacks.get.return_value = stack
|
self.orchestration.stacks.get.return_value = stack
|
||||||
|
self.orchestration.resources.get.return_value = self.resource
|
||||||
|
|
||||||
self.baremetal.node.list.return_value = self.nodes
|
self.baremetal.node.list.return_value = self.nodes
|
||||||
|
|
||||||
@@ -1229,6 +1238,7 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
'outputs': []
|
'outputs': []
|
||||||
}
|
}
|
||||||
stack = mock.Mock()
|
stack = mock.Mock()
|
||||||
|
stack.stack_name = 'overcloud'
|
||||||
stack.to_dict.return_value = stack_dict
|
stack.to_dict.return_value = stack_dict
|
||||||
self.orchestration.stacks.get.return_value = stack
|
self.orchestration.stacks.get.return_value = stack
|
||||||
|
|
||||||
@@ -1273,14 +1283,14 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
}, {
|
}, {
|
||||||
'output_key': 'AnsibleHostVarsMap',
|
'output_key': 'AnsibleHostVarsMap',
|
||||||
'output_value': {
|
'output_value': {
|
||||||
'Compute': [
|
'Compute': {
|
||||||
'overcloud-novacompute-0'
|
'overcloud-novacompute-0': {}
|
||||||
],
|
},
|
||||||
'Controller': [
|
'Controller': {
|
||||||
'overcloud-controller-0',
|
'overcloud-controller-0': {},
|
||||||
'overcloud-controller-1',
|
'overcloud-controller-1': {},
|
||||||
'overcloud-controller-2'
|
'overcloud-controller-2': {},
|
||||||
],
|
},
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'output_key': 'RoleNetIpMap',
|
'output_key': 'RoleNetIpMap',
|
||||||
@@ -1303,6 +1313,8 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
stack.to_dict.return_value = stack_dict
|
stack.to_dict.return_value = stack_dict
|
||||||
stack.environment.return_value = {}
|
stack.environment.return_value = {}
|
||||||
self.orchestration.stacks.get.return_value = stack
|
self.orchestration.stacks.get.return_value = stack
|
||||||
|
self.orchestration.resources.get.return_value = self.resource
|
||||||
|
|
||||||
self.baremetal.node.list.return_value = self.nodes
|
self.baremetal.node.list.return_value = self.nodes
|
||||||
self.network.find_network.side_effect = [
|
self.network.find_network.side_effect = [
|
||||||
self.ctlplane_net, None,
|
self.ctlplane_net, None,
|
||||||
@@ -1377,3 +1389,112 @@ class TestExtractProvisionedNode(test_utils.TestCommand):
|
|||||||
'name': 'bm-2-uuid',
|
'name': 'bm-2-uuid',
|
||||||
}],
|
}],
|
||||||
}], yaml.safe_load(result))
|
}], yaml.safe_load(result))
|
||||||
|
|
||||||
|
def test_extract_removed_resources(self):
|
||||||
|
roles_data = [{'name': 'Controller',
|
||||||
|
'default_route_networks': ['External'],
|
||||||
|
'networks_skip_config': ['Tenant']}]
|
||||||
|
networks_data = []
|
||||||
|
stack = mock.Mock()
|
||||||
|
stack.stack_name = 'overcloud'
|
||||||
|
stack_dict = {
|
||||||
|
'parameters': {
|
||||||
|
'ControllerHostnameFormat': '%stackname%-controller-%index%',
|
||||||
|
'ControllerNetworkConfigTemplate': 'templates/controller.j2'
|
||||||
|
},
|
||||||
|
'outputs': [{
|
||||||
|
'output_key': 'TripleoHeatTemplatesJinja2RenderingDataSources',
|
||||||
|
'output_value': {
|
||||||
|
'roles_data': roles_data,
|
||||||
|
'networks_data': networks_data,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'output_key': 'AnsibleHostVarsMap',
|
||||||
|
'output_value': {
|
||||||
|
'Controller': {
|
||||||
|
'overcloud-controller-0': {},
|
||||||
|
'overcloud-controller-2': {},
|
||||||
|
'overcloud-controller-3': {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'output_key': 'RoleNetIpMap',
|
||||||
|
'output_value': {
|
||||||
|
'Controller': {
|
||||||
|
'ctlplane': ['192.168.25.21',
|
||||||
|
'192.168.25.25',
|
||||||
|
'192.168.25.28'],
|
||||||
|
'external': ['10.0.0.199',
|
||||||
|
'10.0.0.197',
|
||||||
|
'10.0.0.191'],
|
||||||
|
'internal_api': ['172.17.0.37',
|
||||||
|
'172.17.0.33',
|
||||||
|
'172.17.0.39'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
stack.to_dict.return_value = stack_dict
|
||||||
|
stack.environment.return_value = {}
|
||||||
|
self.orchestration.stacks.get.return_value = stack
|
||||||
|
resource = mock.Mock()
|
||||||
|
resource.attributes = dict()
|
||||||
|
resource.attributes['removed_rsrc_list'] = ['1']
|
||||||
|
self.orchestration.resources.get.return_value = resource
|
||||||
|
nodes = [mock.Mock(), mock.Mock(), mock.Mock()]
|
||||||
|
nodes[0].name = 'bm-0'
|
||||||
|
nodes[0].uuid = 'bm-0-uuid'
|
||||||
|
nodes[0].resource_class = 'controller'
|
||||||
|
nodes[1].name = 'bm-2'
|
||||||
|
nodes[1].uuid = 'bm-2-uuid'
|
||||||
|
nodes[1].resource_class = 'controller'
|
||||||
|
nodes[2].name = 'bm-3'
|
||||||
|
nodes[2].uuid = 'bm-3-uuid'
|
||||||
|
nodes[2].resource_class = None
|
||||||
|
|
||||||
|
nodes[0].instance_info = {'display_name': 'overcloud-controller-0'}
|
||||||
|
nodes[1].instance_info = {'display_name': 'overcloud-controller-2'}
|
||||||
|
nodes[2].instance_info = {'display_name': 'overcloud-controller-3'}
|
||||||
|
|
||||||
|
self.baremetal.node.list.return_value = nodes
|
||||||
|
|
||||||
|
self.network.find_network.side_effect = [
|
||||||
|
# controller-0
|
||||||
|
self.ctlplane_net, self.external_net, self.internal_api_net,
|
||||||
|
# controller-2
|
||||||
|
self.ctlplane_net, self.external_net, self.internal_api_net,
|
||||||
|
# controller-3
|
||||||
|
self.ctlplane_net, self.external_net, self.internal_api_net,
|
||||||
|
]
|
||||||
|
self.network.get_subnet.side_effect = [
|
||||||
|
# controller-0
|
||||||
|
self.ctlplane_a, self.external_a, self.int_api_a,
|
||||||
|
# controller-2
|
||||||
|
self.ctlplane_a, self.external_a, self.int_api_a,
|
||||||
|
# controller-3
|
||||||
|
self.ctlplane_a, self.external_a, self.int_api_a,
|
||||||
|
]
|
||||||
|
|
||||||
|
argslist = ['--output', self.extract_file.name, '--yes']
|
||||||
|
self.app.command_options = argslist
|
||||||
|
verifylist = [('output', self.extract_file.name), ('yes', True)]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||||
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
|
result = self.cmd.app.stdout.make_string()
|
||||||
|
self.assertEqual(
|
||||||
|
[
|
||||||
|
{'hostname': 'overcloud-controller-0',
|
||||||
|
'name': 'bm-0-uuid',
|
||||||
|
'resource_class': 'controller'},
|
||||||
|
{'hostname': 'overcloud-controller-1',
|
||||||
|
'provisioned': False},
|
||||||
|
{'hostname': 'overcloud-controller-2',
|
||||||
|
'name': 'bm-2-uuid',
|
||||||
|
'resource_class': 'controller'},
|
||||||
|
{'hostname': 'overcloud-controller-3',
|
||||||
|
'name': 'bm-3-uuid'}
|
||||||
|
],
|
||||||
|
yaml.safe_load(result)[0]['instances']
|
||||||
|
)
|
||||||
|
|||||||
@@ -1091,6 +1091,19 @@ def get_role_net_ip_map(working_dir):
|
|||||||
'RoleNetIpMap', working_dir)
|
'RoleNetIpMap', working_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def get_role_removed_rsrc_list(orchestration_client, stack_id, role_name):
|
||||||
|
res = orchestration_client.resources.get(stack_id, role_name)
|
||||||
|
|
||||||
|
return res.attributes.get('removed_rsrc_list', [])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_hostname(hostname_format, stack_name, index):
|
||||||
|
|
||||||
|
return hostname_format.replace(
|
||||||
|
'%stackname%', stack_name).replace(
|
||||||
|
'%index%', str(index))
|
||||||
|
|
||||||
|
|
||||||
def get_stack(orchestration_client, stack_name):
|
def get_stack(orchestration_client, stack_name):
|
||||||
"""Get the ID for the current deployed overcloud stack if it exists.
|
"""Get the ID for the current deployed overcloud stack if it exists.
|
||||||
|
|
||||||
|
|||||||
@@ -749,10 +749,31 @@ class ExtractProvisionedNode(command.Command):
|
|||||||
'networks_skip_config')
|
'networks_skip_config')
|
||||||
|
|
||||||
# Add individual instances
|
# Add individual instances
|
||||||
|
removed_rsrc_list = oooutils.get_role_removed_rsrc_list(
|
||||||
|
self.orchestration_client, stack.id, role_name)
|
||||||
ips_from_pool = parameter_defaults.get(
|
ips_from_pool = parameter_defaults.get(
|
||||||
'{}IPs'.format(role_name), {})
|
'{}IPs'.format(role_name), {})
|
||||||
instances = role['instances'] = []
|
instances = role['instances'] = []
|
||||||
for idx, entry in enumerate(sorted(entries)):
|
entry_names = list(entries.keys())
|
||||||
|
for idx in range(len(entries) + len(removed_rsrc_list)):
|
||||||
|
try:
|
||||||
|
entry = entry_names[idx]
|
||||||
|
except IndexError:
|
||||||
|
# In case of scale down removed_rsrc_list can cause
|
||||||
|
# iteration out of range. There should be no need to add
|
||||||
|
# unprovisioned entries at the end of instances list.
|
||||||
|
break
|
||||||
|
|
||||||
|
# Insert unprovisioned entry if removed node was not replaced
|
||||||
|
# by a node re-using the hostname via HostnameMap.
|
||||||
|
# If the hostname was re-used we should safely be able to re-
|
||||||
|
# use the original index in ephemeral heat.
|
||||||
|
gen_name = oooutils.generate_hostname(
|
||||||
|
hostname_format, stack.stack_name, idx)
|
||||||
|
if str(idx) in removed_rsrc_list and gen_name not in entries:
|
||||||
|
instance = {'hostname': gen_name, 'provisioned': False}
|
||||||
|
instances.append(instance)
|
||||||
|
|
||||||
instance = {'hostname': entry}
|
instance = {'hostname': entry}
|
||||||
|
|
||||||
if entry in hostname_node_map:
|
if entry in hostname_node_map:
|
||||||
|
|||||||
Reference in New Issue
Block a user