]> git.proxmox.com Git - proxmox-backup.git/commitdiff
api2/node/services.rs: implement service commands
authorDietmar Maurer <dietmar@proxmox.com>
Sun, 27 Jan 2019 11:40:31 +0000 (12:40 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Sun, 27 Jan 2019 11:40:31 +0000 (12:40 +0100)
src/api2/node.rs
src/api2/node/services.rs

index b40c46bcc33a8d2446f6a432cdb6cea3abdebc1f..6c3b13fc09fa306543c039589c79dce229b4ff69 100644 (file)
@@ -13,7 +13,9 @@ pub fn router() -> Router {
     let route = Router::new()
         .get(ApiMethod::new(
             |_,_,_| Ok(json!([
+                {"subdir": "dns"},
                 {"subdir": "network"},
+                {"subdir": "services"},
                 {"subdir": "syslog"},
                 {"subdir": "time"},
            ])),
index d1c199622d529b1a357852e94c7e8e7397c51602..76279f7cbc2574516c442738b578548a9e4a0052 100644 (file)
@@ -19,15 +19,22 @@ static SERVICE_NAME_LIST: [&str; 6] = [
     "systemd-timesyncd",
 ];
 
-fn get_full_service_state(service: &str) -> Result<Value, Error> {
-
-    let mut real_service_name = service;
+fn real_service_name(service: &str) -> &str {
 
     // since postfix package 3.1.0-3.1 the postfix unit is only here
     // to manage subinstances, of which the default is called "-".
     // 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")
         .args(&["show", real_service_name])
@@ -66,6 +73,23 @@ fn get_full_service_state(service: &str) -> Result<Value, Error> {
     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(
     param: Value,
     _info: &ApiMethod,
@@ -77,15 +101,9 @@ fn list_services(
     for service in &SERVICE_NAME_LIST {
         match get_full_service_state(service) {
             Ok(status) => {
-                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");
-                    list.push(json!({
-                        "service": service,
-                        "name": name,
-                        "desc": desc,
-                        "state": state,
-                    }));
+                let state = json_service_state(service, status);
+                if state != Value::Null {
+                    list.push(state);
                 }
             }
             Err(err) => log::error!("{}", err),
@@ -95,8 +113,170 @@ fn list_services(
     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 {
 
+    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()
         .get(
             ApiMethod::new(
@@ -106,14 +286,15 @@ pub fn router() -> Router {
                 ArraySchema::new(
                     "Returns a list of systemd services.",
                     ObjectSchema::new("Service details.")
-                        .required("service", StringSchema::new("Service ID."))
+                        .required("service", service_id_schema.clone())
                         .required("name", StringSchema::new("systemd service name."))
                         .required("desc", StringSchema::new("systemd service description."))
                         .required("state", StringSchema::new("systemd service 'SubState'."))
                         .into()
                 )
             )
-        );
+        )
+        .match_all("service", service_api);
 
     route
 }