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:
James Slagle 2021-07-16 08:16:04 -04:00
parent 095182c143
commit df4694b743
5 changed files with 126 additions and 168 deletions

View File

@ -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})

View File

@ -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)

View File

@ -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')

View File

@ -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(

View File

@ -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)