Skip to content

Tree Creation

lordmilko edited this page Jul 7, 2021 · 18 revisions

Contents

C#

Automatic

The tree of a PRTG Object can be retrieved via the GetTree method.

//Model the tree of the object with ID 1
var tree = client.GetTree(1);
//Model the tree of a specified device
var tree = client.GetTree(device);

GetTree automatically traverses all descendants of the specified object, constructing PrtgNode objects for each item encountered. For large PRTG Trees, this can potentially be quite time consuming. Progress can be monitored by implementing a custom ITreeProgressCallback.

Manual

For creating PRTG Trees for rearranging PRTG Objects, PrtgAPI provides two mechanisms for creating arbitrary PrtgNode objects

  • PrtgNode factory methods
  • PrtgNodeFactory instance methods

Similar to the Expression class in System.Linq.Expressions, PrtgNode serves as both the base class of all PRTG node types but also defines static factory methods that can be used for generating a new instance of a node type from a specified ITreeValue and optional children.

//Construct a tree where the sensor with ID 2002 is a child of the device with ID 1001
var device = client.GetDevice(1001);
var sensor = client.GetSensor(2002);

var node = PrtgNode.Device(device,
    PrtgNode.Sensor(sensor)
);

PrtgNodeFactory is similar to the SyntaxFactory class found in Roslyn. It is a completely separate type from the more familiar PrtgNode type and is capable of executing API requests and constructing PrtgNode objects for you, all in one go.

//Construct a tree where the sensor with ID 2002 is a child of the device with ID 1001
var factory = new PrtgNodeFactory(client);

var node = factory.Device(1001,
    factory.Sensor(2002)
);

If you wish to retrieve and encapsulate multiple objects at once, PrtgNodeFactory defines simple Property, object overloads for filtering objects by a specified value.

//Create SensorNode objects for all sensors named "Ping"
var node = factory.Sensors(Property.Name, "Ping");

PrtgNodeFactory methods always return at least one object. Methods that retrieve a single object (Sensor, Device, Group, etc) assert that only one object was found, similar to how their equivalent methods on PrtgClient perform (GetSensor, GetDevice, etc).

For methods that perform more advanced filtering that could potentially return a collection of objects, PrtgNodeFactory simply asserts that at least one object was returned. This behavior protects you against creating a tree where mandatory nodes are actually missing.

Progress

Constructing a PrtgNode tree from an entire PRTG Object hierarchy can be an extremely time consuming progress. You can monitor the progress of this process however by defining a custom progress callback for retrieving notifications of progress as PrtgAPI traverses the tree. This is done by defining an implementation of an ITreeProgressCallback.

ITreeProgressCallback defines four members that must be implemented by your type

Name Description
DepthManager Tracks the depth at which PrtgAPI has traversed and makes adjustments to the progress output device as required
OnLevelBegin Called when a new level has begun, specifying the level we're now at and information about the object whose children we'll be retrieving
OnLevelWidthKnown Called when the total number of items that exist at the current level is finally known
OnProcessValue Called when the next object at the current level is to be processed

The following provides a simple example of how a progess callback can be used for writing to the console

internal class ConsoleTreeProgressCallback : ITreeProgressCallback
{
    public DepthManager DepthManager { get; } = new DepthManager();

    public void OnLevelBegin(ITreeValue parent, PrtgNodeType parentType, int depth) =>
        WriteLine($"OnLevelBegin: Parent = {parent}, Type = {parentType}, Depth = {depth}");

    public void OnLevelWidthKnown(ITreeValue parent, PrtgNodeType parentType, int width) =>
        WriteLine($"OnWidthKnown: Parent = {parent}, Type = {parentType}, Width = {width}");

    public void OnProcessValue(ITreeValue value) =>
        WriteLine($"OnProcessValue: {value}");

    private void WriteLine(string str)
    {
        var indent = new string(' ', (DepthManager.Depth - 1) * 4);

        Console.WriteLine($"{indent}{str}");
    }
}

Output:

OnLevelBegin: Parent = Root, Type = Group, Depth = 1
OnWidthKnown: Parent = Root, Type = Group, Width = 1
OnProcessValue: Local Probe
    OnLevelBegin: Parent = Local Probe, Type = Probe, Depth = 2
    OnWidthKnown: Parent = Local Probe, Type = Probe, Width = 2
    OnProcessValue: Probe Device
        OnLevelBegin: Parent = Probe Device, Type = Device, Depth = 3
        OnWidthKnown: Parent = Probe Device, Type = Device, Width = 6
        OnProcessValue: System Health
            OnLevelBegin: Parent = System Health, Type = Sensor, Depth = 4
...

PowerShell

Automatic

Infrastructure As Code

Introduction

PrtgAPI provides a sophisticated modelling system that enables you to describe the "desired state" you would like to have within your PRTG installation. PrtgAPI is then able to compare this state against the actual state of the PRTG server, resulting in a task plan that can then be further evaluated or even executed against the PRTG server.

PrtgAPI provides several cmdlets that enable constructing nodes that describe hierarchies that can exist within PRTG

  • New-ChannelNode
  • New-SensorNode
  • New-DeviceNode
  • New-GroupNode
  • New-ProbeNode
  • New-TriggerNode

As a shorthand, PrtgAPI allows you to eschew the New- prefix at the start of each cmdlet (i.e. rather than typing New-SensorNode, you can simply type SensorNode).

These cmdlets enable you to encapsulate values that you may already have

# Create a new SensorNode for all sensors named "ping"
Get-Sensor ping | New-SensorNode

or retrieve these values for you, and encapsulate them in a node

# Create a new SensorNode for the sensor with ID 1001
New-SensorNode -Id 1001

If you have any existing children you want to wrap up, PrtgAPI can help you there as well

# Retrieve all non-inherited notification triggers from the sensor with ID 1001,
# and then add these as children of SensorNode encapsulating said sensor
New-TriggerNode -ObjectId 1001 | New-SensorNode -Id 1001

However the true power of PrtgAPI's node cmdlets lies in their ability to model, declaratively, the desired state that should exist within a PRTG server.

Deferred Values

Consider the following command:

SensorNode ping

This creates a new SensorNode for any Sensor objects named ping. Or so you'd think. There of course could be many dozen ping sensors on your PRTG installation, and you might only be interested in one of them!

Now consider the following: suppose we have a device, dc-1, that we'd like to model as having a child sensor. i.e, we wish to describe the tree

Server (Device)
└──Ping (Sensor)

PrtgAPI enables you to model this hierarchy via the use of ScriptBlock objects that can be specified to PrtgAPI's node cmdlets. These ScriptBlocks literally allow you to describe the levels at which different objects should appear.

DeviceNode dc-1 {
    SensorNode ping
}

While this all sounds nice, we have a problem: logically, when we do SensorNode ping, what we really want is to retrieve only ping sensors that exist within the context of the parent dc-1 device. However, the SensorNode doesn't know anything about its parent context. How are we supposed to retrieve the right information?

It's even worse in this example!:

Windows Infrastructure (Group)
└──dc-1 (Device)
   └──Ping (Sensor)
└──exch-1 (Device)
   └──Ping (Sensor)
$ping = SensorNode ping

GroupNode "Windows Infrastructure" {
    DeviceNode dc-1 {
        $ping
    }

    DeviceNode exch-1 {
        $ping
    }
}

Not only did we retrieve our ping sensors outside of the scope of their parent objects, but we've then attempted to apply these as the children of two completely different devices. How does PrtgAPI achieve this madness? We certainly don't want to retrieve all ping sensors and then filter them down to just 1 or 2.

The answer is simple: when a node cmdlet is invoked with non-uniquely identifiable parameters (e.g. there could be several objects all over the tree with a given Name, Position or Tag), PrtgAPI simply wraps up your request and returns a deferred value that will be evaluated later, once it has been made the child of a suitable parent.

# Create a SensorNode around sensors named ping. Upon emitting the value to the pipeline,
# we see that two such sensors exist within the PRTG server
C:\> SensorNode ping

Name                 Type   Parent Children
----                 ----   ------ --------
[Maybe] Ping, Ping   Sensor        {}

If any members of the node's Value are accessed (such as its Name), PrtgAPI will execute an API request to PRTG based on what it knows so far to identify the potential candidates that may satisfy your original request. When a single matching candidate is found, you will be able to access the members of that candidate via your deferred value

# Get the Tags of the single "Uptime" candidate"
C:\> $node = SensorNode Uptime
C:\> $node.Value.Tags

wmiuptimesensor
C_OS_VMware

# Note that it's not a real sensor however!
C:\> $node.Value.GetType()

IsPublic IsSerial Name              BaseType
-------- -------- ----              --------
False    False    SensorProxy       PrtgAPI.Tree.SensorOrDeviceOrGroupO...

If zero matches were found, the PowerShell formatting system will helpfully display to you that an error occurred, however you won't be able to get more specific information until you actually try and resolve your SensorNode under a parent

C:\> $node = SensorNode Uptime1
C:\> $node

Name Type   Parent Children
---- ----   ------ --------
#ERR Sensor        {}
Failed to evaluate expression "Name".
    + CategoryInfo          : InvalidArgument: (:PSObject) [], GetValueInvocationException
    + FullyQualifiedErrorId : mshExpressionError

C:\> DeviceNode dc-1 { $node }

format-default : The following exception occurred while retrieving members: "Failed to resolve any Sensor objects
where 'Name = Uptime1, Device = dc-1'. Could not create a SensorNode."
    + CategoryInfo          : NotSpecified: (:) [format-default], ExtendedTypeSystemException
    + FullyQualifiedErrorId : CatchFromBaseGetMembers,Microsoft.PowerShell.Commands.FormatDefaultCommand
Clone this wiki locally