Unmanaged Resources¶
Unmanaged resources are resources that live in the network that are not yet managed by the orchestrator. They may be of the same type as other resources that are already managed, or something else entirely. The orchestrator can discover unmanaged resources in the network, given proper guidance. This discovery is driven by discovery resources in the model. They express the intent to discover resources of the associated type in the network.
Terminology¶
Discovery resource: The category of resources that express the intent to discover resources in the network. This is an actual resource that is part of the configuration model.
Discovered resource: This is the unit of data that is generated by the discovery process. Each time the discovery process discovers a resource, it creates a record in the inventory for discovered resources. This record contains the set of attributes that define the current state of the discovered resource. Discovered resources only exist in the discovered resources database. They don’t exist in the configuration model.
Example¶
The code snippet below defines a discovery resource called InterfaceDiscovery
. Instances of this resource will
discover the interfaces present on a specific host. A discovery resource must always inherit from
std::DiscoveryResource
. Note that discovery resources are defined in exactly the same way as a
regular resource, except that they inherit from std::DiscoveryResource
instead of std::PurgeableResource
or
std::Resource
.
1import ip
2
3entity InterfaceDiscovery extends std::DiscoveryResource:
4 """
5 A discovery resource that discovers interfaces on a specific host.
6
7 :attr name_filter: If not null, only discover interfaces for which the
8 name matches this regular expression.
9 Otherwise discover all the interfaces on the host.
10 """
11 string? name_filter = null
12end
13
14InterfaceDiscovery.host [1] -- ip::Host
15
16index InterfaceDiscovery(host)
17
18implement InterfaceDiscovery using parents, std::none
The associated handler code is shown below:
1import re
2from collections import abc
3
4import pydantic
5
6from inmanta import resources
7from inmanta.types import ResourceIdStr
8from inmanta.agent.handler import provider, DiscoveryHandler, HandlerContext
9from inmanta.resources import resource, DiscoveryResource
10
11
12@resource("my_module::InterfaceDiscovery", agent="host.name", id_attribute="host")
13class InterfaceDiscovery(DiscoveryResource):
14 fields = ("host", "name_filter")
15
16 host: str
17 name_filter: str
18
19 @staticmethod
20 def get_host(exporter, resource):
21 return resource.host.name
22
23
24class UnmanagedInterface(pydantic.BaseModel):
25 """
26 Datastructure used by the InterfaceDiscoveryHandler to return the attributes
27 of its discovered resources.
28 """
29
30 host: str
31 interface_name: str
32 ip_address: str
33
34
35@provider("my_module::InterfaceDiscovery", name="interface_discovery_handler")
36class InterfaceDiscoveryHandler(DiscoveryHandler[InterfaceDiscovery, UnmanagedInterface]):
37 def discover_resources(
38 self, ctx: HandlerContext, discovery_resource: InterfaceDiscovery
39 ) -> dict[ResourceIdStr, UnmanagedInterface]:
40 """
41 Entrypoint that is called by the agent when the discovery resource is deployed.
42 """
43 discovered: abc.Iterator[UnmanagedInterface] = (
44 UnmanagedInterface(**attributes)
45 for attributes in self._get_discovered_interfaces(discovery_resource)
46 if discovery_resource.name_filter is None or re.match(discovery_resource.name_filter, attributes["interface_name"])
47 )
48 return {
49 resources.Id(
50 entity_type="my_module::Interface",
51 agent_name=res.host,
52 attribute="interface_name",
53 attribute_value=res.interface_name,
54 ).resource_str(): res
55 for res in discovered
56 }
57
58 def _get_discovered_interfaces(self, discovery_resource: InterfaceDiscovery) -> list[dict[str, object]]:
59 """
60 A helper method that contains the logic to discover the unmanaged interfaces in the network.
61 It returns a list of dictionaries where each dictionary contains the attributes of an unmanaged resource.
62 """
63 raise NotImplementedError()
The handler code consists of three parts:
Lines 12-21: The class that describes how the discovery resource
InterfaceDiscovery
should be serialized. This resource definition is analogous to the definition of a regularPurgeableResource
orResource
, except that the class inherits fromDiscoveryResource
.Lines 24-32: A Pydantic BaseModel that represents the datastructure that will be used by the discovery handler to return the attributes of the discovered resources. This specific example uses a Pydantic BaseModel, but discovery handlers can use any json serializable datastructure.
Line: 35-63: This is the handler for the discovery resource. A discovery handler class must satisfy the following requirements:
It must be annotated with the
@provider
annotation, like a regularCRUDHandler
orResourceHandler
.It must inherit from the
DiscoveryHandler
class. This is a generic class with two parameters. The first parameter is the class of the associatedDiscoveryResource
and the second parameter is the type of datastructure that the discovery handler will use to return the attributes of discovered resources.It must implement a method called
discover_resources
that contains the logic to discover the resources in the network. This method returns a dictionary. The keys of this dictionary contain the resource ids of the discovered resources and the values the associated attributes.