Просмотр исходного кода

dnetview: lilith nodes support implemented

aggstam 3 лет назад
Родитель
Сommit
e4838c4335

+ 3 - 0
bin/dnetview/dnetview_config.toml

@@ -6,12 +6,15 @@
 [[nodes]]
 name = "Node 1"
 rpc_url = "tcp://127.0.0.1:8000"
+node_type = "NORMAL"
 
 [[nodes]]
 name = "Node 2"
 rpc_url = "tcp://127.0.0.1:7777"
+node_type = "NORMAL"
 
 [[nodes]]
 name = "Node 3"
 rpc_url = "tcp://127.0.0.1:1234"
+node_type = "NORMAL"
 

+ 11 - 4
bin/dnetview/src/config.rs

@@ -2,13 +2,20 @@ use serde::{Deserialize, Serialize};
 
 pub const CONFIG_FILE_CONTENTS: &[u8] = include_bytes!("../dnetview_config.toml");
 
-#[derive(Clone, Serialize, Deserialize, Debug)]
+#[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct DnvConfig {
-    pub nodes: Vec<IrcNode>,
+    pub nodes: Vec<Node>,
 }
 
-#[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct IrcNode {
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct Node {
     pub name: String,
     pub rpc_url: String,
+    pub node_type: NodeType,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub enum NodeType {
+    LILITH,
+    NORMAL,
 }

+ 36 - 16
bin/dnetview/src/model.rs

@@ -19,6 +19,8 @@ pub enum Session {
 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
 pub enum SelectableObject {
     Node(NodeInfo),
+    Lilith(LilithInfo),
+    Network(NetworkInfo),
     Session(SessionInfo),
     Connect(ConnectInfo),
 }
@@ -57,8 +59,8 @@ impl NodeInfo {
         children: Vec<SessionInfo>,
         external_addr: Option<String>,
         is_offline: bool,
-    ) -> NodeInfo {
-        NodeInfo { id, name, state, children, external_addr, is_offline }
+    ) -> Self {
+        Self { id, name, state, children, external_addr, is_offline }
     }
 }
 
@@ -80,8 +82,8 @@ impl SessionInfo {
         parent: String,
         children: Vec<ConnectInfo>,
         accept_addr: Option<String>,
-    ) -> SessionInfo {
-        SessionInfo { id, name, is_empty, parent, children, accept_addr }
+    ) -> Self {
+        Self { id, name, is_empty, parent, children, accept_addr }
     }
 }
 
@@ -110,17 +112,35 @@ impl ConnectInfo {
         last_msg: String,
         last_status: String,
         remote_node_id: String,
-    ) -> ConnectInfo {
-        ConnectInfo {
-            id,
-            addr,
-            state,
-            parent,
-            msg_log,
-            is_empty,
-            last_msg,
-            last_status,
-            remote_node_id,
-        }
+    ) -> Self {
+        Self { id, addr, state, parent, msg_log, is_empty, last_msg, last_status, remote_node_id }
+    }
+}
+
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
+pub struct LilithInfo {
+    pub id: String,
+    pub name: String,
+    pub urls: Vec<String>,
+    pub networks: Vec<NetworkInfo>,
+}
+
+impl LilithInfo {
+    pub fn new(id: String, name: String, urls: Vec<String>, networks: Vec<NetworkInfo>) -> Self {
+        Self { id, name, urls, networks }
+    }
+}
+
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq)]
+pub struct NetworkInfo {
+    pub id: String,
+    pub name: String,
+    pub urls: Vec<String>,
+    pub nodes: Vec<String>,
+}
+
+impl NetworkInfo {
+    pub fn new(id: String, name: String, urls: Vec<String>, nodes: Vec<String>) -> Self {
+        Self { id, name, urls, nodes }
     }
 }

+ 90 - 45
bin/dnetview/src/parser.rs

@@ -9,9 +9,12 @@ use url::Url;
 use darkfi::util::NanoTimestamp;
 
 use crate::{
-    config::DnvConfig,
+    config::{DnvConfig, Node, NodeType},
     error::{DnetViewError, DnetViewResult},
-    model::{ConnectInfo, Model, NodeInfo, SelectableObject, Session, SessionInfo},
+    model::{
+        ConnectInfo, LilithInfo, Model, NetworkInfo, NodeInfo, SelectableObject, Session,
+        SessionInfo,
+    },
     rpc::RpcConnect,
     util::{is_empty_session, make_connect_id, make_empty_id, make_node_id, make_session_id},
 };
@@ -29,57 +32,74 @@ impl DataParser {
     pub async fn start_connect_slots(self: Arc<Self>, ex: Arc<Executor<'_>>) -> DnetViewResult<()> {
         debug!(target: "dnetview", "start_connect_slots() START");
         for node in &self.config.nodes {
-            let self2 = self.clone();
             debug!(target: "dnetview", "attempting to spawn...");
-            ex.clone().spawn(self2.try_connect(node.name.clone(), node.rpc_url.clone())).detach();
+            ex.clone().spawn(self.clone().try_connect(node.clone())).detach();
         }
         Ok(())
     }
 
-    async fn try_connect(
-        self: Arc<Self>,
-        node_name: String,
-        rpc_url: String,
-    ) -> DnetViewResult<()> {
+    async fn try_connect(self: Arc<Self>, node: Node) -> DnetViewResult<()> {
         debug!(target: "dnetview", "try_connect() START");
         loop {
-            info!("Attempting to poll {}, RPC URL: {}", node_name, rpc_url);
-            match RpcConnect::new(Url::parse(&rpc_url)?, node_name.clone()).await {
+            info!("Attempting to poll {}, RPC URL: {}", node.name, node.rpc_url);
+            // Parse node config and execute poll.
+            // On any failure, sleep and retry.
+            match RpcConnect::new(Url::parse(&node.rpc_url)?, node.name.clone()).await {
                 Ok(client) => {
-                    self.poll(client).await?;
+                    if let Err(e) = self.poll(&node, client).await {
+                        error!("Poll execution error: {:?}", e);
+                    }
                 }
                 Err(e) => {
-                    error!("{}", e);
-                    self.parse_offline(node_name.clone()).await?;
-                    crate::util::sleep(2000).await;
+                    error!("RPC client creation error: {:?}", e);
                 }
             }
+            self.parse_offline(node.name.clone()).await?;
+            crate::util::sleep(2000).await;
         }
     }
 
-    async fn poll(&self, client: RpcConnect) -> DnetViewResult<()> {
+    async fn poll(&self, node: &Node, client: RpcConnect) -> DnetViewResult<()> {
         loop {
-            match client.ping().await {
-                // TODO
-                Ok(_reply) => {}
-                Err(_e) => {}
+            // Ping the node to verify if its online.
+            if let Err(e) = client.ping().await {
+                return Err(DnetViewError::Darkfi(e))
             }
-            match client.get_info().await {
+
+            // Retrieve node info, based on its type
+            let response = match &node.node_type {
+                NodeType::LILITH => client.lilith_spawns().await,
+                NodeType::NORMAL => client.get_info().await,
+            };
+
+            // Parse response
+            match response {
                 Ok(reply) => {
-                    if reply.as_object().is_some() && !reply.as_object().unwrap().is_empty() {
-                        self.parse_data(reply.as_object().unwrap(), &client).await?;
-                    } else {
+                    if !reply.as_object().is_some() || reply.as_object().unwrap().is_empty() {
                         return Err(DnetViewError::EmptyRpcReply)
                     }
+
+                    match &node.node_type {
+                        NodeType::LILITH => {
+                            self.parse_lilith_data(
+                                reply.as_object().unwrap().clone(),
+                                node.name.clone(),
+                            )
+                            .await?
+                        }
+                        NodeType::NORMAL => {
+                            self.parse_data(reply.as_object().unwrap(), node.name.clone()).await?
+                        }
+                    };
                 }
-                Err(e) => {
-                    error!("{:?}", e);
-                    self.parse_offline(client.name.clone()).await?;
-                }
+                Err(e) => return Err(e),
             }
+
+            // Sleep until next poll
             crate::util::sleep(2000).await;
         }
     }
+
     async fn parse_offline(&self, node_name: String) -> DnetViewResult<()> {
         let name = "Offline".to_string();
         let session_type = Session::Offline;
@@ -113,26 +133,19 @@ impl DataParser {
 
         let accept_addr = None;
         let session_info =
-            SessionInfo::new(session_id, name, is_empty, parent.clone(), connects, accept_addr);
+            SessionInfo::new(session_id, name, is_empty, parent, connects, accept_addr);
         sessions.push(session_info);
 
-        let node = NodeInfo::new(
-            node_id.clone(),
-            node_name.to_string(),
-            state.clone(),
-            sessions.clone(),
-            None,
-            true,
-        );
+        let node = NodeInfo::new(node_id, node_name, state, sessions.clone(), None, true);
 
-        self.update_selectables(sessions, node.clone()).await?;
+        self.update_selectables(sessions, node).await?;
         Ok(())
     }
 
     async fn parse_data(
         &self,
         reply: &serde_json::Map<String, Value>,
-        client: &RpcConnect,
+        node_name: String,
     ) -> DnetViewResult<()> {
         let addr = &reply.get("external_addr");
         let inbound = &reply["session_inbound"];
@@ -142,8 +155,7 @@ impl DataParser {
 
         let mut sessions: Vec<SessionInfo> = Vec::new();
 
-        let node_name = &client.name;
-        let node_id = make_node_id(node_name)?;
+        let node_id = make_node_id(&node_name)?;
 
         let ext_addr = self.parse_external_addr(addr).await?;
         let in_session = self.parse_inbound(inbound, &node_id).await?;
@@ -155,16 +167,16 @@ impl DataParser {
         //sessions.push(man_session.clone());
 
         let node = NodeInfo::new(
-            node_id.clone(),
-            node_name.to_string(),
+            node_id,
+            node_name,
             state.as_str().unwrap().to_string(),
             sessions.clone(),
             ext_addr,
             false,
         );
 
-        self.update_selectables(sessions.clone(), node.clone()).await?;
-        self.update_msgs(sessions.clone()).await?;
+        self.update_selectables(sessions.clone(), node).await?;
+        self.update_msgs(sessions).await?;
 
         //debug!("IDS: {:?}", self.model.ids.lock().await);
         //debug!("INFOS: {:?}", self.model.nodes.lock().await);
@@ -172,6 +184,39 @@ impl DataParser {
         Ok(())
     }
 
+    async fn parse_lilith_data(
+        &self,
+        reply: serde_json::Map<String, Value>,
+        name: String,
+    ) -> DnetViewResult<()> {
+        let urls: Vec<String> = serde_json::from_value(reply.get("urls").unwrap().clone()).unwrap();
+        let spawns: Vec<serde_json::Map<String, Value>> =
+            serde_json::from_value(reply.get("spawns").unwrap().clone()).unwrap();
+
+        let mut networks = vec![];
+        for spawn in spawns {
+            let name = spawn.get("name").unwrap().as_str().unwrap().to_string();
+            let id = make_node_id(&name)?;
+            let urls: Vec<String> =
+                serde_json::from_value(spawn.get("urls").unwrap().clone()).unwrap();
+            let nodes: Vec<String> =
+                serde_json::from_value(spawn.get("hosts").unwrap().clone()).unwrap();
+            let network = NetworkInfo::new(id, name, urls, nodes);
+            networks.push(network);
+        }
+        let id = make_node_id(&name)?;
+        let lilith = LilithInfo::new(id.clone(), name, urls, networks);
+        let lilith_obj = SelectableObject::Lilith(lilith.clone());
+
+        self.model.selectables.lock().await.insert(id, lilith_obj);
+        for network in lilith.networks {
+            let network_obj = SelectableObject::Network(network.clone());
+            self.model.selectables.lock().await.insert(network.id, network_obj);
+        }
+
+        Ok(())
+    }
+
     async fn update_msgs(&self, sessions: Vec<SessionInfo>) -> DnetViewResult<()> {
         for session in sessions {
             for connection in session.children {

+ 12 - 1
bin/dnetview/src/rpc.rs

@@ -26,7 +26,7 @@ impl RpcConnect {
         self.rpc_client.request(req).await
     }
 
-    //--> {"jsonrpc": "2.0", "method": "poll", "params": [], "id": 42}
+    // --> {"jsonrpc": "2.0", "method": "poll", "params": [], "id": 42}
     // <-- {"jsonrpc": "2.0", "result": {"nodeID": [], "nodeinfo" [], "id": 42}
     pub async fn get_info(&self) -> DnetViewResult<Value> {
         let req = JsonRequest::new("get_info", json!([]));
@@ -35,4 +35,15 @@ impl RpcConnect {
             Err(e) => Err(DnetViewError::Darkfi(e)),
         }
     }
+
+    // Returns all lilith node spawned networks names with their node addresses.
+    // --> {"jsonrpc": "2.0", "method": "spawns", "params": [], "id": 42}
+    // <-- {"jsonrpc": "2.0", "result": "{spawns}", "id": 42}
+    pub async fn lilith_spawns(&self) -> DnetViewResult<Value> {
+        let req = JsonRequest::new("spawns", json!([]));
+        match self.rpc_client.request(req).await {
+            Ok(req) => Ok(req),
+            Err(e) => Err(DnetViewError::Darkfi(e)),
+        }
+    }
 }

+ 103 - 61
bin/dnetview/src/view.rs

@@ -70,28 +70,37 @@ impl<'a> View {
 
     fn make_ordered_list(&mut self) {
         for obj in self.selectables.values() {
-            if let SelectableObject::Node(node) = obj {
-                if node.is_offline {
+            match obj {
+                SelectableObject::Node(node) => {
                     if !self.ordered_list.iter().any(|i| i == &node.id) {
                         self.ordered_list.push(node.id.clone());
                     }
-                } else {
-                    if !self.ordered_list.iter().any(|i| i == &node.id) {
-                        self.ordered_list.push(node.id.clone());
-                    }
-                    for session in &node.children {
-                        if !session.is_empty {
-                            if !self.ordered_list.iter().any(|i| i == &session.id) {
-                                self.ordered_list.push(session.id.clone());
-                            }
-                            for connection in &session.children {
-                                if !self.ordered_list.iter().any(|i| i == &connection.id) {
-                                    self.ordered_list.push(connection.id.clone());
+                    if !node.is_offline {
+                        for session in &node.children {
+                            if !session.is_empty {
+                                if !self.ordered_list.iter().any(|i| i == &session.id) {
+                                    self.ordered_list.push(session.id.clone());
+                                }
+                                for connection in &session.children {
+                                    if !self.ordered_list.iter().any(|i| i == &connection.id) {
+                                        self.ordered_list.push(connection.id.clone());
+                                    }
                                 }
                             }
                         }
                     }
                 }
+                SelectableObject::Lilith(lilith) => {
+                    if !self.ordered_list.iter().any(|i| i == &lilith.id) {
+                        self.ordered_list.push(lilith.id.clone());
+                    }
+                    for network in &lilith.networks {
+                        if !self.ordered_list.iter().any(|i| i == &network.id) {
+                            self.ordered_list.push(network.id.clone());
+                        }
+                    }
+                }
+                _ => (),
             }
         }
         //debug!(target: "dnetview", "render_ids()::ordered_list: {:?}", self.ordered_list);
@@ -165,59 +174,74 @@ impl<'a> View {
         let mut nodes = Vec::new();
 
         for obj in self.selectables.values() {
-            if let SelectableObject::Node(node) = obj {
-                if node.is_offline {
-                    let style = Style::default().fg(Color::Blue).add_modifier(Modifier::ITALIC);
-                    let mut name = String::new();
-                    name.push_str(&node.name);
-                    name.push_str("(Offline)");
-                    let name_span = Span::styled(name, style);
-                    let lines = vec![Spans::from(name_span)];
-                    let names = ListItem::new(lines);
-                    nodes.push(names);
-                } else {
-                    let name_span = Span::raw(&node.name);
-                    let lines = vec![Spans::from(name_span)];
-                    let names = ListItem::new(lines);
-                    nodes.push(names);
-                    for session in &node.children {
-                        if !session.is_empty {
-                            let name = Span::styled(format!("    {}", session.name), style);
-                            let lines = vec![Spans::from(name)];
-                            let names = ListItem::new(lines);
-                            nodes.push(names);
-                            for connection in &session.children {
-                                let mut info = Vec::new();
-                                match connection.addr.as_str() {
-                                    "Null" => {
-                                        let style = Style::default()
-                                            .fg(Color::Blue)
-                                            .add_modifier(Modifier::ITALIC);
-                                        let name = Span::styled(
-                                            format!("        {} ", connection.addr),
-                                            style,
-                                        );
-                                        info.push(name);
-                                    }
-                                    addr => {
-                                        let name = Span::styled(
-                                            format!(
-                                                "        {} ({})",
-                                                addr, connection.remote_node_id
-                                            ),
-                                            style,
-                                        );
-                                        info.push(name);
-                                    }
-                                }
-
-                                let lines = vec![Spans::from(info)];
+            match obj {
+                SelectableObject::Node(node) => {
+                    if node.is_offline {
+                        let style = Style::default().fg(Color::Blue).add_modifier(Modifier::ITALIC);
+                        let mut name = String::new();
+                        name.push_str(&node.name);
+                        name.push_str("(Offline)");
+                        let name_span = Span::styled(name, style);
+                        let lines = vec![Spans::from(name_span)];
+                        let names = ListItem::new(lines);
+                        nodes.push(names);
+                    } else {
+                        let name_span = Span::raw(&node.name);
+                        let lines = vec![Spans::from(name_span)];
+                        let names = ListItem::new(lines);
+                        nodes.push(names);
+                        for session in &node.children {
+                            if !session.is_empty {
+                                let name = Span::styled(format!("    {}", session.name), style);
+                                let lines = vec![Spans::from(name)];
                                 let names = ListItem::new(lines);
                                 nodes.push(names);
+                                for connection in &session.children {
+                                    let mut info = Vec::new();
+                                    match connection.addr.as_str() {
+                                        "Null" => {
+                                            let style = Style::default()
+                                                .fg(Color::Blue)
+                                                .add_modifier(Modifier::ITALIC);
+                                            let name = Span::styled(
+                                                format!("        {} ", connection.addr),
+                                                style,
+                                            );
+                                            info.push(name);
+                                        }
+                                        addr => {
+                                            let name = Span::styled(
+                                                format!(
+                                                    "        {} ({})",
+                                                    addr, connection.remote_node_id
+                                                ),
+                                                style,
+                                            );
+                                            info.push(name);
+                                        }
+                                    }
+
+                                    let lines = vec![Spans::from(info)];
+                                    let names = ListItem::new(lines);
+                                    nodes.push(names);
+                                }
                             }
                         }
                     }
                 }
+                SelectableObject::Lilith(lilith) => {
+                    let name_span = Span::raw(&lilith.name);
+                    let lines = vec![Spans::from(name_span)];
+                    let names = ListItem::new(lines);
+                    nodes.push(names);
+                    for network in &lilith.networks {
+                        let name = Span::styled(format!("    {}", network.name), style);
+                        let lines = vec![Spans::from(name)];
+                        let names = ListItem::new(lines);
+                        nodes.push(names);
+                    }
+                }
+                _ => (),
             }
         }
         let nodes =
@@ -279,6 +303,7 @@ impl<'a> View {
             match info {
                 Some(SelectableObject::Node(node)) => {
                     //debug!(target: "dnetview", "render_info()::SelectableObject::Node");
+                    lines.push(Spans::from(Span::styled("Type: Normal", style)));
                     match &node.external_addr {
                         Some(addr) => {
                             let node_info = Span::styled(format!("External addr: {}", addr), style);
@@ -309,6 +334,23 @@ impl<'a> View {
                     let text = self.parse_msg_list(connect.id.clone())?;
                     f.render_stateful_widget(text, slice[1], &mut self.msg_list.state);
                 }
+                Some(SelectableObject::Lilith(lilith)) => {
+                    lines.push(Spans::from(Span::styled("Type: Lilith", style)));
+                    lines.push(Spans::from(Span::styled("URLs:", style)));
+                    for url in &lilith.urls {
+                        lines.push(Spans::from(Span::styled(format!("   {}", url), style)));
+                    }
+                }
+                Some(SelectableObject::Network(network)) => {
+                    lines.push(Spans::from(Span::styled("URLs:", style)));
+                    for url in &network.urls {
+                        lines.push(Spans::from(Span::styled(format!("   {}", url), style)));
+                    }
+                    lines.push(Spans::from(Span::styled("Hosts:", style)));
+                    for node in &network.nodes {
+                        lines.push(Spans::from(Span::styled(format!("   {}", node), style)));
+                    }
+                }
                 None => return Err(DnetViewError::NotSelectableObject),
             }
         }

+ 33 - 5
bin/lilith/src/main.rs

@@ -4,7 +4,6 @@ use async_trait::async_trait;
 use futures_lite::future;
 use log::{error, info};
 use serde_json::{json, Value};
-use std::collections::HashMap;
 use structopt_toml::StructOptToml;
 use url::Url;
 
@@ -42,10 +41,26 @@ impl Spawn {
     async fn addresses(&self) -> Vec<String> {
         self.p2p.hosts().load_all().await.iter().map(|addr| addr.to_string()).collect()
     }
+
+    pub async fn info(&self) -> serde_json::Value {
+        // Building addr_vec string
+        let mut addr_vec = vec![];
+        for addr in &self.p2p.settings().inbound {
+            addr_vec.push(addr.as_ref().to_string());
+        }
+
+        json!({
+            "name": self.name.clone(),
+            "urls": addr_vec,
+            "hosts": self.addresses().await,
+        })
+    }
 }
 
 /// Struct representing the daemon.
 pub struct Lilith {
+    /// Configured urls
+    urls: Vec<Url>,
     /// Spawned networks
     spawns: Vec<Spawn>,
 }
@@ -56,11 +71,24 @@ impl Lilith {
     // --> {"jsonrpc": "2.0", "method": "spawns", "params": [], "id": 42}
     // <-- {"jsonrpc": "2.0", "result": "{spawns}", "id": 42}
     async fn spawns(&self, id: Value, _params: &[Value]) -> JsonResult {
-        let mut spawns: HashMap<String, Vec<String>> = HashMap::default();
+        // Building urls string
+        let mut urls_vec = vec![];
+        for url in &self.urls {
+            urls_vec.push(url.as_ref().to_string());
+        }
+
+        // Gathering spawns info
+        let mut spawns = vec![];
         for spawn in &self.spawns {
-            spawns.insert(spawn.name.clone(), spawn.addresses().await);
+            spawns.push(spawn.info().await);
         }
-        JsonResponse::new(json!(spawns), id).into()
+
+        // Generating json
+        let json = json!({
+            "urls": urls_vec,
+            "spawns": spawns,
+        });
+        JsonResponse::new(json, id).into()
     }
 
     // RPCAPI:
@@ -171,7 +199,7 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
         }
     }
 
-    let lilith = Lilith { spawns };
+    let lilith = Lilith { urls, spawns };
     let lilith = Arc::new(lilith);
 
     // JSON-RPC server