#!/usr/bin/env 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.


"""
Script restores removed attachment in Cinder, it modifies only
attachment id in Nova database.

For restoring all attachments run script without arguments
There are 2 cli args:
    --dry-run - it makes no action, just prints affected instances
    --instances - create attachments for specific instances only.
                  Accepts comma separated instance's uuids
"""

import sys

from os_brick.initiator import connector
from oslo_config import cfg
from nova.cmd import common as cmd_common
import nova.conf
from nova import config
from nova import context
from nova import objects
from nova import utils
from nova.volume import cinder

try:
    from nova.db.api import block_device_mapping_get_all_by_volume_id
    from nova.db import api as db
except ImportError:
    from nova.db.main import api as db

CONF = nova.conf.CONF

args = cmd_common.args
ctx = context.get_admin_context()
root_helper = utils.get_root_helper()
volume_api = cinder.API()


def create_attachment(bdm, volume, host, host_ip):
    # LightOS connector tries to create hostnqn file, mock it
    connector.utils.get_host_nqn = lambda: None
    cntr = connector.get_connector_properties(
        root_helper, host_ip,
        CONF.libvirt.volume_use_multipath,
        enforce_multipath=False,
        host=host,
        # mock execution
        execute=lambda *args, **kwargs: ('', 0))
    volume.reset_state('available')
    attach_ref = volume_api.attachment_create(
        ctx, bdm.volume_id, bdm.instance_uuid,
        connector=cntr, mountpoint=bdm.device_name)
    bdm.attachment_id = attach_ref['id']
    bdm.save()
    volume_api.attachment_complete(ctx, bdm.attachment_id)


def restore_volume_attachments():
    compute_nodes = objects.ComputeNodeList.get_all(ctx)
    compute_nodes_map = {node.host: node.host_ip for node in compute_nodes}

    bdms = []
    volumes = dict()
    if CONF.instances:
        bdms = objects.BlockDeviceMappingList.get_by_instance_uuids(
            ctx, CONF.instances)
    else:
        # pagination?
        in_use_volumes = cinder.cinderclient(ctx).volumes.list(
            detailed=True,
            search_opts={'all_tenants': True, 'status': 'in-use'})
        for volume in in_use_volumes:
            if not volume.attachments:
                vol_bdms = db.block_device_mapping_get_all_by_volume_id(
                    ctx, volume.id)
                volumes[volume.id] = volume
                for bdm in vol_bdms:
                    bdm = objects.BlockDeviceMapping._from_db_object(
                        ctx, objects.BlockDeviceMapping(), bdm)
                    bdms.append(bdm)
    counter = 0
    for bdm in bdms:
        if bdm.is_volume:
            print('Creating attachment for instance %(inst)s '
                  ' and volume %(volume)s' % {
                'inst': bdm.instance_uuid,
                'volume': bdm.volume_id})
            counter += 1
            if CONF.dry_run:
                continue
            volume = volumes.get(bdm.volume_id)
            if not volume:
                volume = cinder.cinderclient(ctx).volumes.get(bdm.volume_id)
            inst = objects.Instance.get_by_uuid(
                ctx, bdm.instance_uuid)
            create_attachment(
                bdm, volume, inst.host, compute_nodes_map[inst.host])
    print('Found %d broken attachments' % counter)


def main():
    CONF.register_cli_opts([cfg.BoolOpt('dry-run', default=False), # noqa
                            cfg.ListOpt('instances')]) # noqa
    config.parse_args(sys.argv)
    objects.register_all()

    restore_volume_attachments()


if __name__ == '__main__':
    main()
