Skip to content

Commit

Permalink
Merge pull request eclipse-iceoryx#380 from orecham/iox2-98-add-iox2-…
Browse files Browse the repository at this point in the history
…nodes-cli

[eclipse-iceoryx#98] add `iox2 nodes` cli
  • Loading branch information
orecham authored Sep 20, 2024
2 parents f974a6b + 37ee56c commit 0d14d50
Show file tree
Hide file tree
Showing 17 changed files with 651 additions and 231 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ members = [

"iceoryx2-cli/iox2",
"iceoryx2-cli/iox2-introspect",
"iceoryx2-cli/iox2-nodes",
"iceoryx2-cli/iox2-processes",
"iceoryx2-cli/iox2-pub",
"iceoryx2-cli/iox2-rpc",
Expand Down
23 changes: 23 additions & 0 deletions iceoryx2-cli/iox2-nodes/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "iox2-nodes"
description = "CLI for managing iceoryx2 nodes"
categories = { workspace = true }
edition = { workspace = true }
homepage = { workspace = true }
keywords = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
rust-version = { workspace = true }
version = { workspace = true }

[dependencies]
iceoryx2 = { workspace = true }
iceoryx2-bb-log = { workspace = true }
iceoryx2-cli-utils = { workspace = true }
iceoryx2-pal-posix = {workspace = true}

anyhow = { workspace = true }
better-panic = { workspace = true }
clap = { workspace = true }
human-panic = { workspace = true }
serde = { workspace = true }
106 changes: 106 additions & 0 deletions iceoryx2-cli/iox2-nodes/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) 2024 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache Software License 2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
// which is available at https://opensource.org/licenses/MIT.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

use std::str::FromStr;

use clap::Args;
use clap::Parser;
use clap::Subcommand;
use clap::ValueEnum;

use iceoryx2_cli_utils::help_template;
use iceoryx2_cli_utils::Format;
use iceoryx2_pal_posix::posix::pid_t;

#[derive(Parser)]
#[command(
name = "iox2-nodes",
about = "Query information about iceoryx2 nodes",
long_about = None,
version = env!("CARGO_PKG_VERSION"),
disable_help_subcommand = true,
arg_required_else_help = false,
help_template = help_template("iox2-nodes", false),
)]
pub struct Cli {
#[clap(subcommand)]
pub action: Option<Action>,

#[clap(long, short = 'f', value_enum, global = true, value_enum, default_value_t = Format::Ron)]
pub format: Format,
}

#[derive(Clone, Debug)]
pub enum NodeIdentifier {
Name(String),
Id(String),
Pid(pid_t),
}

fn is_valid_hex(s: &str) -> bool {
s.len() == 32 && s.chars().all(|c| c.is_ascii_hexdigit())
}

impl FromStr for NodeIdentifier {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(pid) = s.parse::<pid_t>() {
Ok(NodeIdentifier::Pid(pid))
} else if is_valid_hex(s) {
Ok(NodeIdentifier::Id(s.to_string()))
} else {
Ok(NodeIdentifier::Name(s.to_string()))
}
}
}

#[derive(Debug, Clone, ValueEnum)]
#[clap(rename_all = "PascalCase")]
#[derive(Default)]
pub enum StateFilter {
Alive,
Dead,
Inaccessible,
Undefined,
#[default]
All,
}

#[derive(Debug, Clone, Args)]
pub struct OutputFilter {
#[clap(short, long, value_enum, default_value_t = StateFilter::All)]
pub state: StateFilter,
}

#[derive(Args)]
pub struct ListOptions {
#[command(flatten)]
pub filter: OutputFilter,
}

#[derive(Args)]
pub struct DetailsOptions {
#[clap(help = "Name, ID or PID of the node")]
pub node: NodeIdentifier,

#[command(flatten)]
pub filter: OutputFilter,
}

#[derive(Subcommand)]
pub enum Action {
#[clap(about = "List all nodes")]
List(ListOptions),
#[clap(about = "Show details of a node")]
Details(DetailsOptions),
}
64 changes: 64 additions & 0 deletions iceoryx2-cli/iox2-nodes/src/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) 2024 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache Software License 2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
// which is available at https://opensource.org/licenses/MIT.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

use anyhow::{Context, Error, Result};
use iceoryx2::prelude::*;
use iceoryx2_cli_utils::output::NodeDescription;
use iceoryx2_cli_utils::output::NodeDescriptor;
use iceoryx2_cli_utils::output::NodeList;
use iceoryx2_cli_utils::Filter;
use iceoryx2_cli_utils::Format;

use crate::cli::NodeIdentifier;
use crate::cli::OutputFilter;

pub fn list(filter: OutputFilter, format: Format) -> Result<()> {
let mut nodes = Vec::<NodeDescriptor>::new();
Node::<ipc::Service>::list(Config::global_config(), |node| {
if filter.matches(&node) {
nodes.push(NodeDescriptor::from(&node));
}
CallbackProgression::Continue
})
.context("failed to retrieve nodes")?;

print!(
"{}",
format.as_string(&NodeList {
num: nodes.len(),
details: nodes
})?
);

Ok(())
}

pub fn details(identifier: NodeIdentifier, filter: OutputFilter, format: Format) -> Result<()> {
let mut error: Option<Error> = None;

Node::<ipc::Service>::list(Config::global_config(), |node| {
if identifier.matches(&node) && filter.matches(&node) {
match format.as_string(&NodeDescription::from(&node)) {
Ok(output) => {
print!("{}", output);
}
Err(e) => {
error = Some(e);
}
}
}
CallbackProgression::Continue
})
.context("failed to retrieve nodes")?;

Ok(())
}
71 changes: 71 additions & 0 deletions iceoryx2-cli/iox2-nodes/src/filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2024 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache Software License 2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
// which is available at https://opensource.org/licenses/MIT.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::cli::NodeIdentifier;
use crate::cli::OutputFilter;
use crate::cli::StateFilter;
use iceoryx2::node::NodeState;
use iceoryx2::node::NodeView;
use iceoryx2::service::ipc::Service;
use iceoryx2_cli_utils::output::NodeIdString;
use iceoryx2_cli_utils::Filter;

impl Filter<NodeState<Service>> for NodeIdentifier {
fn matches(&self, node: &NodeState<Service>) -> bool {
match self {
NodeIdentifier::Name(ref name) => match node {
NodeState::Alive(view) => view
.details()
.as_ref()
.map(|details| details.name().as_str() == name)
.unwrap_or(false),
NodeState::Dead(view) => view
.details()
.as_ref()
.map(|details| details.name().as_str() == name)
.unwrap_or(false),
NodeState::Inaccessible(_) | NodeState::Undefined(_) => false,
},
NodeIdentifier::Id(ref id) => match node {
NodeState::Alive(view) => NodeIdString::from(view.id()) == **id,
NodeState::Dead(view) => NodeIdString::from(view.id()) == **id,
NodeState::Inaccessible(node_id) => NodeIdString::from(node_id) == **id,
NodeState::Undefined(node_id) => NodeIdString::from(node_id) == **id,
},
NodeIdentifier::Pid(pid) => match node {
NodeState::Alive(view) => view.id().pid().value() == *pid,
NodeState::Dead(view) => view.id().pid().value() == *pid,
NodeState::Inaccessible(node_id) => node_id.pid().value() == *pid,
NodeState::Undefined(node_id) => node_id.pid().value() == *pid,
},
}
}
}

impl Filter<NodeState<Service>> for StateFilter {
fn matches(&self, node: &NodeState<Service>) -> bool {
matches!(
(self, node),
(StateFilter::Alive, NodeState::Alive(_))
| (StateFilter::Dead, NodeState::Dead(_))
| (StateFilter::Inaccessible, NodeState::Inaccessible(_))
| (StateFilter::Undefined, NodeState::Undefined(_))
| (StateFilter::All, _)
)
}
}

impl Filter<NodeState<Service>> for OutputFilter {
fn matches(&self, node: &NodeState<Service>) -> bool {
self.state.matches(node)
}
}
68 changes: 68 additions & 0 deletions iceoryx2-cli/iox2-nodes/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2024 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache Software License 2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
// which is available at https://opensource.org/licenses/MIT.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

mod cli;
mod commands;
mod filter;

use clap::CommandFactory;
use clap::Parser;
use cli::Action;
use cli::Cli;
use iceoryx2_bb_log::{set_log_level, LogLevel};

#[cfg(not(debug_assertions))]
use human_panic::setup_panic;
#[cfg(debug_assertions)]
extern crate better_panic;

fn main() {
#[cfg(not(debug_assertions))]
{
setup_panic!();
}
#[cfg(debug_assertions)]
{
better_panic::Settings::debug()
.most_recent_first(false)
.lineno_suffix(true)
.verbosity(better_panic::Verbosity::Full)
.install();
}

set_log_level(LogLevel::Warn);

match Cli::try_parse() {
Ok(cli) => {
if let Some(action) = cli.action {
match action {
Action::List(options) => {
if let Err(e) = commands::list(options.filter, cli.format) {
eprintln!("Failed to list nodes: {}", e);
}
}
Action::Details(options) => {
if let Err(e) = commands::details(options.node, options.filter, cli.format)
{
eprintln!("Failed to retrieve node details: {}", e);
}
}
}
} else {
Cli::command().print_help().expect("Failed to print help");
}
}
Err(e) => {
eprintln!("{}", e);
}
}
}
2 changes: 0 additions & 2 deletions iceoryx2-cli/iox2-services/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ repository = { workspace = true }
rust-version = { workspace = true }
version = { workspace = true }

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
iceoryx2 = { workspace = true }
iceoryx2-bb-log = { workspace = true }
Expand Down
Loading

0 comments on commit 0d14d50

Please sign in to comment.