Forum Discussion

TADAndyMarshall's avatar
TADAndyMarshall
Contributor II
2 months ago

How Do I Set The Parent Device Of A Device?

Hello, I hope someone can assist with the following. I am using Device Component Mapping to create a hierarchical/tree structure of devices, but SL seems to assign the Parent Device attribute randomly for each device created, and I need to be able to set the Parent Device correctly, so that the hierarchical structure is correct.

I have data that contain a set of locations that correspond to the tree structure. The structure has this concept: One Country -> Many Cities -> Many Buildings -> Many Rooms. Each data record has an id, a name, a parent id and a type. The data is split into cities, buildings and rooms. For example, a city record might be as follows: id: CITYPARIS, name: Paris, parent id: CTRYFRANCE, type: city. A building record might be as follows: id: BLDGMONTPARNASSE, name: Tour Montparnasse, parent id: CITYPARIS, type: building. And to complete the picture, a room record might be as follows: id: ROOMMEETING1, name: Meeting Room 1, parent id: BLDGMONTPARNASSE, type: room.

When I create one of the 'tree' levels of devices (it could be city, or building, or room), in my Dynamic Application, the code snippet extracts cached data where the type is 'city', then passes the extracted data to the result handler, which populates the four collection fields: id, name, parent_id, type. The id field is set to be the Unique Identifier, the name field is set to be the Device Name, and the type field is set to be Class Identifier 1 (I've created device classes for each tree level, allows me to have funky icons to distinguish the devices visually). The DA then creates devices for each record found, and I see them appearing on the Device Manager screen. However, when I examine each device, the Parent Device that is set seems to be random.  For example, three city devices created (Paris, Nantes, Poitiers) all were assigned a Parent Device of 'Paris', the name of one of the devices created, instead of using their own parent_id values.

Is there a way to tell SL what the Parent Device for each device record should be, perhaps in the snippet or somewhere else? Can I use one of the field values extracted from the cached data (ideally, my parent_id) as the value for the Parent Device field?

This is my first post here, so apologies if this is in the wrong place!

  • When you create an app with Dynamic Component Mapping checked, that causes children to be created. That is when the parent device is assigned. To create a device, you must set three fields: name, unique_id, and distinguished_name. The unique_id field is how the system knows that the component is unique within a DCM tree.

    If you have duplicates of that field, the system will see it as though the same device is being discovered by different parents. A device can only have one parent which means that the system interprets the second device discovery with the same UID as the device being moved from one parent to the other.

    Reading through the PDF that you shared, it sounds like you're doing discovery from the bottom to the top. In our system, you must do discovery from top to bottom. So you would have apps setup like this:
    Country Discovery (this creates your root devices)
        -> Aligns State Discovery to children
        -> Probably aligns a configuration app with details on the country
    State Discovery
        -> Aligns city discovery to children
        -> Probably aligns a configuration app with details on the state
    City Discovery
        -> Aligns building discovery to children
        -> Probably aligns a configuration app with details on the city
    Building Discovery
        -> Aligns collection apps to children to collect performance/config data

    If you want to have an app that goes down to the room level, you keep following that pattern. The data you're getting from the API is oriented around locations. When processing it for use, there are a couple of ways that you could prepare it. One would be to take all of the location data and sort it by its level in the hierarchy. Then you can build a tree in memory that matches the correct order, top to bottom. Each discovery app would then look at its part of the tree to find its children and then build the next layer.

    The other way to do it would be to get all of the data in a big list and then when creating the app at each level, you go through each location record and let through the ones that have the current device listed as a parent.  For example, you would align country discovery to a reoot device. At the country level, you would output all countries that you find. At the state level, you would look at which country your device represents and then you would output all of the states with that as its parent. For each level below that, you would do the same thing.

    Looking at your code, you are discovering all devices for each parent. That's causing it them to move randomly between devices since whichever device runs that app last gets that child device. In your code, you're setting a value called PID and returning it to the platform. Rather than return it to the platform, check to see if the parent ID equals the UID of the current device (self.unique_id is, I believe the variable name). If it does, collect it, if not, don't include that device in the list.

    Taking a look at how you're processing the data, this would work really well as a snippet framework app. You would have either your requester step get the data from the API and then parse and process it. You can filter out values based on the unique_id using the jmespath parser and variable substitution. This will help future-proof your apps since it'll be using our official framework and will be able to take advantage of all of the logging and performance work that we do for the snippet framework. 

  • Are you writing all custom python or are you using any of our libraries or low-code frameworks? In theory this should be a simple operation and it would be good to confirm this isn't being over complicated with custom stuff. 

    You do not specify a parent of a component, the parent specifies the children components. When a Dynamic Application that is aligned to a device discovers a new component, that child device is aligned to the device the DA is aligned to if it's new. If you use the UUID or GUID component identifier for a collection object in the Dynamic Application discovering devices, SL1 will use that value to move an existing device with that same value to become a child of the device with that DA aligned that discovered it. It's important to remember that when a DA is aligned to a device, you should think of that as a separate instance of that Dynamic Application running, so if you have a DA that discovers devices aligned to mulitple devices, you may have the same action taking place across multiple devices. 

    Device -> Dynamic Application with component mapping enabled aligned to that device -> child component device discovered via that DA is aligned as a child of that device 

    If a DA is aligned to two devices and both return the same list of children devices, SL1 is going to discover those devices as children under both unless you specify a component identifier to inform SL1 that those children are the same device, at which point it'll be a race condition where whichever device-DA combination runs last will move that component to be a child of that device. 


    TL;DR: Parent device -> DA aligned -> that DA running against that device discovers a record to be modeled to a component -> that new component is child of the device the DA was running against 

    If you see any other behavior, something odd is happening. Most commonly it's two instances of a DA aligned to two different devices are discovering the "same" component and moving it back and forth between parents if you are using UUID or GUID component identifiers.

    • TADAndyMarshall's avatar
      TADAndyMarshall
      Contributor II

      Thanks for responding so promptly Taylor! I am using custom Python. I am relatively new to the SL platform, only a few weeks of experience/exposure (when added together), so it's highly likely my lack of knowledge is the cause, rather than the platform.

      This is what I did...  I created a 'base' device, let's call it 'Tree'.  I created a DA to retrieve the data and to cache it, and added that to the Collections on the Tree device. I then created three further DAs, 'Create City', 'Create Building', Create Room'; all three were set to consume cached data, and to use Dynamic Component Modelling. They all had the same DA Collection fields (id, name, parent_id, type). They all had custom Python snippets to read the cached data. They all have their own Device Class. I added all three to the Collections on the Tree device. I waited 5 mins, checked the Device Manager screen, and saw that the devices had been created, but not with the hoped for/expected Parent Device.

      • TaylorJohnson's avatar
        TaylorJohnson
        Icon for Moderator rankModerator

        Can you provide some screenshots of the DCM tree or the Device Component page showing what you're seeing? 

        It's generally not advised to use the built in caching feature in SL1 anymore. If you're doing REST API calls or CLI commands, we have a low code framework available to do collections for these protocols that will handle caching for you. Please be sure to check these out and use one of the toolkits if you're using one of the supported protocols so you can avoid writing 100% custom stuff, which will help immensely here https://support.sciencelogic.com/s/sl1-studio

        So for "Create City" this is a Snippet Configuration Dynamic Application with the "component mapping" box checked? If you run this DA in debug mode, do you see data collected for the devices you're expecting to be modeled as children? 

         

  • When you create an app with Dynamic Component Mapping checked, that causes children to be created. That is when the parent device is assigned. To create a device, you must set three fields: name, unique_id, and distinguished_name. The unique_id field is how the system knows that the component is unique within a DCM tree.

    If you have duplicates of that field, the system will see it as though the same device is being discovered by different parents. A device can only have one parent which means that the system interprets the second device discovery with the same UID as the device being moved from one parent to the other.

    Reading through the PDF that you shared, it sounds like you're doing discovery from the bottom to the top. In our system, you must do discovery from top to bottom. So you would have apps setup like this:
    Country Discovery (this creates your root devices)
        -> Aligns State Discovery to children
        -> Probably aligns a configuration app with details on the country
    State Discovery
        -> Aligns city discovery to children
        -> Probably aligns a configuration app with details on the state
    City Discovery
        -> Aligns building discovery to children
        -> Probably aligns a configuration app with details on the city
    Building Discovery
        -> Aligns collection apps to children to collect performance/config data

    If you want to have an app that goes down to the room level, you keep following that pattern. The data you're getting from the API is oriented around locations. When processing it for use, there are a couple of ways that you could prepare it. One would be to take all of the location data and sort it by its level in the hierarchy. Then you can build a tree in memory that matches the correct order, top to bottom. Each discovery app would then look at its part of the tree to find its children and then build the next layer.

    The other way to do it would be to get all of the data in a big list and then when creating the app at each level, you go through each location record and let through the ones that have the current device listed as a parent.  For example, you would align country discovery to a reoot device. At the country level, you would output all countries that you find. At the state level, you would look at which country your device represents and then you would output all of the states with that as its parent. For each level below that, you would do the same thing.

    Looking at your code, you are discovering all devices for each parent. That's causing it them to move randomly between devices since whichever device runs that app last gets that child device. In your code, you're setting a value called PID and returning it to the platform. Rather than return it to the platform, check to see if the parent ID equals the UID of the current device (self.unique_id is, I believe the variable name). If it does, collect it, if not, don't include that device in the list.

    Taking a look at how you're processing the data, this would work really well as a snippet framework app. You would have either your requester step get the data from the API and then parse and process it. You can filter out values based on the unique_id using the jmespath parser and variable substitution. This will help future-proof your apps since it'll be using our official framework and will be able to take advantage of all of the logging and performance work that we do for the snippet framework. 

  • One other note, you're using the Microsoft execution environment. I don't think you're taking advantage of anything from that PowerPack. If you don't know which one to use, I would recommend using the one from the latest version of Low Code Tools. That will let you take advantage of the snippet framework and will be regularly updated as new releases come out. 

    • TADAndyMarshall's avatar
      TADAndyMarshall
      Contributor II

      Thank you, Josh and Taylor, for your inputs and insights into this problem.

      • TADAndyMarshall's avatar
        TADAndyMarshall
        Contributor II

        As a postscript, I'll add that the parent - child scenario is now working. I updated the source data so that the parent ids were 'real' names instead of the alphanumeric strings, and added a line into each snippet that said something like: if self.comp_name = parent_id and used it to filter the records being added to the result handler. Job done!