ConfD User Community

Same callback is getting invoked for both get & set operation

Hi,
The callpoint is subscribed to rw field. When i am trying to set few fields of that then get_elem callback is getting invoked. If its expected then is there any way when i will be trying to perform set operation, then before the db gets updated the callback will get invoked. Because currently once DB gets updated, successfully then only set_elem callback is getting invoked.

Regards,
Biswajit

When you set something over a northbound interface, e.g. CLI, NETCONF, RESTCONF, etc. you start a transaction. ConfD will as part of the transaction read the existing data, here using get_elem() for a few good reasons to, for example, calculate a “minimum diff” so that the set_elem() callback will not be called for a value that is already set to the same value.

See for example ConfD UG chapter " User sessions and ConfD Transactions" and the confd_lib_dp(3) man page under the confd_register_trans_cb() function for more details on transactions.

Hi Conny,
thanks a lot for you input.
I am facing two problems now:
1.Even if i am creating new list entry still before the set_element call back, get_element call back is getting invoked.
1.As a consequence, i am not able to figure out whether the get_element call back is getting called for “get” operation or for edit-config request.
2.If you have answer for that, then if you can provide a method to read the data which has been passed on edit-config request then I will be able to provide the same in CONFD_SET_STR API.

Regards,
Biswajit

Not that I understand why that matters, it should not for a normal use case, but
see confd_trans_ctx->confd_user_info->context and confd_trans_ctx->confd_trans_mode. Refer to the $CONFD_DIR/include/confd_lib.h file for details.

If you are not using CDB, but have a callpoint that you register callbacks to, you can accumulate the resulting change operations and derive the data from the edit_config from them. However, I do not understand why you use a DP API callpoint if that is what you want to do?

Hi Conny,
we have o-ran-interface.yang which is as below:
module: o-ran-interfaces

augment /if:interfaces/if:interface:
+--rw l2-mtu?             
+--rw alias-macs*         
+--rw vlan-tagging?       
+--rw class-of-service
   +--rw u-plane-marking?            
   +--rw c-plane-marking?            
   +--rw m-plane-marking?            
   +--rw s-plane-marking?            
   +--rw other-marking?              
   +--rw enhanced-uplane-markings* [up-marking-name]
      +--rw up-marking-name     
      +--rw enhanced-marking?   
augment /if:interfaces/if:interface:
+--rw base-interface?   
+--rw vlan-id?          
augment /if:interfaces/if:interface:
+--rw mac-address?      
+--rw port-reference
|  +--rw port-name?     
|  +--rw port-number?   
+--ro last-cleared?     

The flow is like : At first the client will query the interface details as below:

              <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" **message-id="23"** >
            <get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
       <filter type="subtree">
       <**interfaces** xmlns="**urn:ietf:params:xml:ns:yang:ietf-interfaces** ">
          </interfaces>
           </filter>
         </get>
    </rpc>


<rpc-reply
xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="23">
<data
	xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
	<interfaces
		xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
		<interface>
			<name>MAC0</name>
			<type
				xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd
			</type>
			<enabled>true</enabled>
			<mac-address xmlns="urn:o-ran:interfaces:1.0">d4:92:34:fc:00:32
			</mac-address>
			
			<vlan-tagging
				xmlns="urn:o-ran:interfaces:1.0">true
			</vlan-tagging>
		</interface>
	</interfaces>
</data>

Step -2:Client will invoke a edit-config request as below:

 <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="24">
   <edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><target><running/></target> 
     <config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
 <interface>
    <name>cuplane0</name>
    <description>CU Plane Vlan 1</description>
   <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:l2vlan</type>
   <enabled>true</enabled>
   <base-interface xmlns="urn:o-ran:interfaces:1.0">MAC0</base-interface>
   <vlan-id xmlns="urn:o-ran:interfaces:1.0">753</vlan-id>
   <mac-address xmlns="urn:o-ran:interfaces:1.0">d4:92:34:fc:00:32</mac-address>
 </interface>
</interfaces>


For that i have just resgistered a call point as below:

module o-ran-interfaces-ann {

   yang-version 1.1;
   namespace "urn:ietf:params:xml:ns:yang:o-ran-interfaces-ann:1.0";
  prefix o-ran-if-ann;
 import ietf-interfaces {
  prefix if;
}
 import tailf-common {
  prefix tailf;
}
tailf:annotate "/if:interfaces/if:interface" {
tailf:callpoint cb_op_if_interface;
  }
}

For the step-1 I can manage with get-elem & get-next .But when same get-element call back is getting hit for step-2 as well, then I am not able to proceed further. Because if its a get-operation i dont need to send any value for base-interface.Where as for edit-config request i have to set it which i am trying handle on same get-element call back.
But if you have any sample application which can be used as above requirement,will be really helpful.
Also if you can help me in accumulate the resulting change operations and derive the data
,then it will be really helpful.
If you have any other suggestion which can fulfil our request then it will be more helpful.

Regards,
Biswajit

“get” callbacks can be invoked by ConfD as existence check → to see if such item is or is not present already in “database” hidden behind your callback implementation. It let’s ConfD know whether it should invoke your create/set callbacks for edit-config NETCONF operation.

I assume you normally reply with something like confd_data_reply_value() or similar. If requested item does not exist, just use the confd_data_reply_not_found() instead. ConfD will most probably subsequently invoke your set/create callbacks as it has been told (by your get callback) that this record does not exist (yet).

Hi Joseph,
As you suggested, in get_element callback if the interface has not been created then i am calling confd_data_reply_not_found.

struct interface* s = find_interface(&(kp->v[1][0]));
if (s ==NULL){
printf("NO Interface\n");
confd_data_reply_not_found(tctx);
    return CONFD_OK;
}

Now dont know why set_element call back is not getting invoked.
Log:
TRACE CALL data get_elem(thandle=2248,/interfaces/interface{cuplane1}/name)NO Interface
(NOEXISTS) → CONFD_OK
TRACE CALL data get_elem(thandle=2248,/interfaces/interface{cuplane1}/vlan-tagging)NO Interface
(NOEXISTS) → CONFD_OK
TRACE CALL data get_elem(thandle=2248,/interfaces/interface{cuplane1}/vlan-tagging)NO Interface
(NOEXISTS) → CONFD_OK
TRACE NULL trans finish(thandle=2248)
TRACE Close user sess 14
→ CONFD_OK

Can you please help why set_element is not getting invoked even if i have provided callback for “set_elem” also for “create”

Regards,
Biswajit

as with any potential problem in regard to “external application” using confd-lib, try enabling developer log in ConfD - check whether sequence of callback invocations matches the one reported by your app and if it possibly shows some extra error or info…

Hi Joseph,
I have already enabled developer log. But there is no message seen on devel.log & confd.log.
The edit-config is failing as i am not able to set the node data in confd provided in rpc.

#netconf-console set.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="32">
     <rpc-error>
    <error-type>application</error-type>
    <error-tag>unknown-element</error-tag>
    <error-severity>error</error-severity>
    <error-path xmlns:o-ran-int="urn:o-ran:interfaces:1.0" 
    xmlns:if="urn:ietf:params:xml:ns:yang:ietf-interfaces">
      /rpc/edit-config/config/if:interfaces/if:interface[if:name='cuplane1']/o-ran-int:vlan-tagging
    </error-path>
     <error-message xml:lang="en">/interfaces/interface[name='cuplane1']/o-ran-int:vlan-tagging: the 'when' expression "if:type = 'ianaift:ethernetCsmacd'" failed</error-message>
    <error-info>
      <bad-element>vlan-tagging</bad-element>
    </error-info>
    </rpc-error>
    </rpc-reply>

set.xml content:

<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="32">
<edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<target><running/></target>
<config>

      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
      <interface>
      <name>cuplane</name>
      <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:l2vlan</type>
      <vlan-id xmlns="urn:o-ran:interfaces:1.0">408</vlan-id>
      <mac-address xmlns="urn:o-ran:interfaces:1.0">00:1e:3e:0f:fc:1b</mac-address>
      </interface>
      </interfaces>
      </config>
      </edit-config>
      </rpc>

sample Code:

     struct interface {
    char name[256];
    struct in_addr ip;
    unsigned int port;
    u_int8_t mac[6];
    u_int16_t vlan;
    char baseName[256];
    };

     static struct interface running_db[256];
     static int num_interface = 0;
      static struct confd_daemon_ctx *dctx;
     static int ctlsock;
    static int workersock;

    static int s_init(struct confd_trans_ctx *tctx)
    {
      confd_trans_set_fd(tctx, workersock);
      return CONFD_OK;
    }
      static struct interface *find_interface(confd_value_t *v)
      {
          int i;
          for (i=0; i< num_interface; i++) {
              if (confd_svcmp(running_db[i].name, v) == 0){
          printf("#########Find:%d\n",i);
                  return &running_db[i];
      	}
          }
          return NULL;
      }

      static struct interface *add_interface(char *name)
      {
          int i, j;
      
          for (i=0; i < num_interface; i++) {
              if (strcmp(running_db[i].name, name) > 0) {
                  /* found the position to add at, now shuffle the */
                  /* remaining elems in the array one step */
                  for (j = num_interface; j > i; j--) {
                      running_db[j] = running_db[j-1];
                  }
                  break;
              }
          }
          num_interface++;
          memset(&running_db[i], 0, sizeof(struct interface));
          strcpy(running_db[i].name, name);
          return &running_db[i];
      }
	#if 1 
	static int hw_get_optional(struct confd_trans_ctx *tctx, confd_hkeypath_t * kp)
	{
		int tag=CONFD_GET_XMLTAG(&kp->v[0][0]);
		if (tag == ip_ipv4) {
			printf("INSIDE IPV4******8\n");
			confd_data_reply_not_found(tctx);
		//	tag = GET_NEXT_TAG;
		}
		
		else if (tag == ip_ipv6) {
			printf("INSIDE IPV6******8\n");
			confd_data_reply_not_found(tctx);
		}
		return CONFD_OK;
	}
	#endif
		static int hw_get_elem(struct confd_trans_ctx *tctx, confd_hkeypath_t * kp)
		{
			confd_value_t v;
			confd_value_t *key,*key1;
			key = &kp->v[1][0];
			struct interface* s = find_interface(&(kp->v[1][0]));
		
			if (s ==  NULL) {
			printf("NO Interface\n");
			confd_data_reply_not_found(tctx);
		    return CONFD_OK;
			}
		
			struct confd_identityref idref;
			int tag = CONFD_GET_XMLTAG(&kp->v[0][0]);
			// printf("*******ELEM TAG:%d\n",tag);
			int idx;
			char * ifname;
			idx = kp->len;
			idx = idx - 1;
			if(tag==o_ran_int_mac_address)
			{
				//	  printf("MAC ADDRESS******************");
				CONFD_SET_BINARY(&v,s->mac,6);
			}
			else if(tag==if_type)
			{
				//	  printf("IINTERFACE TYPE\n");
				idref.ns = ianaift__ns;
				idref.id = ianaift_l2vlan;
				CONFD_SET_IDENTITYREF(&v,idref);
			}
			else if(tag==if_name)
			{
				//ifname = (char *) CONFD_GET_BUFPTR(&kp->v[1][0]);
				if (s== NULL)
				{
					// key = &kp->v[0][0];
					// printf("****Adding Interface*******\n");
					add_interface((char *)CONFD_GET_BUFPTR(key));
				}
				//printf("$$$KEY Name:%s\n",s->name);
				//printf("*******TAG:%d\n",tag);
				s = find_interface(&(kp->v[1][0]));
		
				CONFD_SET_STR(&v,s->name);
			}
			else if(tag==o_ran_int_vlan_id)
			{
				if(s->vlan!=0)
				{
					printf("vlan accepted");
					CONFD_SET_UINT16(&v,s->vlan);
				}
				else{
		
					printf("vlan not accepted");
					CONFD_SET_NOEXISTS(&v);
				}
		
			}
			else if(tag == o_ran_int_vlan_tagging)
			{
				CONFD_SET_BOOL(&v,1);
				printf("VLAN TAGGING");
		
			}
		#if 1
			else if( tag == o_ran_int_base_interface)
			{
		
				if (tctx->mode == 1)
				{
					if(strlen(s->baseName)==0) 
						CONFD_SET_NOEXISTS(&v);
				}
				else
				{
		
					//CONFD_SET_STR(&v,"cuplane");
					CONFD_SET_STR(&v,(char *)CONFD_GET_BUFPTR(key));
					//strcpy(s->baseName,(char *)CONFD_GET_BUFPTR(key));
				}
		
			}
		#endif
			else if (tag == if_discontinuity_time)
			{
				time_t  t;
				struct confd_datetime dt;
				time(&t);
				time_to_dt(t, &dt);
				//getdatetime(&eventTime);
				CONFD_SET_DATETIME(&v, dt);
			}
		
			else
				CONFD_SET_NOEXISTS(&v);
		
			confd_data_reply_value(tctx, &v);
		
			return CONFD_OK;
		}

                    int hw_get_next(struct confd_trans_ctx *tctx,
                    confd_hkeypath_t * kp, long next)
                    {
            
            confd_value_t v;
            static int pos =0;
            printf("*****Get NEXT\n");
            switch (CONFD_GET_XMLTAG(&kp->v[0][0])) {
          
              case if_interface:
                if (next == -1) {  /* first call */
                  pos = 0;
                } else {
                  pos++;// = (int)next;
                }
                if (pos == num_interface ) { /* We have reached the end of the list*/
                  confd_data_reply_next_key(tctx, NULL, -1, -1);
                  return CONFD_OK;
                }
              CONFD_SET_STR(&v, running_db[pos].name);
              confd_data_reply_next_key(tctx, &v, 1,(long)(pos+1));
              break;
              case if_higher_layer_if:
                  confd_data_reply_next_key(tctx, NULL, -1, -1);
          	break;
              case if_lower_layer_if:
                  confd_data_reply_next_key(tctx, NULL, -1, -1);
          	break;
              case ip_ipv4:
          		printf("**********IPV4********");
          	confd_data_reply_next_key(tctx, NULL, -1, -1);
          	break;
          	  case ip_ipv6:
          
          	confd_data_reply_next_key(tctx, NULL, -1, -1);
          	break;
              default:
                return CONFD_ERR;
            }
          
            return CONFD_OK;
          
          }
          static int hw_set_elem(struct confd_trans_ctx *tctx,
                              confd_hkeypath_t *keypath,
                              confd_value_t *newval)
          {
          	printf("*************SET ELEMENT");
          	unsigned char *bin;
          	int len;
              confd_value_t *tag = &(keypath->v[0][0]);
              
              switch (CONFD_GET_XMLTAG(tag)){
          
              case o_ran_int_mac_address:
          	    bin=CONFD_GET_BINARY_PTR(newval);
          	    len=CONFD_GET_BINARY_SIZE(newval);
          	    printf("MAC:%x:%x:%x:%x\n",bin[0],bin[1],bin[2],bin[3],bin[4]);
          	    memcpy(s->mac,bin,len);
          
                  break;
          #if 1
              case o_ran_int_vlan_id:
            
                 s->vlan=CONFD_GET_UINT16(newval);
          	  printf("&&&&&&\nSET ELEM VLAN:%d\n",s->vlan);
          	  break;
          #if 0
              case  o_ran_int_base_interface:
            
          	  printf("****8BASE INTERFACE********\n");
          
                 CONFD_SET_STR(&v,"MAC0");
            break;
          #endif
             default:
          	  confd_data_reply_not_found(tctx);
              //    return CONFD_ERR;
          #endif
          
          //    case if_name:
          
              }
          
              return CONFD_OK;
          }


        static int hw_create(struct confd_trans_ctx *tctx,
                          confd_hkeypath_t *keypath)
        {
            confd_value_t *key = &keypath->v[0][0];
            add_interface((char *)CONFD_GET_BUFPTR(key));
            return CONFD_OK;
        }
      static struct interface *new_interface(char *name, u_int8_t *mac, u_int16_t vlan)
      {
          struct interface *sp = add_interface(name);
          strcpy(sp->mac,mac);//inet_addr(ip);
          if(vlan!=0)
      	{
          sp->vlan =vlan;// atoi(port);
      	}
          return sp;
      }
	init_db()
	{
		num_interface = 0;
		u_int8_t addr[6];
		u_int16_t vlan=0;//405;
		// u_int16_t vlan=0;//408;
		addr[0] = 0x11;
		addr[1] = 0x22;
		addr[2] = 0x33;
		addr[3] = 0x44;
		addr[4] = 0x55;
		addr[5] = 0x66;
		
		new_interface("cuplane",addr,vlan);
		
	}



        int main(int argc, char *argv[])
        {
          struct sockaddr_in addr;
          //int debuglevel = CONFD_PROTO_TRACE;
          int debuglevel = CONFD_TRACE;
          struct confd_trans_cbs trans;
          struct confd_data_cbs data;
        
          /* Initialize confd library */
        
          confd_init("dataprovider", stderr, debuglevel);
        
          addr.sin_addr.s_addr = inet_addr("127.0.0.1");
          addr.sin_family = AF_INET;
          addr.sin_port = htons(CONFD_PORT);
        
          if (confd_load_schemas((struct sockaddr*)&addr,
                                 sizeof (struct sockaddr_in)) != CONFD_OK)
            confd_fatal("Failed to load schemas from confd\n");
        
          if ((dctx = confd_init_daemon("portstatus")) == NULL)
            confd_fatal("Failed to initialize daemon\n");
        
          /* Create and connect the control and worker sockets */
        
          if ((ctlsock = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
            confd_fatal("Failed to open ctlsocket\n");
          if (confd_connect(dctx, ctlsock, CONTROL_SOCKET, (struct sockaddr*)&addr,
                            sizeof (struct sockaddr_in)) < 0)
            confd_fatal("Failed to confd_connect() to confd \n");
        
          if ((workersock = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
            confd_fatal("Failed to open workersocket\n");
          if (confd_connect(dctx, workersock, WORKER_SOCKET,(struct sockaddr*)&addr,
                            sizeof (struct sockaddr_in)) < 0)
            confd_fatal("Failed to confd_connect() to confd \n");
        
          init_db();
          /* Register callbacks */
        #if 1
          memset(&trans, 0, sizeof(trans));
          trans.init = s_init;
          if (confd_register_trans_cb(dctx, &trans) == CONFD_ERR)
            confd_fatal("Failed to register trans cb\n");
        
          memset(&data, 0, sizeof (struct confd_data_cbs));
        
          data.get_elem = hw_get_elem;
          data.get_next = hw_get_next;
          data.exists_optional=hw_get_optional;
          data.set_elem = hw_set_elem;
          data.create   = hw_create;
        //data.remove   = doremove;
        
          //strcpy(data.callpoint, o_ran_hw__callpointid_cb_op_component);
          strcpy(data.callpoint,if__callpointid_cb_op_if_interface);
          if (confd_register_data_cb(dctx, &data) == CONFD_ERR)
            confd_fatal("Failed to register data cb\n");
        #endif
          if (confd_register_done(dctx) != CONFD_OK)
            confd_fatal("Failed to complete registration \n");
        
          while(1) {
            struct pollfd set[2];
            int ret;
        
            set[0].fd = ctlsock;
            set[0].events = POLLIN;
            set[0].revents = 0;
        
            set[1].fd = workersock;
            set[1].events = POLLIN;
            set[1].revents = 0;
        
            if (poll(set, sizeof(set)/sizeof(set[0]), -1) < 0) {
              perror("Poll failed:");
              continue;
            }
        
            /* Check for I/O */
            if (set[0].revents & POLLIN) {
              if ((ret = confd_fd_ready(dctx, ctlsock)) == CONFD_EOF) {
                confd_fatal("Control socket closed\n");
              } else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) {
                confd_fatal("Error on control socket request: %s (%d): %s\n",
                            confd_strerror(confd_errno), confd_errno, confd_lasterr());
              }
            }
            if (set[1].revents & POLLIN) {
              if ((ret = confd_fd_ready(dctx, workersock)) == CONFD_EOF) {
                confd_fatal("Worker socket closed\n");
              } else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) {
                confd_fatal("Error on worker socket request: %s (%d): %s\n",
                            confd_strerror(confd_errno), confd_errno, confd_lasterr());
              }
            }
          }
        }

well, there’s NETCONF error, and operation is interrupted → your callbacks to modify configuration do not get invoked…

<rpc-error>
  <error-type>application</error-type>
  <error-tag>unknown-element</error-tag>
  <error-severity>error</error-severity>
  <error-path xmlns:o-ran-int="urn:o-ran:interfaces:1.0" xmlns:if="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    /rpc/edit-config/config/if:interfaces/if:interface[if:name='cuplane1']/o-ran-int:vlan-tagging
  </error-path>
  <error-message xml:lang="en">/interfaces/interface[name='cuplane1']/o-ran-int:vlan-tagging: the 'when' expression "if:type = 'ianaift:ethernetCsmacd'" failed</error-message>
  <error-info>
    <bad-element>vlan-tagging</bad-element>
  </error-info>
</rpc-error>

You cannot do edit-config for vlan-tagging element with config set like you have in RPC request - such element “does not exist in schema” due to when statement condition not being met…

I assume that you have when statement someplace in the YANGs that conditions vlan-tagging element with interface type == ianaift:ethernetCsmacd (you are setting ianaift:l2vlan in RPC), just like error message shows…

1 Like

Hi Joseph,
Thanks a lot for finding the mistake. Appreciate your effort in finding the root cause.

Regards,
Biswajit

no problem, sometimes it might not be apparent how all the pieces and events connect together :slight_smile: