fn parse()

in resctl-bench/src/bench/iocost_qos.rs [103:254]


    fn parse(spec: &JobSpec, prev_data: Option<&JobData>) -> Result<Self> {
        let mut stor_spec = JobSpec::new(
            "storage",
            None,
            Some(format!("none,{}", spec.passive.as_deref().unwrap_or("")).trim_end_matches(',')),
            JobSpec::props(&[&[("apply", "")]]),
        );
        let mut prot_spec = JobSpec::new(
            "protection",
            None,
            spec.passive.as_deref(),
            JobSpec::props(&[
                &[],
                &[
                    ("scenario", "mem-hog-tune"),
                    ("load", "1.0"),
                    ("size-min", "1"),
                    ("size-max", "1"),
                ],
            ]),
        );

        let mut vrate_min = 0.0;
        let mut vrate_max = DFL_VRATE_MAX;
        let mut vrate_intvs = 0;
        let mut stor_base_loops = DFL_STOR_BASE_LOOPS;
        let mut stor_loops = DFL_STOR_LOOPS;
        let mut isol_pct = DFL_ISOL_PCT.to_owned();
        let mut isol_thr = DFL_ISOL_THR;
        let mut retries = DFL_RETRIES;
        let mut allow_fail = false;
        let mut runs = vec![IoCostQoSOvr {
            off: true,
            ..Default::default()
        }];
        let mut dither = false;
        let mut dither_dist = None;
        let mut ign_min_perf = false;

        for (k, v) in spec.props[0].iter() {
            match k.as_str() {
                "vrate-min" => vrate_min = v.parse::<f64>()?,
                "vrate-max" => vrate_max = v.parse::<f64>()?,
                "vrate-intvs" => vrate_intvs = v.parse::<u32>()?,
                "dither" => {
                    dither = true;
                    if v.len() > 0 {
                        dither_dist = Some(v.parse::<f64>()?);
                    }
                }
                "storage-base-loops" => stor_base_loops = v.parse::<u32>()?,
                "storage-loops" => stor_loops = v.parse::<u32>()?,
                "isol-pct" => isol_pct = v.to_owned(),
                "isol-thr" => isol_thr = parse_frac(v)?,
                "retries" => retries = v.parse::<u32>()?,
                "allow-fail" => allow_fail = v.parse::<bool>()?,
                "ignore-min-perf" => ign_min_perf = v.len() == 0 || v.parse::<bool>()?,
                k if k.starts_with("storage-") => {
                    stor_spec.props[0].insert(k[8..].into(), v.into());
                }
                k => bail!("unknown property key {:?}", k),
            }
        }

        if vrate_min < 0.0 || vrate_max < 0.0 || vrate_min >= vrate_max {
            bail!("invalid vrate range [{}, {}]", vrate_min, vrate_max);
        }

        for props in spec.props[1..].iter() {
            let mut ovr = IoCostQoSOvr::default();
            for (k, v) in props.iter() {
                if !ovr.parse(k, v)? {
                    bail!("unknown property key {:?}", k);
                }
            }
            runs.push(ovr);
        }

        prot_spec.props[1].insert("isol-pct".to_owned(), isol_pct.clone());
        prot_spec.props[1].insert("isol-thr".to_owned(), format!("{}", isol_thr));

        let stor_job = StorageJob::parse(&stor_spec)?;
        let prot_job = ProtectionJob::parse(&prot_spec)?;

        if runs.len() == 1 && vrate_intvs == 0 {
            vrate_intvs = DFL_VRATE_INTVS;
        }

        if vrate_intvs > 0 {
            // min of 0 is special case and means that we start at one
            // click, so if min is 0, max is 10 and intvs is 5, the sequence
            // is (10, 7.5, 5, 2.5). If min > 0, the range is inclusive -
            // min 5, max 10, intvs 5 => (10, 9, 8, 7, 6, 5).
            let click;
            let mut dither_shift = 0.0;
            if vrate_min == 0.0 {
                click = vrate_max / vrate_intvs as f64;
                vrate_min = click;
                dither_shift = -click / 2.0;
            } else {
                click = (vrate_max - vrate_min) / (vrate_intvs - 1) as f64;
            };

            if dither {
                if dither_dist.is_none() {
                    if let Some(pd) = prev_data.as_ref() {
                        // If prev has dither_dist set, use the prev dither_dist
                        // so that we can use results from it.
                        let prec: IoCostQoSRecord = pd.parse_record()?;
                        if let Some(pdd) = prec.dither_dist.as_ref() {
                            dither_dist = Some(*pdd);
                        }
                    }
                }
                if dither_dist.is_none() {
                    dither_dist = Some(
                        rand::thread_rng().gen_range(-click / 2.0..click / 2.0) + dither_shift,
                    );
                }
                vrate_min += dither_dist.as_ref().unwrap();
                vrate_max += dither_dist.as_ref().unwrap();
            }

            vrate_min = vrate_min.max(VRATE_INTVS_MIN);

            let mut vrate = vrate_max;
            while vrate > vrate_min - 0.001 {
                let mut ovr = IoCostQoSOvr {
                    min: Some(vrate),
                    max: Some(vrate),
                    ..Default::default()
                };
                ovr.sanitize();
                runs.push(ovr);
                vrate -= click;
            }
        }

        Ok(IoCostQoSJob {
            stor_base_loops,
            stor_loops,
            isol_pct,
            isol_thr,
            dither_dist,
            ign_min_perf,
            retries,
            allow_fail,
            stor_job,
            prot_job,
            runs,
        })
    }