#!/usr/bin/env python
# Copyright C 2002-2013 by Isilon Systems, Inc. All rights not expressly
# granted herein are reserved.
# Created in the United States of America.
#
# This digital media is protected by U.S. and international copyright and
# intellectual property laws. Unless otherwise specified, all information
# and screens appearing as part of this digital medium, including
# software, services, documents, text, images, icons, and logos design;
# the selection, assembly, arrangement, and design thereof; and the code
# that enables its presentation, are the sole property of Isilon Systems,
# Inc.
#
# Except as otherwise required by applicable law, no part of any
# copyrighted material may be reproduced, modified, translated,
# distributed, published, or transmitted by any means, electronic or
# mechanical, for any purpose, without the prior written consent of Isilon
# Systems.
#
# Isilon and Isilon Systems are registered trademarks of Isilon Systems,
# Inc.  "Isilon IQ" is a trademark of Isilon Systems, Inc.  All other
# trademarks and registered trademarks are the property of their
# respective holders.

import errno
import os
import sys

import isi.config.array as array
import isi.fs.drive_purposing as drive_purposing
import isi.hw.drived as drived
import isi.misc as imisc
import isi.smartpools.diskpools as spdp
import isi.fs.diskpool as fsdp
import isi.sys.version as sysver
import isi.upgrade.preupgradechecks.diskpools_compat as compat

MAX_RETRIES = 10

def main(argv):

    nodes = array.getNodes()

    node_info = sysver.Gatherer().gather()
    devid = node_info['devid']
    node_type = compat.strip_ni_to_type(node_info)

    # There may be other entities writing to the drive purposing DB, rendering
    # our in-memory copy stale.  So keep retrying on ESTALE.
    retries = 0
    while True:
        purpose_config = None
        purposedb = drive_purposing.DrivePurposeDB()

        if not purposedb.bootstrapping:
            # If we're < DISK_POOL_VERSION_7_1_0 we need to grab manual from
            # the disk pool db, and pass in a special config to add_entry
            dp_cfg = spdp.open_config()
            upgrading_to_7_1_0 = (dp_cfg.db.version <
                    fsdp.DISK_POOL_VERSION_7_1_0)
            in_manual_pool = dp_cfg.devid_in_mav_manual_pool(devid)

            if upgrading_to_7_1_0 and in_manual_pool:
                # We need to determine what our manual id will be. If any of the
                # other devids in our nodepool are in the purposedb, we will use
                # their manual id. Otherwise we'll create a new one.
                lnn_to_devid = dp_cfg.db.get_lnn_to_devid_map()
                lnns = dp_cfg.db.find_disk_pool_for_devid(devid).owner().nodes
                devids = [lnn_to_devid[lnn] for lnn in lnns]
                eqids = map(purposedb.find_equivalence_id_for_devid, devids)
                eqids = set(filter(None, eqids))

                if eqids:
                    assert len(eqids) == 1
                    eqid = iter(eqids).next()
                    node_type['manual'] = \
                        purposedb.get_manual_id_for_equivalence(eqid)
                else:
                    node_type['manual'] = purposedb._next_manual_id()

            if upgrading_to_7_1_0:
                # If we're pre-7_1_0 we use a special config
                purpose_config = \
                    purposedb.get_default_configuration(pre_7_1_0=True)

            purposedb.add_entry(devid, node_type, purpose_config)
            try:
                purposedb.write()
                break
            except (IOError, OSError), e:
                if (e.errno not in (errno.ESTALE, errno.ENOTDIR) or
                        retries >= MAX_RETRIES):
                    raise
        else:
            break
        retries += 1

    # No worries if we fail because drived isn't up yet, it should trigger us
    # again once it comes up.
    try:
        drives = drived.getDrives()
    except drived.Error as e:
        if e.errno == errno.ECONNREFUSED:
            # DriveD isn't up yet, ignore and abort.
            return e.errno
        raise

    for drive in drives:
        if (drive.usr_purpose == drived.USR_NO_PURPOSE and
                drive.present):
            try:
                purposedb.give_purpose(devid, drive)
            except drived.Error as e:
                if e.errno != errno.ENOENT:
                    raise
                # ENOENT -> Drive disappeared since we were spawned to purpose
                # it. Log but ignore.
                sys.stderr.write("Ignoring absent drive: (%s,bay%s)\n" % (
                    devid, drive.bay))

    # If we're bootstrapping, let our caller know
    if purposedb.bootstrapping:
        return errno.ENOSYS

    return 0

if __name__ == "__main__":
    imisc.setproctitle(os.path.basename(sys.argv[0]))
    error = 0
    try:
        error = main(sys.argv[1:])
    except:
        raise
        error = 1
    sys.exit(error)

