pbs/
api.rs

1use linked_list_c::{ConstList, List};
2use log::{debug, error, info, trace, warn};
3use pbs_sys::{attrl, attropl, batch_status};
4use std::ffi::{CStr, CString};
5use std::ptr::{self, null_mut};
6
7use crate::bindings::{get_err, is_err, stat};
8use crate::helpers::{self, optstr_to_cstr};
9use crate::types::{Attribs, Attrl, Op, Server, StatResp};
10
11#[derive(PartialEq)]
12pub enum ResvModFlag {
13    Force,
14}
15
16#[derive(PartialEq)]
17pub enum ResvSubFlag {
18    Maintenance,
19}
20
21// signature for most of the pbs_stat* functions
22type PbsStatSignature =
23    unsafe extern "C" fn(i32, *mut i8, *mut attrl, *mut i8) -> *mut batch_status;
24
25// hacks to make Server::stat match signature consistent across all resources
26unsafe extern "C" fn sched_stat(
27    conn: i32,
28    n: *mut i8,
29    a: *mut attrl,
30    _ex: *mut i8,
31) -> *mut batch_status {
32    stat::pbs_statsched(conn, a, n)
33}
34unsafe extern "C" fn srv_stat(
35    conn: i32,
36    n: *mut i8,
37    a: *mut attrl,
38    _ex: *mut i8,
39) -> *mut batch_status {
40    stat::pbs_statserver(conn, a, n)
41}
42impl Server {
43    pub fn stat_host(
44        &self,
45        name: &Option<String>,
46        info: Option<Attribs>,
47    ) -> Result<StatResp, String> {
48        debug!("performing a host stat");
49        self.stat(name, info, stat::pbs_stathost)
50    }
51    pub fn stat_reservation(
52        &self,
53        name: &Option<String>,
54        info: Option<Attribs>,
55    ) -> Result<StatResp, String> {
56        debug!("performing a reservation stat");
57        self.stat(name, info, stat::pbs_statresv)
58    }
59    pub fn stat_resource(
60        &self,
61        name: &Option<String>,
62        info: Option<Attribs>,
63    ) -> Result<StatResp, String> {
64        debug!("performing a resource stat");
65        self.stat(name, info, stat::pbs_statrsc)
66    }
67    pub fn stat_vnode(
68        &self,
69        name: &Option<String>,
70        info: Option<Attribs>,
71    ) -> Result<StatResp, String> {
72        debug!("performing a vnode stat");
73        self.stat(name, info, stat::pbs_statvnode)
74    }
75    pub fn stat_que(
76        &self,
77        name: &Option<String>,
78        info: Option<Attribs>,
79    ) -> Result<StatResp, String> {
80        debug!("performing a que stat");
81        self.stat(name, info, stat::pbs_statque)
82    }
83    pub fn stat_scheduler(
84        &self,
85        name: &Option<String>,
86        info: Option<Attribs>,
87    ) -> Result<StatResp, String> {
88        debug!("performing a scheduler stat");
89        self.stat(name, info, sched_stat)
90    }
91    pub fn stat_server(
92        &self,
93        name: &Option<String>,
94        info: Option<Attribs>,
95    ) -> Result<StatResp, String> {
96        debug!("performing a server stat");
97        self.stat(name, info, srv_stat)
98    }
99    pub fn stat_job(
100        &self,
101        criteria: Attribs,
102        _output: Option<Attribs>,
103    ) -> Result<StatResp, String> {
104        debug!("performing a job stat");
105        let crit: ConstList<attrl> = criteria.into();
106        //TODO send criteria to api
107        //let out: ConstList<attrl> = output.unwrap().into();
108        //todo send extend flags
109        // T, t to include subjobs, job arrays are not included
110        // x include finished and moved jobs
111        trace!("calling pbs server");
112        let data = unsafe {
113            stat::pbs_selstat(
114                self.conn(),
115                crit.head() as *mut attropl,
116                null_mut(),
117                null_mut(),
118            )
119        };
120        if data.is_null() && is_err() {
121            error!("job stat request failed {}", get_err());
122            Err(get_err())
123        } else {
124            debug!("stat complete, returning list {:?}", &data);
125            Ok(data.into())
126        }
127    }
128
129    fn stat(
130        &self,
131        name: &Option<String>,
132        info: Option<Attribs>,
133        api: PbsStatSignature,
134    ) -> Result<StatResp, String> {
135        let attribs: ConstList<attrl> = if let Some(i) = info {
136            i.into()
137        } else {
138            List::new().into()
139        };
140        let n_ptr = optstr_to_cstr(name.as_deref());
141        let data = {
142            trace!("Performing stat");
143            let resp = unsafe { api(self.conn(), n_ptr, attribs.head(), null_mut()) };
144            if !n_ptr.is_null() {
145                trace!("dropping n_ptr");
146                _ = unsafe { CString::from_raw(n_ptr) };
147            }
148            if resp.is_null() && is_err() {
149                error!("stat request failed {}", get_err());
150                Err(get_err())
151            } else {
152                trace!("got good response");
153                Ok(resp)
154            }
155        }?;
156        debug!("stat complete, returning list {:?}", &data);
157        Ok(data.into())
158    }
159
160    pub fn submit_job(
161        &self,
162        attributes: Attribs,
163        script: &str,
164        queue: &str,
165    ) -> Result<String, String> {
166        trace!("Job submission, generating attributes list");
167        let attribs: ConstList<pbs_sys::attrl> = attributes.into();
168        //bindings::attropl and bindings::attrl are interchangable
169        trace!("Submitting job request");
170        let jobid = unsafe {
171            pbs_sys::pbs_submit(
172                self.conn(),
173                attribs.head() as *mut pbs_sys::attropl,
174                helpers::str_to_cstr(script),
175                helpers::str_to_cstr(queue),
176                null_mut(),
177            )
178        };
179        if !jobid.is_null() {
180            let resp = Ok(unsafe { CStr::from_ptr(jobid) }
181                .to_str()
182                .unwrap()
183                .to_string());
184            trace!("Job submitted, got resp {:?}", &resp);
185            unsafe { libc::free(jobid as *mut libc::c_void) };
186            resp
187        } else {
188            warn!("Error submitting job {}", get_err());
189            Err(get_err())
190        }
191    }
192
193    pub fn submit_resv(
194        &self,
195        attributes: Attribs,
196        flags: Vec<ResvSubFlag>,
197    ) -> Result<String, String> {
198        trace!("Reservation submission, generating attributes list");
199        let attribs: ConstList<pbs_sys::attrl> = attributes.into();
200        let extend = if flags.contains(&ResvSubFlag::Maintenance) {
201            helpers::str_to_cstr("m")
202        } else {
203            null_mut()
204        };
205        trace!("Submitting reservation request");
206        //bindings::attropl and bindings::attrl are interchangable
207        //TODO option to pass 'm' as extend arg for maintenance reservations
208        let resvid = unsafe {
209            pbs_sys::pbs_submit_resv(self.conn(), attribs.head() as *mut pbs_sys::attropl, extend)
210        };
211        if !resvid.is_null() {
212            let resp = Ok(unsafe { CStr::from_ptr(resvid) }
213                .to_str()
214                .unwrap()
215                .to_string());
216            trace!("Reservation submitted, got resp {:?}", &resp);
217            unsafe { libc::free(resvid as *mut libc::c_void) };
218            resp
219        } else {
220            warn!("Error submitting reservation {}", get_err());
221            Err(get_err())
222        }
223    }
224
225    pub fn mod_resv(
226        &self,
227        resv: &str,
228        attributes: Attribs,
229        flags: Vec<ResvModFlag>,
230    ) -> Result<String, String> {
231        trace!("Modify reservation submission, generating attributes list");
232        let attribs: ConstList<pbs_sys::attrl> = attributes.into();
233        let extend = if flags.contains(&ResvModFlag::Force) {
234            helpers::str_to_cstr("force")
235        } else {
236            null_mut()
237        };
238        trace!("Submitting reservation modification request");
239        //bindings::attropl and bindings::attrl are interchangable
240        let resvid = unsafe {
241            pbs_sys::pbs_modify_resv(
242                self.conn(),
243                helpers::str_to_cstr(resv),
244                attribs.head() as *mut pbs_sys::attropl,
245                extend,
246            )
247        };
248        if !resvid.is_null() {
249            let resp = Ok(unsafe { CStr::from_ptr(resvid) }
250                .to_str()
251                .unwrap()
252                .to_string());
253            trace!("Reservation modification submitted, got resp {:?}", &resp);
254            unsafe { libc::free(resvid as *mut libc::c_void) };
255            resp
256        } else {
257            warn!("Error submitting reservation modification {}", get_err());
258            Err(get_err())
259        }
260    }
261
262    pub fn del_job(&self, jobid: &str) -> Result<(), String> {
263        trace!("Deleting job {jobid}");
264        let resp = unsafe {
265            pbs_sys::pbs_deljob(self.conn(), helpers::str_to_cstr(jobid), ptr::null_mut())
266        };
267        if resp != 0 {
268            info!("Error deleting job {jobid}: {}", get_err());
269            return Err(get_err());
270        }
271        Ok(())
272    }
273    pub fn del_resv(&self, id: &str) -> Result<(), String> {
274        trace!("Deleting Reservation {id}");
275        let resp =
276            unsafe { pbs_sys::pbs_delresv(self.conn(), helpers::str_to_cstr(id), ptr::null_mut()) };
277        if resp != 0 {
278            info!("Error deleting Reservation {id}: {}", get_err());
279            return Err(get_err());
280        }
281        Ok(())
282    }
283    pub fn offline_vnode(&self, name: &str, comment: Option<&str>) -> Result<(), String> {
284        trace!("offlining vnode: {name}");
285        //ret = marknode(con, name, ND_offline, pbs_sys::batch_op::INCR, null_mut(), pbs_sys::batch_op::INCR, comment)
286        let mut new = Attribs::new();
287        new.add(
288            helpers::cstr_to_str(pbs_sys::ATTR_NODE_state.as_ptr() as *mut i8).to_string(),
289            Attrl::Value(Op::Incr("offline".to_string())),
290        );
291        if let Some(c) = comment {
292            new.add(
293                helpers::cstr_to_str(pbs_sys::ATTR_comment.as_ptr() as *mut i8).to_string(),
294                Attrl::Value(Op::Set(c.to_string())),
295            )
296        }
297        let new: ConstList<attrl> = new.into();
298        let resp = unsafe {
299            pbs_sys::pbs_manager(
300                self.conn(),
301                pbs_sys::mgr_cmd_MGR_CMD_SET,
302                pbs_sys::mgr_obj_MGR_OBJ_HOST,
303                helpers::str_to_cstr(name),
304                new.head() as *mut attropl,
305                null_mut(),
306            )
307        };
308        if resp != 0 {
309            info!("Error offlining vnode {name}: {}", get_err());
310            return Err(get_err());
311        }
312        Ok(())
313    }
314    pub fn clear_vnode(&self, name: &str, comment: Option<&str>) -> Result<(), String> {
315        trace!("clearing offline,down for vnode: {name}");
316        //ret = marknode(con, name, "offline", pbs_sys::batch_op::DECR, "down", pbs_sys::batch_op::DECR, comment)
317        let mut new = Attribs::new();
318        new.add(
319            helpers::cstr_to_str(pbs_sys::ATTR_NODE_state.as_ptr() as *mut i8).to_string(),
320            Attrl::Value(Op::Decr("offline".to_string())),
321        );
322        if let Some(c) = comment {
323            new.add(
324                helpers::cstr_to_str(pbs_sys::ATTR_comment.as_ptr() as *mut i8).to_string(),
325                Attrl::Value(Op::Set(c.to_string())),
326            )
327        }
328        let new: ConstList<attrl> = new.into();
329        let resp = unsafe {
330            pbs_sys::pbs_manager(
331                self.conn(),
332                pbs_sys::mgr_cmd_MGR_CMD_SET,
333                pbs_sys::mgr_obj_MGR_OBJ_HOST,
334                helpers::str_to_cstr(name),
335                new.head() as *mut attropl,
336                null_mut(),
337            )
338        };
339        if resp != 0 {
340            info!("Error offlining vnode {name}: {}", get_err());
341            return Err(get_err());
342        }
343        Ok(())
344    }
345}