Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -166,25 +166,25 @@ endif

musl-x86_64-modules-dist-dev:
$(call check_present,x86_64-linux-musl-gcc)
cargo build -v --workspace $(MUSL_WORKSPACE_EXCLUDES) --target x86_64-unknown-linux-musl
@sh scripts/run-musl-cargo.sh x86_64-unknown-linux-musl x86_64-linux-musl-gcc build -v --workspace $(MUSL_WORKSPACE_EXCLUDES) --target x86_64-unknown-linux-musl
$(call stage_profile_modules,debug,x86_64-unknown-linux-musl)
$(call stage_modules_dist_from,debug,x86_64-unknown-linux-musl,$(MUSL_MODULE_PACKAGE_SPECS),$(call musl_modules_dist_dir,x86_64,debug))

musl-x86_64-modules-dist:
$(call check_present,x86_64-linux-musl-gcc)
cargo build --release --workspace $(MUSL_WORKSPACE_EXCLUDES) --target x86_64-unknown-linux-musl
@sh scripts/run-musl-cargo.sh x86_64-unknown-linux-musl x86_64-linux-musl-gcc build --release --workspace $(MUSL_WORKSPACE_EXCLUDES) --target x86_64-unknown-linux-musl
$(call stage_profile_modules,release,x86_64-unknown-linux-musl)
$(call stage_modules_dist_from,release,x86_64-unknown-linux-musl,$(MUSL_MODULE_PACKAGE_SPECS),$(call musl_modules_dist_dir,x86_64,release))

musl-aarch64-modules-dist-dev:
$(call check_present,aarch64-linux-musl-gcc)
cargo build -v --workspace $(MUSL_WORKSPACE_EXCLUDES) --target aarch64-unknown-linux-musl
@sh scripts/run-musl-cargo.sh aarch64-unknown-linux-musl aarch64-linux-musl-gcc build -v --workspace $(MUSL_WORKSPACE_EXCLUDES) --target aarch64-unknown-linux-musl
$(call stage_profile_modules,debug,aarch64-unknown-linux-musl)
$(call stage_modules_dist_from,debug,aarch64-unknown-linux-musl,$(MUSL_MODULE_PACKAGE_SPECS),$(call musl_modules_dist_dir,aarch64,debug))

musl-aarch64-modules-dist:
$(call check_present,aarch64-linux-musl-gcc)
cargo build --release --workspace $(MUSL_WORKSPACE_EXCLUDES) --target aarch64-unknown-linux-musl
@sh scripts/run-musl-cargo.sh aarch64-unknown-linux-musl aarch64-linux-musl-gcc build --release --workspace $(MUSL_WORKSPACE_EXCLUDES) --target aarch64-unknown-linux-musl
$(call stage_profile_modules,release,aarch64-unknown-linux-musl)
$(call stage_modules_dist_from,release,aarch64-unknown-linux-musl,$(MUSL_MODULE_PACKAGE_SPECS),$(call musl_modules_dist_dir,aarch64,release))

Expand Down
315 changes: 315 additions & 0 deletions examples/demos/infoping/model.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
name: Infrastructure Health Ping
version: "0.1"
description: |

═══════════════════════════════════════════════════
INFRAPING -- Infrastructure Health Inspector
═══════════════════════════════════════════════════

A comprehensive autonomous infrastructure health check
model designed for disconnected-capable environments.

--- What it does --------------------------------------

This model performs a full-stack health audit of any
minion without requiring continuous master connectivity.
It discovers system identity, hardware resources,
network topology, internet reachability, and critical
service status -- all driven by declarative constraints
rather than imperative task lists.

--- Execution flow ------------------------------------

Phase 1 System Identity
Collects kernel fingerprint via uname and gathers 13
structured operating-system facts: hostname, OS
release, kernel version, CPU model and core count,
total and free memory, uptime, and 1/5-minute load
averages. All facts are produced as machine-readable
structured data, not raw text.

Phase 2 Network Audit
Enumerates every active network interface with MAC
addresses and IPv4/IPv6 assignments, reads the kernel
routing table to locate the default gateway, and
performs a real ICMP ping against a configurable
internet endpoint (default 1.1.1.1). Latency, packet
loss, and TTL are collected per probe.

Phase 3 Service Guard
Verifies that critical system daemons -- SSH server
and cron scheduler by default -- are actively running.
Each check produces structured telemetry including the
detected service manager, process IDs, and exit codes.

--- Constraint-driven validation ----------------------

Every collected fact is routed through eight declarative
constraints that compare observed reality against
expected claims. No imperative scripting is used: the
constraint engine judges success or failure from the
model definition alone. Failed constraints produce
routable events that can trigger follow-up models,
alerts, or operator notifications.

--- Key design properties -----------------------------

* Disconnected-safe -- facts are collected and judged
locally; results are journaled for later replay.
* Execution semantics -- local completion is explicitly
separated from delivery completion.
* Structured output -- all module results are structured
JSON, not free-form text, enabling downstream automation.
* Declarative -- the model describes what correct state
looks like; the runtime figures out how to verify it.
* Extensible -- additional entities, actions, and
constraints can be added without restructuring the model.

--- Modules used --------------------------------------

sys.run Kernel identity fingerprint
os.facts Structured system fact inventory (13 keys)
sys.net Interface enumeration, routing table,
ICMP ping
sys.service Daemon health verification (cross-platform)

--- Example trace -------------------------------------

When executed against a Linux minion the model produces
a trace such as:

INFOPING uname Linux alien 5.19.0-50-generic...
INFOPING facts 13 facts collected
INFOPING if-list eth0 52:54:00:36:8D:71
INFOPING route-table default via 192.168.1.1
INFOPING ping-check 1.1.1.1: sent=2 recv=2
loss=0% rtt=12.3ms
INFOPING check-sshd running (pid 1234)
INFOPING check-cron running (pid 5678)
INFOPING Constraints 8/8 passed OK

The constraint engine then compares each structured
fact against the model's declared claims and emits
routable events for any mismatch.

--- Use cases -----------------------------------------

* Pre-deployment host readiness verification
* Continuous compliance auditing in air-gapped sites
* Fleet-wide health snapshot collection via periodic
scheduled checkbook execution
* Troubleshooting triage that answers "is the host
healthy?" before an operator even logs in

maintainer: SysInspect Developers <dev@sysinspect.io>

checkbook:
full-scan:
- hardware-profile
- network-profile
- service-profile

relations:
hardware-profile:
$:
requires:
- system-info
network-profile:
$:
requires:
- net-info
service-profile:
$:
requires:
- svc-info

entities:
system-info:
descr: Host identity, kernel, CPU, memory, uptime, load averages
claims:
$:
- identity:
kernel-expect: Linux
- resources:
load-warn: "2.0"
mem-free-min-mb: "500"

net-info:
descr: Network interfaces, routing table, internet connectivity
claims:
$:
- connectivity:
target: 1.1.1.1
ping-count: 2

svc-info:
descr: Critical system daemon health
claims:
$:
- daemons:
sshd: sshd
cron: cron

actions:
uname:
descr: Full kernel identity fingerprint
module: sys.run
bind:
- system-info
state:
$:
args:
cmd: uname -a

facts:
descr: Comprehensive system facts inventory
module: os.facts
bind:
- system-info
state:
$:
opts:
- gather

if-list:
descr: Enumerate active network interfaces
module: sys.net
bind:
- net-info
state:
$:
opts:
- if-up

route-table:
descr: Read kernel routing table
module: sys.net
bind:
- net-info
state:
$:
opts:
- route-table

ping-check:
descr: Verify internet reachability via ICMP
module: sys.net
bind:
- net-info
state:
$:
opts:
- ping
args:
host: "claim(connectivity.target)"
count: "claim(connectivity.ping-count)"

check-sshd:
descr: Verify SSH daemon status
module: sys.service
bind:
- svc-info
state:
$:
opts:
- check
args:
name: "claim(daemons.sshd)"

check-cron:
descr: Verify cron daemon status
module: sys.service
bind:
- svc-info
state:
$:
opts:
- check
args:
name: "claim(daemons.cron)"

constraints:
kernel-linux:
descr: Operating system is Linux
entities:
- system-info
all:
$:
- fact: stdout
contains: Linux

cpu-detected:
descr: CPU model information collected
entities:
- system-info
all:
$:
- fact: cpu_model
matches: ".+"

memory-detected:
descr: Memory information collected
entities:
- system-info
all:
$:
- fact: memory_total_kb
more: 0

network-interface-up:
descr: At least one network interface is active
entities:
- net-info
all:
$:
- fact: if-up
matches: ".+"

gateway-defined:
descr: Default route gateway is configured
entities:
- net-info
all:
$:
- fact: route-table
matches: "gateway"

internet-reachable:
descr: Internet connectivity confirmed
entities:
- net-info
all:
$:
- fact: received
more: 0

sshd-running:
descr: SSH daemon is operational
entities:
- svc-info
all:
$:
- fact: running
equals: true

cron-running:
descr: Cron daemon is operational
entities:
- svc-info
all:
$:
- fact: running
equals: true

events:
$|$|$|$:
handlers:
- console-logger
- outcome-logger

console-logger:
concise: false
prefix: "INFOPING"

outcome-logger:
prefix: "INFOPING Constraints"
24 changes: 21 additions & 3 deletions libsysinspect/src/cfg/mmconf.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::{cfg::APP_CONF, intp::functions::get_by_namespace, util};
use crate::{
cfg::APP_CONF,
intp::functions::{deep_merge, get_by_namespace},
util,
};
use indexmap::IndexMap;
use libcommon::SysinspectError;
use nix::libc;
Expand Down Expand Up @@ -575,7 +579,11 @@ impl MinionConfig {
return Err(SysinspectError::ConfigError(format!("File not found: {cp}")));
}

if let Some(cfgv) = get_by_namespace(Some(from_str::<Value>(&fs::read_to_string(&p)?)?), "config.minion") {
let mut root_val = from_str::<Value>(&fs::read_to_string(&p)?)?;
for dv in crate::cfg::load_dropins(&crate::cfg::dropins_dir(&p)) {
deep_merge(&mut root_val, &dv);
}
if let Some(cfgv) = get_by_namespace(Some(root_val), "config.minion") {
return Ok(from_value::<MinionConfig>(cfgv)?);
}

Expand Down Expand Up @@ -1233,7 +1241,11 @@ impl MasterConfig {
return Err(SysinspectError::ConfigError(format!("File not found: {cp}")));
}

if let Some(cfgv) = get_by_namespace(Some(from_str::<Value>(&fs::read_to_string(&p)?)?), "config.master") {
let mut root_val = from_str::<Value>(&fs::read_to_string(&p)?)?;
for dv in crate::cfg::load_dropins(&crate::cfg::dropins_dir(&p)) {
deep_merge(&mut root_val, &dv);
}
if let Some(cfgv) = get_by_namespace(Some(root_val), "config.master") {
return Ok(from_value::<MasterConfig>(cfgv)?);
}

Expand Down Expand Up @@ -1414,6 +1426,12 @@ impl MasterConfig {
self.root_dir().join(CFG_FILESERVER_ROOT)
}

/// Path to the master configuration file for this layout.
pub fn config_path(&self) -> PathBuf {
let root = self.root_dir();
if root == *DEFAULT_SYSINSPECT_ROOT { root.join(APP_CONF) } else { root.join(DEFAULT_MINION_CFG_DIR).join(APP_CONF) }
}

/// Get models root on the fileserver
pub fn fileserver_models_root(&self, uri_only: bool) -> PathBuf {
if uri_only {
Expand Down
Loading
Loading