add option to run brocade commands in dry-run
This commit is contained in:
parent
7b6ac6641a
commit
f2f55d98d4
@ -1,4 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
@ -6,8 +7,11 @@ use std::{
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use harmony_types::net::{IpAddress, MacAddress};
|
use harmony_types::net::{IpAddress, MacAddress};
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use russh::client::{Handle, Handler};
|
use russh::{
|
||||||
use russh_keys::key;
|
client::{Handle, Handler},
|
||||||
|
kex::DH_G1_SHA1,
|
||||||
|
};
|
||||||
|
use russh_keys::key::{self, SSH_RSA};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||||
@ -19,6 +23,30 @@ pub struct MacAddressEntry {
|
|||||||
|
|
||||||
pub struct BrocadeClient {
|
pub struct BrocadeClient {
|
||||||
client: Handle<Client>,
|
client: Handle<Client>,
|
||||||
|
options: BrocadeOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
pub struct BrocadeOptions {
|
||||||
|
pub dry_run: bool,
|
||||||
|
pub ssh: SshOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SshOptions {
|
||||||
|
pub preferred_algorithms: russh::Preferred,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SshOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
preferred_algorithms: russh::Preferred {
|
||||||
|
kex: Cow::Borrowed(&[DH_G1_SHA1]),
|
||||||
|
key: Cow::Borrowed(&[SSH_RSA]),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BrocadeClient {
|
impl BrocadeClient {
|
||||||
@ -26,14 +54,25 @@ impl BrocadeClient {
|
|||||||
ip_addresses: &[IpAddress],
|
ip_addresses: &[IpAddress],
|
||||||
username: &str,
|
username: &str,
|
||||||
password: &str,
|
password: &str,
|
||||||
|
options: Option<BrocadeOptions>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let ip = ip_addresses[0]; // FIXME: Find a better way to get master switch IP address
|
if ip_addresses.is_empty() {
|
||||||
|
return Err(Error::ConfigurationError(
|
||||||
|
"No IP addresses provided".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let config = russh::client::Config::default();
|
let ip = ip_addresses[0]; // FIXME: Find a better way to get master switch IP address
|
||||||
|
let options = options.unwrap_or_default();
|
||||||
|
|
||||||
|
let config = russh::client::Config {
|
||||||
|
preferred: options.ssh.preferred_algorithms.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
let mut client = russh::client::connect(Arc::new(config), (ip, 22), Client {}).await?;
|
let mut client = russh::client::connect(Arc::new(config), (ip, 22), Client {}).await?;
|
||||||
|
|
||||||
match client.authenticate_password(username, password).await? {
|
match client.authenticate_password(username, password).await? {
|
||||||
true => Ok(Self { client }),
|
true => Ok(Self { client, options }),
|
||||||
false => Err(Error::AuthenticationError(
|
false => Err(Error::AuthenticationError(
|
||||||
"ssh authentication failed".to_string(),
|
"ssh authentication failed".to_string(),
|
||||||
)),
|
)),
|
||||||
@ -41,7 +80,7 @@ impl BrocadeClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn show_mac_address_table(&self) -> Result<Vec<MacAddressEntry>, Error> {
|
pub async fn show_mac_address_table(&self) -> Result<Vec<MacAddressEntry>, Error> {
|
||||||
let output = self.run_command("show mac-address-table").await?;
|
let output = self.run_command("show mac-address").await?;
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
|
|
||||||
// The Brocade output usually has a header and then one entry per line.
|
// The Brocade output usually has a header and then one entry per line.
|
||||||
@ -104,7 +143,7 @@ impl BrocadeClient {
|
|||||||
pub async fn find_available_channel_id(&self) -> Result<u8, Error> {
|
pub async fn find_available_channel_id(&self) -> Result<u8, Error> {
|
||||||
debug!("[Brocade] Finding next available channel id...");
|
debug!("[Brocade] Finding next available channel id...");
|
||||||
|
|
||||||
let output = self.run_command("show port-channel summary").await?;
|
let output = self.run_command("show lag").await?;
|
||||||
let mut used_ids = Vec::new();
|
let mut used_ids = Vec::new();
|
||||||
|
|
||||||
// Sample output line: "3 Po3(SU) LACP Eth Yes 128/128 active "
|
// Sample output line: "3 Po3(SU) LACP Eth Yes 128/128 active "
|
||||||
@ -121,7 +160,7 @@ impl BrocadeClient {
|
|||||||
// Sort the used IDs to find the next available number.
|
// Sort the used IDs to find the next available number.
|
||||||
used_ids.sort();
|
used_ids.sort();
|
||||||
|
|
||||||
let mut next_id = 1;
|
let mut next_id = 0;
|
||||||
for &id in &used_ids {
|
for &id in &used_ids {
|
||||||
if id == next_id {
|
if id == next_id {
|
||||||
next_id += 1;
|
next_id += 1;
|
||||||
@ -136,6 +175,11 @@ impl BrocadeClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn run_command(&self, command: &str) -> Result<String, Error> {
|
async fn run_command(&self, command: &str) -> Result<String, Error> {
|
||||||
|
if !command.starts_with("show") && self.options.dry_run {
|
||||||
|
info!("[Brocade] Dry-run mode enabled, skipping command: {command}");
|
||||||
|
return Ok("".into());
|
||||||
|
}
|
||||||
|
|
||||||
debug!("[Brocade] Running command: '{command}'...");
|
debug!("[Brocade] Running command: '{command}'...");
|
||||||
|
|
||||||
let mut channel = self.client.channel_open_session().await?;
|
let mut channel = self.client.channel_open_session().await?;
|
||||||
@ -161,9 +205,13 @@ impl BrocadeClient {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
russh::ChannelMsg::Success
|
russh::ChannelMsg::Eof => {
|
||||||
| russh::ChannelMsg::WindowAdjusted { .. }
|
channel.close().await?;
|
||||||
| russh::ChannelMsg::Eof => {}
|
}
|
||||||
|
russh::ChannelMsg::Close => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
russh::ChannelMsg::Success | russh::ChannelMsg::WindowAdjusted { .. } => {}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::UnexpectedError(format!(
|
return Err(Error::UnexpectedError(format!(
|
||||||
"Russh got unexpected msg {msg:?}"
|
"Russh got unexpected msg {msg:?}"
|
||||||
@ -172,20 +220,25 @@ impl BrocadeClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.close().await?;
|
|
||||||
|
|
||||||
let output = String::from_utf8(output).expect("Output should be UTF-8 compatible");
|
let output = String::from_utf8(output).expect("Output should be UTF-8 compatible");
|
||||||
|
debug!("[Brocade] Command output:\n{output}");
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_commands(&self, commands: Vec<String>) -> Result<(), Error> {
|
async fn run_commands(&self, commands: Vec<String>) -> Result<(), Error> {
|
||||||
let mut channel = self.client.channel_open_session().await?;
|
|
||||||
|
|
||||||
// Execute commands sequentially and check for errors immediately.
|
// Execute commands sequentially and check for errors immediately.
|
||||||
for command in commands {
|
for command in commands {
|
||||||
|
if !command.starts_with("show") && self.options.dry_run {
|
||||||
|
info!("[Brocade] Dry-run mode enabled, skipping command: {command}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
debug!("[Brocade] Running command: '{command}'...");
|
debug!("[Brocade] Running command: '{command}'...");
|
||||||
|
|
||||||
|
let mut channel = self.client.channel_open_session().await?;
|
||||||
let mut output = Vec::new();
|
let mut output = Vec::new();
|
||||||
|
let mut close_received = false;
|
||||||
|
|
||||||
channel.exec(true, command.as_str()).await?;
|
channel.exec(true, command.as_str()).await?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -206,12 +259,30 @@ impl BrocadeClient {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {} // Ignore other messages like success or EOF for now.
|
russh::ChannelMsg::Eof => {
|
||||||
|
channel.close().await?;
|
||||||
|
}
|
||||||
|
russh::ChannelMsg::Close => {
|
||||||
|
close_received = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
russh::ChannelMsg::Success | russh::ChannelMsg::WindowAdjusted { .. } => {}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::UnexpectedError(format!(
|
||||||
|
"Russh got unexpected msg {msg:?}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !close_received {
|
||||||
|
return Err(Error::UnexpectedError(format!(
|
||||||
|
"Channel closed without receiving a final CLOSE message for command: {}",
|
||||||
|
command
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.close().await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,11 +12,11 @@ pub type FirewallGroup = Vec<PhysicalHost>;
|
|||||||
pub struct PhysicalHost {
|
pub struct PhysicalHost {
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
pub category: HostCategory,
|
pub category: HostCategory,
|
||||||
pub network: Vec<NetworkInterface>,
|
pub network: Vec<NetworkInterface>, // FIXME: Don't use harmony_inventory_agent::NetworkInterface
|
||||||
pub storage: Vec<StorageDrive>,
|
pub storage: Vec<StorageDrive>, // FIXME: Don't use harmony_inventory_agent::StorageDrive
|
||||||
pub labels: Vec<Label>,
|
pub labels: Vec<Label>,
|
||||||
pub memory_modules: Vec<MemoryModule>,
|
pub memory_modules: Vec<MemoryModule>, // FIXME: Don't use harmony_inventory_agent::MemoryModule
|
||||||
pub cpus: Vec<CPU>,
|
pub cpus: Vec<CPU>, // FIXME: Don't use harmony_inventory_agent::CPU
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhysicalHost {
|
impl PhysicalHost {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use brocade::BrocadeOptions;
|
||||||
use harmony_macros::ip;
|
use harmony_macros::ip;
|
||||||
use harmony_secret::SecretManager;
|
use harmony_secret::SecretManager;
|
||||||
use harmony_types::net::MacAddress;
|
use harmony_types::net::MacAddress;
|
||||||
@ -305,9 +306,14 @@ impl HAClusterTopology {
|
|||||||
|
|
||||||
// FIXME: We assume Brocade switches
|
// FIXME: We assume Brocade switches
|
||||||
let switches: Vec<IpAddr> = self.switch.iter().map(|s| s.ip).collect();
|
let switches: Vec<IpAddr> = self.switch.iter().map(|s| s.ip).collect();
|
||||||
let client = BrocadeSwitchClient::init(&switches, &auth.username, &auth.password)
|
let brocade_options = Some(BrocadeOptions {
|
||||||
.await
|
dry_run: *crate::config::DRY_RUN,
|
||||||
.map_err(|e| SwitchError::new(format!("Failed to connect to switch: {e}")))?;
|
..Default::default()
|
||||||
|
});
|
||||||
|
let client =
|
||||||
|
BrocadeSwitchClient::init(&switches, &auth.username, &auth.password, brocade_options)
|
||||||
|
.await
|
||||||
|
.map_err(|e| SwitchError::new(format!("Failed to connect to switch: {e}")))?;
|
||||||
|
|
||||||
Ok(Box::new(client))
|
Ok(Box::new(client))
|
||||||
}
|
}
|
||||||
@ -506,14 +512,13 @@ impl HttpServer for HAClusterTopology {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Switch for HAClusterTopology {
|
impl Switch for HAClusterTopology {
|
||||||
async fn get_port_for_mac_address(&self, mac_address: &MacAddress) -> Option<String> {
|
async fn get_port_for_mac_address(
|
||||||
let client = self.get_switch_client().await;
|
&self,
|
||||||
|
mac_address: &MacAddress,
|
||||||
let Ok(client) = client else {
|
) -> Result<Option<String>, SwitchError> {
|
||||||
return None;
|
let client = self.get_switch_client().await?;
|
||||||
};
|
let port = client.find_port(mac_address).await?;
|
||||||
|
Ok(port)
|
||||||
client.find_port(mac_address).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn configure_host_network(
|
async fn configure_host_network(
|
||||||
|
|||||||
@ -175,7 +175,10 @@ impl FromStr for DnsRecordType {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Switch: Send + Sync {
|
pub trait Switch: Send + Sync {
|
||||||
async fn get_port_for_mac_address(&self, mac_address: &MacAddress) -> Option<String>;
|
async fn get_port_for_mac_address(
|
||||||
|
&self,
|
||||||
|
mac_address: &MacAddress,
|
||||||
|
) -> Result<Option<String>, SwitchError>;
|
||||||
|
|
||||||
async fn configure_host_network(
|
async fn configure_host_network(
|
||||||
&self,
|
&self,
|
||||||
@ -218,7 +221,7 @@ impl Error for SwitchError {}
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait SwitchClient: Send + Sync {
|
pub trait SwitchClient: Send + Sync {
|
||||||
async fn find_port(&self, mac_address: &MacAddress) -> Option<String>;
|
async fn find_port(&self, mac_address: &MacAddress) -> Result<Option<String>, SwitchError>;
|
||||||
async fn configure_port_channel(&self, switch_ports: Vec<String>) -> Result<u8, SwitchError>;
|
async fn configure_port_channel(&self, switch_ports: Vec<String>) -> Result<u8, SwitchError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use brocade::BrocadeClient;
|
use brocade::{BrocadeClient, BrocadeOptions};
|
||||||
use harmony_secret::Secret;
|
use harmony_secret::Secret;
|
||||||
use harmony_types::net::{IpAddress, MacAddress};
|
use harmony_types::net::{IpAddress, MacAddress};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -15,23 +15,26 @@ impl BrocadeSwitchClient {
|
|||||||
ip_addresses: &[IpAddress],
|
ip_addresses: &[IpAddress],
|
||||||
username: &str,
|
username: &str,
|
||||||
password: &str,
|
password: &str,
|
||||||
|
options: Option<BrocadeOptions>,
|
||||||
) -> Result<Self, brocade::Error> {
|
) -> Result<Self, brocade::Error> {
|
||||||
let brocade = BrocadeClient::init(ip_addresses, username, password).await?;
|
let brocade = BrocadeClient::init(ip_addresses, username, password, options).await?;
|
||||||
Ok(Self { brocade })
|
Ok(Self { brocade })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl SwitchClient for BrocadeSwitchClient {
|
impl SwitchClient for BrocadeSwitchClient {
|
||||||
async fn find_port(&self, mac_address: &MacAddress) -> Option<String> {
|
async fn find_port(&self, mac_address: &MacAddress) -> Result<Option<String>, SwitchError> {
|
||||||
let Ok(table) = self.brocade.show_mac_address_table().await else {
|
let table = self
|
||||||
return None;
|
.brocade
|
||||||
};
|
.show_mac_address_table()
|
||||||
|
.await
|
||||||
|
.map_err(|e| SwitchError::new(format!("Failed to get mac address table: {e}")))?;
|
||||||
|
|
||||||
table
|
Ok(table
|
||||||
.iter()
|
.iter()
|
||||||
.find(|entry| entry.mac_address == *mac_address)
|
.find(|entry| entry.mac_address == *mac_address)
|
||||||
.map(|entry| entry.port_name.clone())
|
.map(|entry| entry.port_name.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn configure_port_channel(&self, switch_ports: Vec<String>) -> Result<u8, SwitchError> {
|
async fn configure_port_channel(&self, switch_ports: Vec<String>) -> Result<u8, SwitchError> {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use harmony_types::id::Id;
|
use harmony_types::id::Id;
|
||||||
|
use log::{debug, info, warn};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -56,31 +57,52 @@ impl<T: Topology + Switch> Interpret<T> for HostNetworkConfigurationInterpret {
|
|||||||
_inventory: &Inventory,
|
_inventory: &Inventory,
|
||||||
topology: &T,
|
topology: &T,
|
||||||
) -> Result<Outcome, InterpretError> {
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
info!(
|
||||||
|
"Started network configuration for {} host(s)...",
|
||||||
|
self.score.hosts.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut configured_host_count = 0;
|
||||||
|
|
||||||
for host in &self.score.hosts {
|
for host in &self.score.hosts {
|
||||||
let mut switch_ports = vec![];
|
let mut switch_ports = vec![];
|
||||||
|
|
||||||
for network_interface in &host.network {
|
for network_interface in &host.network {
|
||||||
let mac_address = network_interface.mac_address;
|
let mac_address = network_interface.mac_address;
|
||||||
|
|
||||||
if let Some(port_name) = topology.get_port_for_mac_address(&mac_address).await {
|
match topology.get_port_for_mac_address(&mac_address).await {
|
||||||
switch_ports.push(SwitchPort {
|
Ok(Some(port_name)) => {
|
||||||
interface: NetworkInterface {
|
switch_ports.push(SwitchPort {
|
||||||
name: network_interface.name.clone(),
|
interface: NetworkInterface {
|
||||||
mac_address,
|
name: network_interface.name.clone(),
|
||||||
speed_mbps: network_interface.speed_mbps,
|
mac_address,
|
||||||
mtu: network_interface.mtu,
|
speed_mbps: network_interface.speed_mbps,
|
||||||
},
|
mtu: network_interface.mtu,
|
||||||
port_name,
|
},
|
||||||
});
|
port_name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(None) => debug!("No port found for host '{}', skipping", host.id),
|
||||||
|
Err(e) => warn!("Failed to get port for host '{}': {}", host.id, e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = topology
|
if !switch_ports.is_empty() {
|
||||||
.configure_host_network(host, HostNetworkConfig { switch_ports })
|
configured_host_count += 1;
|
||||||
.await;
|
let _ = topology
|
||||||
|
.configure_host_network(host, HostNetworkConfig { switch_ports })
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Outcome::success("".into()))
|
if configured_host_count > 0 {
|
||||||
|
Ok(Outcome::success(format!(
|
||||||
|
"Configured {configured_host_count}/{} host(s)",
|
||||||
|
self.score.hosts.len()
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(Outcome::noop("No hosts configured".into()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,12 +243,7 @@ mod tests {
|
|||||||
let _ = score.interpret(&Inventory::empty(), &topology).await;
|
let _ = score.interpret(&Inventory::empty(), &topology).await;
|
||||||
|
|
||||||
let configured_host_networks = topology.configured_host_networks.lock().unwrap();
|
let configured_host_networks = topology.configured_host_networks.lock().unwrap();
|
||||||
assert_that!(*configured_host_networks).contains_exactly(vec![(
|
assert_that!(*configured_host_networks).is_empty();
|
||||||
HOST_ID.clone(),
|
|
||||||
HostNetworkConfig {
|
|
||||||
switch_ports: vec![],
|
|
||||||
},
|
|
||||||
)]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn given_score(hosts: Vec<PhysicalHost>) -> HostNetworkConfigurationScore {
|
fn given_score(hosts: Vec<PhysicalHost>) -> HostNetworkConfigurationScore {
|
||||||
@ -297,12 +314,15 @@ mod tests {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Switch for TopologyWithSwitch {
|
impl Switch for TopologyWithSwitch {
|
||||||
async fn get_port_for_mac_address(&self, _mac_address: &MacAddress) -> Option<String> {
|
async fn get_port_for_mac_address(
|
||||||
|
&self,
|
||||||
|
_mac_address: &MacAddress,
|
||||||
|
) -> Result<Option<String>, SwitchError> {
|
||||||
let mut ports = self.available_ports.lock().unwrap();
|
let mut ports = self.available_ports.lock().unwrap();
|
||||||
if ports.is_empty() {
|
if ports.is_empty() {
|
||||||
return None;
|
return Ok(None);
|
||||||
}
|
}
|
||||||
Some(ports.remove(0))
|
Ok(Some(ports.remove(0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn configure_host_network(
|
async fn configure_host_network(
|
||||||
|
|||||||
@ -54,6 +54,9 @@ struct DeployArgs {
|
|||||||
|
|
||||||
#[arg(long = "profile", short = 'p', default_value = "dev")]
|
#[arg(long = "profile", short = 'p', default_value = "dev")]
|
||||||
harmony_profile: HarmonyProfile,
|
harmony_profile: HarmonyProfile,
|
||||||
|
|
||||||
|
#[arg(long = "dry-run", short = 'd', default_value = "false")]
|
||||||
|
dry_run: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args, Clone, Debug)]
|
#[derive(Args, Clone, Debug)]
|
||||||
@ -178,6 +181,7 @@ async fn main() {
|
|||||||
command
|
command
|
||||||
.env("HARMONY_USE_LOCAL_K3D", format!("{use_local_k3d}"))
|
.env("HARMONY_USE_LOCAL_K3D", format!("{use_local_k3d}"))
|
||||||
.env("HARMONY_PROFILE", format!("{}", args.harmony_profile))
|
.env("HARMONY_PROFILE", format!("{}", args.harmony_profile))
|
||||||
|
.env("HARMONY_DRY_RUN", format!("{}", args.dry_run))
|
||||||
.arg("-y")
|
.arg("-y")
|
||||||
.arg("-a");
|
.arg("-a");
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user