Classification endpoints

The classification endpoints accepts a node name and a set of facts, and then return information about how the specified node is classified. The output can help you test your node group classification rules.

POST /v1/classified/nodes/<name>

Retrieve a specific node's classification information based on facts supplied in the body of your request.

Request format

When Forming node classifier API requests to this endpoint, the URI path must contain the name of a specific node, and the body can contain a JSON object using these keys:
Key Definition
fact A JSON object containing regular, non-trusted facts associated with the node. The object contains key/value pairs of fact names and fact values. Fact values can be strings, integers, Booleans, arrays, or objects.
trusted A JSON object containing trusted facts associated with the node. The object contains key/value pairs of fact names and fact values. Fact values can be strings, integers, Booleans, arrays, or objects.
Tip: There is also a POST /v2/classified/nodes/<name> endpoint.
Here is an example of a curl command for the /v1/classified/nodes/<name> endpoint:
type_header='Content-Type: application/json'
auth_header="X-Authentication: $(puppet-access show)"
uri="https://$(puppet config print server):4433/classifier-api/v1/classified/nodes/<NAME>"
data='{"fact" : { "<FACT_NAME>" : "<FACT_VALUE>" }}'

curl --insecure --header "$type_header" --header "$auth_header" --request POST "$uri" --data "$data"

Response format

A successful response returns a JSON object using these keys to describe the node's classification:
Key Definition
name The node name, as a string.
groups An array of group IDs for the groups that the node was classified into.
environment The name of the environment that the node uses, which is taken from the node groups the node was classified into.
classes An object containing key/value pairs describing the classes that this node received from the groups it was classified into.

Each key/value pair consists of a class name, as a string, and a subsequent object containing the names and values of parameters within the named class.

parameters An object containing key/value pairs of top-level variables and their assigned values.
For example:
{
  "name": "foo.example.com",
  "groups": ["9c0c7d07-a199-48b7-9999-3cdf7654e0bf", "96d1a058-225d-48e2-a1a8-80819d31751d"],
  "environment": "staging",
  "parameters": {},
  "classes": {
    "apache": {
      "keepalive_timeout": 30,
      "log_level": "notice"
    }
  }
}

Error responses

If there is an error, Node classifier API errors provide error information in the kind key.

If the node is classified into multiple node groups that supply conflicting classifications to the node, the server returns a 500 Server error response.

For classification-conflict errors, the msg describes generally why the conflict happened, and the details contains an object that uses the environment, variables, or classes key to indicate the type of conflict (whether it was in setting the environment, setting variables, or setting class parameters). Each key contains value-detail objects describing the specific conflicts:
Value-detail object key Definition
value The specific value having a conflict. For environment and classes, these are strings. For variables, these can be any JSON value type.
from The node group that the node was classified into that caused the conflicting value to be added to the node's classification. Refer to defined_by for further details.
defined_by The node group that actually defined the conflicting value. This is often the from group, but could be an ancestor of that group, due to How node group inheritance works.
The following example demonstrates a conflicting value being inherited from an ancestor group and a conflicting value supplied directly from the assigned node group. The conflicting value Blue Suede Shoes was included in the classification because the node matched the Elvis Presley group (as indicated by from). However, the conflicting value was actually defined by the Carl Perkins group, which is an ancestor of the Elvis Presley group. This caused the child group to inherit the value from the ancestor group. The Since You've Been Gone conflicting value is defined by the same group that the node was assigned to.
{
  "kind": "classification-conflict",
  "msg": "The node was classified into multiple unrelated groups that defined conflicting class parameters or top-level variables. See `details` for a list of the specific conflicts.",
  "details": {
    "classes": {
      "songColors": {
        "blue": [
          {
            "value": "Blue Suede Shoes",
            "from": {
              "name": "Elvis Presley",
              "classes": {},
              "rule": ["=", "nodename", "the-node"],
              ...
            },
            "defined_by": {
              "name": "Carl Perkins",
              "classes": {"songColors": {"blue": "Blue Suede Shoes"}},
              "rule": ["not", ["=", "nodename", "the-node"]],
              ...
            }
          },
          {
            "value": "Since You've Been Gone",
            "from": {
              "name": "Aretha Franklin",
              "classes": {"songColors": {"blue": "Since You've Been Gone"}},
              ...
            },
            "defined_by": {
              "name": "Aretha Franklin",
              "classes": {"songColors": {"blue": "Since You've Been Gone"}},
              ...
            }
          }
        ]
      }
    }
  }
}

POST /v1/classified/nodes/<name>/explanation

Retrieve a detailed explanation about how a node is classified based on facts supplied in the body of your request.

Request format

When Forming node classifier API requests to this endpoint, the URI path must contain the name of a specific node, and the body can contain a JSON object using these keys:
Key Definition
fact A JSON object containing regular, non-trusted facts associated with the node. The object contains key/value pairs of fact names and fact values. Fact values can be strings, integers, Booleans, arrays, or objects.
trusted A JSON object containing trusted facts associated with the node. The object contains key/value pairs of fact names and fact values. Fact values can be strings, integers, Booleans, arrays, or objects.
For example:
{
  "fact": {
    "ear-tips": "pointed",
    "eyebrow pitch": "40",
    "hair": "dark",
    "resting bpm": "120",
    "blood oxygen transporter": "hemocyanin",
    "anterior tricuspids": "2",
    "appendices": "1",
    "spunk": "10"
  }
}

Response format

The response is a JSON object describing how the node would be classified based on the submitted facts.
  • If the node would be successfully classified, the response object contains the successful classification outcome.
  • If the classification would fail due to conflicts, the response object describes the conflicts.

The response is intended to provide insight into the classification process, so that, if a node isn't classified as expected, you can trace the classification sequence to the source of the deviation.

Classification proceeds in this order:
  1. All node group rules are tested on the node's facts and name. Groups that don't match the node are culled, leaving only the matching groups. This step contributes to the match_explanations key in the response body.
  2. Inheritance relations are used to further cull the matching groups, by removing any matching node group that has a descendant that is also a matching node group. The remaining node groups are referred to as leaf groups. This step contributes to the leaf_groups key in the response body.
  3. Each leaf group is transformed into its inherited classification by adding all the inherited class and class parameter values from their ancestors. This step contributes to the inherited_classifications key in the response body.
  4. All inherited classifications and individual node classifications are inspected for conflicts. A conflict occurs whenever two inherited classifications define different values for the same environment, class parameter, or top-level variable. This step contributes to the conflicts key in the response body.
  5. Any individual node classifications (including classes, class parameters, configuration data, and variables) are added. This step contributes to the individual_classification key in the response body.
  6. Individual node classifications are applied to the group classification, which forms the final classification. This step contributes to the final_classification key in the response body.
The response's JSON object uses these keys to describe the classification:
Key Definition
match_explanations An object containing group ID's the node matched to. For each group ID, there is an object explaining why the node matched that particular group's rules.
leaf_groups This key's value is an array of the leaf groups. This represent a condensed list of matching groups after filtering out any matching node group that had a descendant that was also a matching node group.
inherited_classifications This key's value is an object mapping a leaf group's ID to the classification values provided by that group (including inheritance from ancestors).
conflicts This key is present only if there are conflicts in the inherited classifications. For each conflict there is an array of conflict details. Each of these details is an object with three keys: value, from, and defined_by. The value key is a conflicting value, the from key is the group whose classification provided the conflicting value, and the defined_by key is the group that actually defined the value (which can be an ancestor of the from group).
individual_classification This key's value includes classes, class parameters, configuration data, and variables applied directly to the node.
final_classification This key is present only if there are no conflicts between the inherited classifications. Its value is the result of merging all individual node classifications and group classifications.
node_as_received Represents the node object as defined in the request, including the name and facts (if supplied).
classification_source An annotated version of the node's classification where the environment, each class parameter, and each variable are replaced with an annotated value object.
This example shows a successful (non-conflicting) classification response:
{
  "node_as_received": {
    "name": "Tuvok",
    "trusted": {},
    "fact": {
      "ear-tips": "pointed",
      "eyebrow pitch": "30",
      "blood oxygen transporter": "hemocyanin",
      "anterior tricuspids": "2",
      "hair": "dark",
      "resting bpm": "200",
      "appendices": "0",
      "spunk": "0"
    }
  },
  "match_explanations": {
    "00000000-0000-4000-8000-000000000000": {
      "value": true,
      "form": ["~", {"path": "name", "value": "Tuvok"}, ".*"]
    },
    "8aeeb640-8dca-4b99-9c40-3b75de6579c2": {
      "value": true,
      "form": ["and",
               {
                 "value": true,
                 "form": [">=", {"path": ["fact", "eyebrow pitch"], "value": "30"}, "25"]
               },
               {
                 "value": true,
                 "form": ["=", {"path": ["fact", "ear-tips"], "value": "pointed"}, "pointed"]
               },
               {
                 "value": true,
                 "form": ["=", {"path": ["fact", "hair"], "value": "dark"}, "dark"]
               },
               {
                 "value": true,
                 "form": [">=", {"path": ["fact", "resting bpm"], "value": "200"}, "100"]
               },
               {
                 "value": true,
                 "form": ["=",
                          {
                            "path": ["fact", "blood oxygen transporter"],
                            "value": "hemocyanin"
                          },
                          "hemocyanin"
                 ]
               }
      ]
    }
  },
  "leaf_groups": {
    "8aeeb640-8dca-4b99-9c40-3b75de6579c2": {
      "name": "Vulcans",
      "id": "8aeeb640-8dca-4b99-9c40-3b75de6579c2",
      "parent": "00000000-0000-4000-8000-000000000000",
      "rule": ["and", [">=", ["fact", "eyebrow pitch"], "25"],
                      ["=", ["fact", "ear-tips"], "pointed"],
                      ["=", ["fact", "hair"], "dark"],
                      [">=", ["fact", "resting bpm"], "100"],
                      ["=", ["fact", "blood oxygen transporter"], "hemocyanin"]
      ],
      "environment": "alpha-quadrant",
      "variables": {},
      "classes": {
        "emotion": {"importance": "ignored"},
        "logic": {"importance": "primary"}
      },
      "config_data": {
        "USS::Voyager": {"designation": "subsequent"}
      }
    }
  },
  "inherited_classifications": {
    "8aeeb640-8dca-4b99-9c40-3b75de6579c2": {
      "environment": "alpha-quadrant",
      "variables": {},
      "classes": {
        "logic": {"importance": "primary"},
        "emotion": {"importance": "ignored"}
      },
      "config_data": {
        "USS::Enterprise": {"designation": "original"},
        "USS::Voyager": {"designation": "subsequent"}
      }
    }
  },
  "individual_classification": {
    "classes": {
      "emotion": {
        "importance": "secondary"
      }
    },
    "variables": {
      "full_name": "S'chn T'gai Spock"
    }
  },
  "final_classification": {
    "environment": "alpha-quadrant",
    "variables": {
      "full_name": "S'chn T'gai Spock"
    },
    "classes": {
      "logic": {"importance": "primary"},
      "emotion": {"importance": "secondary"}
    },
    "config_data": {
      "USS::Enterprise": {"designation": "original"},
      "USS::Voyager": {"designation": "subsequent"}
    }
  },
  "classification_sources": {
    "environment": {
      "value": "alpha-quadrant",
      "sources": ["8aeeb640-8dca-4b99-9c40-3b75de6579c2"]
    },
    "variables": {},
    "classes": {
      "emotion": {
        "puppetlabs.classifier/sources": ["8aeeb640-8dca-4b99-9c40-3b75de6579c2"],
        "importance": {
          "value": "secondary",
          "sources": ["node"]
        }
      },
      "logic": {
        "puppetlabs.classifier/sources": ["8aeeb640-8dca-4b99-9c40-3b75de6579c2"],
        "importance": {
          "value": "primary",
          "sources": ["8aeeb640-8dca-4b99-9c40-3b75de6579c2"]
        }
      },
      "config_data": {
        "USS::Enterprise": {
          "designation": {
            "value": "original",
            "sources": ["00000000-0000-4000-8000-000000000000"]
          }
        },
        "USS::Voyager": {
          "designation": {
            "value": "subsequent",
            "sources": ["8aeeb640-8dca-4b99-9c40-3b75de6579c2"]
          }
        }
      }
    }
  }
}
This example shows a conflicting classification response:
{
  "node_as_received": {
    "name": "Spock",
    "trusted": {},
    "fact": {
      "ear-tips": "pointed",
      "eyebrow pitch": "40",
      "blood oxygen transporter": "hemocyanin",
      "anterior tricuspids": "2",
      "hair": "dark",
      "resting bpm": "120",
      "appendices": "1",
      "spunk": "10"
    }
  },
  "match_explanations": {
    "00000000-0000-4000-8000-000000000000": {
      "value": true,
      "form": ["~", {"path": "name", "value": "Spock"}, ".*"]
    },
    "a130f715-c929-448b-82cd-fe21d3f83b58": {
      "value": true,
      "form": [">=", {"path": ["fact", "spunk"], "value": "10"}, "5"]
    },
    "8aeeb640-8dca-4b99-9c40-3b75de6579c2": {
      "value": true,
      "form": ["and",
               {
                 "value": true,
                 "form": [">=", {"path": ["fact", "eyebrow pitch"], "value": "30"}, "25"]
               },
               {
                 "value": true,
                 "form": ["=", {"path": ["fact", "ear-tips"], "value": "pointed"}, "pointed"]
               },
               {
                 "value": true,
                 "form": ["=", {"path": ["fact", "hair"], "value": "dark"}, "dark"]
               },
               {
                 "value": true,
                 "form": [">=", {"path": ["fact", "resting bpm"], "value": "200"}, "100"]
               },
               {
                 "value": true,
                 "form": ["=",
                          {
                            "path": ["fact", "blood oxygen transporter"],
                            "value": "hemocyanin"
                          },
                          "hemocyanin"
                 ]
               }
      ]
    }
  },
  "leaf_groups": {
                   "a130f715-c929-448b-82cd-fe21d3f83b58": {
                     "name": "Humans",
                     "id": "a130f715-c929-448b-82cd-fe21d3f83b58",
                     "parent": "00000000-0000-4000-8000-000000000000",
                     "rule": [">=", ["fact", "spunk"], "5"],
                     "environment": "alpha-quadrant",
                     "variables": {},
                     "classes": {
                       "emotion": {"importance": "primary"},
                       "logic": {"importance": "secondary"}
                     }
                   },
                   "8aeeb640-8dca-4b99-9c40-3b75de6579c2": {
                     "name": "Vulcans",
                     "id": "8aeeb640-8dca-4b99-9c40-3b75de6579c2",
                     "parent": "00000000-0000-4000-8000-000000000000",
                     "rule": ["and", [">=", ["fact", "eyebrow pitch"], "25"],
                                     ["=", ["fact", "ear-tips"], "pointed"],
                                     ["=", ["fact", "hair"], "dark"],
                                     [">=", ["fact", "resting bpm"], "100"],
                                     ["=", ["fact", "blood oxygen transporter"], "hemocyanin"]
                     ],
                     "environment": "alpha-quadrant",
                     "variables": {},
                     "classes": {
                       "emotion": {"importance": "ignored"},
                       "logic": {"importance": "primary"}
                     }
                   }
  },
  "inherited_classifications": {
    "a130f715-c929-448b-82cd-fe21d3f83b58": {
      "environment": "alpha-quadrant",
      "variables": {},
      "classes": {
        "logic": {"importance": "secondary"},
        "emotion": {"importance": "primary"}
      }
    },
    "8aeeb640-8dca-4b99-9c40-3b75de6579c2": {
      "environment": "alpha-quadrant",
      "variables": {},
      "classes": {
        "logic": {"importance": "primary"},
        "emotion": {"importance": "ignored"}
      }
    }
  },
  "conflicts": {
    "classes": {
      "logic": {
        "importance": [
                        {
                          "value": "secondary",
                          "from": {
                            "name": "Humans",
                            "id": "a130f715-c929-448b-82cd-fe21d3f83b58",
                            ...
                          },
                          "defined_by": {
                            "name": "Humans",
                            "id": "a130f715-c929-448b-82cd-fe21d3f83b58",
                            ...
                          }
                        },
                        {
                          "value": "primary",
                          "from": {
                            "name": "Vulcans",
                            "id": "8aeeb640-8dca-4b99-9c40-3b75de6579c2",
                            ...
                          },
                          "defined_by": {
                            "name": "Vulcans",
                            "id": "8aeeb640-8dca-4b99-9c40-3b75de6579c2",
                            ...
                          }
                        }
        ]
      },
      "emotion": {
        "importance": [
                        {
                          "value": "ignored",
                          "from": {
                            "name": "Vulcans",
                            "id": "8aeeb640-8dca-4b99-9c40-3b75de6579c2",
                            ...
                          },
                          "defined_by": {
                            "name": "Vulcans",
                            "id": "8aeeb640-8dca-4b99-9c40-3b75de6579c2",
                            ...
                          }
                        },
                        {
                          "value": "primary",
                          "from": {
                            "name": "Humans",
                            "id": "a130f715-c929-448b-82cd-fe21d3f83b58",
                            ...
                          },
                          "defined_by": {
                            "name": "Humans",
                            "id": "a130f715-c929-448b-82cd-fe21d3f83b58",
                            ...
                          }
                        }
        ]
      }
    }
  },
  "individual_classification": {
    "classes": {
      "emotion": {
        "importance": "secondary"
      }
    },
    "variables": {
      "full_name": "S'chn T'gai Spock"
    }
  }
}

Error responses

If there is an error, Node classifier API errors provide error information in the kind key.