]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/api2/node/dns.rs
update to first proxmox crate split
[proxmox-backup.git] / src / api2 / node / dns.rs
index e6a630c81068b4263fd4296087a906178ec57233..34a42a6504d45d52efd42221b4125838a9e2012e 100644 (file)
@@ -1,30 +1,47 @@
-use failure::*;
-
-
-use crate::tools;
-use crate::api2::*;
-//use crate::api_schema::schema::*;
-//use crate::api_schema::router::*;
+use std::sync::{Arc, Mutex};
 
+use anyhow::{Error};
 use lazy_static::lazy_static;
-
-use std::sync::{Arc, Mutex};
 use openssl::sha;
 use regex::Regex;
-
 use serde_json::{json, Value};
+use ::serde::{Deserialize, Serialize};
+
+use proxmox_router::{ApiMethod, Router, RpcEnvironment, Permission};
+use proxmox_schema::api;
+use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
+use proxmox::{IPRE, IPV4RE, IPV6RE, IPV4OCTET, IPV6H16, IPV6LS32};
+
+use pbs_api_types::{
+    PROXMOX_CONFIG_DIGEST_SCHEMA, FIRST_DNS_SERVER_SCHEMA, SECOND_DNS_SERVER_SCHEMA,
+    THIRD_DNS_SERVER_SCHEMA, NODE_SCHEMA, SEARCH_DOMAIN_SCHEMA,
+    PRIV_SYS_AUDIT, PRIV_SYS_MODIFY,
+};
 
 static RESOLV_CONF_FN: &str = "/etc/resolv.conf";
 
-fn read_etc_resolv_conf() -> Result<Value, Error> {
+#[api()]
+#[derive(Serialize, Deserialize)]
+#[allow(non_camel_case_types)]
+/// Deletable property name
+pub enum DeletableProperty {
+    /// Delete first nameserver entry
+    dns1,
+    /// Delete second nameserver entry
+    dns2,
+    /// Delete third nameserver entry
+    dns3,
+}
+
+pub fn read_etc_resolv_conf() -> Result<Value, Error> {
 
     let mut result = json!({});
 
     let mut nscount = 0;
 
-    let raw = tools::file_get_contents(RESOLV_CONF_FN)?;
+    let raw = file_get_contents(RESOLV_CONF_FN)?;
 
-    result["digest"] = Value::from(tools::digest_to_hex(&sha::sha256(&raw)));
+    result["digest"] = Value::from(proxmox::tools::digest_to_hex(&sha::sha256(&raw)));
 
     let data = String::from_utf8(raw)?;
 
@@ -34,6 +51,8 @@ fn read_etc_resolv_conf() -> Result<Value, Error> {
             concat!(r"^\s*nameserver\s+(", IPRE!(),  r")\s*")).unwrap();
     }
 
+    let mut options = String::new();
+
     for line in data.lines() {
 
         if let Some(caps) = DOMAIN_REGEX.captures(&line) {
@@ -44,111 +63,166 @@ fn read_etc_resolv_conf() -> Result<Value, Error> {
             let nameserver = &caps[1];
             let id = format!("dns{}", nscount);
             result[id] = Value::from(nameserver);
+        } else {
+            if !options.is_empty() { options.push('\n'); }
+            options.push_str(line);
         }
     }
 
+    if !options.is_empty() {
+        result["options"] = options.into();
+    }
+
     Ok(result)
 }
 
-fn update_dns(
-    param: Value,
-    _info: &ApiMethod,
-    _rpcenv: &mut RpcEnvironment,
+#[api(
+    protected: true,
+    input: {
+        description: "Update DNS settings.",
+        properties: {
+            node: {
+                schema: NODE_SCHEMA,
+            },
+            search: {
+                schema: SEARCH_DOMAIN_SCHEMA,
+                optional: true,
+            },
+            dns1: {
+                optional: true,
+                schema: FIRST_DNS_SERVER_SCHEMA,
+            },
+            dns2: {
+                optional: true,
+                schema: SECOND_DNS_SERVER_SCHEMA,
+            },
+            dns3: {
+                optional: true,
+                schema: THIRD_DNS_SERVER_SCHEMA,
+            },
+            delete: {
+                description: "List of properties to delete.",
+                type: Array,
+                optional: true,
+                items: {
+                    type: DeletableProperty,
+                }
+            },
+            digest: {
+                optional: true,
+                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "network", "dns"], PRIV_SYS_MODIFY, false),
+    }
+)]
+/// Update DNS settings
+pub fn update_dns(
+    search: Option<String>,
+    dns1: Option<String>,
+    dns2: Option<String>,
+    dns3: Option<String>,
+    delete: Option<Vec<DeletableProperty>>,
+    digest: Option<String>,
 ) -> Result<Value, Error> {
 
     lazy_static! {
-        static ref MUTEX: Arc<Mutex<usize>> = Arc::new(Mutex::new(0));
+        static ref MUTEX: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
     }
 
     let _guard = MUTEX.lock();
 
-    let search = tools::required_string_param(&param, "search")?;
+    let mut config = read_etc_resolv_conf()?;
+    let old_digest = config["digest"].as_str().unwrap();
 
-    let raw = tools::file_get_contents(RESOLV_CONF_FN)?;
-    let old_digest = tools::digest_to_hex(&sha::sha256(&raw));
+    if let Some(digest) = digest {
+        crate::tools::assert_if_modified(old_digest, &digest)?;
+    }
 
-    if let Some(digest) = param["digest"].as_str() {
-        tools::assert_if_modified(&old_digest, &digest)?;
+    if let Some(delete) = delete {
+        for delete_prop in delete {
+            let config = config.as_object_mut().unwrap();
+            match delete_prop {
+                DeletableProperty::dns1 => { config.remove("dns1"); },
+                DeletableProperty::dns2 => { config.remove("dns2"); },
+                DeletableProperty::dns3 => { config.remove("dns3"); },
+            }
+        }
     }
 
-    let old_data = String::from_utf8(raw)?;
+    if let Some(search) = search { config["search"] = search.into(); }
+    if let Some(dns1) = dns1 { config["dns1"] = dns1.into(); }
+    if let Some(dns2) = dns2 { config["dns2"] = dns2.into(); }
+    if let Some(dns3) = dns3 { config["dns3"] = dns3.into(); }
 
-    let mut data = format!("search {}\n", search);
+    let mut data = String::new();
 
+    if let Some(search) = config["search"].as_str() {
+        data.push_str(&format!("search {}\n", search));
+    }
     for opt in &["dns1", "dns2", "dns3"] {
-        if let Some(server) = param[opt].as_str() {
+        if let Some(server) = config[opt].as_str() {
             data.push_str(&format!("nameserver {}\n", server));
         }
     }
-
-    // append other data
-    lazy_static! {
-        static ref SKIP_REGEX: Regex = Regex::new(r"^(search|domain|nameserver)\s+").unwrap();
-    }
-    for line in old_data.lines() {
-        if SKIP_REGEX.is_match(line) { continue; }
-        data.push_str(line);
-        data.push('\n');
+    if let Some(options) = config["options"].as_str() {
+        data.push_str(options);
     }
 
-    tools::file_set_contents(RESOLV_CONF_FN, data.as_bytes(), None)?;
+    replace_file(RESOLV_CONF_FN, data.as_bytes(), CreateOptions::new())?;
 
     Ok(Value::Null)
 }
 
-fn get_dns(
+#[api(
+    input: {
+        properties: {
+            node: {
+                schema: NODE_SCHEMA,
+            },
+        },
+    },
+    returns: {
+        description: "Returns DNS server IPs and sreach domain.",
+        type: Object,
+        properties: {
+            digest: {
+                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+            },
+            search: {
+                optional: true,
+                schema: SEARCH_DOMAIN_SCHEMA,
+            },
+            dns1: {
+                optional: true,
+                schema: FIRST_DNS_SERVER_SCHEMA,
+            },
+            dns2: {
+                optional: true,
+                schema: SECOND_DNS_SERVER_SCHEMA,
+            },
+            dns3: {
+                optional: true,
+                schema: THIRD_DNS_SERVER_SCHEMA,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "network", "dns"], PRIV_SYS_AUDIT, false),
+    }
+)]
+/// Read DNS settings.
+pub fn get_dns(
     _param: Value,
     _info: &ApiMethod,
-    _rpcenv: &mut RpcEnvironment,
+    _rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
 
     read_etc_resolv_conf()
 }
 
-lazy_static! {
-    pub static ref SEARCH_DOMAIN_SCHEMA: Arc<Schema> =
-        StringSchema::new("Search domain for host-name lookup.").into();
-
-    pub static ref FIRST_DNS_SERVER_SCHEMA: Arc<Schema> =
-        StringSchema::new("First name server IP address.")
-        .format(IP_FORMAT.clone()).into();
-
-    pub static ref SECOND_DNS_SERVER_SCHEMA: Arc<Schema> =
-        StringSchema::new("Second name server IP address.")
-        .format(IP_FORMAT.clone()).into();
-
-    pub static ref THIRD_DNS_SERVER_SCHEMA: Arc<Schema> =
-        StringSchema::new("Third name server IP address.")
-        .format(IP_FORMAT.clone()).into();
-}
-
-pub fn router() -> Router {
-
-    let route = Router::new()
-        .get(
-            ApiMethod::new(
-                get_dns,
-                ObjectSchema::new("Read DNS settings.")
-            ).returns(
-                ObjectSchema::new("Returns DNS server IPs and sreach domain.")
-                    .required("digest", PVE_CONFIG_DIGEST_SCHEMA.clone())
-                    .optional("search", SEARCH_DOMAIN_SCHEMA.clone())
-                    .optional("dns1", FIRST_DNS_SERVER_SCHEMA.clone())
-                    .optional("dns2", SECOND_DNS_SERVER_SCHEMA.clone())
-                    .optional("dns3", THIRD_DNS_SERVER_SCHEMA.clone())
-            )
-        )
-        .put(
-            ApiMethod::new(
-                update_dns,
-                ObjectSchema::new("Returns DNS server IPs and sreach domain.")
-                    .required("search", SEARCH_DOMAIN_SCHEMA.clone())
-                    .optional("dns1", FIRST_DNS_SERVER_SCHEMA.clone())
-                    .optional("dns2", SECOND_DNS_SERVER_SCHEMA.clone())
-                    .optional("dns3", THIRD_DNS_SERVER_SCHEMA.clone())
-                    .optional("digest", PVE_CONFIG_DIGEST_SCHEMA.clone())
-            ).protected(true)
-        );
-
-    route
-}
+pub const ROUTER: Router = Router::new()
+    .get(&API_METHOD_GET_DNS)
+    .put(&API_METHOD_UPDATE_DNS);