Remove Heat API dependency for overcloud export
Update the overcloud export command to work without a dependency on Heat
API. The export data is updated to create the export data based solely
on saved stack output data and the config-download generated
overcloud.json so that Heat is not needed to generate the export at all.
Change-Id: I0bd4b4a0e33184e0f4d731899138bf4063f3cdac
Signed-off-by: James Slagle <jslagle@redhat.com>
(cherry picked from commit d7af2f5482
)
This commit is contained in:
parent
095182c143
commit
df4694b743
@ -29,12 +29,12 @@ from tripleoclient import utils as oooutils
|
|||||||
LOG = logging.getLogger(__name__ + ".utils")
|
LOG = logging.getLogger(__name__ + ".utils")
|
||||||
|
|
||||||
|
|
||||||
def export_passwords(heat, stack, excludes=True):
|
def export_passwords(working_dir, stack, excludes=True):
|
||||||
"""For each password, check if it's excluded, then check if there's a user
|
"""For each password, check if it's excluded, then check if there's a user
|
||||||
defined value from parameter_defaults, and if not use the value from the
|
defined value from parameter_defaults, and if not use the value from the
|
||||||
generated passwords.
|
generated passwords.
|
||||||
:param heat: tht client
|
:param working_dir: Working dir for the deployment
|
||||||
:type heat: Client
|
:type working_dir: string
|
||||||
:param stack: stack name for password generator
|
:param stack: stack name for password generator
|
||||||
:type stack: string
|
:type stack: string
|
||||||
:param excludes: filter the passwords or not, defaults to `True`
|
:param excludes: filter the passwords or not, defaults to `True`
|
||||||
@ -48,8 +48,13 @@ def export_passwords(heat, stack, excludes=True):
|
|||||||
if re.match(pattern, password, re.I):
|
if re.match(pattern, password, re.I):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
passwords_file = os.path.join(
|
||||||
|
working_dir,
|
||||||
|
constants.PASSWORDS_ENV_FORMAT.format(stack))
|
||||||
|
with open(passwords_file) as f:
|
||||||
|
passwords_env = yaml.safe_load(f.read())
|
||||||
generated_passwords = plan_utils.generate_passwords(
|
generated_passwords = plan_utils.generate_passwords(
|
||||||
heat=heat, container=stack)
|
passwords_env=passwords_env)
|
||||||
|
|
||||||
filtered_passwords = generated_passwords.copy()
|
filtered_passwords = generated_passwords.copy()
|
||||||
|
|
||||||
@ -61,14 +66,14 @@ def export_passwords(heat, stack, excludes=True):
|
|||||||
return filtered_passwords
|
return filtered_passwords
|
||||||
|
|
||||||
|
|
||||||
def export_stack(heat, stack, should_filter=False,
|
def export_stack(working_dir, stack, should_filter=False,
|
||||||
config_download_dir=constants.DEFAULT_WORK_DIR):
|
config_download_dir=constants.DEFAULT_WORK_DIR):
|
||||||
"""Export stack information.
|
"""Export stack information.
|
||||||
Iterates over parameters selected for export and loads
|
Iterates over parameters selected for export and loads
|
||||||
additional data from the referenced files.
|
additional data from the referenced files.
|
||||||
|
|
||||||
:param heat: tht client
|
:param working_dir: Working dir for the deployment
|
||||||
:type heat: Client
|
:type working_dir: string
|
||||||
:param stack: stack name for password generator
|
:param stack: stack name for password generator
|
||||||
:type stack: string
|
:type stack: string
|
||||||
:params should_filter:
|
:params should_filter:
|
||||||
@ -93,7 +98,6 @@ def export_stack(heat, stack, should_filter=False,
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
heat_stack = oooutils.get_stack(heat, stack)
|
|
||||||
|
|
||||||
for export_key, export_param in constants.EXPORT_DATA.items():
|
for export_key, export_param in constants.EXPORT_DATA.items():
|
||||||
param = export_param["parameter"]
|
param = export_param["parameter"]
|
||||||
@ -106,8 +110,8 @@ def export_stack(heat, stack, should_filter=False,
|
|||||||
export_data = oooutils.get_parameter_file(file)
|
export_data = oooutils.get_parameter_file(file)
|
||||||
else:
|
else:
|
||||||
# get stack data
|
# get stack data
|
||||||
export_data = oooutils.get_stack_output_item(
|
export_data = oooutils.get_stack_saved_output_item(
|
||||||
heat_stack, export_key)
|
export_key, working_dir)
|
||||||
|
|
||||||
if export_data:
|
if export_data:
|
||||||
# When we export information from a cell controller stack
|
# When we export information from a cell controller stack
|
||||||
@ -121,14 +125,21 @@ def export_stack(heat, stack, should_filter=False,
|
|||||||
data[param] = export_data
|
data[param] = export_data
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
LOG.warning("No data returned to export %s from." % param)
|
||||||
"No data returned to export %s from." % param)
|
|
||||||
|
|
||||||
# Check if AuthCloudName is in the stack environment, and if so add it to
|
# Check if AuthCloudName is in the stack environment, and if so add it to
|
||||||
# the export data. Otherwise set it to the exported stack's name.
|
# the export data. Otherwise set it to the exported stack's name.
|
||||||
auth_cloud_name = heat_stack.environment().get(
|
auth_cloud_name = oooutils.get_stack_saved_output_item(
|
||||||
'parameter_defaults').get(
|
'AuthCloudName', working_dir)
|
||||||
'AuthCloudName', None)
|
if auth_cloud_name:
|
||||||
|
data['AuthCloudName'] = auth_cloud_name
|
||||||
|
else:
|
||||||
|
data['AuthCloudName'] = stack
|
||||||
|
|
||||||
|
# Check if AuthCloudName is in the stack environment, and if so add it to
|
||||||
|
# the export data. Otherwise set it to the exported stack's name.
|
||||||
|
auth_cloud_name = oooutils.get_stack_saved_output_item(
|
||||||
|
'AuthCloudName', working_dir)
|
||||||
if auth_cloud_name:
|
if auth_cloud_name:
|
||||||
data['AuthCloudName'] = auth_cloud_name
|
data['AuthCloudName'] = auth_cloud_name
|
||||||
else:
|
else:
|
||||||
@ -213,11 +224,11 @@ def export_ceph(stack, cephx,
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def export_overcloud(heat, stack, excludes, should_filter,
|
def export_overcloud(working_dir, stack, excludes, should_filter,
|
||||||
config_download_dir):
|
config_download_dir):
|
||||||
data = export_passwords(heat, stack, excludes)
|
data = export_passwords(working_dir, stack, excludes)
|
||||||
data.update(export_stack(
|
data.update(export_stack(
|
||||||
heat, stack, should_filter, config_download_dir))
|
working_dir, stack, should_filter, config_download_dir))
|
||||||
# do not add extra host entries for VIPs for stacks deployed off that
|
# do not add extra host entries for VIPs for stacks deployed off that
|
||||||
# exported data, since it already contains those entries
|
# exported data, since it already contains those entries
|
||||||
data.update({'AddVipsToEtcHosts': False})
|
data.update({'AddVipsToEtcHosts': False})
|
||||||
|
@ -12,13 +12,13 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
from json.decoder import JSONDecodeError
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from tripleoclient import export
|
from tripleoclient import export
|
||||||
|
from tripleoclient import utils
|
||||||
|
|
||||||
|
|
||||||
class TestExport(TestCase):
|
class TestExport(TestCase):
|
||||||
@ -28,23 +28,8 @@ class TestExport(TestCase):
|
|||||||
self.unlink_patch.start()
|
self.unlink_patch.start()
|
||||||
self.mock_log = mock.Mock('logging.getLogger')
|
self.mock_log = mock.Mock('logging.getLogger')
|
||||||
|
|
||||||
outputs = [
|
|
||||||
{'output_key': 'EndpointMap',
|
|
||||||
'output_value': dict(em_key='em_value')},
|
|
||||||
{'output_key': 'HostsEntry',
|
|
||||||
'output_value': 'hosts entry'},
|
|
||||||
{'output_key': 'GlobalConfig',
|
|
||||||
'output_value': dict(gc_key='gc_value')},
|
|
||||||
]
|
|
||||||
self.mock_stack = mock.Mock()
|
|
||||||
self.mock_stack.to_dict.return_value = dict(outputs=outputs)
|
|
||||||
self.mock_open = mock.mock_open(read_data='{"an_key":"an_value"}')
|
self.mock_open = mock.mock_open(read_data='{"an_key":"an_value"}')
|
||||||
|
|
||||||
mock_environment = mock.Mock()
|
|
||||||
self.mock_stack.environment = mock_environment
|
|
||||||
mock_environment.return_value = dict(
|
|
||||||
parameter_defaults=dict())
|
|
||||||
|
|
||||||
ceph_inv = {
|
ceph_inv = {
|
||||||
'DistributedComputeHCI': {
|
'DistributedComputeHCI': {
|
||||||
'hosts': {
|
'hosts': {
|
||||||
@ -80,40 +65,23 @@ class TestExport(TestCase):
|
|||||||
}
|
}
|
||||||
self.mock_open_ceph_all = mock.mock_open(read_data=str(ceph_all))
|
self.mock_open_ceph_all = mock.mock_open(read_data=str(ceph_all))
|
||||||
|
|
||||||
|
def _get_stack_saved_output_item(self, output_key, working_dir):
|
||||||
|
outputs = {
|
||||||
|
'EndpointMap': dict(em_key='em_value'),
|
||||||
|
'HostsEntry': 'hosts entry',
|
||||||
|
'GlobalConfig': dict(gc_key='gc_value'),
|
||||||
|
'AuthCloudName': 'central',
|
||||||
|
}
|
||||||
|
return outputs[output_key]
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.get_stack_saved_output_item')
|
||||||
@mock.patch('tripleoclient.utils.os.path.exists',
|
@mock.patch('tripleoclient.utils.os.path.exists',
|
||||||
autospec=True, reutrn_value=True)
|
autospec=True, reutrn_value=True)
|
||||||
@mock.patch('tripleoclient.utils.get_stack')
|
def test_export_stack(self, mock_exists, mock_output_item):
|
||||||
def test_export_stack(self, mock_get_stack, mock_exists):
|
mock_output_item.side_effect = self._get_stack_saved_output_item
|
||||||
heat = mock.Mock()
|
working_dir = utils.get_default_working_dir('overcloud')
|
||||||
mock_get_stack.return_value = self.mock_stack
|
|
||||||
with mock.patch('tripleoclient.utils.open', self.mock_open):
|
with mock.patch('tripleoclient.utils.open', self.mock_open):
|
||||||
data = export.export_stack(heat, "overcloud")
|
data = export.export_stack(working_dir, "overcloud")
|
||||||
|
|
||||||
expected = \
|
|
||||||
{'AllNodesExtraMapData': {u'an_key': u'an_value'},
|
|
||||||
'AuthCloudName': 'overcloud',
|
|
||||||
'EndpointMapOverride': {'em_key': 'em_value'},
|
|
||||||
'ExtraHostFileEntries': 'hosts entry',
|
|
||||||
'GlobalConfigExtraMapData': {'gc_key': 'gc_value'}}
|
|
||||||
|
|
||||||
self.assertEqual(expected, data)
|
|
||||||
self.mock_open.assert_called_once_with(
|
|
||||||
os.path.join(
|
|
||||||
os.environ.get('HOME'),
|
|
||||||
'config-download/overcloud/group_vars/overcloud.json'),
|
|
||||||
'r')
|
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.os.path.exists',
|
|
||||||
autospec=True, reutrn_value=True)
|
|
||||||
@mock.patch('tripleoclient.utils.get_stack')
|
|
||||||
def test_export_stack_auth_cloud_name_set(
|
|
||||||
self, mock_get_stack, mock_exists):
|
|
||||||
heat = mock.Mock()
|
|
||||||
mock_get_stack.return_value = self.mock_stack
|
|
||||||
self.mock_stack.environment.return_value['parameter_defaults'] = (
|
|
||||||
dict(AuthCloudName='central'))
|
|
||||||
with mock.patch('tripleoclient.utils.open', self.mock_open):
|
|
||||||
data = export.export_stack(heat, "overcloud")
|
|
||||||
|
|
||||||
expected = \
|
expected = \
|
||||||
{'AllNodesExtraMapData': {u'an_key': u'an_value'},
|
{'AllNodesExtraMapData': {u'an_key': u'an_value'},
|
||||||
@ -129,20 +97,45 @@ class TestExport(TestCase):
|
|||||||
'config-download/overcloud/group_vars/overcloud.json'),
|
'config-download/overcloud/group_vars/overcloud.json'),
|
||||||
'r')
|
'r')
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.get_stack_saved_output_item')
|
||||||
@mock.patch('tripleoclient.utils.os.path.exists',
|
@mock.patch('tripleoclient.utils.os.path.exists',
|
||||||
autospec=True, reutrn_value=True)
|
autospec=True, reutrn_value=True)
|
||||||
@mock.patch('tripleoclient.utils.get_stack')
|
def test_export_stack_auth_cloud_name_set(
|
||||||
def test_export_stack_should_filter(self, mock_get_stack, mock_exists):
|
self, mock_exists, mock_output_item):
|
||||||
heat = mock.Mock()
|
mock_output_item.side_effect = self._get_stack_saved_output_item
|
||||||
mock_get_stack.return_value = self.mock_stack
|
working_dir = utils.get_default_working_dir('overcloud')
|
||||||
|
with mock.patch('tripleoclient.utils.open', self.mock_open):
|
||||||
|
data = export.export_stack(working_dir, "overcloud")
|
||||||
|
|
||||||
|
expected = \
|
||||||
|
{'AllNodesExtraMapData': {u'an_key': u'an_value'},
|
||||||
|
'AuthCloudName': 'central',
|
||||||
|
'EndpointMapOverride': {'em_key': 'em_value'},
|
||||||
|
'ExtraHostFileEntries': 'hosts entry',
|
||||||
|
'GlobalConfigExtraMapData': {'gc_key': 'gc_value'}}
|
||||||
|
|
||||||
|
self.assertEqual(expected, data)
|
||||||
|
self.mock_open.assert_called_once_with(
|
||||||
|
os.path.join(
|
||||||
|
os.environ.get('HOME'),
|
||||||
|
'config-download/overcloud/group_vars/overcloud.json'),
|
||||||
|
'r')
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.get_stack_saved_output_item')
|
||||||
|
@mock.patch('tripleoclient.utils.os.path.exists',
|
||||||
|
autospec=True, reutrn_value=True)
|
||||||
|
def test_export_stack_should_filter(self, mock_exists, mock_stack_output):
|
||||||
|
working_dir = utils.get_default_working_dir('overcloud')
|
||||||
|
mock_stack_output.side_effect = self._get_stack_saved_output_item
|
||||||
self.mock_open = mock.mock_open(
|
self.mock_open = mock.mock_open(
|
||||||
read_data='{"an_key":"an_value","ovn_dbs_vip":"vip"}')
|
read_data='{"an_key":"an_value","ovn_dbs_vip":"vip"}')
|
||||||
with mock.patch('builtins.open', self.mock_open):
|
with mock.patch('builtins.open', self.mock_open):
|
||||||
data = export.export_stack(heat, "overcloud", should_filter=True)
|
data = export.export_stack(
|
||||||
|
working_dir, "overcloud", should_filter=True)
|
||||||
|
|
||||||
expected = \
|
expected = \
|
||||||
{'AllNodesExtraMapData': {u'ovn_dbs_vip': u'vip'},
|
{'AllNodesExtraMapData': {u'ovn_dbs_vip': u'vip'},
|
||||||
'AuthCloudName': 'overcloud',
|
'AuthCloudName': 'central',
|
||||||
'EndpointMapOverride': {'em_key': 'em_value'},
|
'EndpointMapOverride': {'em_key': 'em_value'},
|
||||||
'ExtraHostFileEntries': 'hosts entry',
|
'ExtraHostFileEntries': 'hosts entry',
|
||||||
'GlobalConfigExtraMapData': {'gc_key': 'gc_value'}}
|
'GlobalConfigExtraMapData': {'gc_key': 'gc_value'}}
|
||||||
@ -156,51 +149,17 @@ class TestExport(TestCase):
|
|||||||
|
|
||||||
@mock.patch('tripleoclient.utils.os.path.exists',
|
@mock.patch('tripleoclient.utils.os.path.exists',
|
||||||
autospec=True, reutrn_value=True)
|
autospec=True, reutrn_value=True)
|
||||||
@mock.patch('tripleoclient.utils.get_stack')
|
def test_export_stack_cd_dir(self, mock_exists):
|
||||||
def test_export_stack_cd_dir(self, mock_get_stack, mock_exists):
|
working_dir = utils.get_default_working_dir('overcloud')
|
||||||
heat = mock.Mock()
|
|
||||||
mock_get_stack.return_value = self.mock_stack
|
|
||||||
with mock.patch('tripleoclient.utils.open', self.mock_open):
|
with mock.patch('tripleoclient.utils.open', self.mock_open):
|
||||||
export.export_stack(heat, "overcloud",
|
export.export_stack(working_dir, "overcloud",
|
||||||
config_download_dir='/foo')
|
config_download_dir='/foo')
|
||||||
self.mock_open.assert_called_once_with(
|
self.mock_open.assert_called_with(
|
||||||
'/foo/overcloud/group_vars/overcloud.json', 'r')
|
'/foo/overcloud/group_vars/overcloud.json', 'r')
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.os.path.exists',
|
|
||||||
autospec=True, reutrn_value=True)
|
|
||||||
@mock.patch('tripleoclient.utils.get_stack')
|
|
||||||
def test_export_stack_stack_name(self, mock_get_stack, mock_exists):
|
|
||||||
heat = mock.Mock()
|
|
||||||
mock_get_stack.return_value = self.mock_stack
|
|
||||||
with mock.patch('tripleoclient.utils.open', self.mock_open):
|
|
||||||
export.export_stack(heat, "control")
|
|
||||||
mock_get_stack.assert_called_once_with(heat, 'control')
|
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.LOG.error', autospec=True)
|
|
||||||
@mock.patch('tripleoclient.utils.json.load', autospec=True,
|
|
||||||
side_effect=JSONDecodeError)
|
|
||||||
@mock.patch('tripleoclient.utils.open')
|
|
||||||
@mock.patch('tripleoclient.utils.os.path.exists', autospec=True,
|
|
||||||
return_value=True)
|
|
||||||
@mock.patch('tripleoclient.utils.get_stack', autospec=True)
|
|
||||||
def test_export_stack_decode_error(self, mock_get_stack, mock_exists,
|
|
||||||
mock_open, mock_json_load, mock_log):
|
|
||||||
|
|
||||||
heat = mock.MagicMock()
|
|
||||||
mock_get_stack.return_value = self.mock_stack
|
|
||||||
self.assertRaises(
|
|
||||||
RuntimeError, export.export_stack, heat, "overcloud")
|
|
||||||
|
|
||||||
mock_open.assert_called_once_with(
|
|
||||||
os.path.join(
|
|
||||||
os.environ.get('HOME'),
|
|
||||||
'config-download/overcloud/group_vars/overcloud.json'),
|
|
||||||
'r')
|
|
||||||
|
|
||||||
@mock.patch('tripleoclient.export.LOG')
|
@mock.patch('tripleoclient.export.LOG')
|
||||||
@mock.patch('tripleo_common.utils.plan.generate_passwords')
|
@mock.patch('tripleo_common.utils.plan.generate_passwords')
|
||||||
def test_export_passwords(self, mock_gen_pass, mock_log):
|
def test_export_passwords(self, mock_gen_pass, mock_log):
|
||||||
heat = mock.Mock()
|
|
||||||
mock_passwords = {
|
mock_passwords = {
|
||||||
'AdminPassword': 'A',
|
'AdminPassword': 'A',
|
||||||
'RpcPassword': 'B',
|
'RpcPassword': 'B',
|
||||||
@ -211,16 +170,20 @@ class TestExport(TestCase):
|
|||||||
mock_gen_pass.return_value = mock_passwords
|
mock_gen_pass.return_value = mock_passwords
|
||||||
|
|
||||||
expected_password_export = mock_passwords.copy()
|
expected_password_export = mock_passwords.copy()
|
||||||
data = export.export_passwords(heat, 'overcloud', False)
|
working_dir = utils.get_default_working_dir('overcloud')
|
||||||
|
with mock.patch('builtins.open', mock.mock_open()):
|
||||||
|
data = export.export_passwords(working_dir, 'overcloud', False)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
expected_password_export,
|
expected_password_export,
|
||||||
data)
|
data)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.get_stack_saved_output_item')
|
||||||
@mock.patch('tripleoclient.export.LOG')
|
@mock.patch('tripleoclient.export.LOG')
|
||||||
@mock.patch('tripleo_common.utils.plan.generate_passwords')
|
@mock.patch('tripleo_common.utils.plan.generate_passwords')
|
||||||
def test_export_passwords_excludes(self, mock_gen_pass, mock_log):
|
def test_export_passwords_excludes(self, mock_gen_pass, mock_log,
|
||||||
heat = mock.Mock()
|
mock_output_item):
|
||||||
|
mock_output_item.side_effect = self._get_stack_saved_output_item
|
||||||
mock_passwords = {
|
mock_passwords = {
|
||||||
'AdminPassword': 'A',
|
'AdminPassword': 'A',
|
||||||
'RpcPassword': 'B',
|
'RpcPassword': 'B',
|
||||||
@ -234,7 +197,9 @@ class TestExport(TestCase):
|
|||||||
'AdminPassword': 'A',
|
'AdminPassword': 'A',
|
||||||
'RpcPassword': 'B'}
|
'RpcPassword': 'B'}
|
||||||
|
|
||||||
data = export.export_passwords(heat, 'overcloud')
|
working_dir = utils.get_default_working_dir('overcloud')
|
||||||
|
with mock.patch('builtins.open', mock.mock_open()):
|
||||||
|
data = export.export_passwords(working_dir, 'overcloud')
|
||||||
|
|
||||||
self.assertEqual(expected_password_export, data)
|
self.assertEqual(expected_password_export, data)
|
||||||
|
|
||||||
|
@ -15,9 +15,9 @@ import os
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from keystoneauth1.exceptions.catalog import EndpointNotFound
|
|
||||||
from osc_lib.tests import utils
|
from osc_lib.tests import utils
|
||||||
|
|
||||||
|
from tripleoclient import utils as ooo_utils
|
||||||
from tripleoclient.v1 import overcloud_export
|
from tripleoclient.v1 import overcloud_export
|
||||||
|
|
||||||
|
|
||||||
@ -50,14 +50,14 @@ class TestOvercloudExport(utils.TestCommand):
|
|||||||
with mock.patch('builtins.open', self.mock_open):
|
with mock.patch('builtins.open', self.mock_open):
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_export_passwords.assert_called_once_with(
|
mock_export_passwords.assert_called_once_with(
|
||||||
self.app.client_manager.orchestration,
|
ooo_utils.get_default_working_dir('overcloud'),
|
||||||
'overcloud', True)
|
'overcloud', True)
|
||||||
path = os.path.join(os.environ.get('HOME'),
|
path = os.path.join(os.environ.get('HOME'),
|
||||||
'overcloud-deploy',
|
'overcloud-deploy',
|
||||||
'overcloud',
|
'overcloud',
|
||||||
'config-download')
|
'config-download')
|
||||||
mock_export_stack.assert_called_once_with(
|
mock_export_stack.assert_called_once_with(
|
||||||
self.app.client_manager.orchestration,
|
ooo_utils.get_default_working_dir('overcloud'),
|
||||||
'overcloud',
|
'overcloud',
|
||||||
False,
|
False,
|
||||||
path)
|
path)
|
||||||
@ -82,14 +82,14 @@ class TestOvercloudExport(utils.TestCommand):
|
|||||||
with mock.patch('builtins.open', self.mock_open):
|
with mock.patch('builtins.open', self.mock_open):
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_export_passwords.assert_called_once_with(
|
mock_export_passwords.assert_called_once_with(
|
||||||
self.app.client_manager.orchestration,
|
ooo_utils.get_default_working_dir('foo'),
|
||||||
'foo', True)
|
'foo', True)
|
||||||
path = os.path.join(os.environ.get('HOME'),
|
path = os.path.join(os.environ.get('HOME'),
|
||||||
'overcloud-deploy',
|
'overcloud-deploy',
|
||||||
'foo',
|
'foo',
|
||||||
'config-download')
|
'config-download')
|
||||||
mock_export_stack.assert_called_once_with(
|
mock_export_stack.assert_called_once_with(
|
||||||
self.app.client_manager.orchestration,
|
ooo_utils.get_default_working_dir('foo'),
|
||||||
'foo',
|
'foo',
|
||||||
False,
|
False,
|
||||||
path)
|
path)
|
||||||
@ -110,10 +110,10 @@ class TestOvercloudExport(utils.TestCommand):
|
|||||||
with mock.patch('builtins.open', self.mock_open):
|
with mock.patch('builtins.open', self.mock_open):
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_export_passwords.assert_called_once_with(
|
mock_export_passwords.assert_called_once_with(
|
||||||
self.app.client_manager.orchestration,
|
ooo_utils.get_default_working_dir('foo'),
|
||||||
'foo', True)
|
'foo', True)
|
||||||
mock_export_stack.assert_called_once_with(
|
mock_export_stack.assert_called_once_with(
|
||||||
self.app.client_manager.orchestration,
|
ooo_utils.get_default_working_dir('foo'),
|
||||||
'foo',
|
'foo',
|
||||||
False,
|
False,
|
||||||
'/tmp/bar')
|
'/tmp/bar')
|
||||||
@ -136,28 +136,28 @@ class TestOvercloudExport(utils.TestCommand):
|
|||||||
with mock.patch('builtins.open', self.mock_open):
|
with mock.patch('builtins.open', self.mock_open):
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_export_passwords.assert_called_once_with(
|
mock_export_passwords.assert_called_once_with(
|
||||||
self.app.client_manager.orchestration,
|
ooo_utils.get_default_working_dir('foo'),
|
||||||
'foo', False)
|
'foo', False)
|
||||||
mock_export_stack.assert_called_once_with(
|
mock_export_stack.assert_called_once_with(
|
||||||
self.app.client_manager.orchestration,
|
ooo_utils.get_default_working_dir('foo'),
|
||||||
'foo',
|
'foo',
|
||||||
False,
|
False,
|
||||||
'/tmp/bar')
|
'/tmp/bar')
|
||||||
|
|
||||||
|
@mock.patch('tripleo_common.utils.plan.generate_passwords')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
@mock.patch('os.path.exists')
|
@mock.patch('os.path.exists')
|
||||||
@mock.patch('tripleoclient.utils.get_default_working_dir')
|
@mock.patch('tripleoclient.utils.get_default_working_dir')
|
||||||
def test_export_ephemeral_heat(self, mock_working_dir, mock_exists,
|
def test_export_ephemeral_heat(self, mock_working_dir, mock_exists,
|
||||||
mock_copy):
|
mock_copy, mock_passwords):
|
||||||
argslist = ['--force-overwrite']
|
argslist = ['--force-overwrite']
|
||||||
verifylist = [('force_overwrite', True)]
|
verifylist = [('force_overwrite', True)]
|
||||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||||
mock_exists.return_value = True
|
mock_exists.return_value = True
|
||||||
mock_working_dir.return_value = 'wd'
|
mock_working_dir.return_value = 'wd'
|
||||||
heat = self.app.client_manager.orchestration
|
mock_open = mock.mock_open(read_data='{}')
|
||||||
heat.stacks.client.session.get_endpoint.side_effect = EndpointNotFound
|
mock_passwords.return_value = dict()
|
||||||
with mock.patch('six.moves.builtins.open', self.mock_open):
|
with mock.patch('six.moves.builtins.open', mock_open):
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_working_dir.assert_called()
|
mock_working_dir.assert_called()
|
||||||
mock_copy.assert_called_with(
|
mock_passwords.assert_called()
|
||||||
'wd/overcloud-export.yaml', 'overcloud-export.yaml')
|
|
||||||
|
@ -1392,7 +1392,7 @@ class DeployOvercloud(command.Command):
|
|||||||
parsed_args.config_download):
|
parsed_args.config_download):
|
||||||
# Create overcloud export
|
# Create overcloud export
|
||||||
data = export.export_overcloud(
|
data = export.export_overcloud(
|
||||||
self.orchestration_client,
|
self.working_dir,
|
||||||
parsed_args.stack, True, False,
|
parsed_args.stack, True, False,
|
||||||
config_download_dir)
|
config_download_dir)
|
||||||
export_file = os.path.join(
|
export_file = os.path.join(
|
||||||
|
@ -13,13 +13,9 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
import shutil
|
|
||||||
import sys
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from keystoneauth1.exceptions.catalog import EndpointNotFound
|
|
||||||
from osc_lib.i18n import _
|
from osc_lib.i18n import _
|
||||||
from osc_lib import utils as osc_utils
|
|
||||||
|
|
||||||
from tripleoclient import command
|
from tripleoclient import command
|
||||||
from tripleoclient import export
|
from tripleoclient import export
|
||||||
@ -39,10 +35,8 @@ class ExportOvercloud(command.Command):
|
|||||||
metavar='<stack>',
|
metavar='<stack>',
|
||||||
help=_('Name of the environment main Heat stack '
|
help=_('Name of the environment main Heat stack '
|
||||||
'to export information from. '
|
'to export information from. '
|
||||||
'(default=Env: OVERCLOUD_STACK_NAME)'),
|
'(default=overcloud)'),
|
||||||
default=osc_utils.env(
|
default='overcloud')
|
||||||
'OVERCLOUD_STACK_NAME',
|
|
||||||
default='overcloud'))
|
|
||||||
parser.add_argument('--output-file', '-o', metavar='<output file>',
|
parser.add_argument('--output-file', '-o', metavar='<output file>',
|
||||||
help=_('Name of the output file for the stack '
|
help=_('Name of the output file for the stack '
|
||||||
'data export. It will default to '
|
'data export. It will default to '
|
||||||
@ -50,6 +44,12 @@ class ExportOvercloud(command.Command):
|
|||||||
parser.add_argument('--force-overwrite', '-f', action='store_true',
|
parser.add_argument('--force-overwrite', '-f', action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help=_('Overwrite output file if it exists.'))
|
help=_('Overwrite output file if it exists.'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--working-dir',
|
||||||
|
action='store',
|
||||||
|
help=_('The working directory for the deployment where all '
|
||||||
|
'input, output, and generated files are stored.\n'
|
||||||
|
'Defaults to "$HOME/overcloud-deploy/<stack>"'))
|
||||||
parser.add_argument('--config-download-dir',
|
parser.add_argument('--config-download-dir',
|
||||||
action='store',
|
action='store',
|
||||||
help=_('Directory to search for config-download '
|
help=_('Directory to search for config-download '
|
||||||
@ -76,9 +76,10 @@ class ExportOvercloud(command.Command):
|
|||||||
self.now,
|
self.now,
|
||||||
parsed_args)
|
parsed_args)
|
||||||
|
|
||||||
if os.path.exists(output_file) and not parsed_args.force_overwrite:
|
if not parsed_args.working_dir:
|
||||||
raise Exception(
|
working_dir = utils.get_default_working_dir(stack)
|
||||||
"File '%s' already exists, not exporting." % output_file)
|
else:
|
||||||
|
working_dir = parsed_args.working_dir
|
||||||
|
|
||||||
if not parsed_args.config_download_dir:
|
if not parsed_args.config_download_dir:
|
||||||
config_download_dir = os.path.join(os.environ.get('HOME'),
|
config_download_dir = os.path.join(os.environ.get('HOME'),
|
||||||
@ -88,38 +89,19 @@ class ExportOvercloud(command.Command):
|
|||||||
else:
|
else:
|
||||||
config_download_dir = parsed_args.config_download_dir
|
config_download_dir = parsed_args.config_download_dir
|
||||||
|
|
||||||
# prepare clients to access the environment
|
export_file_path = os.path.join(
|
||||||
clients = self.app.client_manager
|
working_dir,
|
||||||
try:
|
'{}-export.yaml'.format(parsed_args.stack))
|
||||||
heat = clients.orchestration
|
if (os.path.exists(export_file_path) and
|
||||||
heat.stacks.client.session.get_endpoint(
|
not parsed_args.force_overwrite):
|
||||||
service_type='orchestration')
|
raise Exception(
|
||||||
except EndpointNotFound:
|
"File '%s' already exists, not exporting." % export_file_path)
|
||||||
self.log.warning(
|
|
||||||
"Heat endpoint not found. When using ephemeral Heat, "
|
|
||||||
"the export file exists in the stack working directory "
|
|
||||||
"as $HOME/overlcoud-deploy/<stack>/<stack>-export.yaml. "
|
|
||||||
"(default). The existing export file will be copied "
|
|
||||||
"to {}".format(output_file))
|
|
||||||
export_file_path = os.path.join(
|
|
||||||
utils.get_default_working_dir(parsed_args.stack),
|
|
||||||
'{}-export.yaml'.format(parsed_args.stack))
|
|
||||||
if os.path.exists(export_file_path):
|
|
||||||
print(
|
|
||||||
"Export file found at {}, copying to {}.".format(
|
|
||||||
export_file_path, output_file))
|
|
||||||
shutil.copy(export_file_path, output_file)
|
|
||||||
else:
|
|
||||||
print("Export file not found at {}.".format(
|
|
||||||
export_file_path))
|
|
||||||
sys.exit(1)
|
|
||||||
return
|
|
||||||
|
|
||||||
data = export.export_overcloud(
|
data = export.export_overcloud(
|
||||||
heat, stack, excludes=not parsed_args.no_password_excludes,
|
working_dir, stack, excludes=not parsed_args.no_password_excludes,
|
||||||
should_filter=False, config_download_dir=config_download_dir)
|
should_filter=False, config_download_dir=config_download_dir)
|
||||||
# write the exported data
|
# write the exported data
|
||||||
with open(output_file, 'w') as f:
|
with open(export_file_path, 'w') as f:
|
||||||
yaml.safe_dump(data, f, default_flow_style=False)
|
yaml.safe_dump(data, f, default_flow_style=False)
|
||||||
|
|
||||||
print("Stack information exported to %s." % output_file)
|
print("Stack information exported to %s." % output_file)
|
||||||
|
Loading…
Reference in New Issue
Block a user