bool aznfsc_cfg::parse_config_yaml()

in turbonfs/src/config.cpp [19:252]


bool aznfsc_cfg::parse_config_yaml()
{

#define __CHECK_INT(var, min, max, zeroisvalid) \
do { \
    if (((var) == -1) && config[#var]) { \
        (var) = config[#var].as<int>(); \
        if ((var) < min || (var) > max) { \
            if ((zeroisvalid) && ((var) == 0)) { \
                break; \
            } \
            throw YAML::Exception( \
                    config[#var].Mark(), \
                    std::string("Invalid value for config "#var": ") + \
                    std::to_string(var) + \
                    std::string(" (valid range [") + \
                    std::to_string(min) + ", " + std::to_string(max) + "])"); \
        }\
    } \
} while (0);

/*
 * Macro to check validity of config var of integer type.
 */
#define _CHECK_INT(var, min, max) __CHECK_INT(var, min, max, false)

/*
 * Macro to check validity of config var of integer type with 0 being a
 * valid value.
 */
#define _CHECK_INTZ(var, min, max) __CHECK_INT(var, min, max, true)

/*
 * Macro to check validity of config var of boolean type.
 */
#define _CHECK_BOOL(var) \
do { \
    if (config[#var]) { \
        (var) = config[#var].as<bool>(); \
    } \
} while (0);


/*
 * Macro to check validity of config var of string type.
 */
#define __CHECK_STR(var, is_valid, ignore_empty) \
do { \
    if (((var) == nullptr) && config[#var]) { \
        /* Empty key is returned as "null" by the yaml parser */ \
        if ((ignore_empty) && config[#var].as<std::string>() == "null") { \
            break; \
        } \
        (var) = ::strdup(config[#var].as<std::string>().c_str()); \
        if (!is_valid(var)) { \
            throw YAML::Exception( \
                    config[#var].Mark(), \
                    std::string("Invalid value for config "#var": ") + \
                    std::string(var)); \
        } \
    } \
} while (0);

#define _CHECK_STR(var) __CHECK_STR(var, is_valid_##var, false)
#define _CHECK_STR2(var, is_valid) __CHECK_STR(var, is_valid, false)

/*
 * Macro to check validity of config var of percentage type.
 * var can be set to one of the following values:
 * 1) -2 => var is not specified in the config.
 * 2) -1 => var is not a valid percentage string. Caller must try other valid
 *          interpretations.
 * 3) var >= 0 && var <= 100 => var is specified as a percentage value.
 */
#define _MAYBE_PERCENT(var) \
do { \
    if (!config[#var] || config[#var].as<std::string>() == "null") { \
        var = -2; \
        break; \
    }\
    var = get_percent_value(config[#var].as<std::string>()); \
    assert((var == -1) || (var >= 0 && var <= 100)); \
} while (0);

    if (config_yaml == nullptr) {
        return true;
    }

    AZLogDebug("Parsing config yaml {}", config_yaml);

    /*
     * We parse the config yaml and set *only* those options which are not yet
     * set by cmdline. Thus cmdline options are given higher priority than the
     * corresponding option in the config yaml.
     */
    try {
        YAML::Node config = YAML::LoadFile(config_yaml);

        _CHECK_BOOL(debug);

        _CHECK_STR(account);
        _CHECK_STR(container);
        _CHECK_STR(cloud_suffix);

        if ((port == -1) && config["port"]) {
            port = config["port"].as<int>();
#ifndef ENABLE_NON_AZURE_NFS
            if (port != 2048 && port != 2047) {
                throw YAML::Exception(
                    config["port"].Mark(),
                    std::string("Invalid port number: ") +
                    std::to_string(port) +
                    std::string(" (can be 2048 or 2047)"));
            }
#endif
        }

        _CHECK_INT(nconnect, AZNFSCFG_NCONNECT_MIN, AZNFSCFG_NCONNECT_MAX);
        _CHECK_INT(timeo, AZNFSCFG_TIMEO_MIN, AZNFSCFG_TIMEO_MAX);
        _CHECK_INT(acregmin, AZNFSCFG_ACTIMEO_MIN, AZNFSCFG_ACTIMEO_MAX);
        _CHECK_INT(acregmax, AZNFSCFG_ACTIMEO_MIN, AZNFSCFG_ACTIMEO_MAX);
        _CHECK_INT(acdirmin, AZNFSCFG_ACTIMEO_MIN, AZNFSCFG_ACTIMEO_MAX);
        _CHECK_INT(acdirmax, AZNFSCFG_ACTIMEO_MIN, AZNFSCFG_ACTIMEO_MAX);
        _CHECK_INT(actimeo, AZNFSCFG_ACTIMEO_MIN, AZNFSCFG_ACTIMEO_MAX);
        _CHECK_STR(lookupcache);
        _CHECK_STR(consistency);

        _CHECK_BOOL(auth);

        /*
         * bc_iovec::add_bc() does not accept bytes_chunk larger than wsize.
         * Since fuse can send max 1MiB sized writes, we must not allow wsize
         * to be set less than 1MiB.
         * Since write can get a bytes_chunk allocated by read/readahead, rsize
         * must not be allowed to be greater than wsize.
         */
        _CHECK_INT(wsize, AZNFSCFG_WSIZE_MIN, AZNFSCFG_WSIZE_MAX);
        _CHECK_INT(rsize, AZNFSCFG_RSIZE_MIN, std::min(wsize, AZNFSCFG_RSIZE_MAX));

        _CHECK_INT(retrans, AZNFSCFG_RETRANS_MIN, AZNFSCFG_RETRANS_MAX);
        _CHECK_INT(readdir_maxcount, AZNFSCFG_READDIR_MIN, AZNFSCFG_READDIR_MAX);
        /*
         * Allow special value of 0 to disable readahead.
         * Mostly useful for testing.
         */
        _CHECK_INTZ(readahead_kb, AZNFSCFG_READAHEAD_KB_MIN, AZNFSCFG_READAHEAD_KB_MAX);
        _CHECK_INT(fuse_max_background, AZNFSCFG_FUSE_MAX_BG_MIN, AZNFSCFG_FUSE_MAX_BG_MAX);
        _CHECK_INT(fuse_max_threads, AZNFSCFG_FUSE_MAX_THR_MIN, AZNFSCFG_FUSE_MAX_THR_MAX);
        _CHECK_INT(fuse_max_idle_threads, AZNFSCFG_FUSE_MAX_IDLE_THR_MIN, AZNFSCFG_FUSE_MAX_IDLE_THR_MAX);

        _CHECK_STR(xprtsec);
        _CHECK_BOOL(oom_kill_disable);

        _CHECK_BOOL(cache.attr.user.enable);
        _CHECK_BOOL(cache.readdir.kernel.enable);
        _CHECK_INT(cache.readdir.user.max_size_mb,
                   AZNFSCFG_CACHE_MAX_MB_MIN, AZNFSCFG_CACHE_MAX_MB_MAX);
        // User readdir cache cannot be turned off.
        assert(cache.readdir.user.enable);
        _CHECK_BOOL(cache.data.kernel.enable);

        // User data cache cannot be turned off.
        assert(cache.data.user.enable);
        if (cache.data.user.enable) {
            /*
             * cache.data.user.max_size_mb can be specified as a percentage
             * value, in which case it's the percentage of the total RAM size
             * else it's an absolute size in MB.
             */
            _MAYBE_PERCENT(cache.data.user.max_size_mb);

            if (cache.data.user.max_size_mb >= 0) {
                AZLogDebug("Using {}% of total RAM {}MB for "
                           "cache.data.user.max_size_mb",
                           cache.data.user.max_size_mb,
                           (get_total_ram() / (1024 * 1024)));
                cache.data.user.max_size_mb =
                    (cache.data.user.max_size_mb / 100.0) *
                    (get_total_ram() / (1024 * 1024));
                if (cache.data.user.max_size_mb < AZNFSCFG_CACHE_MAX_MB_MIN ||
                    cache.data.user.max_size_mb > AZNFSCFG_CACHE_MAX_MB_MAX) {
                    throw YAML::Exception(
                            config["cache.data.user.max_size_mb"].Mark(),
                            std::string("Invalid value for config cache.data.user.max_size_mb: ") +
                            config["cache.data.user.max_size_mb"].as<std::string>().c_str() +
                            std::string(", resultant value ") +
                            std::to_string(cache.data.user.max_size_mb) +
                            std::string(" is not in valid range [") +
                            std::to_string(AZNFSCFG_CACHE_MAX_MB_MIN) +
                            std::string(", ") +
                            std::to_string(AZNFSCFG_CACHE_MAX_MB_MAX) +
                            std::string("]"));
                }
            } else if (cache.data.user.max_size_mb == -1) {
                /*
                 * Not expressed as a percentage, try absolute MB.
                 */
                _CHECK_INT(cache.data.user.max_size_mb,
                           AZNFSCFG_CACHE_MAX_MB_MIN, AZNFSCFG_CACHE_MAX_MB_MAX);
            }
        } else {
            cache.data.user.max_size_mb = 0;
        }

        _CHECK_BOOL(filecache.enable);
        if (filecache.enable) {
            _CHECK_STR2(filecache.cachedir, is_valid_cachedir);
            _CHECK_INT(filecache.max_size_gb, AZNFSCFG_FILECACHE_MAX_GB_MIN, AZNFSCFG_FILECACHE_MAX_GB_MAX);
        } else {
            filecache.max_size_gb = 0;
        }

        /*
         * Config affecting misc system behaviour.
         */
        _CHECK_BOOL(sys.force_stable_writes);
        _CHECK_BOOL(sys.resolve_before_reconnect);
        _CHECK_BOOL(sys.nodrc.remove_noent_as_success);
        _CHECK_BOOL(sys.nodrc.create_exist_as_success);
        _CHECK_BOOL(sys.nodrc.rename_noent_as_success);

    } catch (const YAML::BadFile& e) {
        AZLogError("Error loading config file {}: {}", config_yaml, e.what());
        return false;
    } catch (const YAML::Exception& e) {
        AZLogError("Error parsing config file {}: {}", config_yaml, e.what());
        return false;
    } catch (...) {
        AZLogError("Unknown error parsing config file {}", config_yaml);
        return false;
    }

    return true;
}