text: # Copyright 2014 Rackspace Hosting
  # 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.
  from unittest import mock
  
  import fixtures
  from six.moves import StringIO
  
  from glance.cmd import manage
  from glance.common import exception
  from glance.db.sqlalchemy import alembic_migrations
  from glance.db.sqlalchemy import api as db_api
  from glance.db.sqlalchemy import metadata as db_metadata
  from glance.tests import utils as test_utils
  from sqlalchemy.engine.url import make_url as sqlalchemy_make_url
  
  
  class TestManageBase(test_utils.BaseTestCase):
  
      def setUp(self):
          super(TestManageBase, self).setUp()
  
          def clear_conf():
              manage.CONF.reset()
              manage.CONF.unregister_opt(manage.command_opt)
          clear_conf()
          self.addCleanup(clear_conf)
  
          self.useFixture(fixtures.MonkeyPatch(
              'oslo_log.log.setup', lambda product_name, version='test': None))
  
          patcher = mock.patch('glance.db.sqlalchemy.api.get_engine')
          patcher.start()
          self.addCleanup(patcher.stop)
  
      def _main_test_helper(self, argv, func_name=None, *exp_args, **exp_kwargs):
          self.useFixture(fixtures.MonkeyPatch('sys.argv', argv))
          manage.main()
          func_name.assert_called_once_with(*exp_args, **exp_kwargs)
  
  
  class TestLegacyManage(TestManageBase):
  
      @mock.patch.object(manage.DbCommands, 'version')
      def test_legacy_db_version(self, db_upgrade):
          self._main_test_helper(['glance.cmd.manage', 'db_version'],
                                 manage.DbCommands.version)
  
      @mock.patch.object(manage.DbCommands, 'sync')
      def test_legacy_db_sync(self, db_sync):
          self._main_test_helper(['glance.cmd.manage', 'db_sync'],
                                 manage.DbCommands.sync, None)
  
      @mock.patch.object(manage.DbCommands, 'upgrade')
      def test_legacy_db_upgrade(self, db_upgrade):
          self._main_test_helper(['glance.cmd.manage', 'db_upgrade'],
                                 manage.DbCommands.upgrade, None)
  
      @mock.patch.object(manage.DbCommands, 'version_control')
      def test_legacy_db_version_control(self, db_version_control):
          self._main_test_helper(['glance.cmd.manage', 'db_version_control'],
                                 manage.DbCommands.version_control, None)
  
      @mock.patch.object(manage.DbCommands, 'sync')
      def test_legacy_db_sync_version(self, db_sync):
          self._main_test_helper(['glance.cmd.manage', 'db_sync', 'liberty'],
                                 manage.DbCommands.sync, 'liberty')
  
      @mock.patch.object(manage.DbCommands, 'upgrade')
      def test_legacy_db_upgrade_version(self, db_upgrade):
          self._main_test_helper(['glance.cmd.manage', 'db_upgrade', 'liberty'],
                                 manage.DbCommands.upgrade, 'liberty')
  
      @mock.patch.object(manage.DbCommands, 'expand')
      def test_legacy_db_expand(self, db_expand):
          self._main_test_helper(['glance.cmd.manage', 'db_expand'],
                                 manage.DbCommands.expand)
  
      @mock.patch.object(manage.DbCommands, 'migrate')
      def test_legacy_db_migrate(self, db_migrate):
          self._main_test_helper(['glance.cmd.manage', 'db_migrate'],
                                 manage.DbCommands.migrate)
  
      @mock.patch.object(manage.DbCommands, 'contract')
      def test_legacy_db_contract(self, db_contract):
          self._main_test_helper(['glance.cmd.manage', 'db_contract'],
                                 manage.DbCommands.contract)
  
      def test_db_metadefs_unload(self):
          db_metadata.db_unload_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db_unload_metadefs'],
                                 db_metadata.db_unload_metadefs,
                                 db_api.get_engine())
  
      def test_db_metadefs_load(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db_load_metadefs'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 None, None, None, None)
  
      def test_db_metadefs_load_with_specified_path(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db_load_metadefs',
                                  '/mock/'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 '/mock/', None, None, None)
  
      def test_db_metadefs_load_from_path_merge(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db_load_metadefs',
                                  '/mock/', 'True'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 '/mock/', 'True', None, None)
  
      def test_db_metadefs_load_from_merge_and_prefer_new(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db_load_metadefs',
                                  '/mock/', 'True', 'True'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 '/mock/', 'True', 'True', None)
  
      def test_db_metadefs_load_from_merge_and_prefer_new_and_overwrite(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db_load_metadefs',
                                  '/mock/', 'True', 'True', 'True'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 '/mock/', 'True', 'True', 'True')
  
      def test_db_metadefs_export(self):
          db_metadata.db_export_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db_export_metadefs'],
                                 db_metadata.db_export_metadefs,
                                 db_api.get_engine(),
                                 None)
  
      def test_db_metadefs_export_with_specified_path(self):
          db_metadata.db_export_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db_export_metadefs',
                                 '/mock/'],
                                 db_metadata.db_export_metadefs,
                                 db_api.get_engine(),
                                 '/mock/')
  
  
  class TestManage(TestManageBase):
  
      def setUp(self):
          super(TestManage, self).setUp()
          self.db = manage.DbCommands()
          self.output = StringIO()
          self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.output))
  
      def test_db_complex_password(self):
          engine = mock.Mock()
          # See comments in get_alembic_config; make an engine url with
          # password characters that will be escaped, to ensure the
          # resulting value makes it into alembic unaltered.
          engine.url = sqlalchemy_make_url(
              'mysql+pymysql://username:pw@%/!#$()@host:1234/dbname')
          alembic_config = alembic_migrations.get_alembic_config(engine)
          self.assertEqual(str(engine.url),
                           alembic_config.get_main_option('sqlalchemy.url'))
  
      @mock.patch('glance.db.sqlalchemy.api.get_engine')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.data_migrations.'
          'has_pending_migrations')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      def test_db_check_result(self, mock_get_alembic_branch_head,
                               mock_get_current_alembic_heads,
                               mock_has_pending_migrations,
                               get_mock_engine):
  
          get_mock_engine.return_value = mock.Mock()
          engine = get_mock_engine.return_value
          engine.engine.name = 'postgresql'
          exit = self.assertRaises(SystemExit, self.db.check)
          self.assertIn('Rolling upgrades are currently supported only for '
                        'MySQL and Sqlite', exit.code)
  
          engine = get_mock_engine.return_value
          engine.engine.name = 'mysql'
  
          mock_get_current_alembic_heads.return_value = ['ocata_contract01']
          mock_get_alembic_branch_head.return_value = 'pike_expand01'
          exit = self.assertRaises(SystemExit, self.db.check)
          self.assertEqual(3, exit.code)
          self.assertIn('Your database is not up to date. '
                        'Your first step is to run `glance-manage db expand`.',
                        self.output.getvalue())
  
          mock_get_current_alembic_heads.return_value = ['pike_expand01']
          mock_get_alembic_branch_head.side_effect = ['pike_expand01', None]
          mock_has_pending_migrations.return_value = [mock.Mock()]
          exit = self.assertRaises(SystemExit, self.db.check)
          self.assertEqual(4, exit.code)
          self.assertIn('Your database is not up to date. '
                        'Your next step is to run `glance-manage db migrate`.',
                        self.output.getvalue())
  
          mock_get_current_alembic_heads.return_value = ['pike_expand01']
          mock_get_alembic_branch_head.side_effect = ['pike_expand01',
                                                      'pike_contract01']
          mock_has_pending_migrations.return_value = None
          exit = self.assertRaises(SystemExit, self.db.check)
          self.assertEqual(5, exit.code)
          self.assertIn('Your database is not up to date. '
                        'Your next step is to run `glance-manage db contract`.',
                        self.output.getvalue())
  
          mock_get_current_alembic_heads.return_value = ['pike_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_expand01',
                                                      'pike_contract01']
          mock_has_pending_migrations.return_value = None
          self.assertRaises(SystemExit, self.db.check)
          self.assertIn('Database is up to date. No upgrades needed.',
                        self.output.getvalue())
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, 'expand')
      @mock.patch.object(manage.DbCommands, 'migrate')
      @mock.patch.object(manage.DbCommands, 'contract')
      def test_sync(self, mock_contract, mock_migrate, mock_expand,
                    mock_get_alembic_branch_head,
                    mock_get_current_alembic_heads):
          mock_get_current_alembic_heads.return_value = ['ocata_contract01']
          mock_get_alembic_branch_head.return_value = ['pike_contract01']
          self.db.sync()
          mock_expand.assert_called_once_with(online_migration=False)
          mock_migrate.assert_called_once_with(online_migration=False)
          mock_contract.assert_called_once_with(online_migration=False)
          self.assertIn('Database is synced successfully.',
                        self.output.getvalue())
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch('glance.db.sqlalchemy.alembic_migrations.'
                  'place_database_under_alembic_control')
      @mock.patch('alembic.command.upgrade')
      def test_sync_db_is_already_sync(self, mock_upgrade,
                                       mock_db_under_alembic_control,
                                       mock_get_alembic_branch_head,
                                       mock_get_current_alembic_heads):
          mock_get_current_alembic_heads.return_value = ['pike_contract01']
          mock_get_alembic_branch_head.return_value = ['pike_contract01']
          self.assertRaises(SystemExit, self.db.sync)
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      @mock.patch.object(manage.DbCommands, 'expand')
      def test_sync_failed_to_sync(self, mock_expand, mock_validate_engine,
                                   mock_get_alembic_branch_head,
                                   mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['ocata_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_contract01', '']
          mock_expand.side_effect = exception.GlanceException
          exit = self.assertRaises(SystemExit, self.db.sync)
          self.assertIn('Failed to sync database: ERROR:', exit.code)
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      @mock.patch.object(manage.DbCommands, '_sync')
      def test_expand(self, mock_sync, mock_validate_engine,
                      mock_get_alembic_branch_head,
                      mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.side_effect = ['ocata_contract01',
                                                        'pike_expand01']
          mock_get_alembic_branch_head.side_effect = ['pike_expand01',
                                                      'pike_contract01']
          self.db.expand()
          mock_sync.assert_called_once_with(version='pike_expand01')
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_expand_if_not_expand_head(self, mock_validate_engine,
                                         mock_get_alembic_branch_head,
                                         mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['ocata_contract01']
          mock_get_alembic_branch_head.return_value = []
          exit = self.assertRaises(SystemExit, self.db.expand)
          self.assertIn('Database expansion failed. Couldn\'t find head '
                        'revision of expand branch.', exit.code)
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_expand_db_is_already_sync(self, mock_validate_engine,
                                         mock_get_alembic_branch_head,
                                         mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['pike_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_expand01',
                                                      'pike_contract01']
          self.assertRaises(SystemExit, self.db.expand)
          self.assertIn('Database is up to date. No migrations needed.',
                        self.output.getvalue())
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_expand_already_sync(self, mock_validate_engine,
                                   mock_get_alembic_branch_head,
                                   mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['pike_expand01']
          mock_get_alembic_branch_head.side_effect = ['pike_expand01',
                                                      'pike_contract01']
          self.db.expand()
          self.assertIn('Database expansion is up to date. '
                        'No expansion needed.', self.output.getvalue())
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      @mock.patch.object(manage.DbCommands, '_sync')
      def test_expand_failed(self, mock_sync, mock_validate_engine,
                             mock_get_alembic_branch_head,
                             mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.side_effect = ['ocata_contract01',
                                                        'test']
          mock_get_alembic_branch_head.side_effect = ['pike_expand01',
                                                      'pike_contract01']
          exit = self.assertRaises(SystemExit, self.db.expand)
          mock_sync.assert_called_once_with(version='pike_expand01')
          self.assertIn('Database expansion failed. Database expansion should '
                        'have brought the database version up to "pike_expand01"'
                        ' revision. But, current revisions are: test ',
                        exit.code)
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.data_migrations.'
          'has_pending_migrations')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      @mock.patch.object(manage.DbCommands, '_sync')
      def test_contract(self, mock_sync, mock_validate_engine,
                        mock_get_alembic_branch_head,
                        mock_get_current_alembic_heads,
                        mock_has_pending_migrations):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.side_effect = ['pike_expand01',
                                                        'pike_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_contract01',
                                                      'pike_expand01']
          mock_has_pending_migrations.return_value = False
          self.db.contract()
          mock_sync.assert_called_once_with(version='pike_contract01')
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_contract_if_not_contract_head(self, mock_validate_engine,
                                             mock_get_alembic_branch_head,
                                             mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['ocata_contract01']
          mock_get_alembic_branch_head.return_value = []
          exit = self.assertRaises(SystemExit, self.db.contract)
          self.assertIn('Database contraction failed. Couldn\'t find head '
                        'revision of contract branch.', exit.code)
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_contract_db_is_already_sync(self, mock_validate_engine,
                                           mock_get_alembic_branch_head,
                                           mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['pike_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_contract01',
                                                      'pike_expand01']
          self.assertRaises(SystemExit, self.db.contract)
          self.assertIn('Database is up to date. No migrations needed.',
                        self.output.getvalue())
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_contract_before_expand(self, mock_validate_engine,
                                      mock_get_alembic_branch_head,
                                      mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['ocata_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_expand01',
                                                      'pike_contract01']
          exit = self.assertRaises(SystemExit, self.db.contract)
          self.assertIn('Database contraction did not run. Database '
                        'contraction cannot be run before database expansion. '
                        'Run database expansion first using "glance-manage db '
                        'expand"', exit.code)
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.data_migrations.'
          'has_pending_migrations')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_contract_before_migrate(self, mock_validate_engine,
                                       mock_get_alembic_branch_head,
                                       mock_get_curr_alembic_heads,
                                       mock_has_pending_migrations):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_curr_alembic_heads.side_effect = ['pike_expand01']
          mock_get_alembic_branch_head.side_effect = ['pike_contract01',
                                                      'pike_expand01']
          mock_has_pending_migrations.return_value = [mock.Mock()]
          exit = self.assertRaises(SystemExit, self.db.contract)
          self.assertIn('Database contraction did not run. Database '
                        'contraction cannot be run before data migration is '
                        'complete. Run data migration using "glance-manage db '
                        'migrate".', exit.code)
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.data_migrations.'
          'has_pending_migrations')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_migrate(self, mock_validate_engine, mock_get_alembic_branch_head,
                       mock_get_current_alembic_heads,
                       mock_has_pending_migrations):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.side_effect = ['pike_expand01',
                                                        'pike_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_contract01',
                                                      'pike_expand01']
          mock_has_pending_migrations.return_value = None
          self.db.migrate()
          self.assertIn('Database migration is up to date. '
                        'No migration needed.', self.output.getvalue())
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_migrate_db_is_already_sync(self, mock_validate_engine,
                                          mock_get_alembic_branch_head,
                                          mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['pike_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_contract01',
                                                      'pike_expand01']
          self.assertRaises(SystemExit, self.db.migrate)
          self.assertIn('Database is up to date. No migrations needed.',
                        self.output.getvalue())
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_migrate_already_sync(self, mock_validate_engine,
                                    mock_get_alembic_branch_head,
                                    mock_get_current_alembic_heads):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['ocata_contract01']
          mock_get_alembic_branch_head.side_effect = ['pike_contract01',
                                                      'pike_expand01']
          exit = self.assertRaises(SystemExit, self.db.migrate)
          self.assertIn('Data migration did not run. Data migration cannot be '
                        'run before database expansion. Run database expansion '
                        'first using "glance-manage db expand"', exit.code)
  
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.data_migrations.'
          'has_pending_migrations')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
      @mock.patch(
          'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
      @mock.patch.object(manage.DbCommands, '_validate_engine')
      def test_migrate_before_expand(self, mock_validate_engine,
                                     mock_get_alembic_branch_head,
                                     mock_get_current_alembic_heads,
                                     mock_has_pending_migrations):
          engine = mock_validate_engine.return_value
          engine.engine.name = 'mysql'
          mock_get_current_alembic_heads.return_value = ['pike_expand01']
          mock_get_alembic_branch_head.side_effect = ['pike_contract01',
                                                      'pike_expand01']
          mock_has_pending_migrations.return_value = None
          self.db.migrate()
          self.assertIn('Database migration is up to date. '
                        'No migration needed.', self.output.getvalue())
  
      @mock.patch.object(manage.DbCommands, 'version')
      def test_db_version(self, version):
          self._main_test_helper(['glance.cmd.manage', 'db', 'version'],
                                 manage.DbCommands.version)
  
      @mock.patch.object(manage.DbCommands, 'check')
      def test_db_check(self, check):
          self._main_test_helper(['glance.cmd.manage', 'db', 'check'],
                                 manage.DbCommands.check)
  
      @mock.patch.object(manage.DbCommands, 'sync')
      def test_db_sync(self, sync):
          self._main_test_helper(['glance.cmd.manage', 'db', 'sync'],
                                 manage.DbCommands.sync)
  
      @mock.patch.object(manage.DbCommands, 'upgrade')
      def test_db_upgrade(self, upgrade):
          self._main_test_helper(['glance.cmd.manage', 'db', 'upgrade'],
                                 manage.DbCommands.upgrade)
  
      @mock.patch.object(manage.DbCommands, 'version_control')
      def test_db_version_control(self, version_control):
          self._main_test_helper(['glance.cmd.manage', 'db', 'version_control'],
                                 manage.DbCommands.version_control)
  
      @mock.patch.object(manage.DbCommands, 'sync')
      def test_db_sync_version(self, sync):
          self._main_test_helper(['glance.cmd.manage', 'db', 'sync', 'liberty'],
                                 manage.DbCommands.sync, 'liberty')
  
      @mock.patch.object(manage.DbCommands, 'upgrade')
      def test_db_upgrade_version(self, upgrade):
          self._main_test_helper(['glance.cmd.manage', 'db',
                                  'upgrade', 'liberty'],
                                 manage.DbCommands.upgrade, 'liberty')
  
      @mock.patch.object(manage.DbCommands, 'expand')
      def test_db_expand(self, expand):
          self._main_test_helper(['glance.cmd.manage', 'db', 'expand'],
                                 manage.DbCommands.expand)
  
      @mock.patch.object(manage.DbCommands, 'migrate')
      def test_db_migrate(self, migrate):
          self._main_test_helper(['glance.cmd.manage', 'db', 'migrate'],
                                 manage.DbCommands.migrate)
  
      @mock.patch.object(manage.DbCommands, 'contract')
      def test_db_contract(self, contract):
          self._main_test_helper(['glance.cmd.manage', 'db', 'contract'],
                                 manage.DbCommands.contract)
  
      def test_db_metadefs_unload(self):
          db_metadata.db_unload_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'unload_metadefs'],
                                 db_metadata.db_unload_metadefs,
                                 db_api.get_engine())
  
      def test_db_metadefs_load(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'load_metadefs'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 None, False, False, False)
  
      def test_db_metadefs_load_with_specified_path(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'load_metadefs',
                                  '--path', '/mock/'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 '/mock/', False, False, False)
  
      def test_db_metadefs_load_prefer_new_with_path(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'load_metadefs',
                                  '--path', '/mock/', '--merge', '--prefer_new'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 '/mock/', True, True, False)
  
      def test_db_metadefs_load_prefer_new(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'load_metadefs',
                                  '--merge', '--prefer_new'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 None, True, True, False)
  
      def test_db_metadefs_load_overwrite_existing(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'load_metadefs',
                                  '--merge', '--overwrite'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 None, True, False, True)
  
      def test_db_metadefs_load_prefer_new_and_overwrite_existing(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'load_metadefs',
                                  '--merge', '--prefer_new', '--overwrite'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 None, True, True, True)
  
      def test_db_metadefs_load_from_path_overwrite_existing(self):
          db_metadata.db_load_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'load_metadefs',
                                  '--path', '/mock/', '--merge', '--overwrite'],
                                 db_metadata.db_load_metadefs,
                                 db_api.get_engine(),
                                 '/mock/', True, False, True)
  
      def test_db_metadefs_export(self):
          db_metadata.db_export_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'export_metadefs'],
                                 db_metadata.db_export_metadefs,
                                 db_api.get_engine(),
                                 None)
  
      def test_db_metadefs_export_with_specified_path(self):
          db_metadata.db_export_metadefs = mock.Mock()
          self._main_test_helper(['glance.cmd.manage', 'db', 'export_metadefs',
                                  '--path', '/mock/'],
                                 db_metadata.db_export_metadefs,
                                 db_api.get_engine(),
                                 '/mock/')
  