]> git.proxmox.com Git - proxmox-backup.git/commitdiff
src/config/network.rs: make it compatible with pve
authorDietmar Maurer <dietmar@proxmox.com>
Wed, 6 May 2020 05:51:05 +0000 (07:51 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Thu, 7 May 2020 07:28:25 +0000 (09:28 +0200)
and depend on proxmox 0.1.26

12 files changed:
Cargo.toml
src/api2/node/network.rs
src/api2/types.rs
src/backup/catalog_shell.rs
src/bin/completion.rs
src/bin/proxmox-backup-client.rs
src/bin/proxmox-backup-manager.rs
src/bin/pxar.rs
src/config/network.rs
src/config/network/lexer.rs
src/config/network/parser.rs
www/SystemConfiguration.js

index 84c92b86a89a7e2378687a1a9c2a179e2f355ea8..16730f02b8d59213d89761a9141b4844d940c5c0 100644 (file)
@@ -37,7 +37,7 @@ pam = "0.7"
 pam-sys = "0.5"
 percent-encoding = "2.1"
 pin-utils = "0.1.0-alpha"
-proxmox = { version = "0.1.25", features = [ "sortable-macro", "api-macro" ] }
+proxmox = { version = "0.1.26", features = [ "sortable-macro", "api-macro" ] }
 #proxmox = { git = "ssh://gitolite3@proxdev.maurer-it.com/rust/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] }
 #proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro" ] }
 regex = "1.2"
index 9003808d6f11ea9b108d39c1d2b2223da5f59025..0160cef61ee86adbcc89fbab7439fc2282c58a56 100644 (file)
@@ -7,6 +7,7 @@ use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
 use crate::config::network;
 use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
 use crate::api2::types::*;
+use crate::server::{WorkerTask};
 
 #[api(
     input: {
@@ -39,9 +40,11 @@ pub fn list_network_devices(
 
     let mut list = Vec::new();
 
-    for interface in config.interfaces.values() {
+    for (iface, interface) in config.interfaces.iter() {
+        if iface == "lo" { continue; } // do not list lo
         let mut item: Value = to_value(interface)?;
         item["digest"] = digest.clone().into();
+        item["iface"] = iface.to_string().into();
         list.push(item);
     }
 
@@ -54,12 +57,12 @@ pub fn list_network_devices(
 }
 
 #[api(
-   input: {
+    input: {
         properties: {
             node: {
                 schema: NODE_SCHEMA,
             },
-            name: {
+            iface: {
                 schema: NETWORK_INTERFACE_NAME_SCHEMA,
             },
         },
@@ -73,11 +76,11 @@ pub fn list_network_devices(
     },
 )]
 /// Read a network interface configuration.
-pub fn read_interface(name: String) -> Result<Value, Error> {
+pub fn read_interface(iface: String) -> Result<Value, Error> {
 
     let (config, digest) = network::config()?;
 
-    let interface = config.lookup(&name)?;
+    let interface = config.lookup(&iface)?;
 
     let mut data: Value = to_value(interface)?;
     data["digest"] = proxmox::tools::digest_to_hex(&digest).into();
@@ -91,27 +94,29 @@ pub fn read_interface(name: String) -> Result<Value, Error> {
 /// Deletable property name
 pub enum DeletableProperty {
     /// Delete the IPv4 address property.
-    address_v4,
+    cidr,
     /// Delete the IPv6 address property.
-    address_v6,
+    cidr6,
     /// Delete the IPv4 gateway property.
-    gateway_v4,
+    gateway,
     /// Delete the IPv6 gateway property.
-    gateway_v6,
+    gateway6,
     /// Delete the whole IPv4 configuration entry.
-    method_v4,
+    method,
     /// Delete the whole IPv6 configuration entry.
-    method_v6,
+    method6,
     /// Delete IPv4 comments
-    comments_v4,
+    comments,
     /// Delete IPv6 comments
-    comments_v6,
+    comments6,
     /// Delete mtu.
     mtu,
-    /// Delete auto flag
-    auto,
+    /// Delete autostart flag
+    autostart,
     /// Delete bridge ports (set to 'none')
     bridge_ports,
+    /// Delet bridge-vlan-aware flag
+    bridge_vlan_aware,
     /// Delete bond-slaves (set to 'none')
     bond_slaves,
 }
@@ -124,38 +129,51 @@ pub enum DeletableProperty {
             node: {
                 schema: NODE_SCHEMA,
             },
-            name: {
+            iface: {
                 schema: NETWORK_INTERFACE_NAME_SCHEMA,
             },
-            auto: {
+            "type": {
+                description: "Interface type. If specified, need to match the current type.",
+                type: NetworkInterfaceType,
+                optional: true,
+            },
+            autostart: {
                 description: "Autostart interface.",
                 type: bool,
                 optional: true,
             },
-            method_v4: {
+            method: {
                 type: NetworkConfigMethod,
                 optional: true,
             },
-            method_v6: {
+            method6: {
                 type: NetworkConfigMethod,
                 optional: true,
             },
-            comments_v4: {
+            comments: {
                 description: "Comments (inet, may span multiple lines)",
                 type: String,
                 optional: true,
             },
-            comments_v6: {
+            comments6: {
                 description: "Comments (inet5, may span multiple lines)",
                 type: String,
                 optional: true,
             },
-            address: {
-                schema: CIDR_SCHEMA,
+            cidr: {
+                schema: CIDR_V4_SCHEMA,
+                optional: true,
+            },
+            cidr6: {
+                schema: CIDR_V6_SCHEMA,
                 optional: true,
             },
             gateway: {
-                schema: IP_SCHEMA,
+                schema: IP_V4_SCHEMA,
+                optional: true,
+            },
+            gateway6: {
+                schema: IP_V6_SCHEMA,
                 optional: true,
             },
             mtu: {
@@ -169,6 +187,11 @@ pub enum DeletableProperty {
                 schema: NETWORK_INTERFACE_LIST_SCHEMA,
                 optional: true,
             },
+            bridge_vlan_aware: {
+               description: "Enable bridge vlan support.",
+               type: bool,
+               optional: true,
+            },
             bond_slaves: {
                 schema: NETWORK_INTERFACE_LIST_SCHEMA,
                 optional: true,
@@ -188,24 +211,28 @@ pub enum DeletableProperty {
         },
     },
     access: {
-        permission: &Permission::Privilege(&["system", "network", "interfaces", "{name}"], PRIV_SYS_MODIFY, false),
+        permission: &Permission::Privilege(&["system", "network", "interfaces", "{iface}"], PRIV_SYS_MODIFY, false),
     },
 )]
 /// Update network interface config.
 pub fn update_interface(
-    name: String,
-    auto: Option<bool>,
-    method_v4: Option<NetworkConfigMethod>,
-    method_v6: Option<NetworkConfigMethod>,
-    comments_v4: Option<String>,
-    comments_v6: Option<String>,
-    address: Option<String>,
+    iface: String,
+    autostart: Option<bool>,
+    method: Option<NetworkConfigMethod>,
+    method6: Option<NetworkConfigMethod>,
+    comments: Option<String>,
+    comments6: Option<String>,
+    cidr: Option<String>,
     gateway: Option<String>,
+    cidr6: Option<String>,
+    gateway6: Option<String>,
     mtu: Option<u64>,
     bridge_ports: Option<Vec<String>>,
+    bridge_vlan_aware: Option<bool>,
     bond_slaves: Option<Vec<String>>,
     delete: Option<Vec<DeletableProperty>>,
     digest: Option<String>,
+    param: Value,
 ) -> Result<(), Error> {
 
     let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
@@ -218,71 +245,98 @@ pub fn update_interface(
     }
 
     let current_gateway_v4 = config.interfaces.iter()
-        .find(|(_, interface)| interface.gateway_v4.is_some())
+        .find(|(_, interface)| interface.gateway.is_some())
         .map(|(name, _)| name.to_string());
 
     let current_gateway_v6 = config.interfaces.iter()
-        .find(|(_, interface)| interface.gateway_v4.is_some())
+        .find(|(_, interface)| interface.gateway6.is_some())
         .map(|(name, _)| name.to_string());
 
-    let interface = config.lookup_mut(&name)?;
+    let interface = config.lookup_mut(&iface)?;
+
+    if let Some(interface_type) = param.get("type") {
+        let interface_type: NetworkInterfaceType = serde_json::from_value(interface_type.clone())?;
+        if  interface_type != interface.interface_type {
+            bail!("got unexpected interface type ({:?} != {:?})", interface_type, interface.interface_type);
+        }
+    }
 
     if let Some(delete) = delete {
         for delete_prop in delete {
             match delete_prop {
-                DeletableProperty::address_v4 => { interface.cidr_v4 = None; },
-                DeletableProperty::address_v6 => { interface.cidr_v6 = None; },
-                DeletableProperty::gateway_v4 => { interface.gateway_v4 = None; },
-                DeletableProperty::gateway_v6 => { interface.gateway_v6 = None; },
-                DeletableProperty::method_v4 => { interface.method_v4 = None; },
-                DeletableProperty::method_v6 => { interface.method_v6 = None; },
-                DeletableProperty::comments_v4 => { interface.comments_v4 = None; },
-                DeletableProperty::comments_v6 => { interface.comments_v6 = None; },
+                DeletableProperty::cidr => { interface.cidr = None; },
+                DeletableProperty::cidr6 => { interface.cidr6 = None; },
+                DeletableProperty::gateway => { interface.gateway = None; },
+                DeletableProperty::gateway6 => { interface.gateway6 = None; },
+                DeletableProperty::method => { interface.method = None; },
+                DeletableProperty::method6 => { interface.method6 = None; },
+                DeletableProperty::comments => { interface.comments = None; },
+                DeletableProperty::comments6 => { interface.comments6 = None; },
                 DeletableProperty::mtu => { interface.mtu = None; },
-                DeletableProperty::auto => { interface.auto = false; },
+                DeletableProperty::autostart => { interface.autostart = false; },
                 DeletableProperty::bridge_ports => { interface.set_bridge_ports(Vec::new())?; }
+                DeletableProperty::bridge_vlan_aware => { interface.bridge_vlan_aware = None; }
                 DeletableProperty::bond_slaves => { interface.set_bond_slaves(Vec::new())?; }
             }
         }
     }
 
-    if let Some(auto) = auto { interface.auto = auto; }
-    if method_v4.is_some() { interface.method_v4 = method_v4; }
-    if method_v6.is_some() { interface.method_v6 = method_v6; }
+    if let Some(autostart) = autostart { interface.autostart = autostart; }
+    if method.is_some() { interface.method = method; }
+    if method6.is_some() { interface.method6 = method6; }
     if mtu.is_some() { interface.mtu = mtu; }
     if let Some(ports) = bridge_ports { interface.set_bridge_ports(ports)?; }
+    if bridge_vlan_aware.is_some() { interface.bridge_vlan_aware = bridge_vlan_aware; }
     if let Some(slaves) = bond_slaves { interface.set_bond_slaves(slaves)?; }
 
-    if let Some(address) = address {
-        let (_, _, is_v6) = network::parse_cidr(&address)?;
-        if is_v6 {
-            interface.cidr_v6 = Some(address);
-        } else {
-            interface.cidr_v4 = Some(address);
-        }
+    if let Some(cidr) = cidr {
+        let (_, _, is_v6) = network::parse_cidr(&cidr)?;
+        if is_v6 { bail!("invalid address type (expected IPv4, got IPv6)"); }
+        interface.cidr = Some(cidr);
+    }
+
+    if let Some(cidr6) = cidr6 {
+        let (_, _, is_v6) = network::parse_cidr(&cidr6)?;
+        if !is_v6 { bail!("invalid address type (expected IPv6, got IPv4)"); }
+        interface.cidr6 = Some(cidr6);
     }
 
     if let Some(gateway) = gateway {
         let is_v6 = gateway.contains(':');
-        if is_v6 {
-            if let Some(current_gateway_v6) = current_gateway_v6 {
-                if current_gateway_v6 != name {
-                    bail!("Default IPv6 gateway already exists on interface '{}'", current_gateway_v6);
-                }
+        if is_v6 {  bail!("invalid address type (expected IPv4, got IPv6)"); }
+        if let Some(current_gateway_v4) = current_gateway_v4 {
+            if current_gateway_v4 != iface {
+                bail!("Default IPv4 gateway already exists on interface '{}'", current_gateway_v4);
             }
-            interface.gateway_v6 = Some(gateway);
-        } else {
-            if let Some(current_gateway_v4) = current_gateway_v4 {
-                if current_gateway_v4 != name {
-                    bail!("Default IPv4 gateway already exists on interface '{}'", current_gateway_v4);
-                }
+        }
+        interface.gateway = Some(gateway);
+    }
+
+    if let Some(gateway6) = gateway6 {
+        let is_v6 = gateway6.contains(':');
+        if !is_v6 {  bail!("invalid address type (expected IPv6, got IPv4)"); }
+        if let Some(current_gateway_v6) = current_gateway_v6 {
+            if current_gateway_v6 != iface {
+                bail!("Default IPv6 gateway already exists on interface '{}'", current_gateway_v6);
             }
-            interface.gateway_v4 = Some(gateway);
         }
+        interface.gateway6 = Some(gateway6);
+    }
+
+    if comments.is_some() { interface.comments = comments; }
+    if comments6.is_some() { interface.comments6 = comments6; }
+
+    if interface.cidr.is_some() || interface.gateway.is_some() {
+        interface.method = Some(NetworkConfigMethod::Static);
+    } else {
+        interface.method = Some(NetworkConfigMethod::Manual);
     }
 
-    if comments_v4.is_some() { interface.comments_v4 = comments_v4; }
-    if comments_v6.is_some() { interface.comments_v6 = comments_v6; }
+    if interface.cidr6.is_some() || interface.gateway6.is_some() {
+        interface.method6 = Some(NetworkConfigMethod::Static);
+    } else {
+        interface.method6 = Some(NetworkConfigMethod::Manual);
+    }
 
     network::save_config(&config)?;
 
@@ -296,7 +350,7 @@ pub fn update_interface(
             node: {
                 schema: NODE_SCHEMA,
             },
-            name: {
+            iface: {
                 schema: NETWORK_INTERFACE_NAME_SCHEMA,
             },
             digest: {
@@ -306,11 +360,11 @@ pub fn update_interface(
         },
     },
     access: {
-        permission: &Permission::Privilege(&["system", "network", "interfaces", "{name}"], PRIV_SYS_MODIFY, false),
+        permission: &Permission::Privilege(&["system", "network", "interfaces", "{iface}"], PRIV_SYS_MODIFY, false),
     },
 )]
 /// Remove network interface configuration.
-pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Error> {
+pub fn delete_interface(iface: String, digest: Option<String>) -> Result<(), Error> {
 
     let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
 
@@ -321,9 +375,9 @@ pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Erro
         crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
     }
 
-    let _interface = config.lookup(&name)?; // check if interface exists
+    let _interface = config.lookup(&iface)?; // check if interface exists
 
-    config.interfaces.remove(&name);
+    config.interfaces.remove(&iface);
 
     network::save_config(&config)?;
 
@@ -331,6 +385,7 @@ pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Erro
 }
 
 #[api(
+    protected: true,
     input: {
         properties: {
             node: {
@@ -343,15 +398,23 @@ pub fn delete_interface(name: String, digest: Option<String>) -> Result<(), Erro
     },
 )]
 /// Reload network configuration (requires ifupdown2).
-pub fn reload_network_config() -> Result<(), Error> {
+pub async fn reload_network_config(
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<String, Error> {
 
     network::assert_ifupdown2_installed()?;
 
-    let _ = std::fs::rename(network::NETWORK_INTERFACES_NEW_FILENAME, network::NETWORK_INTERFACES_FILENAME);
+    let username = rpcenv.get_user().unwrap();
 
-    network::network_reload()?;
+    let upid_str = WorkerTask::spawn("srvreload", Some(String::from("networking")), &username.clone(), true, |_worker| async {
 
-    Ok(())
+        let _ = std::fs::rename(network::NETWORK_INTERFACES_NEW_FILENAME, network::NETWORK_INTERFACES_FILENAME);
+
+        network::network_reload()?;
+        Ok(())
+    })?;
+
+    Ok(upid_str)
 }
 
 #[api(
@@ -383,4 +446,4 @@ pub const ROUTER: Router = Router::new()
     .get(&API_METHOD_LIST_NETWORK_DEVICES)
     .put(&API_METHOD_RELOAD_NETWORK_CONFIG)
     .delete(&API_METHOD_REVERT_NETWORK_CONFIG)
-    .match_all("name", &ITEM_ROUTER);
+    .match_all("iface", &ITEM_ROUTER);
index 62345d768820d8004284289b70608452dd983960..9278d9467b1b0a16c4672ac4641e97656425e8d9 100644 (file)
@@ -557,9 +557,7 @@ pub enum NetworkInterfaceType {
     /// Loopback
     Loopback,
     /// Physical Ethernet device
-    Ethernet,
-    /// Name looks like a physical ethernet device, but device is not found
-    Vanished,
+    Eth,
     /// Linux Bridge
     Bridge,
     /// Linux Bond
@@ -587,18 +585,34 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema = ArraySchema::new(
         name: {
             schema: NETWORK_INTERFACE_NAME_SCHEMA,
         },
-        interface_type: {
+        "type": {
             type: NetworkInterfaceType,
         },
-        method_v4: {
+        method: {
             type: NetworkConfigMethod,
             optional: true,
         },
-        method_v6: {
+        method6: {
             type: NetworkConfigMethod,
             optional: true,
         },
-        options_v4: {
+        cidr: {
+            schema: CIDR_V4_SCHEMA,
+            optional: true,
+        },
+        cidr6: {
+            schema: CIDR_V6_SCHEMA,
+            optional: true,
+        },
+        gateway: {
+            schema: IP_V4_SCHEMA,
+            optional: true,
+        },
+        gateway6: {
+            schema: IP_V6_SCHEMA,
+            optional: true,
+        },
+        options: {
             description: "Option list (inet)",
             type: Array,
             items: {
@@ -606,7 +620,7 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema = ArraySchema::new(
                 type: String,
             },
         },
-        options_v6: {
+        options6: {
             description: "Option list (inet6)",
             type: Array,
             items: {
@@ -614,12 +628,12 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema = ArraySchema::new(
                 type: String,
             },
         },
-        comments_v4: {
+        comments: {
             description: "Comments (inet, may span multiple lines)",
             type: String,
             optional: true,
         },
-        comments_v6: {
+        comments6: {
             description: "Comments (inet6, may span multiple lines)",
             type: String,
             optional: true,
@@ -638,39 +652,41 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema = ArraySchema::new(
 /// Network Interface configuration
 pub struct Interface {
     /// Autostart interface
-    pub auto: bool,
+    #[serde(rename = "autostart")]
+    pub autostart: bool,
     /// Interface is active (UP)
     pub active: bool,
     /// Interface name
     pub name: String,
     /// Interface type
+    #[serde(rename = "type")]
     pub interface_type: NetworkInterfaceType,
     #[serde(skip_serializing_if="Option::is_none")]
-    pub method_v4: Option<NetworkConfigMethod>,
+    pub method: Option<NetworkConfigMethod>,
     #[serde(skip_serializing_if="Option::is_none")]
-    pub method_v6: Option<NetworkConfigMethod>,
+    pub method6: Option<NetworkConfigMethod>,
     #[serde(skip_serializing_if="Option::is_none")]
     /// IPv4 address with netmask
-    pub cidr_v4: Option<String>,
+    pub cidr: Option<String>,
     #[serde(skip_serializing_if="Option::is_none")]
     /// IPv4 gateway
-    pub gateway_v4: Option<String>,
+    pub gateway: Option<String>,
     #[serde(skip_serializing_if="Option::is_none")]
     /// IPv6 address with netmask
-    pub cidr_v6: Option<String>,
+    pub cidr6: Option<String>,
     #[serde(skip_serializing_if="Option::is_none")]
     /// IPv6 gateway
-    pub gateway_v6: Option<String>,
+    pub gateway6: Option<String>,
 
     #[serde(skip_serializing_if="Vec::is_empty")]
-    pub options_v4: Vec<String>,
+    pub options: Vec<String>,
     #[serde(skip_serializing_if="Vec::is_empty")]
-    pub options_v6: Vec<String>,
+    pub options6: Vec<String>,
 
     #[serde(skip_serializing_if="Option::is_none")]
-    pub comments_v4: Option<String>,
+    pub comments: Option<String>,
     #[serde(skip_serializing_if="Option::is_none")]
-    pub comments_v6: Option<String>,
+    pub comments6: Option<String>,
 
     #[serde(skip_serializing_if="Option::is_none")]
     /// Maximum Transmission Unit
@@ -678,6 +694,9 @@ pub struct Interface {
 
     #[serde(skip_serializing_if="Option::is_none")]
     pub bridge_ports: Option<Vec<String>>,
+    /// Enable bridge vlan support.
+    #[serde(skip_serializing_if="Option::is_none")]
+    pub bridge_vlan_aware: Option<bool>,
 
     #[serde(skip_serializing_if="Option::is_none")]
     pub bond_slaves: Option<Vec<String>>,
index 2f30ea2afda06b7e7f2668029b962a6c2f6b84cd..7683ed075710dbd837ffba6b01e9cd39aa8577b6 100644 (file)
@@ -140,7 +140,9 @@ impl Shell {
                     continue;
                 }
             };
-            let _ = handle_command(helper.cmd_def(), "", args, None);
+
+            let rpcenv = CliEnvironment::new();
+            let _ = handle_command(helper.cmd_def(), "", args, rpcenv, None);
             self.rl.add_history_entry(line);
             self.update_prompt()?;
         }
index 6e5630bdc4c59f0d401e0f1b8f75e320ee5d0e43..c1c3b69af7fb1467e0e833cc5cdab6f00a56d0d6 100644 (file)
@@ -83,7 +83,8 @@ fn main() -> Result<(), Error> {
 
         let args = shellword_split(&line)?;
 
-        let _ = handle_command(helper.cmd_def(), "", args, None);
+        let rpcenv = CliEnvironment::new();
+        let _ = handle_command(helper.cmd_def(), "", args, rpcenv, None);
 
         rl.add_history_entry(line);
     }
index a4f2e9ea9f4f2816383ed86d5a1dadc780aeae27..4d6c00365d5aa5027b21154a367d6e6b81330711 100644 (file)
@@ -2421,7 +2421,8 @@ fn main() {
         .insert("catalog", catalog_mgmt_cli())
         .insert("task", task_mgmt_cli());
 
-    run_cli_command(cmd_def, Some(|future| {
+    let rpcenv = CliEnvironment::new();
+    run_cli_command(cmd_def, rpcenv, Some(|future| {
         proxmox_backup::tools::runtime::main(future)
     }));
 }
index 6c0cf63f4e62887823bbf3a0955c4f89ac0cf860..09d5b661453b5a21589b5b4927c3153ae32ae124 100644 (file)
@@ -273,10 +273,10 @@ fn list_network_devices(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Re
     fn render_address(_value: &Value, record: &Value) -> Result<String, Error> {
         let mut text = String::new();
 
-        if let Some(cidr) = record["cidr_v4"].as_str() {
+        if let Some(cidr) = record["cidr"].as_str() {
             text.push_str(cidr);
         }
-        if let Some(cidr) = record["cidr_v6"].as_str() {
+        if let Some(cidr) = record["cidr6"].as_str() {
             if !text.is_empty() { text.push('\n'); }
             text.push_str(cidr);
         }
@@ -287,10 +287,10 @@ fn list_network_devices(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Re
     fn render_gateway(_value: &Value, record: &Value) -> Result<String, Error> {
         let mut text = String::new();
 
-        if let Some(gateway) = record["gateway_v4"].as_str() {
+        if let Some(gateway) = record["gateway"].as_str() {
             text.push_str(gateway);
         }
-        if let Some(gateway) = record["gateway_v6"].as_str() {
+        if let Some(gateway) = record["gateway6"].as_str() {
             if !text.is_empty() { text.push('\n'); }
             text.push_str(gateway);
         }
@@ -299,13 +299,13 @@ fn list_network_devices(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Re
     }
 
     let options = default_table_format_options()
-        .column(ColumnConfig::new("interface_type").header("type"))
+        .column(ColumnConfig::new("type").header("type"))
         .column(ColumnConfig::new("name"))
-        .column(ColumnConfig::new("auto"))
-        .column(ColumnConfig::new("method_v4"))
-        .column(ColumnConfig::new("method_v6"))
-        .column(ColumnConfig::new("cidr_v4").header("address").renderer(render_address))
-        .column(ColumnConfig::new("gateway_v4").header("gateway").renderer(render_gateway));
+        .column(ColumnConfig::new("autostart"))
+        .column(ColumnConfig::new("method"))
+        .column(ColumnConfig::new("method6"))
+        .column(ColumnConfig::new("cidr").header("address").renderer(render_address))
+        .column(ColumnConfig::new("gateway").header("gateway").renderer(render_gateway));
 
     format_and_print_result_full(&mut data, info.returns, &output_format, &options);
 
@@ -347,15 +347,15 @@ fn network_commands() -> CommandLineInterface {
             "update",
             CliCommand::new(&api2::node::network::API_METHOD_UPDATE_INTERFACE)
                 .fixed_param("node", String::from("localhost"))
-                .arg_param(&["name"])
-                .completion_cb("name", config::network::complete_interface_name)
+                .arg_param(&["iface"])
+                .completion_cb("iface", config::network::complete_interface_name)
         )
         .insert(
             "remove",
             CliCommand::new(&api2::node::network::API_METHOD_DELETE_INTERFACE)
                 .fixed_param("node", String::from("localhost"))
-                .arg_param(&["name"])
-                .completion_cb("name", config::network::complete_interface_name)
+                .arg_param(&["iface"])
+                .completion_cb("iface", config::network::complete_interface_name)
         )
         .insert(
             "revert",
@@ -839,7 +839,10 @@ fn main() {
                 .completion_cb("remote-store", complete_remote_datastore_name)
         );
 
-    proxmox_backup::tools::runtime::main(run_async_cli_command(cmd_def));
+    let mut rpcenv = CliEnvironment::new();
+    rpcenv.set_user(Some(String::from("root@pam")));
+    
+   proxmox_backup::tools::runtime::main(run_async_cli_command(cmd_def, rpcenv));
 }
 
 // shell completion helper
index 5d18b28d577ddcb860638d45de48a4f871d58791..5d1eb2e63c2274d3b36c262212939f2364999296 100644 (file)
@@ -519,5 +519,6 @@ fn main() {
             .completion_cb("archive", tools::complete_file_name)
         );
 
-    run_cli_command(cmd_def, None);
+    let rpcenv = CliEnvironment::new();
+    run_cli_command(cmd_def, rpcenv, None);
 }
index 1b739a3701d005e2e09a6eeeda1ea7f93d1d7aba..ef8bf73abf021752eaa357efb37ffe864dba1657 100644 (file)
@@ -22,27 +22,28 @@ impl Interface {
         Self {
             name,
             interface_type: NetworkInterfaceType::Unknown,
-            auto: false,
+            autostart: false,
             active: false,
-            method_v4: None,
-            method_v6: None,
-            cidr_v4: None,
-            gateway_v4: None,
-            cidr_v6: None,
-            gateway_v6: None,
-            options_v4: Vec::new(),
-            options_v6: Vec::new(),
-            comments_v4: None,
-            comments_v6: None,
+            method: None,
+            method6: None,
+            cidr: None,
+            gateway: None,
+            cidr6: None,
+            gateway6: None,
+            options: Vec::new(),
+            options6: Vec::new(),
+            comments: None,
+            comments6: None,
             mtu: None,
             bridge_ports: None,
+            bridge_vlan_aware: None,
             bond_slaves: None,
         }
     }
 
     fn set_method_v4(&mut self, method: NetworkConfigMethod) -> Result<(), Error> {
-        if self.method_v4.is_none() {
-            self.method_v4 = Some(method);
+        if self.method.is_none() {
+            self.method = Some(method);
         } else {
             bail!("inet configuration method already set.");
         }
@@ -50,8 +51,8 @@ impl Interface {
     }
 
     fn set_method_v6(&mut self, method: NetworkConfigMethod) -> Result<(), Error> {
-        if self.method_v6.is_none() {
-            self.method_v6 = Some(method);
+        if self.method6.is_none() {
+            self.method6 = Some(method);
         } else {
             bail!("inet6 configuration method already set.");
         }
@@ -59,8 +60,8 @@ impl Interface {
     }
 
     fn set_cidr_v4(&mut self, address: String) -> Result<(), Error> {
-        if self.cidr_v4.is_none() {
-            self.cidr_v4 = Some(address);
+        if self.cidr.is_none() {
+            self.cidr = Some(address);
         } else {
             bail!("duplicate IPv4 address.");
         }
@@ -68,8 +69,8 @@ impl Interface {
     }
 
     fn set_gateway_v4(&mut self, gateway: String) -> Result<(), Error> {
-        if self.gateway_v4.is_none() {
-            self.gateway_v4 = Some(gateway);
+        if self.gateway.is_none() {
+            self.gateway = Some(gateway);
         } else {
             bail!("duplicate IPv4 gateway.");
         }
@@ -77,8 +78,8 @@ impl Interface {
     }
 
     fn set_cidr_v6(&mut self, address: String) -> Result<(), Error> {
-        if self.cidr_v6.is_none() {
-            self.cidr_v6 = Some(address);
+        if self.cidr6.is_none() {
+            self.cidr6 = Some(address);
         } else {
             bail!("duplicate IPv6 address.");
         }
@@ -86,8 +87,8 @@ impl Interface {
     }
 
     fn set_gateway_v6(&mut self, gateway: String) -> Result<(), Error> {
-        if self.gateway_v6.is_none() {
-            self.gateway_v6 = Some(gateway);
+        if self.gateway6.is_none() {
+            self.gateway6 = Some(gateway);
         } else {
             bail!("duplicate IPv4 gateway.");
         }
@@ -124,20 +125,23 @@ impl Interface {
 
         match self.interface_type {
             NetworkInterfaceType::Bridge => {
+                if let Some(true) = self.bridge_vlan_aware {
+                    writeln!(w, "\tbridge-vlan-aware yes")?;
+                }
                 if let Some(ref ports) = self.bridge_ports {
                     if ports.is_empty() {
-                        writeln!(w, "    bridge-ports none")?;
+                        writeln!(w, "\tbridge-ports none")?;
                     } else {
-                        writeln!(w, "    bridge-ports {}", ports.join(" "))?;
+                        writeln!(w, "\tbridge-ports {}", ports.join(" "))?;
                     }
                 }
             }
             NetworkInterfaceType::Bond => {
                 if let Some(ref slaves) = self.bond_slaves {
                     if slaves.is_empty() {
-                        writeln!(w, "    bond-slaves none")?;
+                        writeln!(w, "\tbond-slaves none")?;
                     } else {
-                        writeln!(w, "    bond-slaves {}", slaves.join(" "))?;
+                        writeln!(w, "\tbond-slaves {}", slaves.join(" "))?;
                     }
                 }
             }
@@ -145,7 +149,7 @@ impl Interface {
         }
 
         if let Some(mtu) = self.mtu {
-            writeln!(w, "    mtu {}", mtu)?;
+            writeln!(w, "\tmtu {}", mtu)?;
         }
 
         Ok(())
@@ -154,19 +158,19 @@ impl Interface {
     /// Write attributes dependening on address family inet (IPv4)
     fn write_iface_attributes_v4(&self, w: &mut dyn Write, method: NetworkConfigMethod) -> Result<(), Error> {
         if method == NetworkConfigMethod::Static {
-            if let Some(address) = &self.cidr_v4 {
-                writeln!(w, "    address {}", address)?;
+            if let Some(address) = &self.cidr {
+                writeln!(w, "\taddress {}", address)?;
             }
-            if let Some(gateway) = &self.gateway_v4 {
-                writeln!(w, "    gateway {}", gateway)?;
+            if let Some(gateway) = &self.gateway {
+                writeln!(w, "\tgateway {}", gateway)?;
             }
         }
 
-        for option in &self.options_v4 {
-            writeln!(w, "    {}", option)?;
+        for option in &self.options {
+            writeln!(w, "\t{}", option)?;
         }
 
-        if let Some(ref comments) = self.comments_v4 {
+        if let Some(ref comments) = self.comments {
             for comment in comments.lines() {
                 writeln!(w, "#{}", comment)?;
             }
@@ -178,19 +182,19 @@ impl Interface {
     /// Write attributes dependening on address family inet6 (IPv6)
     fn write_iface_attributes_v6(&self, w: &mut dyn Write, method: NetworkConfigMethod) -> Result<(), Error> {
         if method == NetworkConfigMethod::Static {
-            if let Some(address) = &self.cidr_v6 {
-                writeln!(w, "    address {}", address)?;
+            if let Some(address) = &self.cidr6 {
+                writeln!(w, "\taddress {}", address)?;
             }
-            if let Some(gateway) = &self.gateway_v6 {
-                writeln!(w, "    gateway {}", gateway)?;
+            if let Some(gateway) = &self.gateway6 {
+                writeln!(w, "\tgateway {}", gateway)?;
             }
         }
 
-        for option in &self.options_v6 {
-            writeln!(w, "    {}", option)?;
+        for option in &self.options6 {
+            writeln!(w, "\t{}", option)?;
         }
 
-        if let Some(ref comments) = self.comments_v6 {
+        if let Some(ref comments) = self.comments6 {
             for comment in comments.lines() {
                 writeln!(w, "#{}", comment)?;
             }
@@ -204,30 +208,31 @@ impl Interface {
         // Note: use match to make sure we considered all values at compile time
         match self {
             Interface {
-                method_v4,
-                method_v6,
-                options_v4,
-                options_v6,
-                comments_v4,
-                comments_v6,
+                method,
+                method6,
+                options,
+                options6,
+                comments,
+                comments6,
                 // the rest does not matter
                 name: _name,
                 interface_type: _interface_type,
-                auto: _auto,
+                autostart: _autostart,
                 active: _active,
-                cidr_v4: _cidr_v4,
-                cidr_v6: _cidr_v6,
-                gateway_v4: _gateway_v4,
-                gateway_v6: _gateway_v6,
+                cidr: _cidr,
+                cidr6: _cidr6,
+                gateway: _gateway,
+                gateway6: _gateway6,
                 mtu: _mtu,
                 bridge_ports: _bridge_ports,
+                bridge_vlan_aware: _bridge_vlan_aware,
                 bond_slaves: _bond_slaves,
             } => {
-                method_v4 == method_v6
-                    && comments_v4.is_none()
-                    && comments_v6.is_none()
-                    && options_v4.is_empty()
-                    && options_v6.is_empty()
+                method == method6
+                    && comments.is_none()
+                    && comments6.is_none()
+                    && options.is_empty()
+                    && options6.is_empty()
             }
         }
     }
@@ -243,36 +248,46 @@ impl Interface {
             }
         }
 
-        if self.method_v4.is_none() && self.method_v6.is_none() { return Ok(()); }
+        if self.method.is_none() && self.method6.is_none() { return Ok(()); }
 
-        if self.auto {
+        if self.autostart {
             writeln!(w, "auto {}", self.name)?;
         }
 
         if self.combine_entry() {
-            if let Some(method) = self.method_v4 {
+            if let Some(method) = self.method {
                 writeln!(w, "iface {} {}", self.name, method_to_str(method))?;
                 self.write_iface_attributes_v4(w, method)?;
                 self.write_iface_attributes_v6(w, method)?;
                 self.write_iface_attributes(w)?;
                 writeln!(w)?;
             }
-        } else {
-            if let Some(method) = self.method_v4 {
-                writeln!(w, "iface {} inet {}", self.name, method_to_str(method))?;
-                self.write_iface_attributes_v4(w, method)?;
-                self.write_iface_attributes(w)?;
-                writeln!(w)?;
+            return Ok(());
+        }
+                    
+        if let Some(method) = self.method {
+            writeln!(w, "iface {} inet {}", self.name, method_to_str(method))?;
+            self.write_iface_attributes_v4(w, method)?;
+            self.write_iface_attributes(w)?;
+            writeln!(w)?;
+        }
+        
+        if let Some(method6) = self.method6 {
+            let mut skip_v6 = false; // avoid empty inet6 manual entry
+            if self.method.is_some() && method6 == NetworkConfigMethod::Manual {
+                if self.comments6.is_none() && self.options6.is_empty() { skip_v6 = true; }
             }
-            if let Some(method) = self.method_v6 {
-                writeln!(w, "iface {} inet6 {}", self.name, method_to_str(method))?;
-                self.write_iface_attributes_v6(w, method)?;
-                if self.method_v4.is_none() { // only write common attributes once
+       
+            if !skip_v6 {
+                writeln!(w, "iface {} inet6 {}", self.name, method_to_str(method6))?;
+                self.write_iface_attributes_v6(w, method6)?;
+                if self.method.is_none() { // only write common attributes once
                     self.write_iface_attributes(w)?;
                 }
                 writeln!(w)?;
             }
         }
+
         Ok(())
     }
 }
index 47ddb02116d48f2f180b98b9d1c82899d861d346..d8a769b240d11da0326d13890e507ba3986ede1d 100644 (file)
@@ -23,6 +23,7 @@ pub enum Token {
     Attribute,
     MTU,
     BridgePorts,
+    BridgeVlanAware,
     BondSlaves,
     EOF,
 }
@@ -44,6 +45,8 @@ lazy_static! {
         map.insert("mtu", Token::MTU);
         map.insert("bridge-ports", Token::BridgePorts);
         map.insert("bridge_ports", Token::BridgePorts);
+        map.insert("bridge-vlan-aware", Token::BridgeVlanAware);
+        map.insert("bridge_vlan_aware", Token::BridgeVlanAware);
         map.insert("bond-slaves", Token::BondSlaves);
         map.insert("bond_slaves", Token::BondSlaves);
         map
index c4ab538a0be42bd26f9ac410fcd5a68060a9b058..f9f950b47bad3c5763bef62989506e465e10c8a7 100644 (file)
@@ -137,6 +137,21 @@ impl <R: BufRead> NetworkParser<R> {
         Ok(mtu)
     }
 
+    fn parse_yes_no(&mut self) -> Result<bool, Error> {
+        let text = self.next_text()?;
+        let value = match text.to_lowercase().as_str() {
+            "yes" => true,
+            "no" => false,
+            _ => {
+                bail!("unable to bool value '{}' - (expected yes/no)", text);
+            }
+        };
+
+        self.eat(Token::Newline)?;
+
+        Ok(value)
+    }
+
     fn parse_to_eol(&mut self) -> Result<String, Error> {
         let mut line = String::new();
         loop {
@@ -182,15 +197,15 @@ impl <R: BufRead> NetworkParser<R> {
                 Token::Comment => {
                     let comment = self.eat(Token::Comment)?;
                     if !address_family_v4 && address_family_v6 {
-                        let mut comments = interface.comments_v6.take().unwrap_or(String::new());
+                        let mut comments = interface.comments6.take().unwrap_or(String::new());
                         if !comments.is_empty() { comments.push('\n'); }
                         comments.push_str(&comment);
-                        interface.comments_v6 = Some(comments);
+                        interface.comments6 = Some(comments);
                     } else {
-                        let mut comments = interface.comments_v4.take().unwrap_or(String::new());
+                        let mut comments = interface.comments.take().unwrap_or(String::new());
                         if !comments.is_empty() { comments.push('\n'); }
                         comments.push_str(&comment);
-                        interface.comments_v4 = Some(comments);
+                        interface.comments = Some(comments);
                     }
                     self.eat(Token::Newline)?;
                     continue;
@@ -207,6 +222,11 @@ impl <R: BufRead> NetworkParser<R> {
                     let mtu = self.parse_iface_mtu()?;
                     interface.mtu = Some(mtu);
                 }
+                Token::BridgeVlanAware => {
+                    self.eat(Token::BridgeVlanAware)?;
+                    let bridge_vlan_aware = self.parse_yes_no()?;
+                    interface.bridge_vlan_aware = Some(bridge_vlan_aware);
+                }
                 Token::BridgePorts => {
                     self.eat(Token::BridgePorts)?;
                     let ports = self.parse_iface_list()?;
@@ -225,9 +245,9 @@ impl <R: BufRead> NetworkParser<R> {
                     let option = self.parse_to_eol()?;
                     if !option.is_empty() {
                         if !address_family_v4 && address_family_v6 {
-                            interface.options_v6.push(option);
+                            interface.options6.push(option);
                         } else {
-                            interface.options_v4.push(option);
+                            interface.options.push(option);
                         }
                    };
                  },
@@ -335,7 +355,7 @@ impl <R: BufRead> NetworkParser<R> {
 
         for iface in auto_flag.iter() {
             if let Some(interface) = config.interfaces.get_mut(iface) {
-                interface.auto = true;
+                interface.autostart = true;
             }
         }
 
@@ -349,13 +369,13 @@ impl <R: BufRead> NetworkParser<R> {
             for (iface, active) in existing_interfaces.iter()  {
                 if let Some(interface) = config.interfaces.get_mut(iface) {
                     interface.active = *active;
-                    if interface.interface_type == NetworkInterfaceType::Unknown {
-                        interface.interface_type = NetworkInterfaceType::Ethernet;
+                    if interface.interface_type == NetworkInterfaceType::Unknown && PHYSICAL_NIC_REGEX.is_match(iface) {
+                        interface.interface_type = NetworkInterfaceType::Eth;
                     }
                 } else if PHYSICAL_NIC_REGEX.is_match(iface) { // also add all physical NICs
                     let mut interface = Interface::new(iface.clone());
                     interface.set_method_v4(NetworkConfigMethod::Manual)?;
-                    interface.interface_type = NetworkInterfaceType::Ethernet;
+                    interface.interface_type = NetworkInterfaceType::Eth;
                     interface.active = *active;
                     config.interfaces.insert(interface.name.clone(), interface);
                     config.order.push(NetworkOrderEntry::Iface(iface.to_string()));
@@ -378,7 +398,7 @@ impl <R: BufRead> NetworkParser<R> {
                 continue;
             }
             if PHYSICAL_NIC_REGEX.is_match(name) {
-                interface.interface_type = NetworkInterfaceType::Vanished;
+                interface.interface_type = NetworkInterfaceType::Eth;
                 continue;
             }
         }
@@ -387,7 +407,7 @@ impl <R: BufRead> NetworkParser<R> {
             let mut interface = Interface::new(String::from("lo"));
             interface.set_method_v4(NetworkConfigMethod::Loopback)?;
             interface.interface_type = NetworkInterfaceType::Loopback;
-            interface.auto = true;
+            interface.autostart = true;
             config.interfaces.insert(interface.name.clone(), interface);
 
             // Note: insert 'lo' as first interface after initial comments
index 86a8b241c29a808e1dba382bb4f62136843936ee..3d9e0f6765416eb6bffd21c509821b6de5dc09fa 100644 (file)
@@ -30,7 +30,8 @@ Ext.define('PBS.SystemConfiguration', {
                    minHeight: 200,
                    title: gettext('Interfaces'),
                    xtype: 'proxmoxNodeNetworkView',
-                   types: ['bond'],
+                   showApplyBtn: true,
+                   types: ['bond', 'bridge', 'vlan'],
                    nodename: 'localhost'
                },
                {