api2/node/services.rs: implement service commands

This commit is contained in:
Dietmar Maurer 2019-01-27 12:40:31 +01:00
parent d7d23785f0
commit 48849593ee
2 changed files with 198 additions and 15 deletions

View File

@ -13,7 +13,9 @@ pub fn router() -> Router {
let route = Router::new() let route = Router::new()
.get(ApiMethod::new( .get(ApiMethod::new(
|_,_,_| Ok(json!([ |_,_,_| Ok(json!([
{"subdir": "dns"},
{"subdir": "network"}, {"subdir": "network"},
{"subdir": "services"},
{"subdir": "syslog"}, {"subdir": "syslog"},
{"subdir": "time"}, {"subdir": "time"},
])), ])),

View File

@ -19,15 +19,22 @@ static SERVICE_NAME_LIST: [&str; 6] = [
"systemd-timesyncd", "systemd-timesyncd",
]; ];
fn get_full_service_state(service: &str) -> Result<Value, Error> { fn real_service_name(service: &str) -> &str {
let mut real_service_name = service;
// since postfix package 3.1.0-3.1 the postfix unit is only here // since postfix package 3.1.0-3.1 the postfix unit is only here
// to manage subinstances, of which the default is called "-". // to manage subinstances, of which the default is called "-".
// This is where we look for the daemon status // This is where we look for the daemon status
if service == "postfix" { real_service_name = "postfix@-"; } if service == "postfix" {
"postfix@-"
} else {
service
}
}
fn get_full_service_state(service: &str) -> Result<Value, Error> {
let real_service_name = real_service_name(service);
let mut child = Command::new("/bin/systemctl") let mut child = Command::new("/bin/systemctl")
.args(&["show", real_service_name]) .args(&["show", real_service_name])
@ -66,6 +73,23 @@ fn get_full_service_state(service: &str) -> Result<Value, Error> {
Ok(result) Ok(result)
} }
fn json_service_state(service: &str, status: Value) -> Value {
if let Some(desc) = status["Description"].as_str() {
let name = status["Name"].as_str().unwrap_or(service);
let state = status["SubState"].as_str().unwrap_or("unknown");
return json!({
"service": service,
"name": name,
"desc": desc,
"state": state,
});
}
Value::Null
}
fn list_services( fn list_services(
param: Value, param: Value,
_info: &ApiMethod, _info: &ApiMethod,
@ -77,15 +101,9 @@ fn list_services(
for service in &SERVICE_NAME_LIST { for service in &SERVICE_NAME_LIST {
match get_full_service_state(service) { match get_full_service_state(service) {
Ok(status) => { Ok(status) => {
if let Some(desc) = status["Description"].as_str() { let state = json_service_state(service, status);
let name = status["Name"].as_str().unwrap_or(service); if state != Value::Null {
let state = status["SubState"].as_str().unwrap_or("unknown"); list.push(state);
list.push(json!({
"service": service,
"name": name,
"desc": desc,
"state": state,
}));
} }
} }
Err(err) => log::error!("{}", err), Err(err) => log::error!("{}", err),
@ -95,8 +113,170 @@ fn list_services(
Ok(Value::from(list)) Ok(Value::from(list))
} }
fn get_service_state(
param: Value,
_info: &ApiMethod,
rpcenv: &mut RpcEnvironment,
) -> Result<Value, Error> {
let service = tools::required_string_param(&param, "service")?;
if !SERVICE_NAME_LIST.contains(&service) {
bail!("unknown service name '{}'", service);
}
let status = get_full_service_state(service)?;
Ok(json_service_state(service, status))
}
fn run_service_command(service: &str, cmd: &str) -> Result<Value, Error> {
// fixme: run background worker (fork_worker) ???
match cmd {
"start"|"stop"|"restart"|"reload" => {},
_ => bail!("unknown service command '{}'", cmd),
}
if service == "proxmox-backup" {
if cmd != "restart" {
bail!("invalid service cmd '{} {}'", service, cmd);
}
}
let real_service_name = real_service_name(service);
let status = Command::new("/bin/systemctl")
.args(&[cmd, real_service_name])
.status()?;
if !status.success() {
bail!("systemctl {} failed with {}", cmd, status);
}
Ok(Value::Null)
}
fn start_service(
param: Value,
_info: &ApiMethod,
rpcenv: &mut RpcEnvironment,
) -> Result<Value, Error> {
let service = tools::required_string_param(&param, "service")?;
log::info!("starting service {}", service);
run_service_command(service, "start")
}
fn stop_service(
param: Value,
_info: &ApiMethod,
rpcenv: &mut RpcEnvironment,
) -> Result<Value, Error> {
let service = tools::required_string_param(&param, "service")?;
log::info!("stoping service {}", service);
run_service_command(service, "stop")
}
fn restart_service(
param: Value,
_info: &ApiMethod,
rpcenv: &mut RpcEnvironment,
) -> Result<Value, Error> {
let service = tools::required_string_param(&param, "service")?;
log::info!("re-starting service {}", service);
run_service_command(service, "restart")
}
fn reload_service(
param: Value,
_info: &ApiMethod,
rpcenv: &mut RpcEnvironment,
) -> Result<Value, Error> {
let service = tools::required_string_param(&param, "service")?;
log::info!("reloading service {}", service);
run_service_command(service, "reload")
}
pub fn router() -> Router { pub fn router() -> Router {
let service_id_schema : Arc<Schema> = Arc::new(
StringSchema::new("Service ID.")
.max_length(256)
.into()
);
let service_api = Router::new()
.get(ApiMethod::new(
|_,_,_| {
let mut result = vec![];
for cmd in &["state", "start", "stop", "restart", "reload"] {
result.push(json!({"subdir": cmd }));
}
Ok(Value::from(result))
},
ObjectSchema::new("Directory index.")
.required("service", service_id_schema.clone()))
)
.subdir(
"state",
Router::new()
.get(ApiMethod::new(
get_service_state,
ObjectSchema::new("Read service properties.")
.required("service", service_id_schema.clone()))
)
)
.subdir(
"start",
Router::new()
.post(ApiMethod::new(
start_service,
ObjectSchema::new("Start service.")
.required("service", service_id_schema.clone()))
)
)
.subdir(
"stop",
Router::new()
.post(ApiMethod::new(
stop_service,
ObjectSchema::new("Stop service.")
.required("service", service_id_schema.clone()))
)
)
.subdir(
"restart",
Router::new()
.post(ApiMethod::new(
restart_service,
ObjectSchema::new("Restart service.")
.required("service", service_id_schema.clone()))
)
)
.subdir(
"reload",
Router::new()
.post(ApiMethod::new(
reload_service,
ObjectSchema::new("Reload service.")
.required("service", service_id_schema.clone()))
)
)
;
let route = Router::new() let route = Router::new()
.get( .get(
ApiMethod::new( ApiMethod::new(
@ -106,14 +286,15 @@ pub fn router() -> Router {
ArraySchema::new( ArraySchema::new(
"Returns a list of systemd services.", "Returns a list of systemd services.",
ObjectSchema::new("Service details.") ObjectSchema::new("Service details.")
.required("service", StringSchema::new("Service ID.")) .required("service", service_id_schema.clone())
.required("name", StringSchema::new("systemd service name.")) .required("name", StringSchema::new("systemd service name."))
.required("desc", StringSchema::new("systemd service description.")) .required("desc", StringSchema::new("systemd service description."))
.required("state", StringSchema::new("systemd service 'SubState'.")) .required("state", StringSchema::new("systemd service 'SubState'."))
.into() .into()
) )
) )
); )
.match_all("service", service_api);
route route
} }