Adds detection for legacy API network resource

Currently in network_data.yaml there is compat_name which is used as a
workaround for upgrades with regards to a bug where the Heat resource
for API network was being created as InternalNetwork instead of
InternalApiNetwork.  This workaround does work for upgrades, but
consequently also causes all future new deployments to have the wrong
resource name.

This patch adds detection, so that if the legacy resource is detected
already in the overcloud stack, then the compatibility will be enabled
if not already set in network_data.yaml.  This allows for the removal of
compat_name from the network in network_data.yaml and thus removing the
incorrect name for future deployments.

Partial-Bug: 1718764

Change-Id: I695259ad87e2303f948875078bccb619786470e0
Signed-off-by: Tim Rozet <trozet@redhat.com>
This commit is contained in:
Tim Rozet 2017-10-16 13:56:24 -04:00 committed by Alex Schultz
parent 5af31b876e
commit 602b481804
4 changed files with 159 additions and 8 deletions

View File

@ -0,0 +1,7 @@
---
fixes:
- |
Fixes compatibility between older deployments with Heat resource network
"InternalNetwork" and corrected "InternalApiNetwork". Upgrades from
previous versions will still use the old naming scheme, while new
deployments will use the correct name of "InternalApiNetwork".

View File

@ -157,6 +157,26 @@ class ProcessTemplatesAction(base.TripleOAction):
"the J2 excludes list to: %s" % j2_excl_data)
return j2_excl_data
def _heat_resource_exists(self, resource_name, context):
heatclient = self.get_orchestration_client(context)
stack_exists = False
for stack in heatclient.stacks.list():
if self.container == str(stack.stack_name):
stack_exists = True
break
if not stack_exists:
LOG.debug("Resource does not exist because stack does not exist")
return False
resources = heatclient.resources.list(self.container, nested_depth=6)
for resource in resources:
if str(resource.resource_name) == resource_name:
LOG.debug("Resource exists: {}".format(resource_name))
return True
LOG.debug("Resource does not exist: {}".format(resource_name))
return False
def _process_custom_roles(self, context):
swift = self.get_object_client(context)
@ -199,10 +219,19 @@ class ProcessTemplatesAction(base.TripleOAction):
n_map = {}
for n in network_data:
if (n.get('enabled') is not False):
if n.get('enabled') is not False:
n_map[n.get('name')] = n
if not n.get('name_lower'):
n_map[n.get('name')]['name_lower'] = n.get('name').lower()
if n.get('name') == constants.API_NETWORK and 'compat_name' \
not in n.keys():
# Check to see if legacy named API network exists
# and if so we need to set compat_name
api_net = "{}Network".format(constants.LEGACY_API_NETWORK)
if self._heat_resource_exists(api_net, context):
n['compat_name'] = 'Internal'
LOG.info("Upgrade compatibility enabled for legacy "
"network resource Internal.")
else:
LOG.info("skipping %s network: network is disabled." %
n.get('name'))

View File

@ -141,6 +141,9 @@ TRIPLEO_CACHE_CONTAINER = "__cache__"
TRIPLEO_UI_LOG_FILE_SIZE = 1e7 # 10MB
TRIPLEO_UI_LOG_FILENAME = 'tripleo-ui.logs'
API_NETWORK = 'InternalApi'
LEGACY_API_NETWORK = 'Internal'
# Default nested depth when recursing Heat stacks
NESTED_DEPTH = 7

View File

@ -43,7 +43,7 @@ ROLE_DATA_YAML = r"""
NETWORK_DATA_YAML = r"""
-
name: anetwork
name: InternalApi
"""
EXPECTED_JINJA_RESULT = r"""
@ -86,7 +86,7 @@ ROLE_DATA_DISABLE_CONSTRAINTS_YAML = r"""
ROLE_DATA_ENABLE_NETWORKS = r"""
- name: RoleWithNetworks
networks:
- anetwork
- InternalApi
"""
JINJA_SNIPPET_DISABLE_CONSTRAINTS_OLD = r"""
@ -125,8 +125,8 @@ JINJA_SNIPPET_ROLE_NETWORKS = r"""
"""
EXPECTED_JINJA_RESULT_ROLE_NETWORKS = r"""
anetworkPort:
type: RoleWithNetworks::anetwork::Port
InternalApiPort:
type: RoleWithNetworks::InternalApi::Port
"""
@ -285,8 +285,12 @@ class ProcessTemplatesActionTest(base.TestCase):
side_effect=return_container_files)
return swift
@mock.patch('tripleo_common.actions.templates.ProcessTemplatesAction'
'._heat_resource_exists')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_process_custom_roles(self, get_obj_client_mock):
def test_process_custom_roles(self, get_obj_client_mock,
resource_exists_mock):
resource_exists_mock.return_value = False
swift = self._custom_roles_mock_objclient(
'foo.j2.yaml', JINJA_SNIPPET)
get_obj_client_mock.return_value = swift
@ -315,9 +319,12 @@ class ProcessTemplatesActionTest(base.TestCase):
swift.put_object.assert_has_calls(
put_object_mock_calls, any_order=True)
@mock.patch('tripleo_common.actions.templates.ProcessTemplatesAction'
'._heat_resource_exists')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def _process_custom_roles_disable_constraints(
self, snippet, get_obj_client_mock):
self, snippet, get_obj_client_mock, resource_exists_mock):
resource_exists_mock.return_value = False
swift = self._custom_roles_mock_objclient(
'disable-constraints.role.j2.yaml', snippet,
ROLE_DATA_DISABLE_CONSTRAINTS_YAML)
@ -352,8 +359,12 @@ class ProcessTemplatesActionTest(base.TestCase):
self._process_custom_roles_disable_constraints(
JINJA_SNIPPET_DISABLE_CONSTRAINTS)
@mock.patch('tripleo_common.actions.templates.ProcessTemplatesAction'
'._heat_resource_exists')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_custom_roles_networks(self, get_obj_client_mock):
def test_custom_roles_networks(self, get_obj_client_mock,
resource_exists_mock):
resource_exists_mock.return_value = False
swift = self._custom_roles_mock_objclient(
'role-networks.role.j2.yaml', JINJA_SNIPPET_ROLE_NETWORKS,
ROLE_DATA_ENABLE_NETWORKS)
@ -486,3 +497,104 @@ class ProcessTemplatesActionTest(base.TestCase):
# Test - J2 exclude file empty
action = templates.ProcessTemplatesAction()
self.assertTrue({'name': []} == action._get_j2_excludes_file(mock_ctx))
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_heat_resource_exists(self, client_mock):
mock_ctx = mock.MagicMock()
heat_client = mock.MagicMock()
heat_client.stacks.list.return_value = [
mock.MagicMock(stack_name='overcloud')
]
heat_client.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='InternalNetwork'
),
]
client_mock.return_value = heat_client
action = templates.ProcessTemplatesAction()
self.assertTrue(action._heat_resource_exists('InternalNetwork',
mock_ctx))
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
def test_no_heat_resource_exists(self, client_mock):
mock_ctx = mock.MagicMock()
heat_client = mock.MagicMock()
heat_client.stacks.list.return_value = [
mock.MagicMock(stack_name='overcloud')
]
heat_client.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='InternalApiNetwork'
),
]
client_mock.return_value = heat_client
action = templates.ProcessTemplatesAction()
self.assertFalse(action._heat_resource_exists('InternalNetwork',
mock_ctx))
@mock.patch('tripleo_common.actions.templates.ProcessTemplatesAction'
'._heat_resource_exists')
@mock.patch('tripleo_common.actions.templates.ProcessTemplatesAction'
'._j2_render_and_put')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_legacy_api_network_exists(self, get_obj_client_mock, j2_mock,
resource_exists_mock):
resource_exists_mock.return_value = True
swift = self._custom_roles_mock_objclient(
'role-networks.role.j2.yaml', JINJA_SNIPPET_ROLE_NETWORKS,
ROLE_DATA_ENABLE_NETWORKS)
get_obj_client_mock.return_value = swift
# Test
action = templates.ProcessTemplatesAction()
mock_ctx = mock.MagicMock()
action._process_custom_roles(mock_ctx)
expected_j2_template = get_obj_client_mock.get_object(
action.container, 'foo.j2.yaml')[1]
expected_j2_data = {'roles': [{'name': 'CustomRole'}],
'networks': [{'name': 'InternalApi',
'compat_name': 'Internal'}]
}
assert j2_mock.called_with(expected_j2_template, expected_j2_data,
'foo.yaml', mock_ctx)
@mock.patch('tripleo_common.actions.templates.ProcessTemplatesAction'
'._heat_resource_exists')
@mock.patch('tripleo_common.actions.templates.ProcessTemplatesAction'
'._j2_render_and_put')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_no_legacy_api_network_exists(self, get_obj_client_mock, j2_mock,
resource_exists_mock):
resource_exists_mock.return_value = False
swift = self._custom_roles_mock_objclient(
'role-networks.role.j2.yaml', JINJA_SNIPPET_ROLE_NETWORKS,
ROLE_DATA_ENABLE_NETWORKS)
get_obj_client_mock.return_value = swift
# Test
action = templates.ProcessTemplatesAction()
mock_ctx = mock.MagicMock()
action._process_custom_roles(mock_ctx)
expected_j2_template = get_obj_client_mock.get_object(
action.container, 'foo.j2.yaml')[1]
expected_j2_data = {'roles': [{'name': 'CustomRole'}],
'networks': [{'name': 'InternalApi'}]
}
assert j2_mock.called_with(expected_j2_template, expected_j2_data,
'foo.yaml', mock_ctx)