ironic-inspector/ironic_inspector/cmd/migration.py

128 lines
4.3 KiB
Python

# 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.
"""Migrate introspected data between Swift and database."""
from __future__ import print_function
import sys
from oslo_config import cfg
from oslo_log import log
from oslo_utils import encodeutils
from ironic_inspector.common.i18n import _
from ironic_inspector.conf import opts
from ironic_inspector import node_cache
from ironic_inspector.plugins import base as plugins_base
from ironic_inspector import utils
LOG = log.getLogger(__name__)
CONF = cfg.CONF
_AVAILABLE_STORAGES = [('database', _('The database storage backend')),
('swift', _('The Swift storage backend'))]
_OPTS = [
cfg.StrOpt('from',
dest='source_storage',
required=True,
choices=_AVAILABLE_STORAGES,
help=_('The source storage where the introspected data will be '
'read from.')),
cfg.StrOpt('to',
dest='target_storage',
required=True,
choices=_AVAILABLE_STORAGES,
help=_('The target storage where the introspected data will be '
'saved to.'))
]
# Migration result
RESULT_NOCONTENT = 'no content'
RESULT_FAILED = 'failed'
RESULT_SUCCESS = 'success'
def _setup_logger(args=None):
args = [] if args is None else args
log.register_options(CONF)
opts.set_config_defaults()
opts.parse_args(args)
log.setup(CONF, 'ironic_inspector')
class MigrationTool(object):
def _migrate_one(self, node, processed):
LOG.debug('Starting to migrate introspection data for node '
'%(node)s (processed %(processed)s)',
{'node': node.uuid, 'processed': processed})
try:
data = self.ext_src.get(node.uuid, processed=processed,
get_json=True)
if not data:
return RESULT_NOCONTENT
self.ext_tgt.save(node.uuid, data, processed=processed)
except Exception as e:
LOG.error('Migrate introspection data failed for node '
'%(node)s (processed %(processed)s), error: '
'%(error)s', {'node': node.uuid, 'processed': processed,
'error': e})
return RESULT_FAILED
return RESULT_SUCCESS
def main(self):
CONF.register_cli_opts(_OPTS)
_setup_logger(sys.argv[1:])
if CONF.source_storage == CONF.target_storage:
raise utils.Error(_('Source and destination can not be the same.'))
introspection_data_manager = plugins_base.introspection_data_manager()
self.ext_src = introspection_data_manager[CONF.source_storage].obj
self.ext_tgt = introspection_data_manager[CONF.target_storage].obj
nodes = node_cache.get_node_list()
migration_list = [(n, p) for n in nodes for p in [True, False]]
failed_records = []
for node, processed in migration_list:
result = self._migrate_one(node, processed)
if result == RESULT_FAILED:
failed_records.append((node.uuid, processed))
msg = ('Finished introspection data migration, total records: %d. '
% len(migration_list))
if failed_records:
msg += 'Failed to migrate:\n' + '\n'.join([
'%s(processed=%s)' % (record[0], record[1])
for record in failed_records])
elif len(migration_list) > 0:
msg += 'all records are migrated successfully.'
print(msg)
def main():
try:
MigrationTool().main()
except KeyboardInterrupt:
print(_("... terminating migration tool"), file=sys.stderr)
return 130
except Exception as e:
print(encodeutils.safe_encode(str(e)), file=sys.stderr)
return 1
if __name__ == '__main__':
sys.exit(main())