Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sled-agent] Self assembling external DNS zone #5059

Merged
merged 22 commits into from
Feb 23, 2024
Merged
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use OPTE IP from Port type everywhere
  • Loading branch information
karencfv committed Feb 20, 2024
commit 854493d544e74b7bbb8140c90140a202bf7ea7e1
59 changes: 2 additions & 57 deletions illumos-utils/src/ipadm.rs
Original file line number Diff line number Diff line change
@@ -5,10 +5,8 @@
//! Utilities for managing IP interfaces.

use crate::zone::IPADM;
use crate::{execute, inner, output_to_exec_error, ExecutionError, PFEXEC};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::process::Stdio;
use std::str::FromStr;
use crate::{execute, ExecutionError, PFEXEC};
use std::net::Ipv6Addr;

/// Wraps commands for interacting with interfaces.
pub struct Ipadm {}
@@ -134,57 +132,4 @@ impl Ipadm {
};
Ok(())
}

// Retrieve OPTE IP from interface
pub fn retrieve_opte_ip(
opte_iface: &String,
) -> Result<Ipv4Addr, ExecutionError> {
let addrobj = format!("{}/public", opte_iface);
let mut cmd = std::process::Command::new(PFEXEC);
let child_cmd = cmd
.args(&[IPADM, "show-addr", "-p", "-o", "ADDR", &addrobj])
.stdout(Stdio::piped());
let mut child = child_cmd.spawn().map_err(|err| {
ExecutionError::ExecutionStart {
command: inner::to_string(child_cmd),
err,
}
})?;

let Some(child_stdio) = child.stdout.take() else {
return Err(ExecutionError::EmptyOutput {
command: inner::to_string(child_cmd),
});
};

let child_stdio: Stdio = child_stdio.try_into().map_err(|_| {
ExecutionError::EmptyOutput { command: inner::to_string(child_cmd) }
})?;

let mut cmd = std::process::Command::new("cut");
let cut_cmd = cmd
.args(&["-d", "/", "-f", "1"])
.stdin(child_stdio)
.stdout(Stdio::piped());
let cut =
cut_cmd.spawn().map_err(|err| ExecutionError::ExecutionStart {
command: inner::to_string(cut_cmd),
err,
})?;

let out = cut.wait_with_output().unwrap();
if !out.status.success() {
return Err(output_to_exec_error(cut_cmd, &out));
};

let opte_ip = String::from_utf8_lossy(&out.stdout)
.lines()
.next()
.map(|s| s.trim())
.ok_or_else(|| output_to_exec_error(cut_cmd, &out))?
.to_string();

let addr = Ipv4Addr::from_str(&opte_ip).unwrap();
Ok(addr)
}
}
5 changes: 2 additions & 3 deletions illumos-utils/src/route.rs
Original file line number Diff line number Diff line change
@@ -4,11 +4,10 @@

//! Utilities for manipulating the routing tables.

use crate::ipadm::Ipadm;
use crate::zone::ROUTE;
use crate::{execute, inner, output_to_exec_error, ExecutionError, PFEXEC};
use libc::ESRCH;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

/// Wraps commands for interacting with routing tables.
pub struct Route {}
@@ -64,8 +63,8 @@ impl Route {
pub fn ensure_opte_route(
gateway: &Ipv4Addr,
iface: &String,
opte_ip: &IpAddr,
) -> Result<(), ExecutionError> {
let opte_ip = Ipadm::retrieve_opte_ip(iface)?;
// Add the desired route if it doesn't already exist
let mut cmd = std::process::Command::new(PFEXEC);
let cmd = cmd.args(&[
6 changes: 1 addition & 5 deletions package-manifest.toml
Original file line number Diff line number Diff line change
@@ -266,11 +266,7 @@ output.type = "zone"
service_name = "external-dns-customizations"
only_for_targets.image = "standard"
source.type = "local"
source.paths = [
{ from = "smf/external-dns", to = "/var/svc/manifest/site/external_dns" },
# TODO: Removeme if experiment works
{ from = "smf/external-dns/method_script.sh", to = "/opt/oxide/lib/svc/manifest/external_dns.sh" },
]
source.paths = [ { from = "smf/external-dns", to = "/var/svc/manifest/site/external_dns" } ]
output.intermediate_only = true
output.type = "zone"

8 changes: 5 additions & 3 deletions sled-agent/src/services.rs
Original file line number Diff line number Diff line change
@@ -1459,12 +1459,14 @@ impl ServiceManager {
})?;

let opte_interface = port.vnic_name();
let opte_gateway = &port.gateway().ip().to_string();
let opte_gateway = port.gateway().ip().to_string();
let opte_ip = port.ip().to_string();

let mut config_builder = PropertyGroupBuilder::new("config");
config_builder = config_builder
.add_property("interface", "astring", opte_interface)
.add_property("gateway", "astring", opte_gateway);
.add_property("gateway", "astring", &opte_gateway)
.add_property("ip", "astring", &opte_ip);

Ok(ServiceBuilder::new("oxide/opte-interface-setup")
.add_property_group(config_builder)
@@ -1909,7 +1911,7 @@ impl ServiceManager {

let external_dns_config = PropertyGroupBuilder::new("config")
.add_property("http_address", "astring", &http_addr)
.add_property("dns_addr", "astring", &dns_addr);
.add_property("dns_address", "astring", &dns_addr);
let external_dns_service =
ServiceBuilder::new("oxide/external_dns").add_instance(
ServiceInstanceBuilder::new("default")
4 changes: 2 additions & 2 deletions smf/external-dns/manifest.xml
Original file line number Diff line number Diff line change
@@ -22,13 +22,13 @@
</dependency>

<exec_method type='method' name='start'
exec='/opt/oxide/lib/svc/manifest/external_dns.sh'
exec='ctrun -l child -o noorphan,regent /opt/oxide/dns-server/bin/dns-server --config-file /var/svc/manifest/site/external_dns/config.toml --http-address %{config/http_address} --dns-address %{config/dns_address} &amp;'
timeout_seconds='0' />
<exec_method type='method' name='stop' exec=':kill' timeout_seconds='0' />

<property_group name='config' type='application'>
<propval name='http_address' type='astring' value='unknown' />
<propval name='dns_addr' type='astring' value='unknown' />
<propval name='dns_address' type='astring' value='unknown' />
</property_group>

<property_group name='startd' type='framework'>
18 changes: 0 additions & 18 deletions smf/external-dns/method_script.sh

This file was deleted.

3 changes: 2 additions & 1 deletion smf/opte-interface-setup/manifest.xml
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
</dependency>

<exec_method type='method' name='start'
exec='/opt/oxide/zone-network-cli/bin/zone-networking opte-interface-set-up -i %{config/interface} -g %{config/gateway}'
exec='/opt/oxide/zone-network-cli/bin/zone-networking opte-interface-set-up -i %{config/interface} -g %{config/gateway} -p %{config/ip}'
timeout_seconds='0' />

<property_group name='startd' type='framework'>
@@ -28,6 +28,7 @@
<property_group name='config' type='application'>
<propval name='gateway' type='astring' value='unknown' />
<propval name='interface' type='astring' value='unknown' />
<propval name='ip' type='astring' value='unknown' />
</property_group>

<stability value='Unstable' />
49 changes: 14 additions & 35 deletions zone-network-setup/src/bin/zone-networking.rs
Original file line number Diff line number Diff line change
@@ -12,10 +12,17 @@ use omicron_common::cmd::fatal;
use omicron_common::cmd::CmdError;
use slog::{info, Logger};
use std::fs;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

pub const HOSTS_FILE: &str = "/etc/inet/hosts";

fn parse_ip(s: &str) -> anyhow::Result<IpAddr> {
if s == "unknown" {
return Err(anyhow!("ERROR: Missing input value"));
};
s.parse().map_err(|_| anyhow!("ERROR: Invalid IP address"))
}

fn parse_ipv4(s: &str) -> anyhow::Result<Ipv4Addr> {
if s == "unknown" {
return Err(anyhow!("ERROR: Missing input value"));
@@ -104,17 +111,13 @@ async fn do_run() -> Result<(), CmdError> {
)
.required(true)
.value_parser(parse_ipv4),
),
)
.subcommand(
Command::new("get-opte-ip")
.about("Retrieves OPTE IP from interface")
)
.arg(
arg!(
-i --opte_interface <STRING> "opte_interface"
-p --opte_ip <IpAddr> "opte_ip"
)
.required(true)
.value_parser(parse_opte_iface),
.value_parser(parse_ip),
),
)
.get_matches();
@@ -127,10 +130,6 @@ async fn do_run() -> Result<(), CmdError> {
opte_interface_set_up(matches, log.clone()).await?;
}

if let Some(matches) = matches.subcommand_matches("get-opte-ip") {
retrieve_opte_ip(matches, log).await?;
}

Ok(())
}

@@ -185,19 +184,14 @@ async fn opte_interface_set_up(
) -> Result<(), CmdError> {
let interface: &String = matches.get_one("opte_interface").unwrap();
let gateway: Ipv4Addr = *matches.get_one("opte_gateway").unwrap();
let opte_ip: &IpAddr = matches.get_one("opte_ip").unwrap();

info!(&log, "Creating gateway on the OPTE IP interface if it doesn't already exist"; "OPTE interface" => ?interface);
Ipadm::create_opte_gateway(interface)
.map_err(|err| CmdError::Failure(anyhow!(err)))?;

// Retrieving the OPTE IP here isn't necessary for running the service, it exists solely to record it in the log.
// We'll fail if the command fails anyway, as the service can't run if there is no OPTE IP.
let opte_ip = Ipadm::retrieve_opte_ip(interface)
.map_err(|err| CmdError::Failure(anyhow!(err)))?;
info!(&log, "Retrieved OPTE IP from the interface"; "OPTE interface" => ?interface, "OPTE IP" => ?opte_ip);

info!(&log, "Ensuring there is a gateway route"; "OPTE gateway" => ?gateway, "OPTE interface" => ?interface);
Route::ensure_opte_route(&gateway, interface)
info!(&log, "Ensuring there is a gateway route"; "OPTE gateway" => ?gateway, "OPTE interface" => ?interface, "OPTE IP" => ?opte_ip);
Route::ensure_opte_route(&gateway, interface, &opte_ip)
.map_err(|err| CmdError::Failure(anyhow!(err)))?;

info!(&log, "Ensuring there is a default route"; "gateway" => ?gateway);
@@ -206,18 +200,3 @@ async fn opte_interface_set_up(

Ok(())
}

async fn retrieve_opte_ip(
matches: &ArgMatches,
log: Logger,
) -> Result<(), CmdError> {
let interface: &String = matches.get_one("opte_interface").unwrap();

info!(&log, "Retrieving OPTE IP from the interface"; "OPTE interface" => ?interface);
let opte_ip = Ipadm::retrieve_opte_ip(interface)
.map_err(|err| CmdError::Failure(anyhow!(err)))?;

print!("{opte_ip}");

Ok(())
}
Loading