Skip to main content

Overview

The NMISNG::Inventory module is the base class for managing network device inventory in NMIS. It handles storage, time-series data, datasets, and provides specialized subclasses for different inventory types. Version: 1.1.0 Location: lib/NMISNG/Inventory.pm

Inventory Types

NMIS uses different inventory classes for different concepts:
  • DefaultInventory - Generic inventory items (most concepts)
  • InterfaceInventory - Network interface specific operations
  • ServiceInventory - Service monitoring inventory
The appropriate class is automatically selected based on the concept.

Constructor

new()

Creates a new inventory object. Typically called via $node->inventory() rather than directly.
concept
string
required
Inventory concept (e.g., “interface”, “service”, “catchall”, “storage”)
nmisng
NMISNG
required
NMISNG parent object
node_uuid
string
required
Node UUID this inventory belongs to
cluster_id
string
required
Cluster ID
data
hash
Inventory data
path
array
Inventory path (unique identifier)
path_keys
array
Keys used to construct path from data
enabled
boolean
default:"1"
Whether inventory is enabled for collection
historic
boolean
default:"0"
Whether inventory is historic (no longer present on device)
storage
hash
Storage paths by subconcept (e.g., {interface => "/nodes/router01/interface-eth0.rrd"})
description
string
Human-readable description
# Usually obtained via node->inventory()
my ($inventory, $error) = $node->inventory(
    concept => "interface",
    data => {index => "1", ifDescr => "GigabitEthernet0/0"},
    path_keys => ['index'],
    create => 1
);
inventory
NMISNG::Inventory
Returns inventory object or undef on error

Data Access Methods

data()

Get or set inventory data.
newvalue
hash
New data hash (marks inventory as dirty)
# Get data (returns clone)
my $data = $inventory->data();
print "Interface: $data->{ifDescr}\n";
print "Index: $data->{index}\n";

# Modify data
$data->{ifAlias} = "Uplink to Core";
$inventory->data($data);
$inventory->save();
data
hash
Clone of inventory data hash
Returns a clone of the data - modifications must be saved back using data($modified) then save().

data_live()

Get direct reference to data (not a clone).
# Get live reference (faster for read-only or many modifications)
my $data_ref = $inventory->data_live();
$data_ref->{ifAlias} = "New alias";
$data_ref->{Description} = "Updated";
# No need to call data($data_ref), changes are direct
$inventory->save();
data
hash
Direct reference to data hash (not cloned)
Use data_live() when making multiple changes or for read-only access to avoid cloning overhead. Once called, the object automatically tracks changes and marks itself dirty.

description()

Get or set inventory description.
my $desc = $inventory->description();
print "Description: $desc\n";

$inventory->description("Primary uplink interface");
$inventory->save();
description
string
Current description

Property Accessors

concept()

Get inventory concept (read-only).
my $concept = $inventory->concept();
print "Concept: $concept\n"; # e.g., "interface"
concept
string
Inventory concept

node_uuid()

Get owning node UUID (read-only).
my $uuid = $inventory->node_uuid();
uuid
string
Node UUID

cluster_id()

Get cluster ID (read-only).
my $cluster = $inventory->cluster_id();
cluster_id
string
Cluster ID

id()

Get inventory MongoDB ID (read-only).
my $id = $inventory->id();
print "Inventory ID: $id\n";
id
ObjectId
MongoDB _id, or undef if not saved

enabled()

Get or set enabled status.
newstatus
boolean
New enabled status (0 or 1)
if ($inventory->enabled) {
    print "Inventory is enabled\n";
}

# Disable inventory
$inventory->enabled(0);
$inventory->save();
enabled
boolean
Returns 1 if enabled, 0 if disabled

historic()

Get or set historic status.
newstatus
boolean
New historic status (0 or 1)
if ($inventory->historic) {
    print "Inventory is historic (no longer on device)\n";
}

# Mark as historic
$inventory->historic(1);
$inventory->save();
historic
boolean
Returns 1 if historic, 0 if current

is_new()

Check if inventory is new (not yet saved).
if ($inventory->is_new) {
    print "Inventory not yet saved\n";
}
is_new
boolean
Returns 1 if new, 0 if saved

lastupdate()

Get last update timestamp.
my $timestamp = $inventory->lastupdate();
if ($timestamp) {
    print "Last updated: " . localtime($timestamp) . "\n";
}
timestamp
integer
Unix timestamp or undef if not saved

Time-Series Data

add_timed_data()

Add point-in-time data for this inventory.
data
hash
required
Data hash with dataset values
derived_data
hash
Derived/calculated data hash
subconcept
string
Subconcept name (required if datasets not provided)
datasets
hash
Dataset structure: {subconcept => {dataset1 => 1, dataset2 => 1}}
time
number
Unix timestamp (defaults to current time)
delay_insert
boolean
default:"0"
Delay database insert until save() called
# Add interface statistics
my $error = $inventory->add_timed_data(
    subconcept => "interface",
    data => {
        ifInOctets => 1234567,
        ifOutOctets => 7654321,
        ifInErrors => 0,
        ifOutErrors => 0
    },
    derived_data => {
        inputUtil => 45.2,
        outputUtil => 67.8
    },
    time => time
);

if ($error) {
    die "Failed to add timed data: $error";
}
error
string
Returns undef on success, error message on failure
Data is automatically stored in both timed collection (for historical queries) and latest_data collection (for current state lookups).

get_newest_timed_data()

Retrieve the most recent timed data.
from_timed
boolean
default:"0"
Get from timed collection instead of latest_data cache
my $result = $inventory->get_newest_timed_data();

if ($result->{success}) {
    my $data = $result->{data};
    my $derived = $result->{derived_data};
    my $time = $result->{time};
    
    # Data organized by subconcept
    print "Input octets: $data->{interface}{ifInOctets}\n";
    print "Utilization: $derived->{interface}{inputUtil}%\n";
    print "Collected at: " . localtime($time) . "\n";
} else {
    print "No data available\n";
}
result
hash
Hash with:
  • success (boolean)
  • data (hash) - Data by subconcept
  • derived_data (hash) - Derived data by subconcept
  • time (integer) - Timestamp
  • error (string) - Error message if failed

Dataset Management

dataset_info()

Get or set dataset information for a subconcept.
subconcept
string
required
Subconcept name
datasets
hash
Hash of dataset names => 1
# Get datasets for subconcept
my $datasets = $inventory->dataset_info(subconcept => "interface");
foreach my $ds (keys %$datasets) {
    print "Dataset: $ds\n";
}

# Set datasets
$inventory->dataset_info(
    subconcept => "interface",
    datasets => {
        ifInOctets => 1,
        ifOutOctets => 1,
        ifInErrors => 1
    }
);
datasets
hash
Hash of dataset names (keys) with value 1, or empty hash

data_info()

Get or set data display information for a subconcept.
subconcept
string
required
Subconcept name
enabled
boolean
Whether subconcept display is enabled
display_keys
array
Array of data keys to display
# Get data info
my $info = $inventory->data_info(subconcept => "interface");
print "Enabled: $info->{enabled}\n";
print "Display keys: " . join(", ", @{$info->{display_keys}}) . "\n";

# Set data info
my $error = $inventory->data_info(
    subconcept => "interface",
    enabled => 1,
    display_keys => ['ifDescr', 'ifAlias', 'Description']
);
info
hash
Hash with enabled (boolean) and display_keys (array), or error string

Storage Management

storage()

Get or set storage paths.
newstorage
hash
Hash of subconcept => storage_info
# Get storage
my $storage = $inventory->storage();
foreach my $subconcept (keys %$storage) {
    my $rrd_path = $storage->{$subconcept}{rrd};
    print "$subconcept RRD: $rrd_path\n";
}

# Set storage
$storage->{interface}{rrd} = "/nodes/router01/interface-eth0.rrd";
$inventory->storage($storage);
storage
hash
Clone of storage structure

find_subconcept_type_storage()

Look up storage path for a specific subconcept and type.
subconcept
string
required
Subconcept name
type
string
default:"rrd"
Storage type
my $rrd_path = $inventory->find_subconcept_type_storage(
    subconcept => "interface",
    type => "rrd"
);

if ($rrd_path) {
    print "RRD file: $rrd_path\n";
}
path
string
Storage path or undef if not found

set_subconcept_type_storage()

Set storage path for a subconcept and type.
subconcept
string
required
Subconcept name
type
string
default:"rrd"
Storage type
data
string
Storage path (undef to delete)
$inventory->set_subconcept_type_storage(
    subconcept => "interface",
    type => "rrd",
    data => "/nodes/router01/interface-eth0.rrd"
);

# Delete storage entry
$inventory->set_subconcept_type_storage(
    subconcept => "interface",
    type => "rrd",
    data => undef
);

Path Management

path()

Get or recalculate inventory path.
recalculate
boolean
default:"0"
Force path recalculation
my $path = $inventory->path();
if (ref($path) eq "ARRAY") {
    print "Path: " . join("/", @$path) . "\n";
} else {
    print "Path error: $path\n";
}
path
array
Returns arrayref on success, error string on failure

path_keys()

Get or set path keys.
newvalue
array
New path keys array
my $keys = $inventory->path_keys();
print "Path keys: " . join(", ", @$keys) . "\n";
keys
array
Clone of path keys array

Inventory Lifecycle

save()

Save inventory to database.
node
NMISNG::Node
Node object (required for new inventory)
my ($op, $error) = $inventory->save(node => $node);

if ($op > 0) {
    print "Inventory saved\n";
} elsif ($op == 0) {
    print "No changes to save\n";
} else {
    print "Save failed: $error\n";
}
result
tuple
Returns (operation_count, error_message). operation_count is 1 for success, 0 for no changes, -1 for error

delete()

Delete inventory and associated data.
keep_rrd
boolean
default:"0"
Keep RRD files (don’t delete them)
my ($success, $message) = $inventory->delete(keep_rrd => 0);

if ($success) {
    print "Inventory deleted\n";
} else {
    print "Delete failed: $message\n";
}
result
tuple
Returns (success, message) or (0, error_message)
Deletes inventory record, all timed data, latest data, and RRD files (unless keep_rrd is true).

reload()

Reload inventory from database.
my $error = $inventory->reload();
if ($error) {
    die "Failed to reload: $error";
}
error
string
Returns undef on success, error message on failure

Interface Inventory

The InterfaceInventory class provides additional methods for network interfaces.

Interface-Specific Accessors

# Get interface properties
my $ifDescr = $inventory->ifDescr();
my $ifAlias = $inventory->ifAlias();
my $ifIndex = $inventory->ifIndex();
my $ifType = $inventory->ifType();
my $ifAdminStatus = $inventory->ifAdminStatus();
my $ifOperStatus = $inventory->ifOperStatus();

# Get IP information (array of hashes)
my $ips = $inventory->ip();
foreach my $ip_info (@$ips) {
    print "IP: $ip_info->{ipAdEntAddr}\n";
    print "Netmask: $ip_info->{ipAdEntNetMask}\n";
    print "Subnet: $ip_info->{ipSubnet}\n";
}

Interface Speed Methods

# Get interface speeds
my $speed = $inventory->ifSpeed();      # Overall speed
my $speed_in = $inventory->ifSpeedIn(); # Input speed
my $speed_out = $inventory->ifSpeedOut(); # Output speed

# Get formatted speed string
my $speed_str = $inventory->speed();
print "Speed: $speed_str\n"; # e.g., "1 Gbps" or "IN: 100 Mbps OUT: 50 Mbps"

# Get maximum values for RRD
my $max_octets = $inventory->max_octets();
my $max_bytes = $inventory->max_bytes();
my $max_packets = $inventory->max_packets();

Service Inventory

The ServiceInventory class is used for service monitoring.

Service-Specific Features

# Service inventory requires 'service' and 'uuid' in data
my ($service_inv, $error) = $node->inventory(
    concept => "service",
    data => {
        service => "HTTP",
        # uuid is auto-generated from service name + node uuid
    },
    create => 1
);

my $data = $service_inv->data();
print "Service: $data->{service}\n";
print "UUID: $data->{uuid}\n";
ServiceInventory automatically generates a deterministic UUID from the service name and node UUID.

Utility Functions

get_inventory_class()

Get the appropriate inventory class for a concept (class method).
my $class = NMISNG::Inventory::get_inventory_class("interface");
print "Class: $class\n"; # NMISNG::Inventory::InterfaceInventory

my $class = NMISNG::Inventory::get_inventory_class("custom");
print "Class: $class\n"; # NMISNG::Inventory::DefaultInventory
class
string
Perl class name for the concept

See Also

Build docs developers (and LLMs) love