diff --git a/Cargo.toml b/Cargo.toml index 8382ec96..1442de81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ members = [ "pbs-runtime", "proxmox-rest-server", "proxmox-rrd", - "proxmox-systemd", "pbs-tape", "pbs-tools", @@ -120,7 +119,6 @@ pbs-datastore = { path = "pbs-datastore" } pbs-runtime = { path = "pbs-runtime" } proxmox-rest-server = { path = "proxmox-rest-server" } proxmox-rrd = { path = "proxmox-rrd" } -proxmox-systemd = { path = "proxmox-systemd" } pbs-tools = { path = "pbs-tools" } pbs-tape = { path = "pbs-tape" } diff --git a/Makefile b/Makefile index d6951c9a..20d1382f 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,6 @@ SUBCRATES := \ pbs-runtime \ proxmox-rest-server \ proxmox-rrd \ - proxmox-systemd \ pbs-tape \ pbs-tools \ proxmox-backup-banner \ diff --git a/pbs-api-types/Cargo.toml b/pbs-api-types/Cargo.toml index 585bb9c6..4b188c07 100644 --- a/pbs-api-types/Cargo.toml +++ b/pbs-api-types/Cargo.toml @@ -18,7 +18,5 @@ serde = { version = "1.0", features = ["derive"] } proxmox = "0.15.0" proxmox-lang = "1.0.0" proxmox-schema = { version = "1.0.1", features = [ "api-macro" ] } -proxmox-time = "1.0.0" +proxmox-time = "1.1" proxmox-uuid = { version = "1.0.0", features = [ "serde" ] } - -proxmox-systemd = { path = "../proxmox-systemd" } diff --git a/pbs-api-types/src/jobs.rs b/pbs-api-types/src/jobs.rs index f47a294a..66a8d180 100644 --- a/pbs-api-types/src/jobs.rs +++ b/pbs-api-types/src/jobs.rs @@ -23,25 +23,25 @@ pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.") pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new( "Run sync job at specified schedule.") - .format(&ApiStringFormat::VerifyFn(proxmox_systemd::time::verify_calendar_event)) + .format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) .type_text("") .schema(); pub const GC_SCHEDULE_SCHEMA: Schema = StringSchema::new( "Run garbage collection job at specified schedule.") - .format(&ApiStringFormat::VerifyFn(proxmox_systemd::time::verify_calendar_event)) + .format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) .type_text("") .schema(); pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new( "Run prune job at specified schedule.") - .format(&ApiStringFormat::VerifyFn(proxmox_systemd::time::verify_calendar_event)) + .format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) .type_text("") .schema(); pub const VERIFICATION_SCHEDULE_SCHEMA: Schema = StringSchema::new( "Run verify job at specified schedule.") - .format(&ApiStringFormat::VerifyFn(proxmox_systemd::time::verify_calendar_event)) + .format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) .type_text("") .schema(); diff --git a/pbs-api-types/src/lib.rs b/pbs-api-types/src/lib.rs index eebf5794..01c14cc4 100644 --- a/pbs-api-types/src/lib.rs +++ b/pbs-api-types/src/lib.rs @@ -7,7 +7,7 @@ use proxmox_schema::{ api, const_regex, ApiStringFormat, ApiType, ArraySchema, Schema, StringSchema, ReturnType, }; use proxmox::{IPRE, IPRE_BRACKET, IPV4OCTET, IPV4RE, IPV6H16, IPV6LS32, IPV6RE}; -use proxmox_systemd::daily_duration::parse_daily_duration; +use proxmox_time::parse_daily_duration; #[rustfmt::skip] #[macro_export] diff --git a/pbs-api-types/src/tape/media_pool.rs b/pbs-api-types/src/tape/media_pool.rs index 148ae051..3b1cb0f5 100644 --- a/pbs-api-types/src/tape/media_pool.rs +++ b/pbs-api-types/src/tape/media_pool.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use proxmox_schema::{api, Schema, StringSchema, ApiStringFormat, Updater}; -use proxmox_systemd::time::{parse_calendar_event, parse_time_span, CalendarEvent, TimeSpan}; +use proxmox_time::{parse_calendar_event, parse_time_span, CalendarEvent, TimeSpan}; use crate::{ PROXMOX_SAFE_ID_FORMAT, diff --git a/proxmox-systemd/Cargo.toml b/proxmox-systemd/Cargo.toml deleted file mode 100644 index a1e402f6..00000000 --- a/proxmox-systemd/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "proxmox-systemd" -version = "0.1.0" -authors = ["Proxmox Support Team "] -edition = "2018" -description = "common systemd-related helpers, but no unit parsing" - -[dependencies] -anyhow = "1.0" -bitflags = "1.2.1" -lazy_static = "1.4" -nom = "5.1" - -proxmox-time = "1" -proxmox-lang = "1" - -[dev-dependencies] -proxmox = "0.15.0" diff --git a/proxmox-systemd/src/daily_duration.rs b/proxmox-systemd/src/daily_duration.rs deleted file mode 100644 index 4f5c6932..00000000 --- a/proxmox-systemd/src/daily_duration.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::cmp::{Ordering, PartialOrd}; -use std::convert::{TryFrom, TryInto}; - -use anyhow::Error; - -use proxmox_time::TmEditor; - -use super::time::{WeekDays}; - -pub use super::parse_time::parse_daily_duration; - -/// Time of Day (hour with minute) -#[derive(Default, PartialEq, Clone, Debug)] -pub struct HmTime { - pub hour: u32, - pub minute: u32, -} - -impl PartialOrd for HmTime { - fn partial_cmp(&self, other: &Self) -> Option { - let mut order = self.hour.cmp(&other.hour); - if order == Ordering::Equal { - order = self.minute.cmp(&other.minute); - } - Some(order) - } -} - -#[derive(Default, Clone, Debug)] -pub struct DailyDuration { - /// the days in a week this duration should trigger - pub days: WeekDays, - pub start: HmTime, - pub end: HmTime, -} - -impl DailyDuration { - - /// Test it time is within this frame - pub fn time_match(&self, epoch: i64, utc: bool) -> Result { - - let t = TmEditor::with_epoch(epoch, utc)?; - - Ok(self.time_match_with_tm_editor(&t)) - } - - /// Like time_match, but use [TmEditor] to specify the time - /// - /// Note: This function returns bool (not Result). It - /// simply returns ''false' if passed time 't' contains invalid values. - pub fn time_match_with_tm_editor(&self, t: &TmEditor) -> bool { - let all_days = self.days.is_empty() || self.days.is_all(); - - if !all_days { // match day first - match u32::try_from(t.day_num()) { - Ok(day_num) => { - match WeekDays::from_bits(1< { - if !self.days.contains(day) { - return false; - } - } - None => return false, - } - } - Err(_) => return false, - } - } - - let hour = t.hour().try_into(); - let minute = t.min().try_into(); - - match (hour, minute) { - (Ok(hour), Ok(minute)) => { - let ctime = HmTime { hour, minute }; - ctime >= self.start && ctime < self.end - } - _ => false, - } - } -} - -#[cfg(test)] -mod test { - - use anyhow::{bail, Error}; - - use super::*; - - fn test_parse( - duration_str: &str, - start_h: u32, start_m: u32, - end_h: u32, end_m: u32, - days: &[usize], - ) -> Result<(), Error> { - let mut day_bits = 0; - for day in days { day_bits |= 1< i64 { - (mday*3600*24 + hour*3600 + min*60) as i64 - } - - #[test] - fn test_daily_duration_parser() -> Result<(), Error> { - - assert!(parse_daily_duration("").is_err()); - assert!(parse_daily_duration(" 8-12").is_err()); - assert!(parse_daily_duration("8:60-12").is_err()); - assert!(parse_daily_duration("8-25").is_err()); - assert!(parse_daily_duration("12-8").is_err()); - - test_parse("8-12", 8, 0, 12, 0, &[])?; - test_parse("8:0-12:0", 8, 0, 12, 0, &[])?; - test_parse("8:00-12:00", 8, 0, 12, 0, &[])?; - test_parse("8:05-12:20", 8, 5, 12, 20, &[])?; - test_parse("8:05 - 12:20", 8, 5, 12, 20, &[])?; - - test_parse("mon 8-12", 8, 0, 12, 0, &[0])?; - test_parse("tue..fri 8-12", 8, 0, 12, 0, &[1,2,3,4])?; - test_parse("sat,tue..thu,fri 8-12", 8, 0, 12, 0, &[1,2,3,4,5])?; - - Ok(()) - } - - #[test] - fn test_time_match() -> Result<(), Error> { - const THURSDAY_80_00: i64 = make_test_time(0, 8, 0); - const THURSDAY_12_00: i64 = make_test_time(0, 12, 0); - const DAY: i64 = 3600*24; - - let duration = parse_daily_duration("thu..fri 8:05-12")?; - - assert!(!duration.time_match(THURSDAY_80_00, true)?); - assert!(!duration.time_match(THURSDAY_80_00 + DAY, true)?); - assert!(!duration.time_match(THURSDAY_80_00 + 2*DAY, true)?); - - assert!(duration.time_match(THURSDAY_80_00 + 5*60, true)?); - assert!(duration.time_match(THURSDAY_80_00 + 5*60 + DAY, true)?); - assert!(!duration.time_match(THURSDAY_80_00 + 5*60 + 2*DAY, true)?); - - assert!(duration.time_match(THURSDAY_12_00 - 1, true)?); - assert!(duration.time_match(THURSDAY_12_00 - 1 + DAY, true)?); - assert!(!duration.time_match(THURSDAY_12_00 - 1 + 2*DAY, true)?); - - assert!(!duration.time_match(THURSDAY_12_00, true)?); - assert!(!duration.time_match(THURSDAY_12_00 + DAY, true)?); - assert!(!duration.time_match(THURSDAY_12_00 + 2*DAY, true)?); - - Ok(()) - } -} diff --git a/proxmox-systemd/src/lib.rs b/proxmox-systemd/src/lib.rs deleted file mode 100644 index 7c2b1f90..00000000 --- a/proxmox-systemd/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod time; -pub mod daily_duration; - -mod parse_time; -mod unit; -pub use unit::*; diff --git a/proxmox-systemd/src/parse_time.rs b/proxmox-systemd/src/parse_time.rs deleted file mode 100644 index d212e264..00000000 --- a/proxmox-systemd/src/parse_time.rs +++ /dev/null @@ -1,510 +0,0 @@ -use std::collections::HashMap; - -use anyhow::{bail, Error}; -use lazy_static::lazy_static; - -use super::time::*; -use super::daily_duration::*; - -use nom::{ - error::{context, ParseError, VerboseError}, - bytes::complete::{tag, take_while1}, - combinator::{map_res, all_consuming, opt, recognize}, - sequence::{pair, preceded, tuple}, - character::complete::{alpha1, space0, digit1}, - multi::separated_nonempty_list, -}; - -type IResult> = Result<(I, O), nom::Err>; - -fn parse_error<'a>(i: &'a str, context: &'static str) -> nom::Err> { - let err = VerboseError { errors: Vec::new() }; - let err = VerboseError::add_context(i, context, err); - nom::Err::Error(err) -} - -// Parse a 64 bit unsigned integer -fn parse_u64(i: &str) -> IResult<&str, u64> { - map_res(recognize(digit1), str::parse)(i) -} - -// Parse complete input, generate simple error message (use this for sinple line input). -fn parse_complete_line<'a, F, O>(what: &str, i: &'a str, parser: F) -> Result - where F: Fn(&'a str) -> IResult<&'a str, O>, -{ - match all_consuming(parser)(i) { - Err(nom::Err::Error(VerboseError { errors })) | - Err(nom::Err::Failure(VerboseError { errors })) => { - if errors.is_empty() { - bail!("unable to parse {}", what); - } else { - bail!("unable to parse {} at '{}' - {:?}", what, errors[0].0, errors[0].1); - } - } - Err(err) => { - bail!("unable to parse {} - {}", what, err); - } - Ok((_, data)) => Ok(data), - } -} - -lazy_static! { - pub static ref TIME_SPAN_UNITS: HashMap<&'static str, f64> = { - let mut map = HashMap::new(); - - let second = 1.0; - - map.insert("seconds", second); - map.insert("second", second); - map.insert("sec", second); - map.insert("s", second); - - let msec = second / 1000.0; - - map.insert("msec", msec); - map.insert("ms", msec); - - let usec = msec / 1000.0; - - map.insert("usec", usec); - map.insert("us", usec); - map.insert("µs", usec); - - let nsec = usec / 1000.0; - - map.insert("nsec", nsec); - map.insert("ns", nsec); - - let minute = second * 60.0; - - map.insert("minutes", minute); - map.insert("minute", minute); - map.insert("min", minute); - map.insert("m", minute); - - let hour = minute * 60.0; - - map.insert("hours", hour); - map.insert("hour", hour); - map.insert("hr", hour); - map.insert("h", hour); - - let day = hour * 24.0 ; - - map.insert("days", day); - map.insert("day", day); - map.insert("d", day); - - let week = day * 7.0; - - map.insert("weeks", week); - map.insert("week", week); - map.insert("w", week); - - let month = 30.44 * day; - - map.insert("months", month); - map.insert("month", month); - map.insert("M", month); - - let year = 365.25 * day; - - map.insert("years", year); - map.insert("year", year); - map.insert("y", year); - - map - }; -} - -struct TimeSpec { - hour: Vec, - minute: Vec, - second: Vec, -} - -struct DateSpec { - year: Vec, - month: Vec, - day: Vec, -} - -fn parse_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, u32> { - move |i: &str| { - let (i, v) = map_res(recognize(digit1), str::parse)(i)?; - if (v as usize) >= max { - return Err(parse_error(i, "time value too large")); - } - Ok((i, v)) - } -} - -fn parse_weekday(i: &str) -> IResult<&str, WeekDays> { - let (i, text) = alpha1(i)?; - - match text.to_ascii_lowercase().as_str() { - "monday" | "mon" => Ok((i, WeekDays::MONDAY)), - "tuesday" | "tue" => Ok((i, WeekDays::TUESDAY)), - "wednesday" | "wed" => Ok((i, WeekDays::WEDNESDAY)), - "thursday" | "thu" => Ok((i, WeekDays::THURSDAY)), - "friday" | "fri" => Ok((i, WeekDays::FRIDAY)), - "saturday" | "sat" => Ok((i, WeekDays::SATURDAY)), - "sunday" | "sun" => Ok((i, WeekDays::SUNDAY)), - _ => return Err(parse_error(text, "weekday")), - } -} - -fn parse_weekdays_range(i: &str) -> IResult<&str, WeekDays> { - let (i, startday) = parse_weekday(i)?; - - let generate_range = |start, end| { - let mut res = 0; - let mut pos = start; - loop { - res |= pos; - if pos >= end { break; } - pos <<= 1; - } - WeekDays::from_bits(res).unwrap() - }; - - if let (i, Some((_, endday))) = opt(pair(tag(".."),parse_weekday))(i)? { - let start = startday.bits(); - let end = endday.bits(); - if start > end { - let set1 = generate_range(start, WeekDays::SUNDAY.bits()); - let set2 = generate_range(WeekDays::MONDAY.bits(), end); - Ok((i, set1 | set2)) - } else { - Ok((i, generate_range(start, end))) - } - } else { - Ok((i, startday)) - } -} - -fn parse_date_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, DateTimeValue> { - move |i: &str| { - let (i, value) = parse_time_comp(max)(i)?; - - if let (i, Some(end)) = opt(preceded(tag(".."), parse_time_comp(max)))(i)? { - if value > end { - return Err(parse_error(i, "range start is bigger than end")); - } - return Ok((i, DateTimeValue::Range(value, end))) - } - - if let Some(time) = i.strip_prefix('/') { - let (time, repeat) = parse_time_comp(max)(time)?; - Ok((time, DateTimeValue::Repeated(value, repeat))) - } else { - Ok((i, DateTimeValue::Single(value))) - } - } -} - -fn parse_date_time_comp_list(start: u32, max: usize) -> impl Fn(&str) -> IResult<&str, Vec> { - move |i: &str| { - if let Some(rest) = i.strip_prefix('*') { - if let Some(time) = rest.strip_prefix('/') { - let (n, repeat) = parse_time_comp(max)(time)?; - if repeat > 0 { - return Ok((n, vec![DateTimeValue::Repeated(start, repeat)])); - } - } - return Ok((rest, Vec::new())); - } - - separated_nonempty_list(tag(","), parse_date_time_comp(max))(i) - } -} - -fn parse_time_spec(i: &str) -> IResult<&str, TimeSpec> { - - let (i, (hour, minute, opt_second)) = tuple(( - parse_date_time_comp_list(0, 24), - preceded(tag(":"), parse_date_time_comp_list(0, 60)), - opt(preceded(tag(":"), parse_date_time_comp_list(0, 60))), - ))(i)?; - - if let Some(second) = opt_second { - Ok((i, TimeSpec { hour, minute, second })) - } else { - Ok((i, TimeSpec { hour, minute, second: vec![DateTimeValue::Single(0)] })) - } -} - -fn parse_date_spec(i: &str) -> IResult<&str, DateSpec> { - - // TODO: implement ~ for days (man systemd.time) - if let Ok((i, (year, month, day))) = tuple(( - parse_date_time_comp_list(0, 2200), // the upper limit for systemd, stay compatible - preceded(tag("-"), parse_date_time_comp_list(1, 13)), - preceded(tag("-"), parse_date_time_comp_list(1, 32)), - ))(i) { - Ok((i, DateSpec { year, month, day })) - } else if let Ok((i, (month, day))) = tuple(( - parse_date_time_comp_list(1, 13), - preceded(tag("-"), parse_date_time_comp_list(1, 32)), - ))(i) { - Ok((i, DateSpec { year: Vec::new(), month, day })) - } else { - Err(parse_error(i, "invalid date spec")) - } -} - -pub fn parse_calendar_event(i: &str) -> Result { - parse_complete_line("calendar event", i, parse_calendar_event_incomplete) -} - -fn parse_calendar_event_incomplete(mut i: &str) -> IResult<&str, CalendarEvent> { - - let mut has_dayspec = false; - let mut has_timespec = false; - let mut has_datespec = false; - - let mut event = CalendarEvent::default(); - - if i.starts_with(|c: char| char::is_ascii_alphabetic(&c)) { - - match i { - "minutely" => { - return Ok(("", CalendarEvent { - second: vec![DateTimeValue::Single(0)], - ..Default::default() - })); - } - "hourly" => { - return Ok(("", CalendarEvent { - minute: vec![DateTimeValue::Single(0)], - second: vec![DateTimeValue::Single(0)], - ..Default::default() - })); - } - "daily" => { - return Ok(("", CalendarEvent { - hour: vec![DateTimeValue::Single(0)], - minute: vec![DateTimeValue::Single(0)], - second: vec![DateTimeValue::Single(0)], - ..Default::default() - })); - } - "weekly" => { - return Ok(("", CalendarEvent { - hour: vec![DateTimeValue::Single(0)], - minute: vec![DateTimeValue::Single(0)], - second: vec![DateTimeValue::Single(0)], - days: WeekDays::MONDAY, - ..Default::default() - })); - } - "monthly" => { - return Ok(("", CalendarEvent { - hour: vec![DateTimeValue::Single(0)], - minute: vec![DateTimeValue::Single(0)], - second: vec![DateTimeValue::Single(0)], - day: vec![DateTimeValue::Single(1)], - ..Default::default() - })); - } - "yearly" | "annually" => { - return Ok(("", CalendarEvent { - hour: vec![DateTimeValue::Single(0)], - minute: vec![DateTimeValue::Single(0)], - second: vec![DateTimeValue::Single(0)], - day: vec![DateTimeValue::Single(1)], - month: vec![DateTimeValue::Single(1)], - ..Default::default() - })); - } - "quarterly" => { - return Ok(("", CalendarEvent { - hour: vec![DateTimeValue::Single(0)], - minute: vec![DateTimeValue::Single(0)], - second: vec![DateTimeValue::Single(0)], - day: vec![DateTimeValue::Single(1)], - month: vec![ - DateTimeValue::Single(1), - DateTimeValue::Single(4), - DateTimeValue::Single(7), - DateTimeValue::Single(10), - ], - ..Default::default() - })); - } - "semiannually" | "semi-annually" => { - return Ok(("", CalendarEvent { - hour: vec![DateTimeValue::Single(0)], - minute: vec![DateTimeValue::Single(0)], - second: vec![DateTimeValue::Single(0)], - day: vec![DateTimeValue::Single(1)], - month: vec![ - DateTimeValue::Single(1), - DateTimeValue::Single(7), - ], - ..Default::default() - })); - } - _ => { /* continue */ } - } - - let (n, range_list) = context( - "weekday range list", - separated_nonempty_list(tag(","), parse_weekdays_range) - )(i)?; - - has_dayspec = true; - - i = space0(n)?.0; - - for range in range_list { event.days.insert(range); } - } - - if let (n, Some(date)) = opt(parse_date_spec)(i)? { - event.year = date.year; - event.month = date.month; - event.day = date.day; - has_datespec = true; - i = space0(n)?.0; - } - - if let (n, Some(time)) = opt(parse_time_spec)(i)? { - event.hour = time.hour; - event.minute = time.minute; - event.second = time.second; - has_timespec = true; - i = n; - } else { - event.hour = vec![DateTimeValue::Single(0)]; - event.minute = vec![DateTimeValue::Single(0)]; - event.second = vec![DateTimeValue::Single(0)]; - } - - if !(has_dayspec || has_timespec || has_datespec) { - return Err(parse_error(i, "date or time specification")); - } - - Ok((i, event)) -} - -fn parse_time_unit(i: &str) -> IResult<&str, &str> { - let (n, text) = take_while1(|c: char| char::is_ascii_alphabetic(&c) || c == 'µ')(i)?; - if TIME_SPAN_UNITS.contains_key(&text) { - Ok((n, text)) - } else { - Err(parse_error(text, "time unit")) - } -} - - -pub fn parse_time_span(i: &str) -> Result { - parse_complete_line("time span", i, parse_time_span_incomplete) -} - -fn parse_time_span_incomplete(mut i: &str) -> IResult<&str, TimeSpan> { - - let mut ts = TimeSpan::default(); - - loop { - i = space0(i)?.0; - if i.is_empty() { break; } - let (n, num) = parse_u64(i)?; - i = space0(n)?.0; - - if let (n, Some(unit)) = opt(parse_time_unit)(i)? { - i = n; - match unit { - "seconds" | "second" | "sec" | "s" => { - ts.seconds += num; - } - "msec" | "ms" => { - ts.msec += num; - } - "usec" | "us" | "µs" => { - ts.usec += num; - } - "nsec" | "ns" => { - ts.nsec += num; - } - "minutes" | "minute" | "min" | "m" => { - ts.minutes += num; - } - "hours" | "hour" | "hr" | "h" => { - ts.hours += num; - } - "days" | "day" | "d" => { - ts.days += num; - } - "weeks" | "week" | "w" => { - ts.weeks += num; - } - "months" | "month" | "M" => { - ts.months += num; - } - "years" | "year" | "y" => { - ts.years += num; - } - _ => return Err(parse_error(unit, "internal error")), - } - } else { - ts.seconds += num; - } - } - - Ok((i, ts)) -} - -pub fn parse_daily_duration(i: &str) -> Result { - parse_complete_line("daily duration", i, parse_daily_duration_incomplete) -} - -fn parse_daily_duration_incomplete(mut i: &str) -> IResult<&str, DailyDuration> { - - let mut duration = DailyDuration::default(); - - if i.starts_with(|c: char| char::is_ascii_alphabetic(&c)) { - - let (n, range_list) = context( - "weekday range list", - separated_nonempty_list(tag(","), parse_weekdays_range) - )(i)?; - - i = space0(n)?.0; - - for range in range_list { duration.days.insert(range); } - } - - let (i, start) = parse_hm_time(i)?; - - let i = space0(i)?.0; - - let (i, _) = tag("-")(i)?; - - let i = space0(i)?.0; - - let end_time_start = i; - - let (i, end) = parse_hm_time(i)?; - - if start > end { - return Err(parse_error(end_time_start, "end time before start time")); - } - - duration.start = start; - duration.end = end; - - Ok((i, duration)) -} - -fn parse_hm_time(i: &str) -> IResult<&str, HmTime> { - - let (i, (hour, opt_minute)) = tuple(( - parse_time_comp(24), - opt(preceded(tag(":"), parse_time_comp(60))), - ))(i)?; - - match opt_minute { - Some(minute) => Ok((i, HmTime { hour, minute })), - None => Ok((i, HmTime { hour, minute: 0})), - } -} diff --git a/proxmox-systemd/src/time.rs b/proxmox-systemd/src/time.rs deleted file mode 100644 index e5fe7965..00000000 --- a/proxmox-systemd/src/time.rs +++ /dev/null @@ -1,586 +0,0 @@ -use std::convert::TryInto; - -use anyhow::Error; -use bitflags::bitflags; - -use proxmox_time::TmEditor; - -pub use super::parse_time::{parse_calendar_event, parse_time_span}; - -bitflags!{ - #[derive(Default)] - pub struct WeekDays: u8 { - const MONDAY = 1; - const TUESDAY = 2; - const WEDNESDAY = 4; - const THURSDAY = 8; - const FRIDAY = 16; - const SATURDAY = 32; - const SUNDAY = 64; - } -} - -#[derive(Debug, Clone)] -pub enum DateTimeValue { - Single(u32), - Range(u32, u32), - Repeated(u32, u32), -} - -impl DateTimeValue { - // Test if the entry contains the value - pub fn contains(&self, value: u32) -> bool { - match self { - DateTimeValue::Single(v) => *v == value, - DateTimeValue::Range(start, end) => value >= *start && value <= *end, - DateTimeValue::Repeated(start, repetition) => { - if value >= *start { - if *repetition > 0 { - let offset = value - start; - offset % repetition == 0 - } else { - *start == value - } - } else { - false - } - } - } - } - - pub fn list_contains(list: &[DateTimeValue], value: u32) -> bool { - list.iter().any(|spec| spec.contains(value)) - } - - // Find an return an entry greater than value - pub fn find_next(list: &[DateTimeValue], value: u32) -> Option { - let mut next: Option = None; - let mut set_next = |v: u32| { - if let Some(n) = next { - if v < n { next = Some(v); } - } else { - next = Some(v); - } - }; - for spec in list { - match spec { - DateTimeValue::Single(v) => { - if *v > value { set_next(*v); } - } - DateTimeValue::Range(start, end) => { - if value < *start { - set_next(*start); - } else { - let n = value + 1; - if n >= *start && n <= *end { - set_next(n); - } - } - } - DateTimeValue::Repeated(start, repetition) => { - if value < *start { - set_next(*start); - } else if *repetition > 0 { - set_next(start + ((value - start + repetition) / repetition) * repetition); - } - } - } - } - - next - } -} - -/// Calendar events may be used to refer to one or more points in time in a -/// single expression. They are designed after the systemd.time Calendar Events -/// specification, but are not guaranteed to be 100% compatible. -#[derive(Default, Clone, Debug)] -pub struct CalendarEvent { - /// the days in a week this event should trigger - pub days: WeekDays, - /// the second(s) this event should trigger - pub second: Vec, // todo: support float values - /// the minute(s) this event should trigger - pub minute: Vec, - /// the hour(s) this event should trigger - pub hour: Vec, - /// the day(s) in a month this event should trigger - pub day: Vec, - /// the month(s) in a year this event should trigger - pub month: Vec, - /// the years(s) this event should trigger - pub year: Vec, -} - -#[derive(Default, Clone, Debug)] -pub struct TimeSpan { - pub nsec: u64, - pub usec: u64, - pub msec: u64, - pub seconds: u64, - pub minutes: u64, - pub hours: u64, - pub days: u64, - pub weeks: u64, - pub months: u64, - pub years: u64, -} - -impl From for f64 { - fn from(ts: TimeSpan) -> Self { - (ts.seconds as f64) + - ((ts.nsec as f64) / 1_000_000_000.0) + - ((ts.usec as f64) / 1_000_000.0) + - ((ts.msec as f64) / 1_000.0) + - ((ts.minutes as f64) * 60.0) + - ((ts.hours as f64) * 3600.0) + - ((ts.days as f64) * 3600.0 * 24.0) + - ((ts.weeks as f64) * 3600.0 * 24.0 * 7.0) + - ((ts.months as f64) * 3600.0 * 24.0 * 30.44) + - ((ts.years as f64) * 3600.0 * 24.0 * 365.25) - } -} - -impl From for TimeSpan { - fn from(duration: std::time::Duration) -> Self { - let mut duration = duration.as_nanos(); - let nsec = (duration % 1000) as u64; - duration /= 1000; - let usec = (duration % 1000) as u64; - duration /= 1000; - let msec = (duration % 1000) as u64; - duration /= 1000; - let seconds = (duration % 60) as u64; - duration /= 60; - let minutes = (duration % 60) as u64; - duration /= 60; - let hours = (duration % 24) as u64; - duration /= 24; - let years = (duration as f64 / 365.25) as u64; - let ydays = (duration as f64 % 365.25) as u64; - let months = (ydays as f64 / 30.44) as u64; - let mdays = (ydays as f64 % 30.44) as u64; - let weeks = mdays / 7; - let days = mdays % 7; - Self { - nsec, - usec, - msec, - seconds, - minutes, - hours, - days, - weeks, - months, - years, - } - } -} - -impl std::fmt::Display for TimeSpan { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - let mut first = true; - { // block scope for mutable borrows - let mut do_write = |v: u64, unit: &str| -> Result<(), std::fmt::Error> { - if !first { - write!(f, " ")?; - } - first = false; - write!(f, "{}{}", v, unit) - }; - if self.years > 0 { - do_write(self.years, "y")?; - } - if self.months > 0 { - do_write(self.months, "m")?; - } - if self.weeks > 0 { - do_write(self.weeks, "w")?; - } - if self.days > 0 { - do_write(self.days, "d")?; - } - if self.hours > 0 { - do_write(self.hours, "h")?; - } - if self.minutes > 0 { - do_write(self.minutes, "min")?; - } - } - if !first { - write!(f, " ")?; - } - let seconds = self.seconds as f64 + (self.msec as f64 / 1000.0); - if seconds >= 0.1 { - if seconds >= 1.0 || !first { - write!(f, "{:.0}s", seconds)?; - } else { - write!(f, "{:.1}s", seconds)?; - } - } else if first { - write!(f, "<0.1s")?; - } - Ok(()) - } -} - -pub fn verify_time_span(i: &str) -> Result<(), Error> { - parse_time_span(i)?; - Ok(()) -} - -pub fn verify_calendar_event(i: &str) -> Result<(), Error> { - parse_calendar_event(i)?; - Ok(()) -} - -pub fn compute_next_event( - event: &CalendarEvent, - last: i64, - utc: bool, -) -> Result, Error> { - - let last = last + 1; // at least one second later - - let all_days = event.days.is_empty() || event.days.is_all(); - - let mut t = TmEditor::with_epoch(last, utc)?; - - let mut count = 0; - - loop { - // cancel after 1000 loops - if count > 1000 { - return Ok(None); - } else { - count += 1; - } - - if !event.year.is_empty() { - let year: u32 = t.year().try_into()?; - if !DateTimeValue::list_contains(&event.year, year) { - if let Some(n) = DateTimeValue::find_next(&event.year, year) { - t.add_years((n - year).try_into()?)?; - continue; - } else { - // if we have no valid year, we cannot find a correct timestamp - return Ok(None); - } - } - } - - if !event.month.is_empty() { - let month: u32 = t.month().try_into()?; - if !DateTimeValue::list_contains(&event.month, month) { - if let Some(n) = DateTimeValue::find_next(&event.month, month) { - t.add_months((n - month).try_into()?)?; - } else { - // if we could not find valid month, retry next year - t.add_years(1)?; - } - continue; - } - } - - if !event.day.is_empty() { - let day: u32 = t.day().try_into()?; - if !DateTimeValue::list_contains(&event.day, day) { - if let Some(n) = DateTimeValue::find_next(&event.day, day) { - t.add_days((n - day).try_into()?)?; - } else { - // if we could not find valid mday, retry next month - t.add_months(1)?; - } - continue; - } - } - - if !all_days { // match day first - let day_num: u32 = t.day_num().try_into()?; - let day = WeekDays::from_bits(1< Result<(), Error> { - match parse_calendar_event(v) { - Ok(event) => println!("CalendarEvent '{}' => {:?}", v, event), - Err(err) => bail!("parsing '{}' failed - {}", v, err), - } - - Ok(()) - } - - const fn make_test_time(mday: i32, hour: i32, min: i32) -> i64 { - (mday*3600*24 + hour*3600 + min*60) as i64 - } - - #[test] - fn test_compute_next_event() -> Result<(), Error> { - - let test_value = |v: &'static str, last: i64, expect: i64| -> Result { - let event = match parse_calendar_event(v) { - Ok(event) => event, - Err(err) => bail!("parsing '{}' failed - {}", v, err), - }; - - match compute_next_event(&event, last, true) { - Ok(Some(next)) => { - if next == expect { - println!("next {:?} => {}", event, next); - } else { - bail!( - "next {:?} failed\nnext: {:?}\nexpect: {:?}", - event, - proxmox_time::gmtime(next), - proxmox_time::gmtime(expect), - ); - } - } - Ok(None) => bail!("next {:?} failed to find a timestamp", event), - Err(err) => bail!("compute next for '{}' failed - {}", v, err), - } - - Ok(expect) - }; - - let test_never = |v: &'static str, last: i64| -> Result<(), Error> { - let event = match parse_calendar_event(v) { - Ok(event) => event, - Err(err) => bail!("parsing '{}' failed - {}", v, err), - }; - - match compute_next_event(&event, last, true)? { - None => Ok(()), - Some(next) => bail!("compute next for '{}' succeeded, but expected fail - result {}", v, next), - } - }; - - const MIN: i64 = 60; - const HOUR: i64 = 3600; - const DAY: i64 = 3600*24; - - const THURSDAY_00_00: i64 = make_test_time(0, 0, 0); - const THURSDAY_15_00: i64 = make_test_time(0, 15, 0); - - const JUL_31_2020: i64 = 1596153600; // Friday, 2020-07-31 00:00:00 - const DEC_31_2020: i64 = 1609372800; // Thursday, 2020-12-31 00:00:00 - - test_value("*:0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?; - test_value("*:*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?; - test_value("*:*:*", THURSDAY_00_00, THURSDAY_00_00 + 1)?; - test_value("*:3:5", THURSDAY_00_00, THURSDAY_00_00 + 3*MIN + 5)?; - - test_value("mon *:*", THURSDAY_00_00, THURSDAY_00_00 + 4*DAY)?; - test_value("mon 2:*", THURSDAY_00_00, THURSDAY_00_00 + 4*DAY + 2*HOUR)?; - test_value("mon 2:50", THURSDAY_00_00, THURSDAY_00_00 + 4*DAY + 2*HOUR + 50*MIN)?; - - test_value("tue", THURSDAY_00_00, THURSDAY_00_00 + 5*DAY)?; - test_value("wed", THURSDAY_00_00, THURSDAY_00_00 + 6*DAY)?; - test_value("thu", THURSDAY_00_00, THURSDAY_00_00 + 7*DAY)?; - test_value("fri", THURSDAY_00_00, THURSDAY_00_00 + 1*DAY)?; - test_value("sat", THURSDAY_00_00, THURSDAY_00_00 + 2*DAY)?; - test_value("sun", THURSDAY_00_00, THURSDAY_00_00 + 3*DAY)?; - - // test multiple values for a single field - // and test that the order does not matter - test_value("5,10:4,8", THURSDAY_00_00, THURSDAY_00_00 + 5*HOUR + 4*MIN)?; - test_value("10,5:8,4", THURSDAY_00_00, THURSDAY_00_00 + 5*HOUR + 4*MIN)?; - test_value("6,4..10:23,5/5", THURSDAY_00_00, THURSDAY_00_00 + 4*HOUR + 5*MIN)?; - test_value("4..10,6:5/5,23", THURSDAY_00_00, THURSDAY_00_00 + 4*HOUR + 5*MIN)?; - - // test month wrapping - test_value("sat", JUL_31_2020, JUL_31_2020 + 1*DAY)?; - test_value("sun", JUL_31_2020, JUL_31_2020 + 2*DAY)?; - test_value("mon", JUL_31_2020, JUL_31_2020 + 3*DAY)?; - test_value("tue", JUL_31_2020, JUL_31_2020 + 4*DAY)?; - test_value("wed", JUL_31_2020, JUL_31_2020 + 5*DAY)?; - test_value("thu", JUL_31_2020, JUL_31_2020 + 6*DAY)?; - test_value("fri", JUL_31_2020, JUL_31_2020 + 7*DAY)?; - - // test year wrapping - test_value("fri", DEC_31_2020, DEC_31_2020 + 1*DAY)?; - test_value("sat", DEC_31_2020, DEC_31_2020 + 2*DAY)?; - test_value("sun", DEC_31_2020, DEC_31_2020 + 3*DAY)?; - test_value("mon", DEC_31_2020, DEC_31_2020 + 4*DAY)?; - test_value("tue", DEC_31_2020, DEC_31_2020 + 5*DAY)?; - test_value("wed", DEC_31_2020, DEC_31_2020 + 6*DAY)?; - test_value("thu", DEC_31_2020, DEC_31_2020 + 7*DAY)?; - - test_value("daily", THURSDAY_00_00, THURSDAY_00_00 + DAY)?; - test_value("daily", THURSDAY_00_00+1, THURSDAY_00_00 + DAY)?; - - let n = test_value("5/2:0", THURSDAY_00_00, THURSDAY_00_00 + 5*HOUR)?; - let n = test_value("5/2:0", n, THURSDAY_00_00 + 7*HOUR)?; - let n = test_value("5/2:0", n, THURSDAY_00_00 + 9*HOUR)?; - test_value("5/2:0", n, THURSDAY_00_00 + 11*HOUR)?; - - let mut n = test_value("*:*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?; - for i in 2..100 { - n = test_value("*:*", n, THURSDAY_00_00 + i*MIN)?; - } - - let mut n = test_value("*:0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?; - for i in 2..100 { - n = test_value("*:0", n, THURSDAY_00_00 + i*HOUR)?; - } - - let mut n = test_value("1:0", THURSDAY_15_00, THURSDAY_00_00 + DAY + HOUR)?; - for i in 2..100 { - n = test_value("1:0", n, THURSDAY_00_00 + i*DAY + HOUR)?; - } - - // test date functionality - - test_value("2020-07-31", 0, JUL_31_2020)?; - test_value("02-28", 0, (31+27)*DAY)?; - test_value("02-29", 0, 2*365*DAY + (31+28)*DAY)?; // 1972-02-29 - test_value("1965/5-01-01", -1, THURSDAY_00_00)?; - test_value("2020-7..9-2/2", JUL_31_2020, JUL_31_2020 + 2*DAY)?; - test_value("2020,2021-12-31", JUL_31_2020, DEC_31_2020)?; - - test_value("monthly", 0, 31*DAY)?; - test_value("quarterly", 0, (31+28+31)*DAY)?; - test_value("semiannually", 0, (31+28+31+30+31+30)*DAY)?; - test_value("yearly", 0, (365)*DAY)?; - - test_never("2021-02-29", 0)?; - test_never("02-30", 0)?; - - Ok(()) - } - - #[test] - fn test_calendar_event_weekday() -> Result<(), Error> { - test_event("mon,wed..fri")?; - test_event("fri..mon")?; - - test_event("mon")?; - test_event("MON")?; - test_event("monDay")?; - test_event("tue")?; - test_event("Tuesday")?; - test_event("wed")?; - test_event("wednesday")?; - test_event("thu")?; - test_event("thursday")?; - test_event("fri")?; - test_event("friday")?; - test_event("sat")?; - test_event("saturday")?; - test_event("sun")?; - test_event("sunday")?; - - test_event("mon..fri")?; - test_event("mon,tue,fri")?; - test_event("mon,tue..wednesday,fri..sat")?; - - Ok(()) - } - - #[test] - fn test_time_span_parser() -> Result<(), Error> { - - let test_value = |ts_str: &str, expect: f64| -> Result<(), Error> { - let ts = parse_time_span(ts_str)?; - assert_eq!(f64::from(ts), expect, "{}", ts_str); - Ok(()) - }; - - test_value("2", 2.0)?; - test_value("2s", 2.0)?; - test_value("2sec", 2.0)?; - test_value("2second", 2.0)?; - test_value("2seconds", 2.0)?; - - test_value(" 2s 2 s 2", 6.0)?; - - test_value("1msec 1ms", 0.002)?; - test_value("1usec 1us 1µs", 0.000_003)?; - test_value("1nsec 1ns", 0.000_000_002)?; - test_value("1minutes 1minute 1min 1m", 4.0*60.0)?; - test_value("1hours 1hour 1hr 1h", 4.0*3600.0)?; - test_value("1days 1day 1d", 3.0*86400.0)?; - test_value("1weeks 1 week 1w", 3.0*86400.0*7.0)?; - test_value("1months 1month 1M", 3.0*86400.0*30.44)?; - test_value("1years 1year 1y", 3.0*86400.0*365.25)?; - - test_value("2h", 7200.0)?; - test_value(" 2 h", 7200.0)?; - test_value("2hours", 7200.0)?; - test_value("48hr", 48.0*3600.0)?; - test_value("1y 12month", 365.25*24.0*3600.0 + 12.0*30.44*24.0*3600.0)?; - test_value("55s500ms", 55.5)?; - test_value("300ms20s 5day", 5.0*24.0*3600.0 + 20.0 + 0.3)?; - - Ok(()) - } -} diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs index e36d4d82..407c8a3f 100644 --- a/src/api2/node/disks/directory.rs +++ b/src/api2/node/disks/directory.rs @@ -187,9 +187,9 @@ pub fn create_datastore_disk( let mount_unit_name = create_datastore_mount_unit(&name, &mount_point, filesystem, &uuid_path)?; - proxmox_systemd::reload_daemon()?; - proxmox_systemd::enable_unit(&mount_unit_name)?; - proxmox_systemd::start_unit(&mount_unit_name)?; + crate::tools::systemd::reload_daemon()?; + crate::tools::systemd::enable_unit(&mount_unit_name)?; + crate::tools::systemd::start_unit(&mount_unit_name)?; if add_datastore { let lock = pbs_config::datastore::lock_config()?; @@ -245,7 +245,7 @@ pub fn delete_datastore_disk(name: String) -> Result<(), Error> { // disable systemd mount-unit let mut mount_unit_name = proxmox::tools::systemd::escape_unit(&path, true); mount_unit_name.push_str(".mount"); - proxmox_systemd::disable_unit(&mount_unit_name)?; + crate::tools::systemd::disable_unit(&mount_unit_name)?; // delete .mount-file let mount_unit_path = format!("/etc/systemd/system/{}", mount_unit_name); diff --git a/src/api2/node/disks/zfs.rs b/src/api2/node/disks/zfs.rs index 9c3a0958..085bd32b 100644 --- a/src/api2/node/disks/zfs.rs +++ b/src/api2/node/disks/zfs.rs @@ -270,7 +270,7 @@ pub fn create_zpool( if std::path::Path::new("/lib/systemd/system/zfs-import@.service").exists() { let import_unit = format!("zfs-import@{}.service", proxmox::tools::systemd::escape_unit(&name, false)); - proxmox_systemd::enable_unit(&import_unit)?; + crate::tools::systemd::enable_unit(&import_unit)?; } if let Some(compression) = compression { diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs index 4c9f1b80..60981cee 100644 --- a/src/bin/proxmox-backup-proxy.rs +++ b/src/bin/proxmox-backup-proxy.rs @@ -46,7 +46,7 @@ use proxmox_backup::{ }; use pbs_buildcfg::configdir; -use proxmox_systemd::time::{compute_next_event, parse_calendar_event}; +use proxmox_time::{compute_next_event, parse_calendar_event}; use pbs_tools::logrotate::LogRotate; use pbs_api_types::{ diff --git a/src/cached_traffic_control.rs b/src/cached_traffic_control.rs index 426ebba7..be554f6c 100644 --- a/src/cached_traffic_control.rs +++ b/src/cached_traffic_control.rs @@ -11,8 +11,7 @@ use cidr::IpInet; use proxmox_http::client::{ShareableRateLimit, RateLimiter}; use proxmox_section_config::SectionConfigData; -use proxmox_systemd::daily_duration::{parse_daily_duration, DailyDuration}; -use proxmox_time::TmEditor; +use proxmox_time::{parse_daily_duration, DailyDuration, TmEditor}; use pbs_api_types::TrafficControlRule; diff --git a/src/server/email_notifications.rs b/src/server/email_notifications.rs index db93fea2..b462bd4c 100644 --- a/src/server/email_notifications.rs +++ b/src/server/email_notifications.rs @@ -431,7 +431,7 @@ pub fn send_tape_backup_status( ) -> Result<(), Error> { let (fqdn, port) = get_server_url(); - let duration: proxmox_systemd::time::TimeSpan = summary.duration.into(); + let duration: proxmox_time::TimeSpan = summary.duration.into(); let mut data = json!({ "job": job, "fqdn": fqdn, diff --git a/src/server/jobstate.rs b/src/server/jobstate.rs index 23e3c2bd..f78875ca 100644 --- a/src/server/jobstate.rs +++ b/src/server/jobstate.rs @@ -46,7 +46,7 @@ use proxmox::tools::fs::{ create_path, file_read_optional_string, replace_file, CreateOptions, }; -use proxmox_systemd::time::{compute_next_event, parse_calendar_event}; +use proxmox_time::{compute_next_event, parse_calendar_event}; use pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M; use pbs_config::{open_backup_lockfile, BackupLockGuard}; diff --git a/src/tape/inventory.rs b/src/tape/inventory.rs index 86588617..ae7ed31c 100644 --- a/src/tape/inventory.rs +++ b/src/tape/inventory.rs @@ -33,7 +33,7 @@ use serde_json::json; use proxmox::tools::fs::{replace_file, file_get_json, CreateOptions}; use proxmox_uuid::Uuid; -use proxmox_systemd::time::compute_next_event; +use proxmox_time::compute_next_event; use pbs_config::BackupLockGuard; use pbs_api_types::{MediaSetPolicy, RetentionPolicy, MediaStatus, MediaLocation}; diff --git a/src/tape/media_pool.rs b/src/tape/media_pool.rs index 2989a1c2..68908768 100644 --- a/src/tape/media_pool.rs +++ b/src/tape/media_pool.rs @@ -18,7 +18,7 @@ use pbs_api_types::{ Fingerprint, MediaStatus, MediaLocation, MediaSetPolicy, RetentionPolicy, MediaPoolConfig, }; -use proxmox_systemd::time::compute_next_event; +use proxmox_time::compute_next_event; use pbs_config::BackupLockGuard; use crate::tape::{ diff --git a/src/tape/test/alloc_writable_media.rs b/src/tape/test/alloc_writable_media.rs index ee158eab..ce512962 100644 --- a/src/tape/test/alloc_writable_media.rs +++ b/src/tape/test/alloc_writable_media.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use anyhow::Error; -use proxmox_systemd::time::parse_time_span; +use proxmox_time::parse_time_span; use pbs_api_types::{RetentionPolicy, MediaSetPolicy}; use crate::tape::{Inventory, MediaPool}; diff --git a/src/tape/test/compute_media_state.rs b/src/tape/test/compute_media_state.rs index e7e1c34f..c1dfdba7 100644 --- a/src/tape/test/compute_media_state.rs +++ b/src/tape/test/compute_media_state.rs @@ -100,8 +100,8 @@ fn test_media_expire_time() -> Result<(), Error> { let sl2= MediaSetLabel::with_data("p1", Uuid::generate(), 0, ctime + 120, None); let tape2_uuid = inventory.generate_used_tape("tape2", sl2, 0); - let event = proxmox_systemd::time::parse_calendar_event("*:0/2")?; - let span = proxmox_systemd::time::parse_time_span("120 seconds")?; + let event = proxmox_time::parse_calendar_event("*:0/2")?; + let span = proxmox_time::parse_time_span("120 seconds")?; let pool = MediaPool::new( "p1", diff --git a/src/tools/systemd/mod.rs b/src/tools/systemd/mod.rs index c6635022..af2d4de0 100644 --- a/src/tools/systemd/mod.rs +++ b/src/tools/systemd/mod.rs @@ -1,2 +1,5 @@ pub mod config; pub mod types; + +mod unit; +pub use unit::*; diff --git a/src/tools/systemd/types.rs b/src/tools/systemd/types.rs index d36ea437..00bd9cc2 100644 --- a/src/tools/systemd/types.rs +++ b/src/tools/systemd/types.rs @@ -248,10 +248,10 @@ pub enum ServiceStartup { pub const SYSTEMD_TIMESPAN_SCHEMA: Schema = StringSchema::new( "systemd time span") - .format(&ApiStringFormat::VerifyFn(proxmox_systemd::time::verify_time_span)) + .format(&ApiStringFormat::VerifyFn(proxmox_time::verify_time_span)) .schema(); pub const SYSTEMD_CALENDAR_EVENT_SCHEMA: Schema = StringSchema::new( "systemd calendar event") - .format(&ApiStringFormat::VerifyFn(proxmox_systemd::time::verify_calendar_event)) + .format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) .schema(); diff --git a/proxmox-systemd/src/unit.rs b/src/tools/systemd/unit.rs similarity index 100% rename from proxmox-systemd/src/unit.rs rename to src/tools/systemd/unit.rs