static indigo_error_t
ind_core_cfg_stage(cJSON *config)
{
    char *str;
    indigo_error_t err;

    err = ind_cfg_parse_loglevel(config, "logging.flowtable",
                                 OFSTATEMANAGER_CONFIG_LOG_BITS_DEFAULT,
                                 &staged_config.log_flags);
    if (err != INDIGO_ERROR_NONE) {
        return err;
    }

    /* Not supporting setting log options yet */

    err = 0;
    err |= get_fixed_len_string(staged_config.hw_desc, OF_DESC_STR_LEN,
                                config, "of_hw_desc");
    err |= get_fixed_len_string(staged_config.sw_desc, OF_DESC_STR_LEN,
                                config, "of_sw_desc");
    err |= get_fixed_len_string(staged_config.mfr_desc, OF_DESC_STR_LEN,
                                config, "of_mfr_desc");
    err |= get_fixed_len_string(staged_config.dp_desc, OF_DESC_STR_LEN,
                                config, "of_dp_desc");
    err |= get_fixed_len_string(staged_config.serial_num, OF_SERIAL_NUM_LEN,
                                config, "of_serial_num");
    if (err != 0) {
        /* Error message logged by get_fixed_len_string */
        return INDIGO_ERROR_PARAM;
    }

    err = ind_cfg_lookup_string(config, "of_datapath_id", &str);
    if (err == INDIGO_ERROR_NONE) {
        char *endptr;
        staged_config.dpid = strtoull(str, &endptr, 16);
        if (*endptr) { /* Did not parse string */
            AIM_LOG_ERROR("Config: Could not parse of_datapath_id; "
                          "must be hex digit string");
            return INDIGO_ERROR_PARAM;
        }
    } else {
        if (err == INDIGO_ERROR_PARAM) {
            AIM_LOG_ERROR("Config: Could not parse of_datapath_id");
        } else if (err == INDIGO_ERROR_NOT_FOUND) {
            AIM_LOG_ERROR("Config: Missing required key of_datapath_id");
        }
        return err;
    }

    return INDIGO_ERROR_NONE;
}
static indigo_error_t
ind_soc_cfg_stage(cJSON *config)
{
    indigo_error_t err;

    err = ind_cfg_parse_loglevel(config, "logging.connection",
                                 SOCKETMANAGER_CONFIG_LOG_BITS_DEFAULT,
                                 &staged_config.log_flags);
    if (err != INDIGO_ERROR_NONE) {
        return err;
    }

    /* Always remove tracing when logging set via this interface */
    staged_config.log_flags &= ~AIM_LOG_BIT_TRACE;

    /* Not supporting setting log options yet */

    return INDIGO_ERROR_NONE;
}
indigo_error_t
ind_cxn_cfg_stage(cJSON *config)
{
    indigo_error_t err;
    int i;

    err = ind_cfg_parse_loglevel(config, "logging.connection",
                                 OFCONNECTIONMANAGER_CONFIG_LOG_BITS_DEFAULT,
                                 &staged_config.log_flags);
    if (err != INDIGO_ERROR_NONE) {
        return err;
    }

    /* Not supporting setting log options yet */

    err = ind_cfg_lookup_int(config, "keepalive_period_ms", &staged_config.keepalive_period_ms);
    if (err == INDIGO_ERROR_NONE) {
        if (staged_config.keepalive_period_ms <= 0) {
            AIM_LOG_ERROR("'keepalive_period_ms' must be greater than 0");
            return INDIGO_ERROR_PARAM;
        }
    } else if (err == INDIGO_ERROR_PARAM) {
        AIM_LOG_ERROR("Config: Could not parse 'keepalive_period_ms'");
        return err;
    } else if (err == INDIGO_ERROR_NOT_FOUND) {
        AIM_LOG_ERROR("Config: Mising required key 'keepalive_period_ms'");
        return err;
    }

    err = parse_controllers(config);
    if (err != INDIGO_ERROR_NONE) {
        return err;
    }

    for (i = 0; i < staged_config.num_controllers; i++) {
        /* @FIXME local? listen? priority? */
        staged_config.controllers[i].config.periodic_echo_ms = staged_config.keepalive_period_ms;
        staged_config.controllers[i].config.reset_echo_count = 3; /* @FIXME */
    }

    return INDIGO_ERROR_NONE;
}
indigo_error_t
ind_cxn_cfg_stage(cJSON *config)
{
    indigo_error_t err;

    err = ind_cfg_parse_loglevel(config, "logging.connection",
                                 OFCONNECTIONMANAGER_CONFIG_LOG_BITS_DEFAULT,
                                 &staged_config.log_flags);
    if (err != INDIGO_ERROR_NONE) {
        return err;
    }

    /* Not supporting setting log options yet */

    err = ind_cfg_lookup_int(config, "keepalive_period_ms", &staged_config.keepalive_period_ms);
    if (err == INDIGO_ERROR_NONE) {
        if (staged_config.keepalive_period_ms <= 0) {
            AIM_LOG_ERROR("'keepalive_period_ms' must be greater than 0");
            return INDIGO_ERROR_PARAM;
        }
    } else if (err == INDIGO_ERROR_PARAM) {
        AIM_LOG_ERROR("Config: Could not parse 'keepalive_period_ms'");
        return err;
    } else if (err == INDIGO_ERROR_NOT_FOUND) {
        AIM_LOG_ERROR("Config: Missing required key 'keepalive_period_ms'");
        return err;
    }

    err = parse_controllers(config);
    if (err != INDIGO_ERROR_NONE) {
        return err;
    }

    /* verify TLS parameters */
    /*
     * for now, we should be able to accept:
     * - TLS config with switch cert and key specified,
     *   cipher_list and CA cert optional
     * - if cipher_list is specified, it should be valid
     * - if CA cert is specified, it should be consistent with switch cert
     * - TLS config with parameters specified, but
     *   CA cert, switch cert and switch priv key files not present
     */
    {
        cJSON *node = cJSON_GetObjectItem(config, "tls");
        if (node) {
            if (node->type != cJSON_Object) {
                AIM_LOG_ERROR("Config: expected tls to be an object");
                return INDIGO_ERROR_PARAM;
            }
            err = parse_tls_config(node);
            if (err != INDIGO_ERROR_NONE) {
                AIM_LOG_ERROR("Config: error parsing TLS config: %s",
                              indigo_strerror(err));
                return err;
            }
        } else {
            AIM_LOG_VERBOSE("Config: TLS config not found, continuing");
        }
    }

    staged_config.valid = true;

    return INDIGO_ERROR_NONE;
}