mod common;
mod netbox;
mod netshot;
use anyhow::{Error, Result};
use flexi_logger::{Duplicate, LogTarget, Logger};
use std::collections::HashMap;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(
name = "netbox2netshot",
about = "Synchronization tool between netbox and netshot"
)]
struct Opt {
#[structopt(short, long, help = "Enable debug/verbose mode")]
debug: bool,
#[structopt(long, help = "The Netshot API URL", env)]
netshot_url: String,
#[structopt(long, help = "The Netshot token", env, hide_env_values = true)]
netshot_token: String,
#[structopt(long, help = "The domain ID to use when importing a new device", env)]
netshot_domain_id: u32,
#[structopt(long, help = "The Netbox API URL", env)]
netbox_url: String,
#[structopt(
long,
help = "The Netbox token",
env,
hide_env_values = true,
default_value = ""
)]
netbox_token: String,
#[structopt(
long,
default_value = "",
help = "The querystring to use to select the devices from netbox",
env
)]
netbox_devices_filter: String,
#[structopt(short, long, help = "Check mode, will not push any change to Netshot")]
check: bool,
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let opt = Opt::from_args();
let mut logging_level = "info";
let mut duplicate_level = Duplicate::Info;
if opt.debug {
logging_level = "debug";
duplicate_level = Duplicate::Debug;
}
Logger::with_str(logging_level)
.log_target(LogTarget::File)
.duplicate_to_stdout(duplicate_level)
.start()?;
log::info!("Logger initialized with level {}", logging_level);
log::debug!("CLI Parameters : {:#?}", opt);
let netbox_client = netbox::NetboxClient::new(opt.netbox_url, opt.netbox_token)?;
let _netbox_ping = netbox_client.ping().await?;
let netshot_client = netshot::NetshotClient::new(opt.netshot_url, opt.netshot_token)?;
let _netshot_ping = netshot_client.ping().await?;
log::info!("Getting devices list from Netshot");
let netshot_devices = netshot_client.get_devices().await?;
log::debug!("Building netshot devices hashmap");
let mut netshot_hashmap = HashMap::new();
for device in netshot_devices {
netshot_hashmap.insert(device.management_address.ip, device.name);
}
log::info!("Getting devices list from Netbox");
let netbox_devices = netbox_client
.get_devices(&opt.netbox_devices_filter)
.await?;
log::debug!("Building netbox devices hashmap");
let mut netbox_hashmap = HashMap::new();
for device in netbox_devices {
match device.primary_ip {
Some(x) => netbox_hashmap.insert(
String::from(x.address.split("/").next().unwrap()),
device.name.unwrap_or(device.id.to_string()),
),
None => {
log::warn!(
"Device {} is missing its primary IP address, skipping it",
device.name.unwrap_or(device.id.to_string())
);
continue;
}
};
}
log::debug!(
"Hashmaps: Netbox({}), Netshot({})",
netbox_hashmap.len(),
netshot_hashmap.len()
);
log::debug!("Comparing HashMaps");
let mut missing_devices: Vec<String> = Vec::new();
for device in netbox_hashmap {
match netshot_hashmap.get(&device.0) {
Some(x) => log::debug!("{}({}) is present on both", x, device.0),
None => {
log::debug!("{}({}) missing from Netshot", device.1, device.0);
missing_devices.push(device.0);
}
}
}
log::info!("Found {} devices missing on Netshot", missing_devices.len());
if !opt.check {
for device in missing_devices {
netshot_client
.register_device(&device, opt.netshot_domain_id)
.await?;
}
}
Ok(())
}