Register different get_next/get_elem callbacks for different keys of a list

I have a list element with a specific leaf element as key. I have a container within the list for which I define a callpoint.

   list list1
   {
      key "list1-id";
      leaf list1-id
      {
         type list1-id-type;
      }
      container state
      {
         description "List1 state";
         config false;
         tailf:callpoint list1-state;m
         leaf op-state
         {
            description "List1 operational state";
            type op-state-type;
         }
         leaf internal-state
         {
            description "List1 internal state machine value";
            type string;
         }
      } // state
   } // list1

When I register callbacks for get_next() and get_elem() for every single key from every process, it works perfectly fine.

int GetElemList1( struct confd_trans_ctx *tctx, confd_hkeypath_t *keypath )
{
   GlobalStats& stats = GetGlobalStats();
   confd_value_t v;

   uint32_t xmlTag = CONFD_GET_XMLTAG( &( keypath->v[0][0] ) );

   LogTransactionUsage( tctx->thandle, __FUNCTION__ );

   switch ( xmlTag )
   {
      case op_state:
         CONFD_SET_ENUM_HASH( &v, atomic32_get( &stats.list1OperState ) );
         break;
      case internal_state:
         CONFD_SET_STR( &v, ( char * ) CtrlGetList1StateStr() );
         break;
      default:
         lfPrintf( "ERROR: bad switch in function %s, didn't recognize XMLTAG %d",
                     __FUNCTION__, xmlTag );
         return CONFD_ERR;
   }     
         
   confd_data_reply_value( tctx, &v );
         
   return CONFD_OK;
}     

int GetNextList1( struct confd_trans_ctx *tctx, confd_hkeypath_t *keypath, long next )
{
   confd_value_t v[1];
   int nextIdx = FINISHED_ITERATING;

   LogTransactionUsage( tctx->thandle, __FUNCTION__ );

   if ( next == START_ITERATING )
   {
      /* this path is expected */
      nextIdx = 0;
      CONFD_SET_UINT8( &v[0], GetList1Id() );
      confd_data_reply_next_key( tctx, &v[0], 1, nextIdx );
   }
   else
   {
      /* this path is not expected, if we limited our range to one list1 ID,
         why would ConfD call getnext more than once. */
      lfPrintf( "ERROR: ConfD attempted to get list1 id more than once");
      confd_data_reply_next_key( tctx, NULL, FINISHED_ITERATING,
                                 FINISHED_ITERATING );
   }

   return CONFD_OK;
}


   struct confd_data_cbs list1CallbackData;
   tca_memset( &list1CallbackData, 0, sizeof( struct confd_data_cbs ) );
   list1CallbackData.get_elem = GetElemList1;
   list1CallbackData.get_next = GetNextList1;

   confd_value_t list1IdRange[1];
   CONFD_SET_STR( &list1IdRange[0], ( char * ) GetMyList1Key() );

   strncpy( list1CallbackData.callpoint, "list1-state", MAX_CALLPOINT_LEN );
   if ( CONFD_ERR == confd_register_range_data_cb( dctx, &list1CallbackData,
                                                   &list1IdRange[0],
                                                   &list1IdRange[0], 1,
                                                   "/list1" ) )
   {
      printf( "ERROR: Failed to register List1 State callback");
      return FALSE;
   }

The above code executes from 8 different processes and the value of GetMyList1Key() returns an unique key in each process and it works fine.

If I want to register 2 different callbacks for 2 different keys from same process, I add the below in addition to above.

int GetNextPeerList1( struct confd_trans_ctx *tctx, confd_hkeypath_t *keypath, long next )
{
   confd_value_t v[1];
   int nextIdx = FINISHED_ITERATING;

   LogTransactionUsage( tctx->thandle, __FUNCTION__ );

   if ( next == START_ITERATING )
   {
      /* this path is expected */
      nextIdx = 0;
      CONFD_SET_UINT8( &v[0], GetPeerList1Id() );
      confd_data_reply_next_key( tctx, &v[0], 1, nextIdx );
   }
   else
   {
      /* this path is not expected, if we limited our range to one list1 ID,
         why would ConfD call getnext more than once. */
      lfPrintf( "ERROR: ConfD attempted to get list1 id more than once" );
      confd_data_reply_next_key( tctx, NULL, FINISHED_ITERATING,
                                 FINISHED_ITERATING );
   }

   return CONFD_OK;
}


int GetElemPeerList1( struct confd_trans_ctx *tctx, confd_hkeypath_t *keypath )
{
   confd_value_t v;

   uint32_t xmlTag = CONFD_GET_XMLTAG( &( keypath->v[0][0] ) );

   LogTransactionUsage( tctx->thandle, __FUNCTION__ );

   switch ( xmlTag ) 
   {
      case viasat_op_state:
         CONFD_SET_ENUM_HASH( &v, list1PeerOperState );
         break;
      default:
         break;
   }

   confd_data_reply_value( tctx, &v );

   return CONFD_OK;
}

      /*
       * KEY2 will not get launched - register dummy handler.
       */
      struct confd_data_cbs peerList1CallbackData;
      tca_memset( &peerList1CallbackData, 0, sizeof( struct confd_data_cbs ) );
      peerList1CallbackData.get_elem = GetElemPeerList1;
      peerList1CallbackData.get_next = GetNextPeerList1;

      confd_value_t peerList1IdRange[1];
      CONFD_SET_STR( &peerList1IdRange[0], ( char * ) GetPeerList1Key() );

      strncpy( peerList1CallbackData.callpoint, "list1-state", MAX_CALLPOINT_LEN );
      if ( CONFD_ERR == confd_register_range_data_cb( dctx, &peerList1CallbackData,
                                                      &peerList1IdRange[0],
                                                      &peerList1IdRange[0], 1,
                                                      "/list1" ) )
      {
         lfPrintf( "ERROR: Failed to register List1 State callback" );
         return FALSE;
      }

When I do this, I see individual “show list1 <key1> state” and “show list1 <key2/peerkey> state” working fine(which means GetElemXXX works fine for both keys ?). However, “show list1 state” does not work fine and shows application communication error(which means GetNextXXX does not work as expected ?)

Can you please clarify on what is the issue with the above code and why it does not work ? Any inputs appreciated.

First and foremost, for all data-provider related issues you should start with setting developerLogLevel to trace and looking into devel.log - every DP callback is logged there as well as if there is something wrong with the response. Then, I can see two issues with your code:

  • The get_next callbacks should not be needed - as your only list in the data model you pasted here is configurable, the callback is never invoked by ConfD.

  • The second of your get_elem callbacks, GetElemPeerList1, handles only op-state and ignores internal-state - that might be what is causing the problem you are dealing with.

Thanks a lot Martin Volf !!
I added the case for internal-state and it started to work !!

int GetElemPeerList1( struct confd_trans_ctx *tctx, confd_hkeypath_t *keypath )
{
   confd_value_t v;

   uint32_t xmlTag = CONFD_GET_XMLTAG( &( keypath->v[0][0] ) );

   LogTransactionUsage( tctx->thandle, __FUNCTION__ );

   switch ( xmlTag ) 
   {
      case viasat_op_state:
         CONFD_SET_ENUM_HASH( &v, list1PeerOperState );
         break;
      **case viasat_internal_state:**
**         CONFD_SET_STR( &v, "N/A" );**
**         break;**
      default:
         break;
   }

   confd_data_reply_value( tctx, &v );

   return CONFD_OK;
}