Beispiel #1
0
    def test_write_cache(self):
        """
        Test cache.write_cache().

        Returns
        -------
            No return value.
        """
        ts0 = write_cache(cache_fname=self.file3,
                          cache_content={'hello': 'world'})
        self.assertNotEqual(ts0, None,
                            "New cache write return None as timestamp")
        ts = get_timestamp(self.file3)
        self.assertEqual(
            ts0, ts, "timestamp returned from get_timestamp differ form "
            "one returned by write_cache")
        self.assertEqual(load_cache(self.file3), (ts, {
            'hello': 'world'
        }), 'Unexpected return values from load_cache()')
        self.assertFalse(
            write_cache(cache_fname='/proc/foo1', cache_content={}))
        self.assertFalse(
            write_cache(cache_fname='/proc/foo1',
                        fallback_fname='/proc/foo3',
                        cache_content={}))
        self.assertTrue(
            write_cache(cache_fname='/proc/foo1',
                        fallback_fname=self.file3,
                        cache_content={'hello': 'again'}))
        ts = get_timestamp(self.file3)
        self.assertEqual(load_cache(self.file3), (ts, {'hello': 'again'}))
Beispiel #2
0
    def setUp(self):
        """
        Initialise the OCI cache test.

        Returns
        -------
            No return value.
        """
        super(testOciCache, self).setUp()
        # create 2 files, one newer than the other to verify get_newer()
        self.file1 = "/tmp/oci-test-%s" % uuid.uuid1()
        self.file2 = "/tmp/oci-test-%s" % uuid.uuid1()
        self.file3 = "/tmp/oci-test-%s" % uuid.uuid1()
        # this one won't be created
        self.nofile = "/tmp/oci-test-%s" % uuid.uuid1()
        with open(self.file1, "w") as _f:
            json.dump(testOciCache.file1_content, _f)
        _f.close()
        time.sleep(1)
        with open(self.file2, "w") as _f:
            json.dump(testOciCache.file2_content, _f)
        _f.close()

        self.ts1 = get_timestamp(self.file1)
        self.ts2 = get_timestamp(self.file2)
Beispiel #3
0
    def test_get_timestamp(self):
        """
        Tests cache.get_timestamp().

        Returns
        -------
            No return value.
        """
        self.assertEqual(get_timestamp(None), 0, 'get_timestamp(None) != 0')
        self.assertEqual(get_timestamp('file_path_which_do_not_exists '), 0,
                         'get_timestamp() on non-existing file did not return 0')
        self.assertGreater(get_timestamp(tempfile.gettempdir()), 0,
                           'get_timestamp() on existing path did not return '
                           'positive value')
Beispiel #4
0
def iscsi_func(context, func_logger):
    """
    OCID thread function for discovering and attaching/detaching block
    volumes; context must include 'max_volumes' and 'auto_detach'.

    Parameters
    ----------
    context: dict
        The thread context.
    func_logger: logger

    Returns
    -------
    dict
        The new context.
    """
    if 'oci_sess' not in context:
        oci_sess = None
        try:
            oci_sess = oci_utils.oci_api.OCISession()
        except Exception as e:
            func_logger.debug('Failed to get a session: %s' % str(e))

        max_volumes = 8
        if 'max_volumes' in context:
            max_volumes = int(context['max_volumes'])

        auto_detach = True
        if 'auto_detach' in context:
            auto_detach = context['auto_detach']

        # the number of iterations to wait before detaching an offline volume
        detach_retry = 5
        if 'detach_retry' in context:
            detach_retry = int(context['detach_retry'])

        if max_volumes > _MAX_VOLUMES_LIMIT:
            func_logger.warn("Your configured max_volumes(%s) is over the limit(%s)\n"
                             % (max_volumes, _MAX_VOLUMES_LIMIT))
            max_volumes = _MAX_VOLUMES_LIMIT

        context = {'ignore_file_ts': 0,
                   'ignore_iqns': [],
                   'attach_failed': {},
                   'chap_pw_ts': 0,
                   'chap_pws': {},
                   'oci_sess': oci_sess,
                   'max_volumes': max_volumes,
                   'offline_vols': {},
                   'auto_detach': auto_detach,
                   'detach_retry': detach_retry, }

    # devices currently attached
    session_devs = oci_utils.iscsiadm.session()

    # Load the saved passwords
    chap_passwords = context['chap_pws']
    if context['chap_pw_ts'] == 0 \
            or get_timestamp(oci_utils.__chap_password_file) > context['chap_pw_ts']:
        # the password file has changed or was never loaded
        context['chap_pw_ts'], chap_passwords = load_cache(oci_utils.__chap_password_file)
    if chap_passwords is None:
        chap_passwords = {}

    # save for the next iteration
    context['chap_pws'] = chap_passwords

    # volumes that are offline in this iteration
    new_offline_vols = {}

    all_iqns = {}

    # -------------------------------------------------------------------------------------
    # possible change for LINUX-11440; comment out the in-between
    # verify if user has authorisation to list volumes; if not, scan for new volumes.
    # volumes = None
    # if context['oci_sess'] is not None:
    #     try:
    #         #
    #         # get a list of volumes attached to the instance
    #         instance = context['oci_sess'].this_instance()
    #         if instance is None:
    #             func_logger.debug('Cannot get current instance.')
    #         else:
    #             volumes = instance.all_volumes()
    #     except Exception as e:
    #         func_logger.debug('User is not authorized to list all volumes.')
    # -------------------------------------------------------------------------------------
    #
    # volumes connected to this instance
    inst_volumes = []
    if context['oci_sess'] is not None:
        #
        # get a list of volumes attached to the instance
        instance = context['oci_sess'].this_instance()
        if instance is None:
            func_logger.debug('Cannot get current instance.')
        else:
            volumes = instance.all_volumes()
            for v in volumes:
                vol = {'iqn': v.get_iqn(),
                       'ipaddr': v.get_portal_ip(),
                       'user': v.get_user(),
                       'password': v.get_password()}
                inst_volumes.append(vol)
                if v.get_portal_ip() in all_iqns:
                    all_iqns[v.get_portal_ip()].append(v.get_iqn())
                else:
                    all_iqns[v.get_portal_ip()] = [v.get_iqn()]
            func_logger.debug('All volumes: %s', all_iqns)
    # -------------------------------------------------------------------------------------
    #
    # possible change for LINUX-11440; comment out the above
    # if bool(volumes):
    #     for v in volumes:
    #         vol = {'iqn': v.get_iqn(),
    #                'ipaddr': v.get_portal_ip(),
    #                'user': v.get_user(),
    #                'password': v.get_password()}
    #         inst_volumes.append(vol)
    #         if v.get_portal_ip() in all_iqns:
    #             all_iqns[v.get_portal_ip()].append(v.get_iqn())
    #         else:
    #             all_iqns[v.get_portal_ip()] = [v.get_iqn()]
    #     func_logger.debug('All volumes: %s', all_iqns)
    #
    # -------------------------------------------------------------------------------------
    else:
        #
        # fall back to scanning
        func_logger.debug('Scan for volumes.')
        for r in range(context['max_volumes'] + 1):
            ipaddr = "169.254.2.%d" % (r + 1)
            iqns = oci_utils.iscsiadm.discovery(ipaddr)
            all_iqns[ipaddr] = iqns
            for iqn in iqns:
                vol = {'iqn': iqn,
                       'ipaddr': ipaddr,
                       'user': None,
                       'password': None}
                # look for a saved password
                if iqn in chap_passwords:
                    vol['user'] = chap_passwords[iqn][0]
                    vol['password'] = chap_passwords[iqn][1]
                inst_volumes.append(vol)
            func_logger.debug('Scanned volumes: %s', inst_volumes)
    #
    # Load the list of volumes that were detached using oci-iscsi-config.
    # ocid shouldn't attach these automatically.
    ignore_iqns = context['ignore_iqns']
    if context['ignore_file_ts'] == 0 or get_timestamp(oci_utils.__ignore_file) > context['ignore_file_ts']:
        #
        # the list of detached volumes changed since last reading the file
        context['ignore_file_ts'], ignore_iqns = load_cache(oci_utils.__ignore_file)
    if ignore_iqns is None:
        ignore_iqns = []
    #
    # save for next iteration
    context['ignore_iqns'] = ignore_iqns
    #
    # volumes that failed to attach in an earlier iteration
    attach_failed = context['attach_failed']
    #
    # do we need to cache files?
    cache_changed = False
    ign_changed = False
    chap_changed = False
    #
    # if inst_volumes is empty, clean iscsiadm-cache to.
    if not bool(inst_volumes):
        all_iqns = {}
        write_cache(cache_content=[all_iqns, attach_failed], cache_fname=oci_utils.iscsiadm.ISCSIADM_CACHE)
    #
    # check if all discovered iscsi devices are configured and attached
    for vol in inst_volumes:
        func_logger.debug('iqn: %s', vol['iqn'])
        if vol['iqn'] in ignore_iqns:
            # a device that was manually detached, so don't
            # re-attach it automatically
            continue
        if vol['iqn'] not in session_devs:
            if vol['iqn'] in attach_failed:
                # previous attempt to attach failed, ignore
                continue
            cache_changed = True
            # configure and attach the device
            __ocid_logger.info("Attaching iscsi device: %s:%s (%s)", vol['ipaddr'], "3260", vol['iqn'])
            if vol['user'] is not None:
                attach_result = oci_utils.iscsiadm.attach(vol['ipaddr'],
                                                          3260,
                                                          vol['iqn'],
                                                          vol['user'],
                                                          vol['password'],
                                                          auto_startup=True)
                if vol['iqn'] not in chap_passwords:
                    chap_passwords[vol['iqn']] = (vol['user'], vol['password'])
                    chap_changed = True
            else:
                attach_result = oci_utils.iscsiadm.attach(vol['ipaddr'],
                                                          3260,
                                                          vol['iqn'],
                                                          auto_startup=True)
            if attach_result != 0:
                func_logger.info("Failed to attach device: %s"
                                 % oci_utils.iscsiadm.error_message_from_code(attach_result))
                attach_failed[vol['iqn']] = attach_result
                cache_changed = True
        else:
            #
            # iqn is in session_devs but not in iscsiadm cache
            write_cache(cache_content=[all_iqns, attach_failed], cache_fname=oci_utils.iscsiadm.ISCSIADM_CACHE)

    # look for previously failed volumes that are now in the session
    # (e.g. the user supplied the password using oci-iscsi-config)
    for iqn in list(attach_failed.keys()):
        if iqn in session_devs:
            del attach_failed[iqn]
            cache_changed = True

    detach_retry = 5
    if 'detach_retry' in context:
        detach_retry = int(context['detach_retry'])

    # look for disconnected devices in the current session
    # these devices were disconnected from the instance in the console,
    # we now have to detach them from at the OS level
    for iqn in session_devs:
        #
        # ignore the boot device
        if iqn.endswith('boot:uefi'):
            continue
        if 'state' not in session_devs[iqn]:
            continue
        if session_devs[iqn]['state'] in ['blocked', 'transport-offline']:
            func_logger.debug("Checking iqn %s (state %s)\n" % (iqn, session_devs[iqn]['state']))
            #
            # is the iqn discoverable at the portal?
            if iqn not in inst_volumes:
                # Not found by iscsiadm discovery.
                # To allow time for the volume to recover, wait for detach_retry
                # iterations where the volume was offline before detaching it
                if iqn not in context['offline_vols']:
                    func_logger.info("iSCSI volume appears to be offline: %s" % iqn)
                    new_offline_vols[iqn] = 1
                    continue

                if context['offline_vols'][iqn] < detach_retry:
                    new_offline_vols[iqn] = context['offline_vols'][iqn] + 1
                    func_logger.info("iSCSI volume still offline (%d): %s" % (new_offline_vols[iqn], iqn))
                    continue

                if not context['auto_detach']:
                    func_logger.info("Volume still offline, but iSCSI auto_detach disabled: %s" % iqn)
                    new_offline_vols[iqn] = detach_retry + 1
                    continue

                cache_changed = True
                ipaddr = session_devs[iqn]['persistent_portal_ip']
                func_logger.info("Detaching iSCSI device: %s:%s (%s)" % (ipaddr, "3260", iqn))
                oci_utils.iscsiadm.detach(ipaddr, 3260, iqn)
                #
                # delete from list of previously offline volumes so it
                # doesn't get reported as 'now online'
                del context['offline_vols'][iqn]
                #
                # device is gone, remove from "ignore" list
                if iqn in ignore_iqns:
                    ignore_iqns.remove(iqn)
                    ign_changed = True
                #
                # remove from attach_failed list if present
                if iqn in attach_failed:
                    del attach_failed[iqn]
                    cache_changed = True
    #
    # look for devices that were previously offline but now back online
    # (just for printing a message that it's now online)
    for iqn in context['offline_vols']:
        if iqn not in new_offline_vols:
            func_logger.info("iSCSI volume now online: %s" % iqn)
    context['offline_vols'] = new_offline_vols
    #
    # check if the devices that were previously manually detached are still
    # connected to the instance
    inst_iqns = [vol['iqn'] for vol in inst_volumes]
    for iqn in ignore_iqns:
        # if iqn not in inst_iqns:
        # GT
        if iqn in inst_iqns:
            func_logger.debug("Removing iqn %s from ignore list" % iqn)
            ignore_iqns.remove(iqn)
            ign_changed = True

    # rewrite changed cache files
    if ign_changed:
        context['ignore_file_ts'] = \
            write_cache(cache_content=ignore_iqns, cache_fname=oci_utils.__ignore_file)
    if chap_changed:
        context['chap_pw_ts'] = \
            write_cache(cache_content=chap_passwords, cache_fname=oci_utils.__chap_password_file, mode=0o600)
    if cache_changed or not os.path.exists(oci_utils.iscsiadm.ISCSIADM_CACHE):
        write_cache(cache_content=[all_iqns, attach_failed], cache_fname=oci_utils.iscsiadm.ISCSIADM_CACHE)
    else:
        try:
            os.utime(oci_utils.iscsiadm.ISCSIADM_CACHE, None)
        except Exception as e:
            func_logger.warn("Failed to update cache timestamp: %s" % e)

    return context