Skip to content

Commit

Permalink
Implement machine get request and search (#51)
Browse files Browse the repository at this point in the history
* Add thanix_client dependency

* Fix thanix_client crate

* Add get_machines method to publisher module

* implement get machines function

* implement machine search for post/patch distinguation

* Distinguish between VM and phyiscal device
  • Loading branch information
Christopher Hock authored Feb 15, 2024
1 parent 5f71a81 commit d552d0f
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 118 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ Cargo.lock


# Added by cargo

/target

# output.txt file used for testing
output.txt
output.json
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ reqwest = { version="0.11.22", features = ["blocking", "json"] }
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.108"
toml = "0.7.6"
# This dependency represents the NetBox API client you want to use.
# This one here is a reference client generated by using the api schema from
# NetBox's public testing instance @ https://demo.netbox.dev/api/schema.
#
# This may not work in your environment. You can get your schema by visiting
# https://your.netbox-instance.com/api/schema.
# The yaml file will be downloaded and you can generate your client by using https://github.com/The-Nazara-Project/Thanix.
thanix_client = "0.7.0"
# Uncomment this line if you are using a custom thanix client implementation.
# Change the path to be relative to this Cargo.toml file.
# thanix_client = { path = "path/to/your/client/" }

[dev-dependencies]
mockall = "0.11.4"
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Here is an example for passing these parameters on using the CLI:

#### Configuring via `nbs-config.toml`file.

Alternatively, you can provide the configuration parameters in a config file named `nbs-config.toml`, located in the same
Alternatively, you can provide the configuration parameters in a config file named `nazara-config.toml`, located in the same
directory as the executable file. Here is an example how the config file should look like:

```toml
Expand All @@ -61,7 +61,7 @@ netbox_api_token = "$API_TOKEN"
netbox_uri = "$API_URI"
```

Aside from the NetBox system parameters, configuration via the `.nbs-config.toml` also allows you to add certain
Aside from the NetBox system parameters, configuration via the `.nazara-config.toml` also allows you to add certain
custom fields to your system information that cannot be automatically selected. A great example would be the
`System Location` entry. To specify that, simply add the parameter under the `[system]` block in your configuration file.

Expand Down
42 changes: 21 additions & 21 deletions src/collectors/dmi_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ use std::{
/// * cpu_information: [`CpuInformation`](struct.CpuInformation) - Information about the processor(s).
#[derive(Serialize, Debug)]
pub struct DmiInformation {
system_information: SystemInformation,
chassis_information: ChassisInformation,
cpu_information: CpuInformation,
pub system_information: SystemInformation,
pub chassis_information: ChassisInformation,
pub cpu_information: CpuInformation,
}

/// ## SystemInformation
Expand All @@ -44,12 +44,12 @@ pub struct DmiInformation {
/// * serial: `String` - The serial number of the machine.
/// * is_virtual: `bool` - Whether the machine is a virtual machine or not.
#[derive(Serialize, Debug)]
struct SystemInformation {
vendor: String,
model: String,
uuid: String,
serial: String,
is_virtual: bool,
pub struct SystemInformation {
pub vendor: String,
pub model: String,
pub uuid: String,
pub serial: String,
pub is_virtual: bool,
}

/// ## Chassis Information
Expand All @@ -62,10 +62,10 @@ struct SystemInformation {
/// * asset: `String`- Type of asset.
/// * chassis_serial: `Serial` - Serial number of the chassis.
#[derive(Serialize, Debug)]
struct ChassisInformation {
chassis_type: String,
asset: String,
chassis_serial: String,
pub struct ChassisInformation {
pub chassis_type: String,
pub asset: String,
pub chassis_serial: String,
}

/// ## CpuInformation
Expand All @@ -82,14 +82,14 @@ struct ChassisInformation {
/// * voltage: `String` - The voltage the CPU runs at.
/// * status: `String` - Shows if the socket is enabled/disabled and populated/empty.
#[derive(Serialize, Debug)]
struct CpuInformation {
version: String,
core_count: String,
cores_enabled: String,
thread_count: String,
max_speed: String,
voltage: String,
status: String,
pub struct CpuInformation {
pub version: String,
pub core_count: String,
pub cores_enabled: String,
pub thread_count: String,
pub max_speed: String,
pub voltage: String,
pub status: String,
}

/// List of possible system parameters to collect dmi information from.
Expand Down
10 changes: 5 additions & 5 deletions src/configuration/config_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! location = ""
//! ```
//!
//! It will be created at ` ~/.nbs-config.toml`.
//! It will be created at ` ~/.nazara-config.toml`.
use serde::Deserialize;
use std::fs::File;
Expand All @@ -34,7 +34,7 @@ pub struct ConfigData {

/// Set up configuration
///
/// This function reads the configuration file located at `~/.nbs-config.toml`. If no file can be found, a warning is
/// This function reads the configuration file located at `~/.nazara-config.toml`. If no file can be found, a warning is
/// displayed to the user and a default config file is written.
/// If command line arguments are given, the parameters read from the file will be overwritten.
///
Expand Down Expand Up @@ -158,7 +158,7 @@ fn file_exists(path: &Path) -> bool {
///
/// # Returns
///
/// * `config_file_path: PathBuf` - The directory the config file is located (~/.nbs-config.toml)
/// * `config_file_path: PathBuf` - The directory the config file is located (~/.nazara-config.toml)
///
/// # Panics
///
Expand All @@ -171,7 +171,7 @@ fn get_config_dir() -> PathBuf {
}
};

let config_file_path: PathBuf = Path::new(&home_dir).join(".nbs-config.toml");
let config_file_path: PathBuf = Path::new(&home_dir).join(".nazara-config.toml");

return config_file_path;
}
Expand All @@ -185,7 +185,7 @@ impl ConfigData {
///
/// # Panics
///
/// If it is not able to create a new config file at `~/.nbs-config.toml` or if it cannot write the defaults
/// If it is not able to create a new config file at `~/.nazara-config.toml` or if it cannot write the defaults
/// to the file, the function panics as this is the main method of configuring the program.
fn initialize_config_file() -> std::io::Result<()> {
// Create new toml table
Expand Down
52 changes: 43 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,30 @@ pub mod configuration;
pub mod publisher;

use clap::Parser;
use collectors::{dmi_collector, network_collector};
use collectors::{
dmi_collector::{self, DmiInformation},
network_collector::{self, NetworkInformation},
};
use configuration::config_parser::set_up_configuration;
use publisher::publisher::*;
use reqwest::blocking::Client;
use std::process;
use thanix_client::util::ThanixClient;

use crate::publisher::{api_client::NetBoxClient, publisher_exceptions::NetBoxApiError};
/// The Machine struct
///
/// This struct represents your machine.
/// It holds all information collected and allows for sharing this
/// information between Nazara's modules.
///
/// It is used in places where it is necessary to have access to various
/// pieces of collected information from a single source of truth.
/// It will also be translated into the proper API type by the translator.
pub struct Machine {
pub name: Option<String>,
pub dmi_information: DmiInformation,
pub network_information: Vec<NetworkInformation>,
}

/// The arguments that Nazara expects to get via the cli.
///
Expand Down Expand Up @@ -74,7 +92,7 @@ fn main() {
let config = match set_up_configuration(
args.uri,
args.token,
args.name,
args.name.clone(),
args.location,
args.device_role,
) {
Expand All @@ -85,18 +103,34 @@ fn main() {
}
};

Publisher::probe(&config.get_netbox_uri(), &config.get_api_token());

// println!("Configuration: \n{:#?}", config);
let client: ThanixClient = ThanixClient {
base_url: config.get_netbox_uri().to_string(),
authentication_token: config.get_api_token().to_string(),
client: Client::new(),
};

// println!("Uri: {}\nToken: {}", args.uri.clone().unwrap(), args.token.clone().unwrap());
match probe(&client) {
Ok(()) => {}
Err(err) => println!("{}", err),
};

let dmi_information: dmi_collector::DmiInformation = dmi_collector::construct_dmi_information();

// println!("{:#?}", dmi_information);

let network_information = network_collector::construct_network_information().unwrap();

let machine: Machine = Machine {
name: args.name,
dmi_information,
network_information,
};

// Passing a name in any way is mandatory for a virtual machine
if machine.dmi_information.system_information.is_virtual && machine.name.is_none() {
panic!("[FATAL] No name has been provided for this virtual machine! Providing a name as search parameter is mandatory for virtual machines.")
}

let _ = register_machine(&client, machine);

// println!("{:#?}", network_information);

// let system_information: SystemData = SystemData {
Expand Down
3 changes: 2 additions & 1 deletion src/publisher.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod api_client; // TODO make this non-public
pub mod publisher;
pub mod publisher_exceptions; // TODO make this non-public
pub mod publisher_exceptions;
pub mod translator; // make this not public // TODO make this non-public
96 changes: 31 additions & 65 deletions src/publisher/api_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,16 @@
//! This client allows us to get a list of all machines and create or update machines or VMs.
//! We use the `reqwest` crate for blocking HTTP requests and `serde` together with `serde_json` to serialize and
//! deserialize our data.
extern crate thanix_client;

use reqwest::{blocking::Client, Error as ReqwestError};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json;
use thanix_client::util::ThanixClient;

use crate::collectors::{dmi_collector::DmiInformation, network_collector::NetworkInformation};

use super::publisher_exceptions;

/// NetBox API client.
///
/// This client encapsulates the base URL and API token necessary for authenticating with the NetBox API
/// as well as a `reqwest` client for making HTTP requests.
///
/// # Members
///
/// * base_url: `String` - The base url of your NetBox instance. Read from the config file or command line. E.g: `https://netbox.company.de`
/// * api_token: `String` - The authentication token for the NetBox API.
/// * client: `reqwest::Client` - The Client object from the `reqwest` crate.
pub struct NetBoxClient {
pub base_url: String,
pub api_token: String,
client: Client,
}
use super::{publisher, publisher_exceptions};

/// Represents the combined system information required to create a new machine or update an existing one.
///
Expand Down Expand Up @@ -57,57 +44,36 @@ pub struct CreateMachinePayload {
pub system_information: SystemData,
}

impl NetBoxClient {
/// Constructs a new `NetBoxClient` with the given base URL and API token.
///
/// This constructor initializes the internal reqwest client and stores the provided base URL or API token read
/// from the command line or configuration file.
///
/// # Arguments
///
/// * `base_url: &str` - A string slice that holds the base URL of the NetBox API.
/// * `api_token: &str` - A string slice that holds the API token for authenticating with the NetBox API
///
/// # Returns
///
/// A new instance of the `NetBoxClient`
pub fn new(base_url: &str, api_token: &str) -> Self {
let client: Client = Client::new();
NetBoxClient {
base_url: base_url.to_string(),
api_token: api_token.to_string(),
client,
}
}

/// Tests connection to the NetBox API.
///
/// This method attempts to retrieve the API root from the NetBox API to affirm that it is reachable.
///
/// # Returns
///
/// Returns `Ok(())` if the connection to the API is successful.
/// Returns an `Err` with `publisher_exceptions::NetBoxApiError` if the connection fails.
pub fn test_connection(&self) -> Result<(), publisher_exceptions::NetBoxApiError> {
let url: String = format!("{}/api/", self.base_url);
/// Tests connection to the NetBox API.
///
/// This method attempts to retrieve the API root from the NetBox API to affirm that it is reachable.
///
/// # Returns
///
/// Returns `Ok(())` if the connection to the API is successful.
/// Returns an `Err` with `publisher_exceptions::NetBoxApiError` if the connection fails.
pub fn test_connection(client: &ThanixClient) -> Result<(), publisher_exceptions::NetBoxApiError> {
let url: String = format!("{}/api/", client.base_url);

let response: Result<reqwest::blocking::Response, ReqwestError> = self
.client
.get(&url)
.header("Authorization", format!("Token {}", self.api_token))
.send();
let response: Result<reqwest::blocking::Response, ReqwestError> = client
.client
.get(&url)
.header(
"Authorization",
format!("Token {}", client.authentication_token),
)
.send();

match response {
Ok(resp) => {
if resp.status().is_success() {
Ok(())
} else {
Err(publisher_exceptions::NetBoxApiError::Reqwest(
resp.error_for_status().unwrap_err(),
))
}
match response {
Ok(resp) => {
if resp.status().is_success() {
Ok(())
} else {
Err(publisher_exceptions::NetBoxApiError::Reqwest(
resp.error_for_status().unwrap_err(),
))
}
Err(e) => Err(publisher_exceptions::NetBoxApiError::Reqwest(e)),
}
Err(e) => Err(publisher_exceptions::NetBoxApiError::Reqwest(e)),
}
}
Loading

0 comments on commit d552d0f

Please sign in to comment.