On 2/17/26 12:20 PM, Rukomoinikova Aleksandra wrote:
> On 11.02.2026 18:15, Dumitru Ceara wrote:
>> On 2/7/26 10:48 PM, Alexandra Rukomoinikova wrote:
>>> Add schema and CLI support for health checks on logical switch
>>> ports by introducing a new health check table and linking it
>>> from logical switch ports. Implement corresponding ovn-nbctl
>>> commands to manage LSP health checks. Also extend service
>>> monitoring to support the logical-switch-port type.
>>>
>>> Signed-off-by: Alexandra Rukomoinikova <[email protected]>
>>> ---
>>> v1 --> v2: corrected all the comments and made one service monitor bind to 
>>> only one address
>>> ---
>> Hi Alexandra,
>>
>> At a first glance this seems OK to me, only a few minor comments below.
>> I'll review the rest of the patches in the series and come back to this
>> one with an Ack if applicable.
>>
>> Thanks,
>> Dumitru
> 
> Sorry, I didn't cope with the last letter)
> 
> Hi! I started fixing your comments and I had a question. I'll write it below.
> 

Hi Alexandra,

>>
>>>   ovn-nb.ovsschema          |  28 ++-
>>>   ovn-nb.xml                |  53 ++++
>>>   ovn-sb.ovsschema          |   7 +-
>>>   ovn-sb.xml                |  11 +
>>>   tests/ovn-nbctl.at        | 196 +++++++++++++++
>>>   utilities/ovn-nbctl.8.xml |  52 ++++
>>>   utilities/ovn-nbctl.c     | 516 ++++++++++++++++++++++++++++++++++++--
>>>   7 files changed, 842 insertions(+), 21 deletions(-)
>>>
>>> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
>>> index 8c2c1d861..538b37456 100644
>>> --- a/ovn-nb.ovsschema
>>> +++ b/ovn-nb.ovsschema
>>> @@ -1,7 +1,7 @@
>>>   {
>>>       "name": "OVN_Northbound",
>>> -    "version": "7.15.0",
>>> -    "cksum": "4060410729 43708",
>>> +    "version": "7.16.0",
>>> +    "cksum": "3492890733 44939",
>>>       "tables": {
>>>           "NB_Global": {
>>>               "columns": {
>>> @@ -179,6 +179,11 @@
>>>                                        "refType": "strong"},
>>>                                "min": 0,
>>>                                "max": 1}},
>>> +                "health_checks": {"type": {"key": {"type": "uuid",
>>> +                                          "refTable": 
>>> "Logical_Switch_Port_Health_Check",
>>> +                                          "refType": "strong"},
>> Nit: indentation is weird, "type" and "refTable"/"refType" should be at
>> the same level.
>>
>>> +                                 "min": 0,
>>> +                                 "max": "unlimited"}},
>>>                   "external_ids": {
>>>                       "type": {"key": "string", "value": "string",
>>>                                "min": 0, "max": "unlimited"}}},
>>> @@ -246,6 +251,25 @@
>>>                                "min": 0, "max": "unlimited"}}},
>>>               "indexes": [["name"], ["id"]],
>>>               "isRoot": true},
>>> +        "Logical_Switch_Port_Health_Check": {
>>> +            "columns": {
>>> +                "protocol": {
>>> +                    "type": {"key": {"type": "string",
>>> +                             "enum": ["set", ["tcp", "udp", "icmp"]]},
>>> +                             "min": 0, "max": 1}},
>> Same here.
>>
>>> +                "src_ip": {"type": "string"},
>>> +                "port": {"type": {"key": {"type": "integer",
>>> +                                          "minInteger": 0,
>>> +                                          "maxInteger": 65535}}},
>>> +                "address": {"type": {"key": "string",
>>> +                                       "min": 0,
>>> +                                       "max": 1}},
>>> +                "options": {
>>> +                     "type": {"key": "string",
>>> +                              "value": "string",
>>> +                              "min": 0,
>>> +                              "max": "unlimited"}}},
>>> +            "isRoot": false},
>>>           "Forwarding_Group": {
>>>               "columns": {
>>>                   "name": {"type": "string"},
>>> diff --git a/ovn-nb.xml b/ovn-nb.xml
>>> index 1acbf202b..e60edbd8d 100644
>>> --- a/ovn-nb.xml
>>> +++ b/ovn-nb.xml
>>> @@ -2136,6 +2136,11 @@
>>>           Please see the <ref table="Mirror"/> table.
>>>       </column>
>>>   
>>> +    <column name="health_checks">
>>> +        Health checks configuration for this logical switch port.
>>> +        Please see the <ref table="Logical_Switch_Port_Health_Check"/> 
>>> table.
>>> +    </column>
>>> +
>>>       <column name="ha_chassis_group">
>>>         References a row in the OVN Northbound database's
>>>         <ref table="HA_Chassis_Group" db="OVN_Northbound"/> table.
>>> @@ -2192,6 +2197,54 @@
>>>       </group>
>>>     </table>
>>>   
>>> +  <table name="Logical_Switch_Port_Health_Check"
>>> +         title="logical switch port health check">
>>> +    <p>
>>> +      Each row represents health check configuration for logical switch 
>>> port.
>>> +      Health checks are used to monitor reachability and health of backend
>>> +      endpoints associated with logical switch port. Health check can be
>>> +      configured to use different protocols (TCP, UDP, or ICMP) to verify
>>> +      availability of target IP addresses.
>>> +    </p>
>>> +
>>> +    <column name="protocol">
>>> +      Valid protocols are <code>tcp</code>, <code>udp</code>, or
>>> +      <code>icmp</code>. For <code>tcp</code> and <code>udp</code>
>>> +      protocols - destination port must be specified.
>>> +    </column>
>>> +
>>> +    <column name="src_ip">
>>> +      Source IP address used when sending health check probes.
>>> +    </column>
>>> +
>>> +    <column name="port">
>>> +      Destination port number for TCP and UDP health checks.
>>> +    </column>
>>> +
>>> +    <column name="address">
>>> +      IP address to monitor for the health check.
>>> +    </column>
>>> +
>>> +    <column name="options" key="interval" type='{"type": "integer"}'>
>>> +      The interval, in seconds, between service monitor checks.
>>> +    </column>
>>> +
>>> +    <column name="options" key="timeout" type='{"type": "integer"}'>
>>> +      The time, in seconds, after which the service monitor check times
>>> +      out.
>>> +    </column>
>>> +
>>> +    <column name="options" key="success_count" type='{"type": "integer"}'>
>>> +      The number of successful checks after which the service is
>>> +      considered <code>online</code>.
>>> +    </column>
>>> +
>>> +    <column name="options" key="failure_count" type='{"type": "integer"}'>
>>> +      The number of failure checks after which the service is considered
>>> +    <code>offline</code>.
>>> +    </column>
>>> +  </table>
>>> +
>>>     <table name="Forwarding_Group" title="forwarding group">
>>>       <p>
>>>         Each row represents one forwarding group.
>>> diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
>>> index cf33933da..dd9e32f06 100644
>>> --- a/ovn-sb.ovsschema
>>> +++ b/ovn-sb.ovsschema
>>> @@ -1,7 +1,7 @@
>>>   {
>>>       "name": "OVN_Southbound",
>>> -    "version": "21.7.0",
>>> -    "cksum": "1383351379 36646",
>>> +    "version": "21.8.0",
>>> +    "cksum": "3491605797 36713",
>>>       "tables": {
>>>           "SB_Global": {
>>>               "columns": {
>>> @@ -516,7 +516,8 @@
>>>                   "type": {"type": {"key": {
>>>                              "type": "string",
>>>                              "enum": ["set", ["load-balancer",
>>> -                                            "network-function"]]},
>>> +                                            "network-function",
>>> +                                            "logical-switch-port"]]},
>>>                                "min": 0, "max": 1}},
>>>                   "ip": {"type": "string"},
>>>                   "mac": {"type": "string"},
>>> diff --git a/ovn-sb.xml b/ovn-sb.xml
>>> index 00bae26bf..0ec7d6094 100644
>>> --- a/ovn-sb.xml
>>> +++ b/ovn-sb.xml
>>> @@ -5042,6 +5042,8 @@ tcp.flags = RST;
>>>           icmp, and the health probe is done by injecting an icmp echo 
>>> request
>>>           packet into the <code>inport</code> of the Network_Function and 
>>> then
>>>           montoring the same packet coming out of the <code>outport</code>.
>>> +        For <code>type</code> "logical-switch-port", supported protocols 
>>> are
>>> +        <code>tcp</code>, <code>udp</code> and <code>icmp</code>.
>>>         </column>
>>>   
>>>         <column name="port">
>>> @@ -5124,6 +5126,15 @@ tcp.flags = RST;
>>>             the service and doesn't expect any reply.  If it receives an 
>>> ICMP
>>>             reply, then it considers the service to be <code>offline</code>.
>>>           </p>
>>> +
>>> +        <p>
>>> +          For ICMP service, <code>ovn-controller</code> sends an ICMP
>>> +          Echo Request and expects an ICMP Echo Reply to consider service
>>> +          to be <code>online</code>. If no reply is received or if an ICMP
>>> +          error message is received, the service is considered
>>> +          <code>offline</code>.
>>> +       </p>
>>> +
>>>         </column>
>>>       </group>
>>>   
>>> diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
>>> index dccf30758..177e097fb 100644
>>> --- a/tests/ovn-nbctl.at
>>> +++ b/tests/ovn-nbctl.at
>>> @@ -3499,3 +3499,199 @@ AT_CHECK([ovn-nbctl --may-exist 
>>> lsp-add-localnet-port ls ln_port net1], [1], [],
>>>   check ovn-nbctl lsp-set-options ln_port network_name=net1
>>>   check ovn-nbctl --may-exist lsp-add-localnet-port ls ln_port net1
>>>   ])
>>> +
>>> +dnl ---------------------------------------------------------------------
>>> +
>>> +AT_SETUP([ovn-nbctl - Logical Switch Port Health Check])
>>> +OVN_NBCTL_TEST_START daemon
>>> +
>>> +# Create logical switch port
>> Nit: missing period at end of sentence (this applies to most of the
>> comments in this patch).
>>
>>> +AT_CHECK([ovn-nbctl ls-add ls0])
>>> +AT_CHECK([ovn-nbctl lsp-add ls0 lport0])
>>> +AT_CHECK([ovn-nbctl lsp-set-addresses lport0 "00:11:22:33:44:55 
>>> 192.168.1.10" "00:11:22:33:44:56 192.168.1.11"])
>>> +
>>> +# Create IPV4 ICMP health check
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 icmp 192.168.0.255 192.168.1.10])
>>> +AT_CHECK([ovn-nbctl lsp-hc-list lport0], [0], [dnl
>>> +Logical Switch Port lport0:
>>> +  Protocol      :  icmp
>>> +  Source IP     :  192.168.0.255
>>> +  Addresses     :  192.168.1.10
>>> +])
>>> +
>>> +# Check hc attaching to logical switch port
>>> +hc_icmp_uuid=$(fetch_column nb:logical_switch_port_health_check _uuid 
>>> src_ip="192.168.0.255")
>>> +check_column "$hc_icmp_uuid" nb:logical_switch_port health_checks
>>> +
>>> +# Create IPv4 TCP health check with the specified addresses
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.2 80 192.168.1.10])
>>> +AT_CHECK([ovn-nbctl lsp-hc-list lport0], [0], [dnl
>>> +Logical Switch Port lport0:
>>> +  Protocol      :  tcp
>>> +  Source IP     :  10.0.0.2
>>> +  Port          :  80
>>> +  Addresses     :  192.168.1.10
>>> +
>>> +  Protocol      :  icmp
>>> +  Source IP     :  192.168.0.255
>>> +  Addresses     :  192.168.1.10
>>> +])
>>> +hc_tcp_uuid=$(fetch_column nb:logical_switch_port_health_check _uuid 
>>> src_ip="10.0.0.2")
>>> +
>>> +# Create UDP health check with the specified addresses
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 udp 10.0.0.3 53 192.168.1.11])
>>> +AT_CHECK([ovn-nbctl lsp-hc-list lport0], [0], [dnl
>>> +Logical Switch Port lport0:
>>> +  Protocol      :  tcp
>>> +  Source IP     :  10.0.0.2
>>> +  Port          :  80
>>> +  Addresses     :  192.168.1.10
>>> +
>>> +  Protocol      :  udp
>>> +  Source IP     :  10.0.0.3
>>> +  Port          :  53
>>> +  Addresses     :  192.168.1.11
>>> +
>>> +  Protocol      :  icmp
>>> +  Source IP     :  192.168.0.255
>>> +  Addresses     :  192.168.1.10
>>> +])
>>> +hc_udp_uuid=$(fetch_column nb:logical_switch_port_health_check _uuid 
>>> src_ip="10.0.0.3")
>>> +
>>> +check_column "$hc_icmp_uuid $hc_tcp_uuid $hc_udp_uuid" 
>>> nb:logical_switch_port health_checks
>>> +
>>> +AT_CHECK([ovn-nbctl lsp-hc-del lport0 $hc_icmp_uuid])
>>> +# Check hcs detaching to logical switch port
>>> +check_column "$hc_tcp_uuid $hc_udp_uuid" nb:logical_switch_port 
>>> health_checks
>>> +
>>> +AT_CHECK([ovn-nbctl lsp-hc-list lport0], [0], [dnl
>>> +Logical Switch Port lport0:
>>> +  Protocol      :  tcp
>>> +  Source IP     :  10.0.0.2
>>> +  Port          :  80
>>> +  Addresses     :  192.168.1.10
>>> +
>>> +  Protocol      :  udp
>>> +  Source IP     :  10.0.0.3
>>> +  Port          :  53
>>> +  Addresses     :  192.168.1.11
>>> +])
>>> +
>>> +AT_CHECK([ovn-nbctl lsp-hc-del lport0])
>>> +check_row_count nb:Logical_Switch_Port_Health_Check 0
>>> +AT_CHECK([ovn-nbctl lsp-hc-list lport0], [0], [dnl])
>>> +
>>> +# Check invalid protocol
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 stcp 10.0.0.1 50], [1], [], [stderr])
>>> +AT_CHECK([grep "Type must be icmp, tcp or udp" stderr], [0], [ignore])
>> Nit: in the docs we used Oxford-comma, I guess we can do that here too
>> (and in nbctl.c).
>>
>>> +
>>> +# Check invalid source IP
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 icmp invalid_ip invalid_ip], [1], 
>>> [], [stderr])
>>> +AT_CHECK([grep "Not a valid IPv4 or IPv6 address" stderr], [0], [ignore])
>>> +
>>> +# Check TCP/UDP non-valid destination port
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.1 70000], [1], [], 
>>> [stderr])
>>> +AT_CHECK([grep "port must in range 0...65535" stderr], [0], [ignore])
>>> +
>>> +# Check IP address not configured on port
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 icmp 10.0.0.1 192.168.99.99], [1], 
>>> [], [stderr])
>>> +AT_CHECK([grep "Address 192.168.99.99 not configured on port" stderr], 
>>> [0], [ignore])
>>> +
>>> +AT_CHECK([ovn-nbctl lsp-hc-del lport0])
>>> +
>>> +# Different source IP, same protocol, port and address - should be allowed
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.1 80 192.168.1.10])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.2 80 192.168.1.10], [0], 
>>> [], [ignore])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.3 80 192.168.1.10], [0], 
>>> [], [ignore])
>>> +
>>> +# Same source IP, different ports, same protocol and address - should be 
>>> allowed
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.1 81 192.168.1.10], [0], 
>>> [], [ignore])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.1 82 192.168.1.10], [0], 
>>> [], [ignore])
>>> +
>>> +# Same source IP and port, different protocols, same address - should be 
>>> allowed
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 udp 10.0.0.1 80 192.168.1.10], [0], 
>>> [], [ignore])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 icmp 10.0.0.1 192.168.1.10], [0], 
>>> [], [ignore])
>>> +
>>> +# Check duplication
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.5 90 192.168.1.10])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 tcp 10.0.0.5 90 192.168.1.10], [1], 
>>> [], [stderr])
>>> +AT_CHECK([grep "Health check with address 192.168.1.10 already exists on 
>>> port lport0" stderr], [0], [ignore])
>>> +
>>> +# ICMP - same source IP, different addresses - should be allowed
>>> +AT_CHECK([ovn-nbctl lsp-hc-del lport0])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 icmp 10.0.0.9 192.168.1.10])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 icmp 10.0.0.9 192.168.1.11], [0], 
>>> [], [ignore])
>>> +
>>> +# ICMP - different source IP, same address - should be allowed
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 icmp 10.0.0.10 192.168.1.10], [0], 
>>> [], [ignore])
>>> +
>>> +# Check supported logical switch port type for monitoring
>>> +AT_CHECK([ovn-nbctl lsp-add ls0 lport1])
>>> +AT_CHECK([ovn-nbctl lsp-set-type lport1 router])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport1 icmp 10.0.0.1 10.0.0.2], [1], [], 
>>> [stderr])
>>> +AT_CHECK([grep "Health check monitoring supported only for port with vif 
>>> type" stderr], [0], [ignore])
>>> +
>>> +AT_CHECK([ovn-nbctl lsp-hc-del lport0])
>>> +
>>> +# Test IPv6 addresses support: icmp, tcp, udp
>>> +AT_CHECK([ovn-nbctl lsp-add ls0 lport3])
>>> +AT_CHECK([ovn-nbctl lsp-set-addresses lport3 "00:11:22:33:44:57 
>>> 2001:db8::1"])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport3 icmp 2001:db8::2 2001:db8::1])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport3 tcp 2001:db8::2 80 2001:db8::1])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport3 udp 2001:db8::2 53 2001:db8::1])
>>> +AT_CHECK([ovn-nbctl lsp-hc-list lport3], [0], [dnl
>>> +Logical Switch Port lport3:
>>> +  Protocol      :  icmp
>>> +  Source IP     :  2001:db8::2
>>> +  Addresses     :  2001:db8::1
>>> +
>>> +  Protocol      :  tcp
>>> +  Source IP     :  2001:db8::2
>>> +  Port          :  80
>>> +  Addresses     :  2001:db8::1
>>> +
>>> +  Protocol      :  udp
>>> +  Source IP     :  2001:db8::2
>>> +  Port          :  53
>>> +  Addresses     :  2001:db8::1
>>> +])
>>> +
>>> +AT_CHECK([ovn-nbctl lsp-hc-del lport3])
>>> +
>>> +# Duplicate IPv6 health check - should be disabled
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport3 icmp 2001:db8::2 2001:db8::1])
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport3 icmp 2001:db8::2 2001:db8::1], [1], 
>>> [], [stderr])
>>> +AT_CHECK([grep "Health check with address 2001:db8::1 already exists on 
>>> port lport3" stderr], [0], [ignore])
>>> +
>>> +# Check options printing
>>> +AT_CHECK([ovn-nbctl lsp-hc-add lport0 icmp 10.0.0.4 192.168.1.10 
>>> 192.168.1.11])
>>> +AT_CHECK([ovn-nbctl lsp-hc-list lport0], [0], [dnl
>>> +Logical Switch Port lport0:
>>> +  Protocol      :  icmp
>>> +  Source IP     :  10.0.0.4
>>> +  Addresses     :  192.168.1.10
>>> +])
>>> +hc_icmp_uuid=$(fetch_column nb:logical_switch_port_health_check _uuid 
>>> src_ip="10.0.0.4")
>>> +
>>> +AT_CHECK([ovn-nbctl set logical_switch_port_health_check $hc_icmp_uuid 
>>> options:interval=3])
>>> +AT_CHECK([ovn-nbctl set logical_switch_port_health_check $hc_icmp_uuid 
>>> options:timeout=30])
>>> +AT_CHECK([ovn-nbctl set logical_switch_port_health_check $hc_icmp_uuid 
>>> options:success_count=1])
>>> +AT_CHECK([ovn-nbctl set logical_switch_port_health_check $hc_icmp_uuid 
>>> options:failure_count=2])
>>> +
>>> +AT_CHECK([ovn-nbctl lsp-hc-list lport0], [0], [dnl
>>> +Logical Switch Port lport0:
>>> +  Protocol      :  icmp
>>> +  Source IP     :  10.0.0.4
>>> +  Addresses     :  192.168.1.10
>>> +  Interval      :  3
>>> +  Timeout       :  30
>>> +  Success count :  1
>>> +  Failure count :  2
>>> +])
>>> +
>>> +AT_CHECK([ovn-nbctl lsp-hc-del lport0])
>>> +AT_CHECK([ovn-nbctl lsp-hc-del lport3])
>>> +check_row_count nb:Logical_Switch_Port_Health_Check 0
>>> +
>>> +OVN_NBCTL_TEST_STOP "/terminating with signal 15/d"
>>> +AT_CLEANUP
>>> diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml
>>> index 7df902944..c403fcfb6 100644
>>> --- a/utilities/ovn-nbctl.8.xml
>>> +++ b/utilities/ovn-nbctl.8.xml
>>> @@ -1822,6 +1822,58 @@
>>>         </dd>
>>>       </dl>
>>>   
>>> +    <h2>Health Check commands</h2>
>>> +    <dl>
>>> +      <dt><code>lsp-hc-add</code> <var>PORT</var> <var>PROTOCOL</var>
>>> +      <var>SOURCE_IP</var> [<var>DST_PORT</var>] <var>ADDRESS</var></dt>
>>> +      <dd>
>>> +        <p>
>>> +          Creates a new health check configuration for the logical switch 
>>> port
>>> +          <code>PORT</code> with the below mandatory arguments.
>>> +        </p>
>>> +
>>> +        <p>
>>> +          <var>PROTOCOL</var> specifies the health check protocol -
>>> +          <code>tcp</code>, <code>udp</code>, or <code>icmp</code>.
>>> +        </p>
>>> +
>>> +        <p>
>>> +          <var>SOURCE_IP</var> specifies the source IP address used when
>>> +          sending health check probes.
>>> +        </p>
>>> +
>>> +        <p>
>>> +          <var>DST_PORT</var> specifies the destination port number for
>>> +          <code>tcp</code> and <code>udp</code> health checks. This 
>>> parameter
>>> +          is ignored for <code>icmp</code> protocol.
>>> +        </p>
>>> +
>>> +        <p>
>>> +          <var>ADDRESS</var> specifies the IP address to monitor.
>>> +        </p>
>>> +
>>> +        <p>
>>> +          Additional health check options such as interval, timeout,
>>> +          success count, and failure count can be configured separately.
>>> +        </p>
>>> +      </dd>
>>> +
>>> +      <dt><code>lsp-hc-del</code> <var>PORT</var> [<var>HC_UUID</var>]</dt>
>>> +      <dd>
>>> +        <p>
>>> +          Deletes health check configuration for the logical switch port
>>> +          <code>PORT</code>.
>>> +        </p>
>>> +      </dd>
>>> +
>>> +    <dt><code>lsp-hc-list</code> <var>PORT</var></dt>
>>> +    <dd>
>>> +      Lists all health check configurations for the logical switch port
>>> +      <code>PORT</code>, including protocol, source IP, destination port,
>>> +      monitored addresses, and current status.
>>> +    </dd>
>>> +    </dl>
>>> +
>>>       <h2>Synchronization Commands</h2>
>>>   
>>>       <dl>
>>> diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
>>> index cdf6b578a..e58001148 100644
>>> --- a/utilities/ovn-nbctl.c
>>> +++ b/utilities/ovn-nbctl.c
>>> @@ -66,6 +66,18 @@ string_ptr(char *ptr)
>>>       return (ptr) ? ptr : s;
>>>   }
>>>   
>>> +static char *OVS_WARN_UNUSED_RESULT
>>> +parse_l4_port_range(const char *arg, uint16_t *port_p)
>> I'd call this parse_l4_port().
>>
>>> +{
>>> +    int64_t port;
>>> +    if (!ovs_scan(arg, "%"SCNd64, &port)
>>> +        || port < 0 || port > UINT16_MAX) {
>>> +        return xasprintf("%s: port must in range 0...65535", arg);
>>> +    }
>>> +    *port_p = port;
>>> +    return NULL;
>>> +}
>>> +
>>>   static void
>>>   nbctl_add_base_prerequisites(struct ovsdb_idl *idl,
>>>                                enum nbctl_wait_type wait_type)
>>> @@ -544,6 +556,12 @@ MAC_Binding commands:\n\
>>>                                       Delete Static_MAC_Binding entry\n\
>>>     static-mac-binding-list           List all Static_MAC_Binding entries\n\
>>>   \n\
>>> +Logical Switch Port Health Check:\n\
>>> +  lsp-hc-add PORT PROTOCOL SOURCE_IP DST_PORT ADDRESS...\n\
>>> +                            add health check monitoring for PORT\n\
>>> +  lsp-hc-del PORT HC_UUID   delete health check monitoring for PORT\n\
>>> +  lsp-hc-list PORT              print health check for PORT\n\
>>> +\n\
>>>   %s\
>>>   %s\
>>>   \n\
>>> @@ -1464,18 +1482,23 @@ nbctl_pre_lsp_set_addresses(struct ctl_context *ctx)
>>>                            
>>> &nbrec_logical_switch_port_col_dynamic_addresses);
>>>   }
>>>   
>>> -static char *
>>> +static bool
>>>   lsp_contains_duplicate_ip(struct lport_addresses *laddrs1,
>>>                             struct lport_addresses *laddrs2,
>>> -                          const struct nbrec_logical_switch_port *lsp_test)
>>> +                          const struct nbrec_logical_switch_port *lsp_test,
>>> +                          char **error_str)
>>>   {
>>>       for (size_t i = 0; i < laddrs1->n_ipv4_addrs; i++) {
>>>           for (size_t j = 0; j < laddrs2->n_ipv4_addrs; j++) {
>>>               if (laddrs1->ipv4_addrs[i].addr == 
>>> laddrs2->ipv4_addrs[j].addr) {
>>> -                return xasprintf("duplicate IPv4 address '%s' found on "
>>> -                                 "logical switch port '%s'",
>>> -                                 laddrs1->ipv4_addrs[i].addr_s,
>>> -                                 lsp_test->name);
>>> +                if (error_str) {
>>> +                    *error_str = xasprintf("duplicate IPv4 address '%s' "
>>> +                                           "found on logical switch "
>>> +                                           "port '%s'",
>>> +                                           laddrs1->ipv4_addrs[i].addr_s,
>>> +                                           lsp_test->name);
>>> +                }
>>> +                return true;
>>>               }
>>>           }
>>>       }
>>> @@ -1484,15 +1507,23 @@ lsp_contains_duplicate_ip(struct lport_addresses 
>>> *laddrs1,
>>>           for (size_t j = 0; j < laddrs2->n_ipv6_addrs; j++) {
>>>               if (IN6_ARE_ADDR_EQUAL(&laddrs1->ipv6_addrs[i].addr,
>>>                                      &laddrs2->ipv6_addrs[j].addr)) {
>>> -                return xasprintf("duplicate IPv6 address '%s' found on "
>>> -                                 "logical switch port '%s'",
>>> -                                 laddrs1->ipv6_addrs[i].addr_s,
>>> -                                 lsp_test->name);
>>> +                if (error_str) {
>>> +                    *error_str = xasprintf("duplicate IPv6 address "
>>> +                                           "'%s' found on logical "
>>> +                                           "switch port '%s'",
>>> +                                           laddrs1->ipv6_addrs[i].addr_s,
>>> +                                           lsp_test->name);
>>> +                }
>>> +                return true;
>>>               }
>>>           }
>>>       }
>>>   
>>> -    return NULL;
>>> +    if (error_str) {
>>> +        *error_str = NULL;
>>> +    }
>>> +
>>> +    return false;
>>>   }
>>>   
>>>   static char *
>>> @@ -1500,12 +1531,13 @@ lsp_contains_duplicates(const struct 
>>> nbrec_logical_switch *ls,
>>>                           const struct nbrec_logical_switch_port *lsp,
>>>                           const char *address)
>>>   {
>>> +    char *error = NULL;
>>> +    char *sub_error = NULL;
>>>       struct lport_addresses laddrs;
>>>       if (!extract_lsp_addresses(address, &laddrs)) {
>>>           return NULL;
>>>       }
>>>   
>>> -    char *sub_error = NULL;
>>>       for (size_t i = 0; i < ls->n_ports; i++) {
>>>           struct nbrec_logical_switch_port *lsp_test = ls->ports[i];
>>>           if (lsp_test == lsp) {
>>> @@ -1518,10 +1550,11 @@ lsp_contains_duplicates(const struct 
>>> nbrec_logical_switch *ls,
>>>                   addr = lsp_test->dynamic_addresses;
>>>               }
>>>               if (extract_lsp_addresses(addr, &laddrs_test)) {
>>> -                sub_error = lsp_contains_duplicate_ip(&laddrs, 
>>> &laddrs_test,
>>> -                                                      lsp_test);
>>> +                bool has_duplicate =
>>> +                    lsp_contains_duplicate_ip(&laddrs, &laddrs_test,
>>> +                                              lsp_test, &sub_error);
>>>                   destroy_lport_addresses(&laddrs_test);
>>> -                if (sub_error) {
>>> +                if (has_duplicate) {
>>>                       goto err_out;
>>>                   }
>>>               }
>>> @@ -1529,7 +1562,6 @@ lsp_contains_duplicates(const struct 
>>> nbrec_logical_switch *ls,
>>>       }
>>>   
>>>   err_out: ;
>>> -    char *error = NULL;
>>>       if (sub_error) {
>>>           error = xasprintf("Error on switch %s: %s", ls->name, sub_error);
>>>           free(sub_error);
>>> @@ -8677,6 +8709,448 @@ nbctl_lsp_add_misc_port(struct ctl_context *ctx)
>>>       shash_add(&nbctx->lsp_to_ls_map, lsp_name, ls);
>>>   }
>>>   
>>> +/* Logical Switch Port Health Check Functions. */
>>> +enum health_check_protocol {
>>> +    LSP_ICMP_HEALTH_CHECK,
>>> +    LSP_TCP_HEALTH_CHECK,
>>> +    LSP_UDP_HEALTH_CHECK,
>>> +};
>>> +
>>> +static char *
>>> +lsp_health_check_parse_protocol(const char *type,
>>> +                                enum health_check_protocol *protocol)
>>> +{
>>> +    char *error = NULL;
>>> +
>>> +    if (!strcmp(type, "icmp")) {
>>> +        *protocol = LSP_ICMP_HEALTH_CHECK;
>>> +    } else if (!strcmp(type, "tcp")) {
>>> +        *protocol = LSP_TCP_HEALTH_CHECK;
>>> +    } else if (!strcmp(type, "udp")) {
>>> +        *protocol = LSP_UDP_HEALTH_CHECK;
>>> +    } else {
>>> +        error = xasprintf("%s: Type must be icmp, tcp or udp.", type);
>>> +    }
>>> +
>>> +    return error;
>>> +}
>>> +
>>> +static char *
>>> +lsp_health_check_parse_dst_port(struct ctl_context *ctx,
>>> +                                enum health_check_protocol protocol,
>>> +                                const char *protocol_str,
>>> +                                uint16_t *destination_port,
>>> +                                int *target_ip_index)
>>> +{
>>> +    char *error;
>>> +
>>> +    switch (protocol) {
>>> +    case LSP_TCP_HEALTH_CHECK:
>>> +    case LSP_UDP_HEALTH_CHECK:
>>> +        if (ctx->argc < 5) {
>>> +            error = xasprintf("Destination port required for "
>>> +                              "%s health check.", protocol_str);
>>> +            return error;
>>> +        }
>>> +
>>> +        error = parse_l4_port_range(ctx->argv[4], destination_port);
>>> +        if (error) {
>>> +            return error;
>>> +        }
>>> +
>>> +        *target_ip_index = 5;
>>> +        break;
>>> +
>>> +    case LSP_ICMP_HEALTH_CHECK:
>>> +        *target_ip_index = 4;
>>> +        break;
>>> +
>>> +    default:
>>> +        OVS_NOT_REACHED();
>>> +    }
>>> +
>>> +    if (*target_ip_index == ctx->argc) {
>>> +        error = xasprintf("No addresses specified for health checking.");
>>> +        return error;
>>> +    }
>>> +
>>> +    return NULL;
>>> +}
>>> +
>>> +static bool
>>> +lsp_health_check_get_duplicate_record(
>>> +    int64_t destination_port, const char *protocol_str,
>>> +    const char *source_ip_str, const char *target_ip_str,
>>> +    const struct nbrec_logical_switch_port *lsp)
>>> +{
>>> +    for (size_t i = 0; i < lsp->n_health_checks; i++) {
>>> +        const struct nbrec_logical_switch_port_health_check *lsp_hc_p =
>>> +            lsp->health_checks[i];
>>> +
>>> +        if (!strcmp(lsp_hc_p->src_ip, source_ip_str) &&
>>> +            !strcmp(lsp_hc_p->protocol, protocol_str)) {
>>> +
>>> +            if (strcmp(protocol_str, "icmp") &&
>>> +                lsp_hc_p->port != destination_port) {
>>> +                continue;
>>> +            }
>>> +
>>> +            if (strcmp(lsp_hc_p->address, target_ip_str) == 0) {
>>> +                return true;
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +    return false;
>>> +}
>>> +
>>> +static char *
>>> +lsp_health_check_parse_target_address(
>>> +    const char *target_ip_str, const char *protocol_str,
>>> +    const char *source_ip_str, const int64_t destination_port,
>>> +    const struct nbrec_logical_switch_port *lsp)
>>> +{
>>> +    bool ip_found_on_port = false;
>>> +    char *error = NULL;
>>> +
>>> +    struct lport_addresses target_address;
>>> +    if (!extract_ip_address(target_ip_str, &target_address)) {
>>> +        error = xasprintf("Not a valid IPv4 or IPv6 address %s.",
>>> +                          target_ip_str);
>>> +        return error;
>>> +    }
>>> +
>>> +    struct lport_addresses lsp_address;
>>> +    for (size_t i = 0; i < lsp->n_addresses; i++) {
>>> +        if (!extract_lsp_addresses(lsp->addresses[i], &lsp_address)) {
>>> +            error = xasprintf("Error extracting logical switch port 
>>> address.");
>>> +            goto cleanup;
>>> +        }
>>> +
>>> +        if (lsp_contains_duplicate_ip(&target_address,
>>> +                                      &lsp_address, lsp, NULL)) {
>>> +            ip_found_on_port = true;
>>> +        }
>>> +
>>> +        destroy_lport_addresses(&lsp_address);
>>> +
>>> +        if (ip_found_on_port) {
>>> +            break;
>>> +        }
>>> +    }
>>> +
>>> +    if (!ip_found_on_port) {
>>> +        error = xasprintf("Address %s not configured on port %s.",
>>> +                          target_ip_str, lsp->name);
>>> +        goto cleanup;
>>> +    }
>>> +
>>> +    if (lsp_health_check_get_duplicate_record(destination_port,
>>> +                                              protocol_str,
>>> +                                              source_ip_str,
>>> +                                              target_ip_str, lsp)) {
>>> +        error = xasprintf("Health check with address %s already exists "
>>> +                          "on port %s.", target_ip_str, lsp->name);
>>> +        goto cleanup;
>>> +    }
>>> +
>>> +cleanup:
>>> +    destroy_lport_addresses(&target_address);
>>> +    return error;
>>> +}
>>> +
>>> +static void
>>> +nbctl_pre_lsp_health_check_add(struct ctl_context *ctx)
>>> +{
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_col_name);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_col_addresses);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_col_health_checks);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_port);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_protocol);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_src_ip);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_address);
>>> +}
>>> +
>>> +static void
>>> +nbctl_lsp_health_check_add(struct ctl_context *ctx)
>>> +{
>>> +    const struct nbrec_logical_switch_port *lsp = NULL;
>>> +    const struct nbrec_logical_switch_port_health_check *lsp_hc = NULL;
>>> +
>>> +    const char *lsp_port_name = ctx->argv[1];
>>> +    const char *protocol_type = ctx->argv[2];
>>> +    const char *src_ip = ctx->argv[3];
>>> +    char *error = NULL;
>>> +
>>> +    error = lsp_by_name_or_uuid(ctx, lsp_port_name, true, &lsp);
>>> +    if (error) {
>>> +        ctx->error = error;
>>> +        return;
>>> +    }
>>> +
>>> +    if (lsp->type && lsp->type[0]) {
>>> +        ctl_error(ctx, "%s: Health check monitoring supported only for"
>>> +                  " port with vif type.", lsp->type);
>>> +        return;
>>> +    }
>>> +
>>> +    char *validated_src_ip = normalize_addr_str(src_ip);
>>> +    if (!validated_src_ip) {
>>> +        ctl_error(ctx, "%s: Not a valid IPv4 or IPv6 address.", src_ip);
>>> +        goto cleanup;
>>> +    }
>>> +
>>> +    enum health_check_protocol protocol;
>>> +    error = lsp_health_check_parse_protocol(protocol_type, &protocol);
>>> +    if (error) {
>>> +        ctx->error = error;
>>> +        goto cleanup;
>>> +    }
>>> +
>>> +    uint16_t destination_port = 0;
>>> +    int target_ip_index = 0;
>>> +    error = lsp_health_check_parse_dst_port(ctx, protocol,
>>> +                                            protocol_type,
>>> +                                            &destination_port,
>>> +                                            &target_ip_index);
>>> +    if (error) {
>>> +        ctx->error = error;
>>> +        goto cleanup;
>>> +    }
>>> +
>>> +    const char *target_address = ctx->argv[target_ip_index];
>>> +    error = lsp_health_check_parse_target_address(
>>> +                target_address, protocol_type,
>>> +                validated_src_ip, destination_port, lsp);
>>> +    if (error) {
>>> +        ctx->error = error;
>>> +        goto cleanup;
>>> +    }
>>> +
>>> +    lsp_hc = nbrec_logical_switch_port_health_check_insert(ctx->txn);
>>> +    nbrec_logical_switch_port_health_check_set_protocol(lsp_hc,
>>> +                                                        protocol_type);
>>> +    nbrec_logical_switch_port_health_check_set_src_ip(lsp_hc,
>>> +                                                      validated_src_ip);
>>> +    nbrec_logical_switch_port_health_check_set_port(lsp_hc,
>>> +                                                    destination_port);
>>> +
>>> +    nbrec_logical_switch_port_health_check_set_address(lsp_hc,
>>> +        target_address);
>>> +
>> Nit: these two empty lines above are not really needed.
>>
>>> +    nbrec_logical_switch_port_update_health_checks_addvalue(lsp, lsp_hc);
>> Do we need a nbrec_logical_switch_port_verify_health_checks(lsp) above?
> 

> I haven't encountered this API (_verify_ functions) in code before.
I’ll go ahead and use it for now, but could you help me understand the
specific cases where it’s actually necessary? I want to avoid misusing
it or leaving gaps in the code. It's just not used in all places now in
code.  Is it for some cascanding updates , but I’m not entirely clear on
how this API fits in. I'm also trying to figure out a problem in IDL
right now, maybe this can help me.  If you have a moment, I’d appreciate
a brief explanation ?

The <db>rec_<table>_verify_<column>(<record>) function should be used in
cases when we're doing a "read-modify-write operation".  I.e., read the
column values (if it's a set/list/dict/etc), write (add/update/delete)
some or all of the values of the record's column and then commit the change.

If we want to ensure that at the moment when the transaction is
committed the values that are stored in the record's column are still
the ones we used when "reading" on the client side we need to use the
_verify_*() API.

However, in this specific place, I see now this is a newly inserted
record so we obviously don't need to call verify (as the record didn't
previously exist in the database), my bad.

> 
>>> +
>>> +cleanup:
>>> +    free(validated_src_ip);
>>> +}
>>> +
>>> +static char * OVS_WARN_UNUSED_RESULT
>>> +find_logical_switch_port_health_check_by_uuid(
>>> +    struct ctl_context *ctx,
>>> +    const char *id,
>>> +    const struct nbrec_logical_switch_port_health_check **lsp_hc_p)
>>> +{
>>> +    const struct nbrec_logical_switch_port_health_check *lsp_hc;
>>> +    *lsp_hc_p = NULL;
>>> +
>>> +    struct uuid lsp_hc_uuid;
>>> +    bool is_uuid = uuid_from_string(&lsp_hc_uuid, id);
>>> +
>>> +    if (!is_uuid) {
>>> +        return xasprintf("%s: Invalid UUID format.", id);
>>> +    }
>>> +
>>> +    lsp_hc = nbrec_logical_switch_port_health_check_get_for_uuid(
>>> +                    ctx->idl, &lsp_hc_uuid);
>>> +
>>> +    if (!lsp_hc) {
>>> +        return xasprintf("%s: Logical Switch Port Health Check "
>>> +                         "not found.", id);
>>> +    }
>>> +
>>> +    *lsp_hc_p = lsp_hc;
>>> +
>>> +    return NULL;
>>> +}
>>> +
>>> +static void
>>> +nbctl_pre_lsp_health_check_del(struct ctl_context *ctx)
>>> +{
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_col_name);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_col_health_checks);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_protocol);
>>> +}
>>> +
>>> +static void
>>> +nbctl_lsp_health_check_del(struct ctl_context *ctx)
>>> +{
>>> +    const struct nbrec_logical_switch_port_health_check *lsp_hc = NULL;
>>> +    const struct nbrec_logical_switch_port *lsp = NULL;
>>> +    const char *port_id = ctx->argv[1];
>>> +    const char *hc_uuid = ctx->argv[2];
>>> +
>>> +    char *error;
>>> +    error = lsp_by_name_or_uuid(ctx, port_id, true, &lsp);
>>> +    if (error) {
>>> +        ctx->error = error;
>>> +        return;
>>> +    }
>>> +
>>> +    if (hc_uuid) {
>>> +        error = find_logical_switch_port_health_check_by_uuid(ctx,
>>> +                                                              hc_uuid,
>>> +                                                              &lsp_hc);
>>> +        if (error) {
>>> +            ctx->error = error;
>>> +            return;
>>> +        }
>>> +
>>> +        nbrec_logical_switch_port_update_health_checks_delvalue(lsp, 
>>> lsp_hc);
>> Do we need a nbrec_logical_switch_port_verify_health_checks(lsp) above?

Here too, we're deleting this specific lsp_hc so it doesn't really
matter if the rest of the set of healthcheck records associated to 'lsp'
changed in the meantime, all we care about is removing the value lsp_hc
from the set.

So you can ignore my comment about using
nbrec_logical_switch_port_verify_health_checks().  On this second read
it seems we don't need it either.

Hope this clarifies your concern and sorry for the noise about the
"verify" calls.

Thanks,
Dumitru

>>
>>> +        nbrec_logical_switch_port_health_check_delete(lsp_hc);
>>> +        return;
>>> +    }
>>> +
>>> +    for (size_t i = 0; i < lsp->n_health_checks; i++) {
>>> +        nbrec_logical_switch_port_update_health_checks_delvalue(
>>> +            lsp, lsp->health_checks[i]);
>>> +        
>>> nbrec_logical_switch_port_health_check_delete(lsp->health_checks[i]);
>>> +    }
>>> +}
>>> +
>>> +static int
>>> +lsp_health_check_cmp(const void *lsp_hc_1_, const void *lsp_hc_2_)
>>> +{
>>> +    const struct nbrec_logical_switch_port_health_check *const *lsp_hc_1p =
>>> +        lsp_hc_1_;
>>> +    const struct nbrec_logical_switch_port_health_check *const *lsp_hc_2p =
>>> +        lsp_hc_2_;
>>> +    const struct nbrec_logical_switch_port_health_check *lsp_hc_1 =
>>> +        *lsp_hc_1p;
>>> +    const struct nbrec_logical_switch_port_health_check *lsp_hc_2 =
>>> +        *lsp_hc_2p;
>>> +
>>> +    int src_ip_cmp = strcmp(lsp_hc_1->src_ip, lsp_hc_2->src_ip);
>>> +    if (src_ip_cmp) {
>>> +        return src_ip_cmp;
>>> +    }
>>> +
>>> +    int protocol_cmp = strcmp(lsp_hc_1->protocol, lsp_hc_2->protocol);
>>> +    if (protocol_cmp) {
>>> +        return protocol_cmp;
>>> +    }
>>> +
>>> +    if (strcmp(lsp_hc_1->protocol, "icmp")) {
>>> +        if (lsp_hc_1->port != lsp_hc_2->port) {
>>> +            return lsp_hc_1->port < lsp_hc_2->port ? -1 : 1;
>>> +        }
>>> +    }
>>> +
>>> +    int ip_cmp = strcmp(lsp_hc_1->address, lsp_hc_2->address);
>>> +    if (ip_cmp) {
>>> +        return ip_cmp;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static void
>>> +nbctl_pre_lsp_health_check_list(struct ctl_context *ctx)
>>> +{
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_col_name);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_col_health_checks);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_port);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_protocol);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_src_ip);
>>> +    ovsdb_idl_add_column(ctx->idl,
>>> +        &nbrec_logical_switch_port_health_check_col_address);
>>> +}
>>> +
>>> +static void
>>> +nbctl_lsp_health_check_list(struct ctl_context *ctx)
>>> +{
>>> +    const struct nbrec_logical_switch_port_health_check **lsp_hcs;
>>> +    const struct nbrec_logical_switch_port *lsp = NULL;
>>> +    const char *port = ctx->argv[1];
>>> +
>>> +    char *error;
>>> +    error = lsp_by_name_or_uuid(ctx, port, true, &lsp);
>>> +    if (error) {
>>> +        ctx->error = error;
>>> +        return;
>>> +    }
>>> +
>>> +    if (!lsp->n_health_checks) {
>>> +        return;
>>> +    }
>>> +
>>> +    lsp_hcs = xmalloc(sizeof *lsp_hcs * lsp->n_health_checks);
>>> +    for (size_t i = 0; i < lsp->n_health_checks; i++) {
>>> +        lsp_hcs[i] = lsp->health_checks[i];
>>> +    }
>>> +
>>> +    qsort(lsp_hcs, lsp->n_health_checks,
>>> +          sizeof *lsp_hcs, lsp_health_check_cmp);
>>> +
>>> +    ds_put_format(&ctx->output, "Logical Switch Port %s:\n", port);
>>> +    for (size_t i = 0; i < lsp->n_health_checks; i++) {
>>> +        const struct nbrec_logical_switch_port_health_check *hc
>>> +
>>> +                                                        = lsp_hcs[i];
>> Nit: this looks a bit weird.  Maybe write it on a single line, it seems
>> to fit.
>>
>>> +        ds_put_format(&ctx->output, "  Protocol      :  %s\n",
>>> +                      hc->protocol);
>>> +        ds_put_format(&ctx->output, "  Source IP     :  %s\n",
>>> +                      hc->src_ip);
>>> +
>>> +        if (strcmp(hc->protocol, "icmp")) {
>>> +            ds_put_format(&ctx->output, "  Port          :  %"PRId64"\n",
>>> +                          hc->port);
>>> +        }
>>> +
>>> +        ds_put_format(&ctx->output, "  Addresses     :  ");
>>> +        ds_put_format(&ctx->output, "%s", hc->address);
>>> +        ds_put_format(&ctx->output, "\n");
>>> +
>>> +        int interval = smap_get_int(&hc->options, "interval", 0);
>>> +        int timeout = smap_get_int(&hc->options, "timeout", 0);
>>> +        int success_count = smap_get_int(&hc->options, "success_count", 0);
>>> +        int failure_count = smap_get_int(&hc->options, "failure_count", 0);
>>> +        if (interval) {
>>> +            ds_put_format(&ctx->output, "  Interval      :  %d\n",
>>> +                          interval);
>>> +        }
>>> +        if (timeout) {
>>> +            ds_put_format(&ctx->output, "  Timeout       :  %d\n",
>>> +                          timeout);
>>> +        }
>>> +        if (success_count) {
>>> +            ds_put_format(&ctx->output, "  Success count :  %d\n",
>>> +                          success_count);
>>> +        }
>>> +        if (failure_count) {
>>> +            ds_put_format(&ctx->output, "  Failure count :  %d\n",
>>> +                          failure_count);
>>> +        }
>>> +        if (i < lsp->n_health_checks - 1) {
>>> +            ds_put_format(&ctx->output, "\n");
>>> +        }
>>> +    }
>>> +}
>>> +
>>>   static const struct ctl_table_class tables[NBREC_N_TABLES] = {
>>>       [NBREC_TABLE_DHCP_OPTIONS].row_ids
>>>       = {{&nbrec_logical_switch_port_col_name, NULL,
>>> @@ -9064,6 +9538,16 @@ static const struct ctl_command_syntax 
>>> nbctl_commands[] = {
>>>         nbctl_pre_static_mac_binding, nbctl_static_mac_binding_list, NULL,
>>>         "", RO },
>>>   
>>> +    /* Health Check commands */
>>> +    {"lsp-hc-add", 4, 5, "PORT TYPE SRC_IP [DST_PORT] ADDRESS",
>>> +     nbctl_pre_lsp_health_check_add, nbctl_lsp_health_check_add,
>>> +     NULL, "", RW },
>>> +    {"lsp-hc-del", 1, INT_MAX, "PORT [HC_UUID]",
>>> +     nbctl_pre_lsp_health_check_del, nbctl_lsp_health_check_del,
>>> +     NULL, "", RW },
>>> +    {"lsp-hc-list", 1, 1, "PORT", nbctl_pre_lsp_health_check_list,
>>> +     nbctl_lsp_health_check_list, NULL, "", RW },
>>> +
>>>       {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO},
>>>   };
>>>   
> 
> 

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to