# Copyright 2016 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
import yaml

from swiftclient import exceptions as swiftexceptions

from tripleo_common.actions import parameters
from tripleo_common import constants
from tripleo_common import exception
from tripleo_common.tests import base
from tripleo_common.utils import passwords as password_utils

_EXISTING_PASSWORDS = {
    'PlacementPassword': 'VFJeqBKbatYhQm9jja67hufft',
    'MistralPassword': 'VFJeqBKbatYhQm9jja67hufft',
    'BarbicanPassword': 'MGGQBtgKT7FnywvkcdMwE9nhx',
    'BarbicanSimpleCryptoKek': 'dGhpcnR5X3R3b19ieXRlX2tleWJsYWhibGFoYmxhaGg=',
    'AdminPassword': 'jFmY8FTpvtF2e4d4ReXvmUP8k',
    'CeilometerMeteringSecret': 'CbHTGK4md4Cc8P8ZyzTns6wry',
    'ZaqarPassword': 'bbFgCTFbAH8vf9n3xvZCP8aMR',
    'NovaPassword': '7dZATgVPwD7Ergs9kTTDMCr7F',
    'MysqlRootPassword': 'VqJYpEdKks',
    'RabbitCookie': 'BqJYpEdKksAqJYpEdKks',
    'HeatAuthEncryptionKey': '9xZXehsKc2HbmFFMKjuqxTJHn',
    'PcsdPassword': 'KjEzeitus8eu751a',
    'HorizonSecret': 'mjEzeitus8eu751B',
    'NovajoinPassword': '7dZATgVPwD7Ergs9kTTDMCr7F',
    'IronicPassword': '4hFDgn9ANeVfuqk84pHpD4ksa',
    'RedisPassword': 'xjj3QZDcUQmU6Q7NzWBHRUhGd',
    'SaharaPassword': 'spFvYGezdFwnTk7NPxgYTbUPh',
    'AdminToken': 'jq6G6HyZtj7dcZEvuyhAfjutM',
    'CinderPassword': 'dcxC3xyUcrmvzfrrxpAd3REcm',
    'CongressPassword': 'DwcKvMqXMuNYYFU4zTCuG4234',
    'GlancePassword': 'VqJYNEdKKsGZtgnHct77XBtrV',
    'RabbitPassword': 'ahuHRXdPMx9rzCdjD9CJJNCgA',
    'RpcPassword': 'ahuHRXdPMx9rzCdjD9CJJNCgA',
    'NotifyPassword': 'ahuHRXdPMx9rzCdjD9CJJNCgA',
    'CephAdminKey': b'AQCQXtlXAAAAABAAT4Gk+U8EqqStL+JFa9bp1Q==',
    'HAProxyStatsPassword': 'P8tbdK6n4YUkTaUyy8XgEVTe6',
    'CeilometerPassword': 'RRdpwK6qf2pbKz2UtzxqauAdk',
    'GnocchiPassword': 'cRYHcUkMuJeK3vyU9pCaznUZc',
    'HeatStackDomainAdminPassword': 'GgTRyWzKYsxK4mReTJ4CM6sMc',
    'CephRgwKey': b'AQCQXtlXAAAAABAAUKcqUMu6oMjAXMjoUV4/3A==',
    'AodhPassword': '8VZXehsKc2HbmFFMKYuqxTJHn',
    'PankoPassword': 'cVZXehsSc2KdmFFMKDudxTLKn',
    'OctaviaHeartbeatKey': 'oct-heartbeat-key',
    'OctaviaPassword': 'NMl7j3nKk1VVwMxUZC8Cgw==',
    'OctaviaServerCertsKeyPassphrase': 'aW5zZWN1cmUta2V5LWRvLW5vdC11c2U=',
    'OctaviaCaKeyPassphrase': 'SLj4c3uCk4DDxPwQOG1Heb==',
    'ManilaPassword': 'NYJN86Fua3X8AVFWmMhQa2zTH',
    'NeutronMetadataProxySharedSecret': 'Q2YgUCwmBkYdqsdhhCF4hbghu',
    'CephMdsKey': b'AQCQXtlXAAAAABAAT4Gk+U8EqqStL+JFa9bp1Q==',
    'CephManilaClientKey': b'AQANOFFY1NW6AxAAu6jWI3YSOsp2QWusb5Y3DQ==',
    'CephMonKey': b'AQCQXtlXAAAAABAA9l+59N3yH+C49Y0JiKeGFg==',
    'CephGrafanaAdminPassword': 'NYJN86Fua3X8AVFWmMhQa2zTH',
    'CephDashboardAdminPassword': 'NYJN86Fua3X8AVFWmMhQa2zTH',
    'SwiftHashSuffix': 'td8mV6k7TYEGKCDvjVBwckpn9',
    'SnmpdReadonlyUserPassword': 'TestPassword',
    'SwiftPassword': 'z6EWAVfW7CuxvKdzjWTdrXCeg',
    'HeatPassword': 'bREnsXtMHKTHxt8XW6NXAYr48',
    'MysqlClustercheckPassword': 'jN4RMMWWJ4sycaRwh7UvrAtfX',
    'CephClientKey': b'AQCQXtlXAAAAABAAKyc+8St8i9onHyu2mPk+vg==',
    'NeutronPassword': 'ZxAjdU2UXCV4GM3WyPKrzAZXD',
    'DesignatePassword': 'wHYj7rftFzHMpJKnGxbjjR9CW',
    'DesignateRndcKey': 'hB8XaZRd2Tf00jKsyoXpyw==',
    'KeystoneCredential0': 'ftJNQ_XlDUK7Lgvv1kdWf3SyqVsrvNDgoNV4kJg3yzw=',
    'KeystoneCredential1': 'c4MFq82TQLFLKpiiUjrKkp15dafE2ALcD3jbaIu3rfE=',
    'KeystoneFernetKey0': 'O8NSPxr4zXBBAoGIj-5aUmtE7-Jk5a4ptVsEhzJ8Vd8=',
    'KeystoneFernetKey1': 'AueoL37kd6eLjV29AG-Ruxu5szW47osgXx6aPOqtI6I=',
    'KeystoneFernetKeys': {
        '/etc/keystone/fernet-keys/0': {'content': 'IAMAVERYSAFEKEY'},
        '/etc/keystone/fernet-keys/1': {'content': 'IALSOAMAVERYSAFEKEY'}
    },
    'CephClusterFSID': u'97c16f44-b62c-11e6-aed3-185e0f73fdc5',
    'Ec2ApiPassword': 'FPvz2WiWxrHVWrmSSvv44bqmr',
    'EtcdInitialClusterToken': 'fcVZXehsSc2KdmFFMKDudxTLKa',
    'PacemakerRemoteAuthkey':
        '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'
        'sGhUtCQVed7dtj12',
    'MigrationSshKey': {
        'private_key': 'private_key',
        'public_key': 'public_key'
        },
    'LibvirtTLSPassword': 'xCdt9yeamKz8Fb6EGba9u82XU',
}


class GetParametersActionTest(base.TestCase):

    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_get')
    @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_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run(self, mock_get_object_client,
                 mock_get_orchestration_client,
                 mock_get_template_contents,
                 mock_process_multiple_environments_and_files,
                 mock_cache_get,
                 mock_cache_set):

        mock_ctx = mock.MagicMock()
        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)
        swift.get_object.side_effect = (
            ({}, mock_env),
            swiftexceptions.ClientException('atest2'),
            ({}, mock_env)
        )
        mock_get_object_client.return_value = swift

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30'
        })
        mock_process_multiple_environments_and_files.return_value = ({}, {})

        mock_heat = mock.MagicMock()
        mock_heat.stacks.validate.return_value = {}
        mock_get_orchestration_client.return_value = mock_heat

        mock_cache_get.return_value = None
        # Test
        action = parameters.GetParametersAction()
        action.run(mock_ctx)
        mock_heat.stacks.validate.assert_called_once_with(
            environment={},
            files={},
            show_nested=True,
            template={'heat_template_version': '2016-04-30'},
        )
        mock_cache_get.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get"
        )
        mock_cache_set.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get",
            {'heat_resource_tree': {}, 'environment_parameters': None}
        )


class ResetParametersActionTest(base.TestCase):

    @mock.patch('tripleo_common.utils.plan.'
                'cache_delete')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run(self, mock_get_object_client, mock_cache):

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}],
            'parameter_defaults': {'SomeTestParameter': 42}
        }, default_flow_style=False)
        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        # Test
        action = parameters.ResetParametersAction()

        action.run(mock_ctx)

        mock_env_reset = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)

        swift.put_object.assert_called_once_with(
            constants.DEFAULT_CONTAINER_NAME,
            constants.PLAN_ENVIRONMENT,
            mock_env_reset
        )
        mock_cache.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get"
        )


class UpdateParametersActionTest(base.TestCase):

    @mock.patch('tripleo_common.actions.parameters.uuid')
    @mock.patch('heatclient.common.template_utils.'
                'process_multiple_environments_and_files')
    @mock.patch('heatclient.common.template_utils.'
                'get_template_contents')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_object_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    def test_run(self, mock_get_orchestration_client_client,
                 mock_get_object_client, mock_cache,
                 mock_get_template_contents, mock_env_files,
                 mock_uuid):

        mock_env_files.return_value = ({}, {})

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")

        mock_env = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            '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

        mock_heat = mock.MagicMock()
        mock_get_orchestration_client_client.return_value = mock_heat

        mock_heat.stacks.validate.return_value = {
            "Type": "Foo",
            "Description": "Le foo bar",
            "Parameters": {"bar": {"foo": "bar barz"}},
            "NestedParameters": {"Type": "foobar"}
        }

        mock_uuid.uuid4.return_value = "cheese"

        expected_value = {
            'environment_parameters': None,
            'heat_resource_tree': {
                'parameters': {'bar': {'foo': 'bar barz',
                                       'name': 'bar'}},
                'resources': {'cheese': {
                    'id': 'cheese',
                    'name': 'Root',
                    'description': 'Le foo bar',
                    'parameters': ['bar'],
                    'resources': ['cheese'],
                    'type': 'Foo'}
                }
            }
        }

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30'
        })

        # Test
        test_parameters = {'SomeTestParameter': 42}
        action = parameters.UpdateParametersAction(test_parameters)
        return_value = action.run(mock_ctx)

        mock_env_updated = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            'temp_environment': 'temp_environment',
            'parameter_defaults': {'SomeTestParameter': 42},
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)

        swift.put_object.assert_any_call(
            constants.DEFAULT_CONTAINER_NAME,
            constants.PLAN_ENVIRONMENT,
            mock_env_updated
        )

        mock_heat.stacks.validate.assert_called_once_with(
            environment={},
            files={},
            show_nested=True,
            template={'heat_template_version': '2016-04-30'},
        )

        mock_cache.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get",
            expected_value
        )
        self.assertEqual(return_value, expected_value)

    @mock.patch('heatclient.common.template_utils.'
                'process_multiple_environments_and_files')
    @mock.patch('heatclient.common.template_utils.'
                'get_template_contents')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_object_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    def test_run_new_key(self, mock_get_orchestration_client_client,
                         mock_get_object_client, mock_cache,
                         mock_get_template_contents, mock_env_files):

        mock_env_files.return_value = ({}, {})

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            '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

        heat = mock.MagicMock()
        heat.stacks.validate.return_value = {}
        mock_get_orchestration_client_client.return_value = heat

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30'
        })

        # Test
        test_parameters = {'SomeTestParameter': 42}
        action = parameters.UpdateParametersAction(test_parameters,
                                                   key='test_key')
        action.run(mock_ctx)

        mock_env_updated = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            'temp_environment': 'temp_environment',
            'test_key': {'SomeTestParameter': 42},
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)

        swift.put_object.assert_any_call(
            constants.DEFAULT_CONTAINER_NAME,
            constants.PLAN_ENVIRONMENT,
            mock_env_updated
        )

        heat.stacks.validate.assert_called_once_with(
            environment={},
            files={},
            show_nested=True,
            template={'heat_template_version': '2016-04-30'},
        )

        mock_cache.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get",
            {'environment_parameters': None, 'heat_resource_tree': {}}
        )


class UpdateRoleParametersActionTest(base.TestCase):

    @mock.patch('heatclient.common.template_utils.'
                'process_multiple_environments_and_files')
    @mock.patch('heatclient.common.template_utils.'
                'get_template_contents')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.utils.parameters.set_count_and_flavor_params')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_baremetal_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_compute_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_object_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    def test_run(self, mock_get_orchestration_client_client,
                 mock_get_object_client, mock_get_compute_client,
                 mock_get_baremetal_client, mock_set_count_and_flavor,
                 mock_cache, mock_get_template_contents, mock_env_files):

        mock_env_files.return_value = ({}, {})

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'name': 'overcast'
        }, 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.yaml'}])

        swift.get_container = mock.MagicMock(
            side_effect=return_container_files)

        mock_get_object_client.return_value = swift

        heat = mock.MagicMock()
        heat.stacks.validate.return_value = {}
        mock_get_orchestration_client_client.return_value = heat

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30'
        })

        params = {'CephStorageCount': 1,
                  'OvercloudCephStorageFlavor': 'ceph-storage'}
        mock_set_count_and_flavor.return_value = params

        action = parameters.UpdateRoleParametersAction('ceph-storage',
                                                       'overcast')
        action.run(mock_ctx)

        mock_env_updated = yaml.safe_dump({
            'name': 'overcast',
            'parameter_defaults': params
        }, default_flow_style=False)

        swift.put_object.assert_any_call(
            'overcast',
            constants.PLAN_ENVIRONMENT,
            mock_env_updated
        )

        heat.stacks.validate.assert_called_once_with(
            environment={},
            files={},
            show_nested=True,
            template={'heat_template_version': '2016-04-30'},
        )

        mock_cache.assert_called_once_with(
            swift,
            "overcast",
            "tripleo.parameters.get",
            {'environment_parameters': None, 'heat_resource_tree': {}}
        )


class GeneratePasswordsActionTest(base.TestCase):

    @mock.patch('tripleo_common.utils.plan.'
                'cache_delete')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    @mock.patch('tripleo_common.utils.passwords.'
                'get_snmpd_readonly_user_password')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client', return_value="TestPassword")
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run(self, mock_get_object_client,
                 mock_get_workflow_client,
                 mock_get_snmpd_readonly_user_password,
                 mock_get_orchestration_client, mock_cache):

        mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'name': 'overcast',
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}],
        }, default_flow_style=False)
        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        mock_orchestration = mock.MagicMock()
        mock_orchestration.stacks.environment.return_value = {
            'parameter_defaults': {}
        }
        mock_resource = mock.MagicMock()
        mock_resource.attributes = {
            'value': 'existing_value'
        }
        mock_orchestration.resources.get.return_value = mock_resource
        mock_get_orchestration_client.return_value = mock_orchestration

        action = parameters.GeneratePasswordsAction()
        result = action.run(mock_ctx)

        for password_param_name in constants.PASSWORD_PARAMETER_NAMES:
            self.assertTrue(password_param_name in result,
                            "%s is not in %s" % (password_param_name, result))

            if password_param_name in \
                    constants.LEGACY_HEAT_PASSWORD_RESOURCE_NAMES:
                self.assertEqual(result[password_param_name], 'existing_value')
            else:
                self.assertNotEqual(result[password_param_name],
                                    'existing_value')

        mock_cache.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get"
        )

    @mock.patch('tripleo_common.utils.plan.'
                'cache_delete')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    @mock.patch('tripleo_common.utils.passwords.'
                'create_ssh_keypair')
    @mock.patch('tripleo_common.utils.passwords.'
                'create_fernet_keys_repo_structure_and_keys')
    @mock.patch('tripleo_common.utils.passwords.'
                'get_snmpd_readonly_user_password')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run_passwords_exist(self, mock_get_object_client,
                                 mock_get_workflow_client,
                                 mock_get_snmpd_readonly_user_password,
                                 mock_fernet_keys_setup,
                                 mock_create_ssh_keypair,
                                 mock_get_orchestration_client,
                                 mock_cache):

        mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
        mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
                                                'private_key': 'Bar'}
        mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
                                               '/tmp/bar': {'content': 'Bar'}}

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}],
            'passwords': _EXISTING_PASSWORDS.copy()
        }, default_flow_style=False)
        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        mock_orchestration = mock.MagicMock()
        mock_orchestration.stacks.environment.return_value = {
            'parameter_defaults': {}
        }
        mock_get_orchestration_client.return_value = mock_orchestration

        action = parameters.GeneratePasswordsAction()
        result = action.run(mock_ctx)

        # ensure old passwords used and no new generation
        self.assertEqual(_EXISTING_PASSWORDS, result)
        mock_cache.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get"
        )

    @mock.patch('tripleo_common.utils.plan.'
                'cache_delete')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    @mock.patch('tripleo_common.utils.passwords.'
                'create_ssh_keypair')
    @mock.patch('tripleo_common.utils.passwords.'
                'create_fernet_keys_repo_structure_and_keys')
    @mock.patch('tripleo_common.utils.passwords.'
                'get_snmpd_readonly_user_password')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run_rotate_no_rotate_list(self, mock_get_object_client,
                                       mock_get_workflow_client,
                                       mock_get_snmpd_readonly_user_password,
                                       mock_fernet_keys_setup,
                                       mock_create_ssh_keypair,
                                       mock_get_orchestration_client,
                                       mock_cache):

        mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
        mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
                                                'private_key': 'Bar'}
        mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
                                               '/tmp/bar': {'content': 'Bar'}}

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}],
            'passwords': _EXISTING_PASSWORDS.copy()
        }, default_flow_style=False)
        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        mock_orchestration = mock.MagicMock()
        mock_orchestration.stacks.environment.return_value = {
            'parameter_defaults': {}
        }

        mock_resource = mock.MagicMock()
        mock_resource.attributes = {
            'value': 'existing_value'
        }
        mock_orchestration.resources.get.return_value = mock_resource
        mock_get_orchestration_client.return_value = mock_orchestration

        action = parameters.GeneratePasswordsAction(rotate_passwords=True)
        result = action.run(mock_ctx)

        # ensure passwords in the DO_NOT_ROTATE_LIST are not modified
        for name in constants.DO_NOT_ROTATE_LIST:
            self.assertEqual(_EXISTING_PASSWORDS[name], result[name])

        # ensure all passwords are generated
        for name in constants.PASSWORD_PARAMETER_NAMES:
            self.assertTrue(name in result, "%s is not in %s" % (name, result))

        # ensure new passwords have been generated
        self.assertNotEqual(_EXISTING_PASSWORDS, result)
        mock_cache.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get"
        )

    @mock.patch('tripleo_common.utils.plan.'
                'cache_delete')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    @mock.patch('tripleo_common.utils.passwords.'
                'create_ssh_keypair')
    @mock.patch('tripleo_common.utils.passwords.'
                'create_fernet_keys_repo_structure_and_keys')
    @mock.patch('tripleo_common.utils.passwords.'
                'get_snmpd_readonly_user_password')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run_rotate_with_rotate_list(self, mock_get_object_client,
                                         mock_get_workflow_client,
                                         mock_get_snmpd_readonly_user_password,
                                         mock_fernet_keys_setup,
                                         mock_create_ssh_keypair,
                                         mock_get_orchestration_client,
                                         mock_cache):

        mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
        mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
                                                'private_key': 'Bar'}
        mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
                                               '/tmp/bar': {'content': 'Bar'}}

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}],
            'passwords': _EXISTING_PASSWORDS.copy()
        }, default_flow_style=False)
        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        mock_orchestration = mock.MagicMock()
        mock_orchestration.stacks.environment.return_value = {
            'parameter_defaults': {}
        }
        mock_resource = mock.MagicMock()
        mock_resource.attributes = {
            'value': 'existing_value'
        }
        mock_orchestration.resources.get.return_value = mock_resource
        mock_get_orchestration_client.return_value = mock_orchestration

        rotate_list = [
            'MistralPassword',
            'BarbicanPassword',
            'AdminPassword',
            'CeilometerMeteringSecret',
            'ZaqarPassword',
            'NovaPassword',
            'MysqlRootPassword'
        ]

        action = parameters.GeneratePasswordsAction(
            rotate_passwords=True,
            rotate_pw_list=rotate_list
        )
        result = action.run(mock_ctx)

        # ensure only specified passwords are regenerated
        for name in constants.PASSWORD_PARAMETER_NAMES:
            self.assertTrue(name in result, "%s is not in %s" % (name, result))
            if name in rotate_list:
                self.assertNotEqual(_EXISTING_PASSWORDS[name], result[name])
            else:
                self.assertEqual(_EXISTING_PASSWORDS[name], result[name])

        mock_cache.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get"
        )

    @mock.patch('tripleo_common.utils.plan.'
                'cache_delete')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    @mock.patch('tripleo_common.utils.passwords.'
                'create_ssh_keypair')
    @mock.patch('tripleo_common.utils.passwords.'
                'create_fernet_keys_repo_structure_and_keys')
    @mock.patch('tripleo_common.utils.passwords.'
                'get_snmpd_readonly_user_password')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_passwords_exist_in_heat(self, mock_get_object_client,
                                     mock_get_workflow_client,
                                     mock_get_snmpd_readonly_user_password,
                                     mock_fernet_keys_setup,
                                     mock_create_ssh_keypair,
                                     mock_get_orchestration_client,
                                     mock_cache):

        mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
        mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
                                                'private_key': 'Bar'}
        mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
                                               '/tmp/bar': {'content': 'Bar'}}

        existing_passwords = _EXISTING_PASSWORDS.copy()
        existing_passwords.pop("AdminPassword")

        mock_ctx = mock.MagicMock()
        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'name': constants.DEFAULT_CONTAINER_NAME,
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}],
            'passwords': existing_passwords.copy()
        }, default_flow_style=False)

        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        mock_orchestration = mock.MagicMock()
        mock_orchestration.stacks.environment.return_value = {
            'parameter_defaults': {
                'AdminPassword': 'ExistingPasswordInHeat',
            }
        }
        mock_get_orchestration_client.return_value = mock_orchestration

        action = parameters.GeneratePasswordsAction()
        result = action.run(mock_ctx)

        existing_passwords["AdminPassword"] = "ExistingPasswordInHeat"
        # ensure old passwords used and no new generation
        self.assertEqual(existing_passwords, result)
        mock_cache.assert_called_once_with(
            swift,
            "overcloud",
            "tripleo.parameters.get"
        )


class GetPasswordsActionTest(base.TestCase):

    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_password_from_parameter_defaults(self,
                                              mock_get_object_client,
                                              mock_get_orchestration_client):

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            "name": constants.DEFAULT_CONTAINER_NAME,
            "parameter_defaults": _EXISTING_PASSWORDS,
        }, default_flow_style=False)
        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        mock_orchestration = mock.MagicMock()
        mock_get_orchestration_client.return_value = mock_orchestration

        action = parameters.GetPasswordsAction()
        result = action.run(mock_ctx)

        # ensure old passwords used and no new generation
        self.assertEqual(_EXISTING_PASSWORDS, result)

    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_password_from_generated_passwords(self,
                                               mock_get_object_client,
                                               mock_get_orchestration_client):

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            "name": constants.DEFAULT_CONTAINER_NAME,
            "parameter_defaults": {},
            "passwords": _EXISTING_PASSWORDS,

        }, default_flow_style=False)
        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        mock_orchestration = mock.MagicMock()
        mock_get_orchestration_client.return_value = mock_orchestration

        action = parameters.GetPasswordsAction()
        result = action.run(mock_ctx)

        # ensure old passwords used and no new generation
        self.assertEqual(_EXISTING_PASSWORDS, result)

    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_password_merging_passwords(self,
                                        mock_get_object_client,
                                        mock_get_orchestration_client):

        parameter_defaults = _EXISTING_PASSWORDS.copy()
        passwords = {"AdminPassword": parameter_defaults.pop("AdminPassword")}

        mock_ctx = mock.MagicMock()

        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            "name": constants.DEFAULT_CONTAINER_NAME,
            "parameter_defaults": parameter_defaults,
            "passwords": passwords
        }, default_flow_style=False)

        swift.get_object.return_value = ({}, mock_env)
        mock_get_object_client.return_value = swift

        mock_orchestration = mock.MagicMock()
        mock_get_orchestration_client.return_value = mock_orchestration

        action = parameters.GetPasswordsAction()
        result = action.run(mock_ctx)

        # ensure old passwords used and no new generation
        self.assertEqual(_EXISTING_PASSWORDS, result)


class GenerateFencingParametersActionTestCase(base.TestCase):

    @mock.patch('tripleo_common.utils.nodes.generate_hostmap')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_compute_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_baremetal_client')
    def test_no_success(self, mock_get_baremetal, mock_get_compute,
                        mock_generate_hostmap):
        mock_ctx = mock.MagicMock()
        test_hostmap = {
            "00:11:22:33:44:55": {
                "compute_name": "compute_name_0",
                "baremetal_name": "baremetal_name_0"
                },
            "11:22:33:44:55:66": {
                "compute_name": "compute_name_1",
                "baremetal_name": "baremetal_name_1"
                },
            "aa:bb:cc:dd:ee:ff": {
                "compute_name": "compute_name_4",
                "baremetal_name": "baremetal_name_4"
                },
            "bb:cc:dd:ee:ff:gg": {
                "compute_name": "compute_name_5",
                "baremetal_name": "baremetal_name_5"
                }
            }
        mock_generate_hostmap.return_value = test_hostmap

        test_envjson = [{
            "name": "control-0",
            "pm_password": "control-0-password",
            "pm_type": "ipmi",
            "pm_user": "control-0-admin",
            "pm_addr": "0.1.2.3",
            "pm_port": "0123",
            "mac": [
                "00:11:22:33:44:55"
            ]
        }, {
            "name": "control-1",
            "pm_password": "control-1-password",
            # Still support deprecated drivers
            "pm_type": "pxe_ipmitool",
            "pm_user": "control-1-admin",
            "pm_addr": "1.2.3.4",
            "mac": [
                "11:22:33:44:55:66"
            ]
        }, {
            # test node using redfish pm
            "name": "compute-4",
            "pm_password": "calvin",
            "pm_type": "redfish",
            "pm_user": "root",
            "pm_addr": "172.16.0.1:8000",
            "pm_port": "8000",
            "redfish_verify_ca": "false",
            "pm_system_id": "/redfish/v1/Systems/5678",
            "mac": [
                "aa:bb:cc:dd:ee:ff"
            ]
        }, {
            # This is an extra node on oVirt/RHV
            "name": "control-3",
            "pm_password": "ovirt-password",
            "pm_type": "staging-ovirt",
            "pm_user": "admin@internal",
            "pm_addr": "3.4.5.6",
            "pm_vm_name": "control-3",
            "mac": [
                "bb:cc:dd:ee:ff:gg"
            ]
        }, {
            # This is an extra node that is not in the hostmap, to ensure we
            # cope with unprovisioned nodes
            "name": "control-2",
            "pm_password": "control-2-password",
            "pm_type": "ipmi",
            "pm_user": "control-2-admin",
            "pm_addr": "2.3.4.5",
            "mac": [
                "22:33:44:55:66:77"
            ]
        }
        ]

        action = parameters.GenerateFencingParametersAction(test_envjson,
                                                            28,
                                                            5,
                                                            0,
                                                            True)

        result = action.run(mock_ctx)["parameter_defaults"]

        self.assertTrue(result["EnableFencing"])
        self.assertEqual(len(result["FencingConfig"]["devices"]), 4)
        self.assertEqual(result["FencingConfig"]["devices"][0], {
                         "agent": "fence_ipmilan",
                         "host_mac": "00:11:22:33:44:55",
                         "params": {
                             "delay": 28,
                             "ipaddr": "0.1.2.3",
                             "ipport": "0123",
                             "lanplus": True,
                             "privlvl": 5,
                             "login": "control-0-admin",
                             "passwd": "control-0-password",
                             "pcmk_host_list": "compute_name_0"
                             }
                         })
        self.assertEqual(result["FencingConfig"]["devices"][1], {
                         "agent": "fence_ipmilan",
                         "host_mac": "11:22:33:44:55:66",
                         "params": {
                             "delay": 28,
                             "ipaddr": "1.2.3.4",
                             "lanplus": True,
                             "privlvl": 5,
                             "login": "control-1-admin",
                             "passwd": "control-1-password",
                             "pcmk_host_list": "compute_name_1"
                             }
                         })
        self.assertEqual(result["FencingConfig"]["devices"][2], {
                         "agent": "fence_redfish",
                         "host_mac": "aa:bb:cc:dd:ee:ff",
                         "params": {
                             "delay": 28,
                             "ipaddr": "172.16.0.1:8000",
                             "ipport": "8000",
                             "lanplus": True,
                             "privlvl": 5,
                             "login": "root",
                             "passwd": "calvin",
                             "systems_uri": "/redfish/v1/Systems/5678",
                             "ssl_insecure": "true",
                             "pcmk_host_list": "compute_name_4"
                             }
                         })
        self.assertEqual(result["FencingConfig"]["devices"][3], {
                         "agent": "fence_rhevm",
                         "host_mac": "bb:cc:dd:ee:ff:gg",
                         "params": {
                             "delay": 28,
                             "ipaddr": "3.4.5.6",
                             "login": "admin@internal",
                             "passwd": "ovirt-password",
                             "port": "control-3",
                             "ssl": 1,
                             "ssl_insecure": 1,
                             "pcmk_host_list": "compute_name_5"
                             }
                         })


class GetFlattenedParametersActionTest(base.TestCase):

    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_get')
    @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_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_empty_resource_tree(self, mock_get_object_client,
                                 mock_get_orchestration_client,
                                 mock_get_template_contents,
                                 mock_process_multiple_environments_and_files,
                                 mock_cache_get,
                                 mock_cache_set):

        mock_ctx = mock.MagicMock()
        mock_cache_get.return_value = None
        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)
        swift.get_object.side_effect = (
            ({}, mock_env),
            swiftexceptions.ClientException('atest2'),
            ({}, mock_env)
        )
        mock_get_object_client.return_value = swift

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30'
        })
        mock_process_multiple_environments_and_files.return_value = ({}, {})

        mock_heat = mock.MagicMock()
        mock_get_orchestration_client.return_value = mock_heat

        mock_heat.stacks.validate.return_value = {}

        expected_value = {
            'heat_resource_tree': {},
            'environment_parameters': None,
        }

        # Test
        action = parameters.GetFlattenedParametersAction()
        result = action.run(mock_ctx)
        mock_heat.stacks.validate.assert_called_once_with(
            environment={},
            files={},
            show_nested=True,
            template={'heat_template_version': '2016-04-30'},
        )
        self.assertEqual(result, expected_value)

    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_get')
    @mock.patch('uuid.uuid4', side_effect=['1', '2'])
    @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_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_valid_resource_tree(self, mock_get_object_client,
                                 mock_get_orchestration_client,
                                 mock_get_template_contents,
                                 mock_process_multiple_environments_and_files,
                                 mock_uuid,
                                 mock_cache_get,
                                 mock_cache_set):

        mock_ctx = mock.MagicMock()
        mock_cache_get.return_value = None
        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)
        swift.get_object.side_effect = (
            ({}, mock_env),
            swiftexceptions.ClientException('atest2'),
            ({}, mock_env)
        )
        mock_get_object_client.return_value = swift

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30'
        })
        mock_process_multiple_environments_and_files.return_value = ({}, {})

        mock_heat = mock.MagicMock()
        mock_get_orchestration_client.return_value = mock_heat

        mock_heat.stacks.validate.return_value = {
            'NestedParameters': {
                'CephStorageHostsDeployment': {
                    'Type': 'OS::Heat::StructuredDeployments',
                },
            },
            'description': 'sample',
            'Parameters': {
                'ControllerCount': {
                    'Default': 1,
                    'Type': 'Number',
                },
            }
        }

        expected_value = {
            'heat_resource_tree': {
                'resources': {
                    '1': {
                        'id': '1',
                        'name': 'Root',
                        'resources': [
                            '2'
                        ],
                        'parameters': [
                            'ControllerCount'
                        ]
                    },
                    '2': {
                        'id': '2',
                        'name': 'CephStorageHostsDeployment',
                        'type': 'OS::Heat::StructuredDeployments'
                    }
                },
                'parameters': {
                    'ControllerCount': {
                        'default': 1,
                        'type': 'Number',
                        'name': 'ControllerCount'
                    }
                },
            },
            'environment_parameters': None,
        }

        # Test
        action = parameters.GetFlattenedParametersAction()
        result = action.run(mock_ctx)
        self.assertEqual(result, expected_value)


class GetProfileOfFlavorActionTest(base.TestCase):

    @mock.patch('tripleo_common.utils.parameters.get_profile_of_flavor')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_compute_client')
    def test_profile_found(self, mock_get_compute_client,
                           mock_get_profile_of_flavor):
        mock_ctx = mock.MagicMock()
        mock_get_profile_of_flavor.return_value = 'compute'
        action = parameters.GetProfileOfFlavorAction('oooq_compute')
        result = action.run(mock_ctx)
        expected_result = "compute"
        self.assertEqual(result, expected_result)

    @mock.patch('tripleo_common.utils.parameters.get_profile_of_flavor')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_compute_client')
    def test_profile_not_found(self, mock_get_compute_client,
                               mock_get_profile_of_flavor):
        mock_ctx = mock.MagicMock()
        profile = (exception.DeriveParamsError, )
        mock_get_profile_of_flavor.side_effect = profile
        action = parameters.GetProfileOfFlavorAction('no_profile')
        result = action.run(mock_ctx)
        self.assertTrue(result.is_error())
        mock_get_profile_of_flavor.assert_called_once()


class RotateFernetKeysActionTest(base.TestCase):

    def test_get_next_index(self):
        action = parameters.RotateFernetKeysAction()
        keys_map = {
            password_utils.KEYSTONE_FERNET_REPO + '0': {
                'content': 'Some key'},
            password_utils.KEYSTONE_FERNET_REPO + '1': {
                'content': 'Some other key'},
        }
        next_index = action.get_next_index(keys_map)
        self.assertEqual(next_index, 2)

    @mock.patch('tripleo_common.utils.passwords.'
                'create_keystone_credential')
    def test_rotate_keys(self, mock_keystone_creds):
        action = parameters.RotateFernetKeysAction()
        mock_keystone_creds.return_value = 'Some new key'

        staged_key_index = password_utils.KEYSTONE_FERNET_REPO + '0'
        new_primary_key_index = password_utils.KEYSTONE_FERNET_REPO + '2'
        keys_map = {
            password_utils.KEYSTONE_FERNET_REPO + '0': {
                'content': 'Some key'},
            password_utils.KEYSTONE_FERNET_REPO + '1': {
                'content': 'Some other key'},
        }
        new_keys_map = action.rotate_keys(keys_map, 2)

        # Staged key should be the new key
        self.assertEqual('Some new key',
                         new_keys_map[staged_key_index]['content'])
        # primary key should be the previous staged key
        self.assertEqual('Some key',
                         new_keys_map[new_primary_key_index]['content'])

    def test_purge_excess_keys_should_purge(self):
        action = parameters.RotateFernetKeysAction()
        keys_map = {
            password_utils.KEYSTONE_FERNET_REPO + '0': {
                'content': 'key0'},
            password_utils.KEYSTONE_FERNET_REPO + '1': {
                'content': 'key1'},
            password_utils.KEYSTONE_FERNET_REPO + '2': {
                'content': 'key2'},
            password_utils.KEYSTONE_FERNET_REPO + '3': {
                'content': 'key3'},
            password_utils.KEYSTONE_FERNET_REPO + '4': {
                'content': 'key4'},
        }
        max_keys = 3
        keys_map = action.purge_excess_keys(max_keys, keys_map)
        self.assertEqual(max_keys, len(keys_map))
        # It should keep index 0, 3 and 4
        self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '0', keys_map)
        self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '3', keys_map)
        self.assertIn(password_utils.KEYSTONE_FERNET_REPO + '4', keys_map)
        # It sould have removed index 1 and 2
        self.assertNotIn(password_utils.KEYSTONE_FERNET_REPO + '1', keys_map)
        self.assertNotIn(password_utils.KEYSTONE_FERNET_REPO + '2', keys_map)

    def test_purge_excess_keys_should_not_purge_if_equal_to_max(self):
        action = parameters.RotateFernetKeysAction()
        keys_map = {
            password_utils.KEYSTONE_FERNET_REPO + '0': {
                'content': 'key0'},
            password_utils.KEYSTONE_FERNET_REPO + '1': {
                'content': 'key1'},
            password_utils.KEYSTONE_FERNET_REPO + '2': {
                'content': 'key2'},
        }
        max_keys = 3
        keys_map = action.purge_excess_keys(max_keys, keys_map)
        self.assertEqual(max_keys, len(keys_map))

    def test_purge_excess_keys_should_not_purge_if_less_than_max(self):
        action = parameters.RotateFernetKeysAction()
        keys_map = {
            password_utils.KEYSTONE_FERNET_REPO + '0': {
                'content': 'key0'},
            password_utils.KEYSTONE_FERNET_REPO + '1': {
                'content': 'key1'},
        }
        max_keys = 3
        keys_map = action.purge_excess_keys(max_keys, keys_map)
        self.assertEqual(2, len(keys_map))


class GetNetworkConfigActionTest(base.TestCase):

    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_get')
    @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_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run_valid_network_config(
            self, mock_get_object_client, mock_get_workflow_client,
            mock_get_orchestration_client, mock_get_template_contents,
            mock_process_multiple_environments_and_files,
            mock_cache_get,
            mock_cache_set):

        mock_ctx = mock.MagicMock()
        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)
        swift.get_object.side_effect = (
            ({}, mock_env),
            swiftexceptions.ClientException('atest2'),
            ({}, mock_env)
        )
        mock_get_object_client.return_value = swift

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30'
        })
        mock_process_multiple_environments_and_files.return_value = ({}, {})

        mock_heat = mock.MagicMock()
        mock_heat.stacks.preview.return_value = mock.Mock(resources=[{
            "resource_identity": {"stack_name": "overcloud-TEMP-Compute-0"},
            "resource_name": "OsNetConfigImpl",
            "properties": {"config": "echo \'{\"network_config\": {}}\'"
                           " > /etc/os-net-config/config.json"}
            }])

        mock_get_orchestration_client.return_value = mock_heat

        mock_cache_get.return_value = None
        expected = {"network_config": {}}
        # Test
        action = parameters.GetNetworkConfigAction(container='overcloud',
                                                   role_name='Compute')
        result = action.run(mock_ctx)
        self.assertEqual(expected, result)
        mock_heat.stacks.preview.assert_called_once_with(
            environment={},
            files={},
            template={'heat_template_version': '2016-04-30'},
            stack_name='overcloud-TEMP',
        )

    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_get')
    @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_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run_valid_network_config_with_no_interface_routes_inputs(
            self, mock_get_object_client, mock_get_workflow_client,
            mock_get_orchestration_client, mock_get_template_contents,
            mock_process_multiple_environments_and_files,
            mock_cache_get,
            mock_cache_set):

        mock_ctx = mock.MagicMock()
        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)
        swift.get_object.side_effect = (
            ({}, mock_env),
            swiftexceptions.ClientException('atest2'),
            ({}, mock_env)
        )
        mock_get_object_client.return_value = swift

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30',
            'resources': {'ComputeGroupVars': {'properties': {
                'value': {'role_networks': ['InternalApi', 'Storage']}
                }
            }}
        })
        mock_process_multiple_environments_and_files.return_value = (
            {}, {'parameter_defaults': {}})

        mock_heat = mock.MagicMock()
        mock_heat.stacks.preview.return_value = mock.Mock(resources=[{
            "resource_identity": {"stack_name": "overcloud-TEMP-Compute-0"},
            "resource_name": "OsNetConfigImpl",
            "properties": {"config": "echo \'{\"network_config\": {}}\'"
                           " > /etc/os-net-config/config.json"}
            }])

        mock_get_orchestration_client.return_value = mock_heat

        mock_cache_get.return_value = None
        expected = {"network_config": {}}
        # Test
        action = parameters.GetNetworkConfigAction(container='overcloud',
                                                   role_name='Compute')
        result = action.run(mock_ctx)
        self.assertEqual(expected, result)
        mock_heat.stacks.preview.assert_called_once_with(
            environment={'parameter_defaults': {
                'InternalApiInterfaceRoutes': [[]],
                'StorageInterfaceRoutes': [[]]}},
            files={},
            template={'heat_template_version': '2016-04-30',
                      'resources': {'ComputeGroupVars': {
                          'properties': {'value': {
                              'role_networks': ['InternalApi',
                                                'Storage']}}}}},
            stack_name='overcloud-TEMP',
        )

    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_get')
    @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_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run_valid_network_config_with_interface_routes_inputs(
            self, mock_get_object_client, mock_get_workflow_client,
            mock_get_orchestration_client, mock_get_template_contents,
            mock_process_multiple_environments_and_files,
            mock_cache_get,
            mock_cache_set):

        mock_ctx = mock.MagicMock()
        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)
        swift.get_object.side_effect = (
            ({}, mock_env),
            swiftexceptions.ClientException('atest2'),
            ({}, mock_env)
        )
        mock_get_object_client.return_value = swift

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30',
            'resources': {'ComputeGroupVars': {'properties': {
                'value': {'role_networks': ['InternalApi', 'Storage']}}}}
        })
        mock_process_multiple_environments_and_files.return_value = (
            {}, {'parameter_defaults': {
                'InternalApiInterfaceRoutes': ['test1'],
                'StorageInterfaceRoutes': ['test2']}})

        mock_heat = mock.MagicMock()
        mock_heat.stacks.preview.return_value = mock.Mock(resources=[{
            "resource_identity": {"stack_name": "overcloud-TEMP-Compute-0"},
            "resource_name": "OsNetConfigImpl",
            "properties": {"config": "echo \'{\"network_config\": {}}\'"
                           " > /etc/os-net-config/config.json"}
            }])

        mock_get_orchestration_client.return_value = mock_heat

        mock_cache_get.return_value = None
        expected = {"network_config": {}}
        # Test
        action = parameters.GetNetworkConfigAction(container='overcloud',
                                                   role_name='Compute')
        result = action.run(mock_ctx)
        self.assertEqual(expected, result)
        mock_heat.stacks.preview.assert_called_once_with(
            environment={'parameter_defaults': {
                'InternalApiInterfaceRoutes': ['test1'],
                'StorageInterfaceRoutes': ['test2']}},
            files={},
            template={'heat_template_version': '2016-04-30',
                      'resources': {'ComputeGroupVars': {'properties': {
                          'value': {'role_networks': ['InternalApi',
                                                      'Storage']}}}}},
            stack_name='overcloud-TEMP',
        )

    @mock.patch('tripleo_common.utils.plan.'
                'cache_set')
    @mock.patch('tripleo_common.utils.plan.'
                'cache_get')
    @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_orchestration_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.'
                'get_workflow_client')
    @mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
    def test_run_invalid_network_config(
            self, mock_get_object_client,
            mock_get_workflow_client, mock_get_orchestration_client,
            mock_get_template_contents,
            mock_process_multiple_environments_and_files,
            mock_cache_get, mock_cache_set):

        mock_ctx = mock.MagicMock()
        swift = mock.MagicMock(url="http://test.com")
        mock_env = yaml.safe_dump({
            'temp_environment': 'temp_environment',
            'template': 'template',
            'environments': [{u'path': u'environments/test.yaml'}]
        }, default_flow_style=False)
        swift.get_object.side_effect = (
            ({}, mock_env),
            swiftexceptions.ClientException('atest2'),
            ({}, mock_env)
        )
        mock_get_object_client.return_value = swift

        mock_get_template_contents.return_value = ({}, {
            'heat_template_version': '2016-04-30'
        })
        mock_process_multiple_environments_and_files.return_value = ({}, {})

        mock_heat = mock.MagicMock()
        mock_heat.stacks.preview.return_value = mock.Mock(resources=[{
            "resource_identity": {"stack_name": "overcloud-TEMP-Compute-0"},
            "resource_name": "OsNetConfigImpl",
            "properties": {"config": ""}
            }])

        mock_get_orchestration_client.return_value = mock_heat

        mock_cache_get.return_value = None
        # Test
        action = parameters.GetNetworkConfigAction(container='overcloud',
                                                   role_name='Compute')
        result = action.run(mock_ctx)
        self.assertTrue(result.is_error())
        mock_heat.stacks.preview.assert_called_once_with(
            environment={},
            files={},
            template={'heat_template_version': '2016-04-30'},
            stack_name='overcloud-TEMP',
        )
