Restricting max-elements inside list of list in YANG

I have a requirement where I have below config:

voice class server-group 232
 ipv4 2.3.4.5
 ipv4 10.98.77.6 port 2234
voice class server-group 212
 ipv4 2.3.4.1 preference 1
 ipv4 8.43.33.21 preference 3
 ipv4 10.64.86.70 port 2234
 ipv6 2001:420:54FF:13::666:12
 ipv6 2001:420:54FF:13::666:11 port 22341 preference 2

I want to restrict max 5 address per server-group. Each server-group has total 4 lists - ipv4, ipv4+port, ipv6, ipv6+port.

I have tried below must condition -

must "( 
   count(
       /ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] \
       /ios-voice:ipv4-address/ios-voice:ipv4/ios-voice:address
   ) 
   + count(
       /ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] \ 
       /ios-voice:ipv4-address/ios-voice:ipv4-addr-port/ios-voice:ipv4/ios-voice:address
   )
   + count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] \
       /ios-voice:ipv6-address/ios-voice:ipv6/ios-voice:address)
   + count(
       /ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] \ 
       /ios-voice:ipv6-address/ios-voice:ipv6-addr-port/ios-voice:ipv6/ios-voice:address)
   )
   <= 5
"
{
   error-message "Maximum 5 destination entries allowed";
}

This didnt help. If i remove “current” then this condition checks for all the addresses configured (irrespective of any server-group).

Can someone suggest a way to get rid of this ?

Your help will be highly appreciated.

Hi gunjang, Welcome to the community:
Can you share a snippet of the YANG module that represents your “server-group” subtree, along with where the “must” statement is placed?

Changes are little more please find it below :
You can also check the diffs in this PRRQ for better view: https://polaris-git.cisco.com/csg/polaris/pull/70835/files

grouping config-voice-class-server-group-grouping {
 list server-group {
   description
     "IP Server Group";
   tailf:cli-mode-name "config-class";
   max-elements 10000;
   key id;
   leaf id {
     description
       "Voice class server group tag";
     tailf:cli-full-no;
     tailf:cli-full-command;
     tailf:cli-suppress-range;
     type uint16 {
       range "1..10000";
     }
     }
    container ipv4-address {
      tailf:cli-drop-node-name;
      must "(count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] /ios-voice:ipv4-address/ios-voice:ipv4/ios-voice:address) + count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()]/ios-voice:ipv4-address/ios-voice:ipv4-addr-port/ios-voice:ipv4/ios-voice:address)+ count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] /ios-voice:ipv6-address/ios-voice:ipv6/ios-voice:address)+ count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] /ios-voice:ipv6-address/ios-voice:ipv6-addr-port/ios-voice:ipv6/ios-voice:address)) <= 5" {
       error-message "Maximum 5 destination entries allowed";
     }
      list ipv4 {
        tailf:cli-suppress-mode;
        tailf:cli-compact-syntax;
        max-elements 5;
        key "address";
        leaf address {
          tailf:cli-drop-node-name;
          tailf:cli-full-no;
         description
           "set the Call Manager configuration download TFTP server address/name";
         tailf:cli-suppress-range;
         type inet:ipv4-address;
       }
       leaf preference {
         description
           "Preference order of this server in a group";
         tailf:cli-optional-in-sequence;
         type uint8 {
           range "0..5";
         }
       }
     }
       container ipv4-addr-port {
       tailf:cli-drop-node-name;
       list ipv4 {
         tailf:cli-suppress-mode;
         tailf:cli-compact-syntax;
         max-elements 5;
         key "address port";
         leaf address {
           tailf:cli-drop-node-name;
           tailf:cli-suppress-range;
           description
             "set the Call Manager configuration download TFTP server address/name";
           type inet:ipv4-address;
         }
         leaf port {
           description
             "IP Address Port";
           tailf:cli-expose-key-name;
           tailf:cli-full-no;
           tailf:cli-suppress-range;
           type uint16 {
             range "1..65535";
           }
         }
         leaf preference {
           description
             "Preference order of this server in a group";
           tailf:cli-optional-in-sequence;
           type uint8 {
             range "0..5";
           }
         }
       }
     }
   }
    container ipv6-address {
     tailf:cli-drop-node-name;
     must "(count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] /ios-voice:ipv4-address/ios-voice:ipv4/ios-voice:address) + count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()]/ios-voice:ipv4-address/ios-voice:ipv4-addr-port/ios-voice:ipv4/ios-voice:address)+ count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] /ios-voice:ipv6-address/ios-voice:ipv6/ios-voice:address)+ count(/ios:native/ios-voice:voice/ios-voice:class/ios-voice:server-group[ios-voice:id=current()] /ios-voice:ipv6-address/ios-voice:ipv6-addr-port/ios-voice:ipv6/ios-voice:address)) <= 5" {
       error-message "Maximum 5 destination entries allowed";
     }
     list ipv6 {
       tailf:cli-suppress-mode;
       tailf:cli-compact-syntax;
       max-elements 5;
       key "address";
       leaf address {
         tailf:cli-drop-node-name;
         tailf:cli-full-no;
         description
           "set the Call Manager configuration download TFTP server address/name";
         tailf:cli-suppress-range;
         type inet:ipv6-address;
       }
       leaf preference {
         description
           "Preference order of this server in a group";
         tailf:cli-optional-in-sequence;
         type uint8 {
           range "0..5";
         }
       }
     }
       container ipv6-addr-port {
       tailf:cli-drop-node-name;
       list ipv6 {
         tailf:cli-suppress-mode;
         tailf:cli-compact-syntax;
         max-elements 5;
         key "address port";
         leaf address {
           tailf:cli-drop-node-name;
           description
             "set the Call Manager configuration download TFTP server address/name";
           tailf:cli-suppress-range;
           type inet:ipv6-address;
         }
         leaf port {
           description
             "IP Address Port";
           tailf:cli-expose-key-name;
           tailf:cli-full-no;
           tailf:cli-suppress-range;
           type uint16 {
             range "1..65535";
           }
         }
         leaf preference {
           description
             "Preference order of this server in a group";
           tailf:cli-optional-in-sequence;
           type uint8 {
             range "0..5";
           }              
     }
   }
 }
   }

Since your must statement affects whole server-group entry, it’s best to place it right under list server-group; then, there is no need to use absolute paths, everything is at or below the current level; and finally, you do not have to use prefixes if the node you are referring to comes from the current module. So the must expression (and a bit of a context) might look something like this:

    list server-group {
      must "count(ipv4-address/ipv4) + count(ipv4-address/ipv4-addr-port/ipv4)"
        + "+count(ipv6-address/ipv6) + count(ipv6-address/ipv6-addr-port/ipv6) <= 5" {
        error-message "Maximum 5 destination entries allowed";
      }
      container ipv4-address {
        ...
      }
      container ipv6-address {
        ...
      }
    }

But note that such must statement might have considerable performance impact, especially if you have large number of server-group or individual address instances.

Thanks. This actually worked.

Good! But as I wrote, watch the performance.