From 0c299b43f90ce155635dcec4b1f3c01685ede733 Mon Sep 17 00:00:00 2001
From: Brad House <brad@brad-house.com>
Date: Tue, 11 Feb 2025 12:28:57 -0500
Subject: [PATCH 1/7] IGNORE THIS COMMIT: merge libyang3 step 1 (PR #21679)

---
 .github/CODEOWNERS                            |   6 +-
 .gitmodules                                   |   2 +-
 rules/docker-fpm-frr.mk                       |   4 +-
 rules/frr.mk                                  |   5 +-
 rules/libyang.mk                              |   4 +-
 rules/libyang1.dep                            |  10 -
 rules/libyang1.mk                             |  43 --
 rules/libyang2.dep                            |  10 -
 rules/libyang2.mk                             |  31 -
 rules/libyang3-py3.dep                        |  11 +
 rules/libyang3-py3.mk                         |  18 +
 rules/libyang3.dep                            |  10 +
 rules/libyang3.mk                             |  28 +
 rules/sonic-yang-models-py3.mk                |   4 +-
 slave.mk                                      |   2 +-
 sonic-slave-bookworm/Dockerfile.j2            |   9 +-
 sonic-slave-bullseye/Dockerfile.j2            |  12 +-
 sonic-slave-buster/Dockerfile.j2              |  10 +-
 src/libyang1/Makefile                         |  44 --
 src/libyang2/.gitignore                       |   3 -
 src/libyang2/Makefile                         |  49 --
 src/libyang3-py3/Makefile                     |  24 +
 src/libyang3-py3/patch/0001-debian.patch      |  60 ++
 .../0002-pr134-json-string-datatypes.patch    |  85 +++
 .../patch/0003-parse_module-memleak.patch     |  40 ++
 .../patch/0004-pr132-backlinks.patch          | 322 ++++++++++
 src/libyang3-py3/patch/series                 |   4 +
 src/{libyang1 => libyang3}/.gitignore         |   2 +
 src/libyang3/Makefile                         |  36 ++
 .../patch/0001-pr2344-json-strings.patch      | 220 +++++++
 .../patch/0002-pr2352-backlinks.patch         | 554 ++++++++++++++++++
 .../patch/0003-minmax-errpath-3adb304.patch   | 158 +++++
 .../patch/0004-union-apptag-529a594.patch     |  56 ++
 .../0005-pr2360-validate-union-errors.patch   | 366 ++++++++++++
 .../patch/0006-pr2361-union-sort-assert.patch |  45 ++
 src/libyang3/patch/series                     |   6 +
 .../patch/0081-backport-libyang3.patch        | 230 ++++++++
 src/sonic-frr/patch/series                    |   1 +
 .../tests/yang_model_tests/test_yang_model.py |  22 +-
 .../yang_model_tests/tests/asic-sensors.json  |   2 +-
 .../tests/auto_techsupport.json               |   8 +-
 .../tests/yang_model_tests/tests/bgp.json     |  11 +-
 .../yang_model_tests/tests/buffer_pool.json   |   2 +-
 .../tests/device_metadata.json                |   6 +-
 .../tests/dhcp_server_ipv4.json               |   2 +-
 .../tests/fine-grained-ecmp.json              |   8 +-
 .../yang_model_tests/tests/flex_counter.json  |   9 +-
 .../tests/yang_model_tests/tests/kdump.json   |   4 +-
 .../tests/kubernetes_master.json              |   2 +-
 .../tests/memory_statistics.json              |   8 +-
 .../yang_model_tests/tests/mgmt_port.json     |   6 +-
 .../tests/mirror_session.json                 |   2 +-
 .../tests/yang_model_tests/tests/nat.json     |   9 +-
 .../tests/yang_model_tests/tests/neigh.json   |   4 +-
 .../tests/yang_model_tests/tests/ntp.json     |   4 +-
 .../tests/yang_model_tests/tests/nvgre.json   |   6 +-
 .../tests/password_hardening.json             |  10 +-
 .../tests/yang_model_tests/tests/pbh.json     |   6 +-
 .../yang_model_tests/tests/peer-switch.json   |   2 +-
 .../tests/yang_model_tests/tests/port.json    |  17 +-
 .../yang_model_tests/tests/portchannel.json   |   8 +-
 .../tests/yang_model_tests/tests/qos.json     |   4 +-
 .../tests/yang_model_tests/tests/restapi.json |   4 +-
 .../yang_model_tests/tests/route_filter.json  |   3 +-
 .../tests/serial_console.json                 |   2 +-
 .../tests/yang_model_tests/tests/sflow.json   |   4 +-
 .../tests/yang_model_tests/tests/snmp.json    |  16 +-
 .../tests/sonic-events-bgp.json               |   6 +-
 .../tests/sonic-events-dhcp-relay.json        |   6 +-
 .../tests/sonic-events-host.json              |  28 +-
 .../tests/sonic-events-swss.json              |   8 +-
 .../tests/sonic-events-syncd.json             |   4 +-
 .../yang_model_tests/tests/ssh-server.json    |  10 +-
 .../yang_model_tests/tests/stormond.json      |   4 +-
 .../tests/suppress_asic_sdk_health_event.json |   2 +-
 .../tests/yang_model_tests/tests/syslog.json  |  10 +-
 .../yang_model_tests/tests/system_port.json   |   2 +-
 .../tests/yang_model_tests/tests/tunnel.json  |   6 +-
 .../tests/yang_model_tests/tests/vnet.json    |  12 +-
 .../yang_model_tests/tests_config/neigh.json  |   2 +-
 .../yang_model_tests/tests_config/port.json   |   4 +-
 81 files changed, 2454 insertions(+), 365 deletions(-)
 delete mode 100644 rules/libyang1.dep
 delete mode 100644 rules/libyang1.mk
 delete mode 100644 rules/libyang2.dep
 delete mode 100644 rules/libyang2.mk
 create mode 100644 rules/libyang3-py3.dep
 create mode 100644 rules/libyang3-py3.mk
 create mode 100644 rules/libyang3.dep
 create mode 100644 rules/libyang3.mk
 delete mode 100644 src/libyang1/Makefile
 delete mode 100644 src/libyang2/.gitignore
 delete mode 100644 src/libyang2/Makefile
 create mode 100644 src/libyang3-py3/Makefile
 create mode 100644 src/libyang3-py3/patch/0001-debian.patch
 create mode 100644 src/libyang3-py3/patch/0002-pr134-json-string-datatypes.patch
 create mode 100644 src/libyang3-py3/patch/0003-parse_module-memleak.patch
 create mode 100644 src/libyang3-py3/patch/0004-pr132-backlinks.patch
 create mode 100644 src/libyang3-py3/patch/series
 rename src/{libyang1 => libyang3}/.gitignore (58%)
 create mode 100644 src/libyang3/Makefile
 create mode 100644 src/libyang3/patch/0001-pr2344-json-strings.patch
 create mode 100644 src/libyang3/patch/0002-pr2352-backlinks.patch
 create mode 100644 src/libyang3/patch/0003-minmax-errpath-3adb304.patch
 create mode 100644 src/libyang3/patch/0004-union-apptag-529a594.patch
 create mode 100644 src/libyang3/patch/0005-pr2360-validate-union-errors.patch
 create mode 100644 src/libyang3/patch/0006-pr2361-union-sort-assert.patch
 create mode 100644 src/libyang3/patch/series
 create mode 100644 src/sonic-frr/patch/0081-backport-libyang3.patch

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index f553a042f9a6..269863f04e24 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -63,10 +63,10 @@
 
 # yang
 /src/sonic-yang-models/                 @praveen-li @dgsudharsan @rathnasabapathyv @venkatmahalingam @qiluo-msft
-/src/sonic-yang-mgmt/                   @sonet-net/sonic-management
+/src/sonic-yang-mgmt/                   @sonic-net/sonic-management
 /src/libyang/                           @sonic-net/sonic-management
-/src/libyang1/                          @sonic-net/sonic-management
-/src/libyang2/                          @sonic-net/sonic-management
+/src/libyang3/                          @sonic-net/sonic-management
+/src/libyang3-py3/                      @sonic-net/sonic-management
 
 # bgpcfgd
 /src/sonic-bgpcfgd/                     @StormLiangMS
diff --git a/.gitmodules b/.gitmodules
index f8d10f3af4ab..92a585eefd9f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -132,4 +132,4 @@
 	url = https://github.com/Marvell-switching/sonic-platform-marvell.git
 [submodule "platform/vpp"]
 	path = platform/vpp
-	url = https://github.com/sonic-net/sonic-platform-vpp.git
\ No newline at end of file
+	url = https://github.com/sonic-net/sonic-platform-vpp.git
diff --git a/rules/docker-fpm-frr.mk b/rules/docker-fpm-frr.mk
index c914dbff1da6..441c8054a05d 100644
--- a/rules/docker-fpm-frr.mk
+++ b/rules/docker-fpm-frr.mk
@@ -7,10 +7,10 @@ DOCKER_FPM_FRR_DBG = $(DOCKER_FPM_FRR_STEM)-$(DBG_IMAGE_MARK).gz
 $(DOCKER_FPM_FRR)_PATH = $(DOCKERS_PATH)/$(DOCKER_FPM_FRR_STEM)
 $(DOCKER_FPM_FRR)_PYTHON_WHEELS += $(SONIC_BGPCFGD) $(SONIC_FRR_MGMT_FRAMEWORK)
 
-$(DOCKER_FPM_FRR)_DEPENDS += $(FRR) $(FRR_SNMP) $(SWSS) $(LIBYANG2)
+$(DOCKER_FPM_FRR)_DEPENDS += $(FRR) $(FRR_SNMP) $(SWSS) $(LIBYANG3)
 $(DOCKER_FPM_FRR)_DBG_DEPENDS = $($(DOCKER_SWSS_LAYER_BOOKWORM)_DBG_DEPENDS)
 $(DOCKER_FPM_FRR)_DBG_DEPENDS += $(SWSS_DBG) $(LIBSWSSCOMMON_DBG) \
-                                $(FRR_DBG) $(FRR_SNMP_DBG) $(LIBYANG2_DBG)
+                                $(FRR_DBG) $(FRR_SNMP_DBG) $(LIBYANG3_DBG)
 
 $(DOCKER_FPM_FRR)_DBG_IMAGE_PACKAGES = $($(DOCKER_SWSS_LAYER_BOOKWORM)_DBG_IMAGE_PACKAGES)
 
diff --git a/rules/frr.mk b/rules/frr.mk
index ff3c3ea4ab17..a952a455fe63 100644
--- a/rules/frr.mk
+++ b/rules/frr.mk
@@ -8,9 +8,8 @@ export FRR_VERSION FRR_SUBVERSION FRR_BRANCH FRR_TAG
 
 
 FRR = frr_$(FRR_VERSION)-sonic-$(FRR_SUBVERSION)_$(CONFIGURED_ARCH).deb
-$(FRR)_DEPENDS += $(LIBSNMP_DEV) $(LIBYANG2) $(LIBYANG2_DEV)
-$(FRR)_RDEPENDS += $(LIBYANG2)
-$(FRR)_UNINSTALLS = $(LIBYANG2_DEV) $(LIBYANG2)
+$(FRR)_DEPENDS += $(LIBSNMP_DEV) $(LIBYANG3_DEV)
+$(FRR)_RDEPENDS += $(LIBYANG3)
 $(FRR)_SRC_PATH = $(SRC_PATH)/sonic-frr
 SONIC_MAKE_DEBS += $(FRR)
 
diff --git a/rules/libyang.mk b/rules/libyang.mk
index a8e6734459ea..0819b0a54318 100644
--- a/rules/libyang.mk
+++ b/rules/libyang.mk
@@ -29,7 +29,7 @@ LIBYANG_PY3 = python3-yang_$(LIBYANG_VERSION)_$(CONFIGURED_ARCH).deb
 $(LIBYANG_PY3)_DEPENDS += $(LIBYANG) $(LIBYANG_CPP)
 $(eval $(call add_derived_package,$(LIBYANG),$(LIBYANG_PY3)))
 
-$(eval $(call add_conflict_package,$(LIBYANG),$(LIBYANG1),$(LIBYANG2)))
-$(eval $(call add_conflict_package,$(LIBYANG_DEV),$(LIBYANG1_DEV),$(LIBYANG2_DEV)))
+#$(eval $(call add_conflict_package,$(LIBYANG),$(LIBYANG3)))
+$(eval $(call add_conflict_package,$(LIBYANG_DEV),$(LIBYANG3_DEV)))
 
 export LIBYANG LIBYANG_DBGSYM LIBYANG_DEV LIBYANG_CPP LIBYANG_PY3
diff --git a/rules/libyang1.dep b/rules/libyang1.dep
deleted file mode 100644
index 343b06cf9611..000000000000
--- a/rules/libyang1.dep
+++ /dev/null
@@ -1,10 +0,0 @@
-
-SPATH       := $($(LIBYANG1)_SRC_PATH)
-DEP_FILES   := $(SONIC_COMMON_FILES_LIST) rules/libyang1.mk rules/libyang1.dep
-DEP_FILES   += $(SONIC_COMMON_BASE_FILES_LIST)
-DEP_FILES   += $(shell git ls-files $(SPATH))
-
-$(LIBYANG1)_CACHE_MODE  := GIT_CONTENT_SHA
-$(LIBYANG1)_DEP_FLAGS   := $(SONIC_COMMON_FLAGS_LIST)
-$(LIBYANG1)_DEP_FILES   := $(DEP_FILES)
-
diff --git a/rules/libyang1.mk b/rules/libyang1.mk
deleted file mode 100644
index 6d24e3fc5a4e..000000000000
--- a/rules/libyang1.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# libyang1
-
-LIBYANG1_VERSION_BASE = 1.0
-LIBYANG1_VERSION = $(LIBYANG1_VERSION_BASE).184
-LIBYANG1_SUBVERSION = 2
-LIBYANG1_FULLVERSION = $(LIBYANG1_VERSION)-$(LIBYANG1_SUBVERSION)
-
-export LIBYANG1_VERSION_BASE
-export LIBYANG1_VERSION
-export LIBYANG1_SUBVERSION
-export LIBYANG1_FULLVERSION
-
-LIBYANG1 = libyang1_$(LIBYANG1_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(LIBYANG1)_SRC_PATH = $(SRC_PATH)/libyang1
-SONIC_MAKE_DEBS += $(LIBYANG1)
-
-LIBYANG1_DEV = libyang-dev_$(LIBYANG1_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG1),$(LIBYANG1_DEV)))
-
-LIBYANG1_DBG = libyang1-dbgsym_$(LIBYANG1_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG1),$(LIBYANG1_DBG)))
-
-LIBYANG1_CPP = libyang-cpp1_$(LIBYANG1_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(LIBYANG1_CPP)_DEPENDS += $(LIBYANG1)
-$(eval $(call add_derived_package,$(LIBYANG1),$(LIBYANG1_CPP)))
-
-LIBYANG1_CPP_DEV = libyang-cpp-dev_$(LIBYANG1_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG1),$(LIBYANG1_CPP_DEV)))
-
-LIBYANG1_CPP_DBG = libyang-cpp1-dbgsym_$(LIBYANG1_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG1),$(LIBYANG1_CPP_DBG)))
-
-YANG_TOOLS = yang-tools_$(LIBYANG1_FULLVERSION)_all.deb
-$(YANG_TOOLS)_DEPENDS += $(LIBYANG1)
-$(eval $(call add_derived_package,$(LIBYANG1),$(YANG_TOOLS)))
-
-LIBYANG1_TOOLS = libyang-tools_$(LIBYANG1_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG1),$(LIBYANG1_TOOLS)))
-
-LIBYANG1_TOOLS_DBG = libyang-tools-dbgsym_$(LIBYANG1_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG1),$(LIBYANG1_TOOLS_DBG)))
-
-export LIBYANG1 LIBYANG1_DBG LIBYANG1_DEV LIBYANG1_CPP LIBYANG1_CPP_DEV LIBYANG1_CPP_DBG YANG_TOOLS LIBYANG1_TOOLS LIBYANG1_TOOLS_DBG
diff --git a/rules/libyang2.dep b/rules/libyang2.dep
deleted file mode 100644
index a1b3977f2d86..000000000000
--- a/rules/libyang2.dep
+++ /dev/null
@@ -1,10 +0,0 @@
-
-SPATH       := $($(LIBYANG2)_SRC_PATH)
-DEP_FILES   := $(SONIC_COMMON_FILES_LIST) rules/libyang2.mk rules/libyang2.dep
-DEP_FILES   += $(SONIC_COMMON_BASE_FILES_LIST)
-DEP_FILES   += $(shell git ls-files $(SPATH))
-
-$(LIBYANG2)_CACHE_MODE  := GIT_CONTENT_SHA
-$(LIBYANG2)_DEP_FLAGS   := $(SONIC_COMMON_FLAGS_LIST)
-$(LIBYANG2)_DEP_FILES   := $(DEP_FILES)
-
diff --git a/rules/libyang2.mk b/rules/libyang2.mk
deleted file mode 100644
index e683ff05eb1f..000000000000
--- a/rules/libyang2.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# libyang2
-
-LIBYANG2_VERSION_BASE = 2.1
-LIBYANG2_VERSION = $(LIBYANG2_VERSION_BASE).148
-LIBYANG2_SUBVERSION = 0.2
-LIBYANG2_FULLVERSION = $(LIBYANG2_VERSION)-$(LIBYANG2_SUBVERSION)
-
-export LIBYANG2_VERSION_BASE
-export LIBYANG2_VERSION
-export LIBYANG2_SUBVERSION
-export LIBYANG2_FULLVERSION
-
-LIBYANG2 = libyang2t64_$(LIBYANG2_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(LIBYANG2)_SRC_PATH = $(SRC_PATH)/libyang2
-SONIC_MAKE_DEBS += $(LIBYANG2)
-
-LIBYANG2_DEV = libyang2-dev_$(LIBYANG2_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG2),$(LIBYANG2_DEV)))
-
-LIBYANG2_DBG = libyang2t64-dbgsym_$(LIBYANG2_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG2),$(LIBYANG2_DBG)))
-
-
-LIBYANG2_TOOLS = libyang2-tools_$(LIBYANG2_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG2),$(LIBYANG2_TOOLS)))
-
-LIBYANG2_TOOLS_DBG = libyang2-tools-dbgsym_$(LIBYANG2_FULLVERSION)_$(CONFIGURED_ARCH).deb
-$(eval $(call add_derived_package,$(LIBYANG2),$(LIBYANG2_TOOLS_DBG)))
-
-export LIBYANG2 LIBYANG2_DBG LIBYANG2_DEV LIBYANG2_TOOLS LIBYANG2_TOOLS_DBG
-
diff --git a/rules/libyang3-py3.dep b/rules/libyang3-py3.dep
new file mode 100644
index 000000000000..117f59c641dd
--- /dev/null
+++ b/rules/libyang3-py3.dep
@@ -0,0 +1,11 @@
+
+SPATH       := $($(LIBYANG3_PY3)_SRC_PATH)
+DEP_FILES   := $(SONIC_COMMON_FILES_LIST) rules/libyang3-py3.mk rules/libyang3-py3.dep
+DEP_FILES   += $(SONIC_COMMON_BASE_FILES_LIST)
+SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && find . -type f -exec sh -c 'git ls-files --error-unmatch "$0" >/dev/null 2>&1' {} \; -printf '%P\n'))
+
+$(LIBYANG3_PY3)_CACHE_MODE  := GIT_CONTENT_SHA
+$(LIBYANG3_PY3)_DEP_FLAGS   := $(SONIC_COMMON_FLAGS_LIST)
+$(LIBYANG3_PY3)_DEP_FILES   := $(DEP_FILES)
+$(LIBYANG3_PY3)_SMDEP_FILES := $(SMDEP_FILES)
+$(LIBYANG3_PY3)_SMDEP_PATHS := $(SPATH)
diff --git a/rules/libyang3-py3.mk b/rules/libyang3-py3.mk
new file mode 100644
index 000000000000..a2f1716878ac
--- /dev/null
+++ b/rules/libyang3-py3.mk
@@ -0,0 +1,18 @@
+# libyang3 python3 deb package
+
+LIBYANG3_PY3_VERSION = 3.0.3
+LIBYANG3_PY3_SUBVERSION = 1
+LIBYANG3_PY3_FULLVERSION = $(LIBYANG3_PY3_VERSION)-$(LIBYANG3_PY3_SUBVERSION)
+
+export LIBYANG3_PY3_VERSION
+export LIBYANG3_PY3_SUBVERSION
+export LIBYANG3_PY3_FULLVERSION
+
+LIBYANG3_PY3 = python3-libyang_$(LIBYANG3_PY3_FULLVERSION)_$(CONFIGURED_ARCH).deb
+$(LIBYANG3_PY3)_SRC_PATH = $(SRC_PATH)/libyang3-py3
+$(LIBYANG3_PY3)_DEPENDS += $(LIBYANG3) $(LIBYANG3_DEV)
+$(LIBYANG3_PY3)_RDEPENDS += $(LIBYANG3)
+SONIC_MAKE_DEBS += $(LIBYANG3_PY3)
+
+export LIBYANG3_PY3
+
diff --git a/rules/libyang3.dep b/rules/libyang3.dep
new file mode 100644
index 000000000000..a49a6900ac83
--- /dev/null
+++ b/rules/libyang3.dep
@@ -0,0 +1,10 @@
+
+SPATH       := $($(LIBYANG3)_SRC_PATH)
+DEP_FILES   := $(SONIC_COMMON_FILES_LIST) rules/libyang3.mk rules/libyang3.dep
+DEP_FILES   += $(SONIC_COMMON_BASE_FILES_LIST)
+DEP_FILES   += $(shell git ls-files $(SPATH))
+
+$(LIBYANG3)_CACHE_MODE  := GIT_CONTENT_SHA
+$(LIBYANG3)_DEP_FLAGS   := $(SONIC_COMMON_FLAGS_LIST)
+$(LIBYANG3)_DEP_FILES   := $(DEP_FILES)
+
diff --git a/rules/libyang3.mk b/rules/libyang3.mk
new file mode 100644
index 000000000000..695eb3a6b306
--- /dev/null
+++ b/rules/libyang3.mk
@@ -0,0 +1,28 @@
+# libyang3
+
+LIBYANG3_VERSION = 3.7.8
+LIBYANG3_SUBVERSION = 3
+LIBYANG3_FULLVERSION = $(LIBYANG3_VERSION)-$(LIBYANG3_SUBVERSION)
+
+export LIBYANG3_VERSION
+export LIBYANG3_SUBVERSION
+export LIBYANG3_FULLVERSION
+
+LIBYANG3 = libyang3_$(LIBYANG3_FULLVERSION)_$(CONFIGURED_ARCH).deb
+$(LIBYANG3)_SRC_PATH = $(SRC_PATH)/libyang3
+SONIC_MAKE_DEBS += $(LIBYANG3)
+
+LIBYANG3_DEV = libyang-dev_$(LIBYANG3_FULLVERSION)_$(CONFIGURED_ARCH).deb
+$(eval $(call add_derived_package,$(LIBYANG3),$(LIBYANG3_DEV)))
+
+LIBYANG3_TOOLS = libyang3-tools_$(LIBYANG3_FULLVERSION)_$(CONFIGURED_ARCH).deb
+$(eval $(call add_derived_package,$(LIBYANG3),$(LIBYANG3_TOOLS)))
+
+LIBYANG3_DBG = libyang3-dbgsym_$(LIBYANG3_FULLVERSION)_$(CONFIGURED_ARCH).deb
+$(eval $(call add_derived_package,$(LIBYANG3),$(LIBYANG3_DBG)))
+
+LIBYANG3_TOOLS_DBG = libyang3-tools-dbgsym_$(LIBYANG3_FULLVERSION)_$(CONFIGURED_ARCH).deb
+$(eval $(call add_derived_package,$(LIBYANG3),$(LIBYANG3_TOOLS_DBG)))
+
+export LIBYANG3 LIBYANG3_DBG LIBYANG3_DEV LIBYANG3_TOOLS LIBYANG3_TOOLS_DBG
+
diff --git a/rules/sonic-yang-models-py3.mk b/rules/sonic-yang-models-py3.mk
index f5a606c28c3a..d5e64ac07584 100644
--- a/rules/sonic-yang-models-py3.mk
+++ b/rules/sonic-yang-models-py3.mk
@@ -2,7 +2,9 @@ SONIC_YANG_MODELS_PY3 = sonic_yang_models-1.0-py3-none-any.whl
 $(SONIC_YANG_MODELS_PY3)_SRC_PATH = $(SRC_PATH)/sonic-yang-models
 $(SONIC_YANG_MODELS_PY3)_PYTHON_VERSION = 3
 $(SONIC_YANG_MODELS_PY3)_DEBS_DEPENDS = $(LIBYANG) $(LIBYANG_CPP) \
-                                        $(LIBYANG_PY3)
+                                        $(LIBYANG_PY3) \
+                                        $(LIBYANG3) \
+                                        $(LIBYANG3_PY3)
 
 SONIC_PYTHON_WHEELS += $(SONIC_YANG_MODELS_PY3)
 export SONIC_YANG_MODELS_PY3
diff --git a/slave.mk b/slave.mk
index 8f3864c42c97..2f39f6ded8b6 100644
--- a/slave.mk
+++ b/slave.mk
@@ -1733,4 +1733,4 @@ jessie : $$(addprefix $(TARGET_PATH)/,$$(JESSIE_DOCKER_IMAGES)) \
 
 ## To build some commonly used libs. Some submodules depend on these libs.
 ## It is used in component pipelines. For example: swss needs libnl, libyang
-lib-packages: $(addprefix $(DEBS_PATH)/,$(LIBNL3) $(LIBYANG) $(PROTOBUF) $(LIB_SONIC_DASH_API))
+lib-packages: $(addprefix $(DEBS_PATH)/,$(LIBNL3) $(LIBYANG) $(LIBYANG3) $(PROTOBUF) $(LIB_SONIC_DASH_API))
diff --git a/sonic-slave-bookworm/Dockerfile.j2 b/sonic-slave-bookworm/Dockerfile.j2
index 9bbce1541923..24fd1f882460 100644
--- a/sonic-slave-bookworm/Dockerfile.j2
+++ b/sonic-slave-bookworm/Dockerfile.j2
@@ -281,7 +281,7 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
         debhelper \
         autotools-dev \
         libbsd-dev \
-        pkg-config \
+        pkgconf \
         check \
 # For bmp
         librdkafka-dev \
@@ -370,6 +370,10 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
         libevent-dev \
 # For libyang
         swig \
+        pkgconf \
+        tcl-expect \
+# For libyang3-py3
+        python3-cffi \
 # For build dtb
         device-tree-compiler \
 # For sonic-mgmt-framework
@@ -424,7 +428,6 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
         libdbus-1-dev \
         libgirepository1.0-dev \
         libsystemd-dev \
-        pkg-config \
 # For sonic-utilities build
         python3-cryptography \
 # For audisp-tacplus
@@ -714,7 +717,7 @@ RUN eatmydata apt-get install -y \
         libxtables12:$arch \
         libatm1-dev:$arch \
         libbpf-dev:$arch \
-        libdb-dev:$arch pkg-config:$arch \
+        libdb-dev:$arch pkgconf:$arch \
         libnghttp2-14:$arch \
         librtmp1:$arch \
         libssh2-1:$arch \
diff --git a/sonic-slave-bullseye/Dockerfile.j2 b/sonic-slave-bullseye/Dockerfile.j2
index d9517ca7d5e3..558454055489 100644
--- a/sonic-slave-bullseye/Dockerfile.j2
+++ b/sonic-slave-bullseye/Dockerfile.j2
@@ -283,7 +283,7 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
 	debhelper \
         autotools-dev \
         libbsd-dev \
-        pkg-config \
+        pkgconf \
         check \
 # For mpdecimal
         docutils-common \
@@ -363,6 +363,10 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
         libevent-dev \
 # For libyang
         swig \
+        pkgconf \
+        tcl-expect \
+# For libyang3-py3
+        python3-cffi \
 # For build dtb
         device-tree-compiler \
 # For sonic-mgmt-framework
@@ -416,7 +420,7 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
         libdbus-1-dev \
         libgirepository1.0-dev \
         libsystemd-dev \
-        pkg-config \
+        pkgconf \
 # For sonic-swss-common build
         libhiredis-dev \
 # For audisp-tacplus
@@ -457,7 +461,7 @@ RUN eatmydata apt install -y \
         python3-ply \
         python3-scapy \
         python3-thrift \
-        libabsl20200923 libc-ares2 python3-six libboost-thread1.74.0 libboost-dev libboost-system-dev libboost-thread-dev libboost-filesystem1.74.0 libboost-program-options1.74.0 libboost-thread1.74.0 libnanomsg5 libpcap0.8 libthrift-0.13.0 libboost-dev libboost-filesystem-dev libboost-program-options-dev libgmp-dev libnanomsg-dev libpcap-dev libtool pkg-config libthrift-dev python3-thrift thrift-compiler libboost-iostreams1.74.0 libgc1 cpp libboost-dev libboost-all-dev libboost-graph-dev libboost-iostreams-dev libfl-dev libgc-dev libgmp-dev libbpf-dev tcpdump libelf-dev llvm clang python3-pyroute2 python3-ply python3-scapy python3-setuptools python3-thrift libthrift-0.13.0
+        libabsl20200923 libc-ares2 python3-six libboost-thread1.74.0 libboost-dev libboost-system-dev libboost-thread-dev libboost-filesystem1.74.0 libboost-program-options1.74.0 libboost-thread1.74.0 libnanomsg5 libpcap0.8 libthrift-0.13.0 libboost-dev libboost-filesystem-dev libboost-program-options-dev libgmp-dev libnanomsg-dev libpcap-dev libtool pkgconf libthrift-dev python3-thrift thrift-compiler libboost-iostreams1.74.0 libgc1 cpp libboost-dev libboost-all-dev libboost-graph-dev libboost-iostreams-dev libfl-dev libgc-dev libgmp-dev libbpf-dev tcpdump libelf-dev llvm clang python3-pyroute2 python3-ply python3-scapy python3-setuptools python3-thrift libthrift-0.13.0
 {%- endif %}
 
 {%- if CONFIGURED_ARCH == "amd64" %}
@@ -681,7 +685,7 @@ RUN eatmydata apt-get install -y nodejs
 {%- if CROSS_BUILD_ENVIRON == "y" %}
 RUN eatmydata apt-get install -y rsync dh-python
 
-RUN eatmydata apt-get install -y libelf-dev:$arch libdw-dev:$arch  libbz2-dev:$arch liblzo2-dev:$arch libedit-dev:$arch libevent-dev:$arch libopts25-dev:$arch libssl-dev:$arch pps-tools:$arch libpam-cap:$arch libcap-dev:$arch libpam0g-dev:$arch libaudit-dev:$arch libgtk-3-dev:$arch libkrb5-dev:$arch libsystemd-dev:$arch libwrap0-dev:$arch libkrb5-dev:$arch libboost1.74-dev:$arch libboost-dev:$arch libzmq5:$arch libzmq3-dev:$arch libdaemon-dev:$arch libjansson-dev:$arch libmnl-dev:$arch libsensors5:$arch libsensors4-dev:$arch libperl-dev:$arch libmariadb-dev:$arch libmariadb-dev-compat:$arch libpci-dev:$arch libjson-c-dev:$arch libreadline-dev:$arch librtr-dev:$arch librrd-dev:$arch libnetfilter-conntrack-dev:$arch libnetfilter-conntrack3:$arch libnfnetlink-dev:$arch libnftnl-dev:$arch libldap2-dev:$arch libbind-export-dev:$arch check:$arch libboost-atomic-dev:$arch libboost-test-dev:$arch libglib2.0-dev:$arch libexplain-dev:$arch libc-ares-dev:$arch libiptc0:$arch libxtables12:$arch libatm1-dev:$arch libbpf-dev:$arch libdb-dev:$arch pkg-config:$arch libnghttp2-14:$arch librtmp1:$arch libssh2-1:$arch libcjson1:$arch libcjson-dev:$arch libcurl4-openssl-dev:$arch libboost-thread1.74-dev:$arch libboost-thread-dev:$arch libboost-system1.74-dev:$arch libboost-system-dev:$arch libgtest-dev:$arch libgmock-dev:$arch libfido2-dev:$arch libcunit1:$arch libcunit1-dev:$arch libauparse-dev:$arch libnetsnmptrapd40:$arch qtbase5-dev:$arch libboost-log-dev:$arch libboost-filesystem-dev:$arch libboost-program-options-dev:$arch
+RUN eatmydata apt-get install -y libelf-dev:$arch libdw-dev:$arch  libbz2-dev:$arch liblzo2-dev:$arch libedit-dev:$arch libevent-dev:$arch libopts25-dev:$arch libssl-dev:$arch pps-tools:$arch libpam-cap:$arch libcap-dev:$arch libpam0g-dev:$arch libaudit-dev:$arch libgtk-3-dev:$arch libkrb5-dev:$arch libsystemd-dev:$arch libwrap0-dev:$arch libkrb5-dev:$arch libboost1.74-dev:$arch libboost-dev:$arch libzmq5:$arch libzmq3-dev:$arch libdaemon-dev:$arch libjansson-dev:$arch libmnl-dev:$arch libsensors5:$arch libsensors4-dev:$arch libperl-dev:$arch libmariadb-dev:$arch libmariadb-dev-compat:$arch libpci-dev:$arch libjson-c-dev:$arch libreadline-dev:$arch librtr-dev:$arch librrd-dev:$arch libnetfilter-conntrack-dev:$arch libnetfilter-conntrack3:$arch libnfnetlink-dev:$arch libnftnl-dev:$arch libldap2-dev:$arch libbind-export-dev:$arch check:$arch libboost-atomic-dev:$arch libboost-test-dev:$arch libglib2.0-dev:$arch libexplain-dev:$arch libc-ares-dev:$arch libiptc0:$arch libxtables12:$arch libatm1-dev:$arch libbpf-dev:$arch libdb-dev:$arch pkgconf:$arch libnghttp2-14:$arch librtmp1:$arch libssh2-1:$arch libcjson1:$arch libcjson-dev:$arch libcurl4-openssl-dev:$arch libboost-thread1.74-dev:$arch libboost-thread-dev:$arch libboost-system1.74-dev:$arch libboost-system-dev:$arch libgtest-dev:$arch libgmock-dev:$arch libfido2-dev:$arch libcunit1:$arch libcunit1-dev:$arch libauparse-dev:$arch libnetsnmptrapd40:$arch qtbase5-dev:$arch libboost-log-dev:$arch libboost-filesystem-dev:$arch libboost-program-options-dev:$arch
 
 RUN apt-get download libgirepository1.0-dev:$arch && eatmydata dpkg --force-all -i libgirepository1.0-dev*
 RUN PATH=/python_virtualenv/env3/bin/:$PATH pip3 install pycairo
diff --git a/sonic-slave-buster/Dockerfile.j2 b/sonic-slave-buster/Dockerfile.j2
index b65a1a08957c..1f4316dd43a7 100644
--- a/sonic-slave-buster/Dockerfile.j2
+++ b/sonic-slave-buster/Dockerfile.j2
@@ -278,7 +278,7 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
 	debhelper \
         autotools-dev \
         libbsd-dev \
-        pkg-config \
+        pkgconf \
         check \
 # For mpdecimal
         docutils-common \
@@ -358,6 +358,10 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
         libevent-dev \
 # For libyang
         swig \
+        pkgconf \
+        tcl-expect \
+# For libyang3-py3
+        python3-cffi \
 # For build dtb
         device-tree-compiler \
 # For sonic-mgmt-framework
@@ -416,7 +420,7 @@ RUN apt-get update && apt-get install -y eatmydata && eatmydata apt-get install
         libdbus-1-dev \
         libgirepository1.0-dev \
         libsystemd-dev \
-        pkg-config \
+        pkgconf \
 # For sonic-swss-common build
         libhiredis-dev \
 # For audisp-tacplus
@@ -650,7 +654,7 @@ RUN eatmydata apt-get install -y nodejs
 {%- if CROSS_BUILD_ENVIRON == "y" %}
 RUN eatmydata apt-get install -y rsync dh-python
 
-RUN eatmydata apt-get install -y libelf-dev:$arch libdw-dev:$arch  libbz2-dev:$arch liblzo2-dev:$arch libedit-dev:$arch libevent-dev:$arch libopts25-dev:$arch libssl-dev:$arch pps-tools:$arch libpam-cap:$arch libcap-dev:$arch libpam0g-dev:$arch libaudit-dev:$arch libgtk-3-dev:$arch libkrb5-dev:$arch libsystemd-dev:$arch libwrap0-dev:$arch libkrb5-dev:$arch libboost1.67-dev:$arch libboost-dev:$arch libzmq5:$arch libzmq3-dev:$arch libdaemon-dev:$arch libjansson-dev:$arch libmnl-dev:$arch libsensors5:$arch libsensors4-dev:$arch libperl-dev:$arch libmariadb-dev:$arch libmariadb-dev-compat:$arch libpci-dev:$arch libjson-c3:$arch libjson-c-dev:$arch libreadline-dev:$arch librtr-dev:$arch librrd-dev:$arch libnetfilter-conntrack-dev:$arch libnetfilter-conntrack3:$arch libnfnetlink-dev:$arch libnftnl-dev:$arch libldap2-dev:$arch libbind-export-dev:$arch check:$arch libboost-atomic-dev:$arch libboost-test-dev:$arch libglib2.0-dev:$arch qt5-default:$arch libexplain-dev:$arch libc-ares-dev:$arch libip4tc0:$arch libip6tc0:$arch libiptc0:$arch libxtables12:$arch iptables-dev:$arch libatm1-dev:$arch libdb-dev:$arch pkg-config:$arch libnghttp2-14:$arch librtmp1:$arch libssh2-1:$arch libcjson1:$arch libcjson-dev:$arch libcurl4-openssl-dev:$arch libboost-thread1.67-dev:$arch libboost-thread-dev:$arch libboost-system1.67-dev:$arch libboost-system-dev:$arch libgtest-dev:$arch libgmock-dev:$arch
+RUN eatmydata apt-get install -y libelf-dev:$arch libdw-dev:$arch  libbz2-dev:$arch liblzo2-dev:$arch libedit-dev:$arch libevent-dev:$arch libopts25-dev:$arch libssl-dev:$arch pps-tools:$arch libpam-cap:$arch libcap-dev:$arch libpam0g-dev:$arch libaudit-dev:$arch libgtk-3-dev:$arch libkrb5-dev:$arch libsystemd-dev:$arch libwrap0-dev:$arch libkrb5-dev:$arch libboost1.67-dev:$arch libboost-dev:$arch libzmq5:$arch libzmq3-dev:$arch libdaemon-dev:$arch libjansson-dev:$arch libmnl-dev:$arch libsensors5:$arch libsensors4-dev:$arch libperl-dev:$arch libmariadb-dev:$arch libmariadb-dev-compat:$arch libpci-dev:$arch libjson-c3:$arch libjson-c-dev:$arch libreadline-dev:$arch librtr-dev:$arch librrd-dev:$arch libnetfilter-conntrack-dev:$arch libnetfilter-conntrack3:$arch libnfnetlink-dev:$arch libnftnl-dev:$arch libldap2-dev:$arch libbind-export-dev:$arch check:$arch libboost-atomic-dev:$arch libboost-test-dev:$arch libglib2.0-dev:$arch qt5-default:$arch libexplain-dev:$arch libc-ares-dev:$arch libip4tc0:$arch libip6tc0:$arch libiptc0:$arch libxtables12:$arch iptables-dev:$arch libatm1-dev:$arch libdb-dev:$arch pkgconf:$arch libnghttp2-14:$arch librtmp1:$arch libssh2-1:$arch libcjson1:$arch libcjson-dev:$arch libcurl4-openssl-dev:$arch libboost-thread1.67-dev:$arch libboost-thread-dev:$arch libboost-system1.67-dev:$arch libboost-system-dev:$arch libgtest-dev:$arch libgmock-dev:$arch
 
 RUN eatmydata apt-get install -y -t buster-backports \
         libbpf-dev:$arch
diff --git a/src/libyang1/Makefile b/src/libyang1/Makefile
deleted file mode 100644
index a87f03bf638c..000000000000
--- a/src/libyang1/Makefile
+++ /dev/null
@@ -1,44 +0,0 @@
-.ONESHELL:
-SHELL = /bin/bash
-.SHELLFLAGS += -e
-
-LIBYANG_URL = https://sonicstorage.blob.core.windows.net/debian/pool/main/liby/libyang
-
-DSC_FILE = libyang_$(LIBYANG1_FULLVERSION).dsc
-ORIG_FILE = libyang_$(LIBYANG1_VERSION).orig.tar.gz
-DEBIAN_FILE = libyang_$(LIBYANG1_FULLVERSION).debian.tar.xz
-
-DSC_FILE_URL = $(LIBYANG_URL)/$(DSC_FILE)
-ORIG_FILE_URL = $(LIBYANG_URL)/$(ORIG_FILE)
-DEBIAN_FILE_URL = $(LIBYANG_URL)/$(DEBIAN_FILE)
-
-MAIN_TARGET = $(LIBYANG1)
-DERIVED_TARGETS = $(LIBYANG1_DEV) $(LIBYANG1_DBG) $(LIBYANG1_CPP) $(LIBYANG1_CPP_DEV) $(LIBYANG1_CPP_DBG) $(YANG_TOOLS) $(LIBYANG1_TOOLS) $(LIBYANG1_TOOLS_DBG)
-
-$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
-        # Obtaining the libyang
-	rm -fr ./libyang-$(LIBYANG1_VERSION)
-
-	# download debian libyang
-	wget -NO "$(DSC_FILE)" $(DSC_FILE_URL)
-	wget -NO "$(ORIG_FILE)" $(ORIG_FILE_URL)
-	wget -NO "$(DEBIAN_FILE)" $(DEBIAN_FILE_URL)
-	dpkg-source -x libyang_$(LIBYANG1_FULLVERSION).dsc
-
-	pushd libyang-$(LIBYANG1_VERSION)
-	sed -i 's/set(LIBYANG_MAJOR_SOVERSION 1)/set(LIBYANG_MAJOR_SOVERSION 2)/' CMakeLists.txt
-	sed -i 's/libyang1/libyang2/' debian/libyang1.install
-	# Enable large file support for 32-bit arch
-	echo 'add_definitions(-D_FILE_OFFSET_BITS=64)' >> CMakeLists.txt
-
-ifeq ($(CROSS_BUILD_ENVIRON), y)
-	dpkg-buildpackage -rfakeroot -b -us -uc -a$(CONFIGURED_ARCH) -Pcross,nocheck -j$(SONIC_CONFIG_MAKE_JOBS) --admindir $(SONIC_DPKG_ADMINDIR)
-else
-	dpkg-buildpackage -rfakeroot -b -us -uc -j$(SONIC_CONFIG_MAKE_JOBS) --admindir $(SONIC_DPKG_ADMINDIR)
-endif
-	popd
-
-	# Move the newly-built .deb packages to the destination directory
-	mv $* $(DERIVED_TARGETS) $(DEST)/
-
-$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET)
diff --git a/src/libyang2/.gitignore b/src/libyang2/.gitignore
deleted file mode 100644
index a0991ff4402b..000000000000
--- a/src/libyang2/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-*
-!.gitignore
-!Makefile
diff --git a/src/libyang2/Makefile b/src/libyang2/Makefile
deleted file mode 100644
index 16d80587573e..000000000000
--- a/src/libyang2/Makefile
+++ /dev/null
@@ -1,49 +0,0 @@
-.ONESHELL:
-SHELL = /bin/bash
-.SHELLFLAGS += -e
-
-LIBYANG_URL = https://sonicstorage.blob.core.windows.net/debian/pool/main/liby/libyang
-
-DSC_FILE = libyang2_$(LIBYANG2_FULLVERSION).dsc
-ORIG_FILE = libyang2_$(LIBYANG2_VERSION).orig.tar.xz
-DEBIAN_FILE = libyang2_$(LIBYANG2_FULLVERSION).debian.tar.xz
-
-DSC_FILE_URL = $(LIBYANG_URL)/$(DSC_FILE)
-ORIG_FILE_URL = $(LIBYANG_URL)/$(ORIG_FILE)
-DEBIAN_FILE_URL = $(LIBYANG_URL)/$(DEBIAN_FILE)
-
-MAIN_TARGET = $(LIBYANG2)
-DERIVED_TARGETS = $(LIBYANG2_DEV) $(LIBYANG2_DBG) $(LIBYANG2_TOOLS) $(LIBYANG2_TOOLS_DBG)
-
-$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
-        # Obtaining the libyang
-	rm -fr ./libyang2-$(LIBYANG2_VERSION)
-
-	# download debian libyang
-	wget -NO "$(DSC_FILE)" $(DSC_FILE_URL)
-	wget -NO "$(ORIG_FILE)" $(ORIG_FILE_URL)
-	wget -NO "$(DEBIAN_FILE)" $(DEBIAN_FILE_URL)
-	dpkg-source -x libyang2_$(LIBYANG2_FULLVERSION).dsc
-
-	pushd libyang2-$(LIBYANG2_VERSION)
-	#The package libyang2.1.148 is taken from debian trixie, which only has dpkg-dev version 1.21.22
-	#The bullseye package has dpkg-dev version 1.20.13
-	#The VS package has dpkg-dev version 1.19.8
-	sed -i 's/dpkg-dev (>= 1.22.5)/dpkg-dev (>= 1.19.8)/' debian/control
-	#sed -i 's/set(LIBYANG_MAJOR_SOVERSION 1)/set(LIBYANG_MAJOR_SOVERSION 2)/' CMakeLists.txt
-	#sed -i 's/libyang2/libyang2/' debian/libyang2.install
-	# Enable large file support for 32-bit arch
-	echo 'add_definitions(-D_FILE_OFFSET_BITS=64)' >> CMakeLists.txt
-
-ifeq ($(CROSS_BUILD_ENVIRON), y)
-	dpkg-buildpackage -rfakeroot -d -b -us -uc -a$(CONFIGURED_ARCH) -Pcross,nocheck -j$(SONIC_CONFIG_MAKE_JOBS)
-else
-	dpkg-buildpackage -rfakeroot -b -us -uc -j$(SONIC_CONFIG_MAKE_JOBS) --admindir $(SONIC_DPKG_ADMINDIR)
-endif
-
-	popd
-
-	# Move the newly-built .deb packages to the destination directory
-	mv $* $(DERIVED_TARGETS) $(DEST)/
-
-$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET)
diff --git a/src/libyang3-py3/Makefile b/src/libyang3-py3/Makefile
new file mode 100644
index 000000000000..db35973f590a
--- /dev/null
+++ b/src/libyang3-py3/Makefile
@@ -0,0 +1,24 @@
+SHELL = /bin/bash
+.ONESHELL:
+.SHELLFLAGS += -e
+
+MAIN_TARGET = $(LIBYANG3_PY3)
+
+$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
+	rm -rf ./libyang-python
+	# Obtain libyang-python
+	git clone --depth 1 -b v3.0.3 https://github.com/CESNET/libyang-python.git libyang-python
+	pushd ./libyang-python
+
+	# Apply patch series
+	QUILT_PATCHES=../patch quilt push -a
+
+	# Build package
+ifeq ($(CROSS_BUILD_ENVIRON), y)
+	dpkg-buildpackage -rfakeroot -d -b -us -uc -a$(CONFIGURED_ARCH) -Pcross,nocheck -j$(SONIC_CONFIG_MAKE_JOBS)
+else
+	dpkg-buildpackage -rfakeroot -b -us -uc -j$(SONIC_CONFIG_MAKE_JOBS) --admindir $(SONIC_DPKG_ADMINDIR)
+endif
+	popd
+
+	mv $* $(DEST)/
diff --git a/src/libyang3-py3/patch/0001-debian.patch b/src/libyang3-py3/patch/0001-debian.patch
new file mode 100644
index 000000000000..7775cdac0dac
--- /dev/null
+++ b/src/libyang3-py3/patch/0001-debian.patch
@@ -0,0 +1,60 @@
+diff -ruN libyang-python.orig/debian/changelog libyang-python.new/debian/changelog
+--- libyang-python.orig/debian/changelog	1970-01-01 00:00:00.000000000 +0000
++++ libyang-python.new/debian/changelog	2025-02-09 12:45:51.113010546 +0000
+@@ -0,0 +1,5 @@
++libyang-python (3.0.3-1) unstable; urgency=low
++
++  * source package automatically created by stdeb 0.10.0
++
++ -- None <None>  Sat, 08 Feb 2025 20:55:02 +0000
+diff -ruN libyang-python.orig/debian/compat libyang-python.new/debian/compat
+--- libyang-python.orig/debian/compat	1970-01-01 00:00:00.000000000 +0000
++++ libyang-python.new/debian/compat	2025-02-09 12:45:51.113010546 +0000
+@@ -0,0 +1 @@
++13
+diff -ruN libyang-python.orig/debian/control libyang-python.new/debian/control
+--- libyang-python.orig/debian/control	1970-01-01 00:00:00.000000000 +0000
++++ libyang-python.new/debian/control	2025-02-09 12:45:51.113010546 +0000
+@@ -0,0 +1,15 @@
++Source: libyang-python
++Maintainer: None <None>
++Section: python
++Priority: optional
++Build-Depends: dh-python, python3-setuptools, python3-all-dev, python3-cffi, debhelper (>= 13), libyang-dev ( >= 3.0.3)
++Standards-Version: 3.9.6
++Homepage: https://github.com/CESNET/libyang-python
++
++Package: python3-libyang
++Architecture: any
++Depends: ${misc:Depends}, ${python3:Depends}, ${shlibs:Depends}
++Description: CFFI bindings to libyang
++ Python CFFI bindings to libyang
++ https://github.com/CESNET/libyang/
++
+diff -ruN libyang-python.orig/debian/rules libyang-python.new/debian/rules
+--- libyang-python.orig/debian/rules	1970-01-01 00:00:00.000000000 +0000
++++ libyang-python.new/debian/rules	2025-02-09 12:45:51.113010546 +0000
+@@ -0,0 +1,9 @@
++#!/usr/bin/make -f
++
++# This file was automatically generated by stdeb 0.10.0 at
++# Sat, 08 Feb 2025 20:55:02 +0000
++export PYBUILD_NAME=libyang
++
++%:
++	dh $@ --with python3 --buildsystem=pybuild
++
+diff -ruN libyang-python.orig/debian/source/format libyang-python.new/debian/source/format
+--- libyang-python.orig/debian/source/format	1970-01-01 00:00:00.000000000 +0000
++++ libyang-python.new/debian/source/format	2025-02-09 12:45:51.113010546 +0000
+@@ -0,0 +1 @@
++3.0 (quilt)
+diff -ruN libyang-python.orig/debian/watch libyang-python.new/debian/watch
+--- libyang-python.orig/debian/watch	1970-01-01 00:00:00.000000000 +0000
++++ libyang-python.new/debian/watch	2025-02-09 12:45:51.113010546 +0000
+@@ -0,0 +1,4 @@
++# please also check http://pypi.debian.net/libyang/watch
++version=3
++opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
++http://pypi.debian.net/libyang/libyang-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
+\ No newline at end of file
diff --git a/src/libyang3-py3/patch/0002-pr134-json-string-datatypes.patch b/src/libyang3-py3/patch/0002-pr134-json-string-datatypes.patch
new file mode 100644
index 000000000000..75c0eab9a867
--- /dev/null
+++ b/src/libyang3-py3/patch/0002-pr134-json-string-datatypes.patch
@@ -0,0 +1,85 @@
+diff --git a/cffi/cdefs.h b/cffi/cdefs.h
+index 1c1d8f3..89f607b 100644
+--- a/cffi/cdefs.h
++++ b/cffi/cdefs.h
+@@ -318,6 +318,7 @@ LY_ERR lyd_print_all(struct ly_out *, const struct lyd_node *, LYD_FORMAT, uint3
+ #define LYD_PARSE_OPTS_MASK ...
+ #define LYD_PARSE_ORDERED ...
+ #define LYD_PARSE_STRICT ...
++#define LYD_PARSE_JSON_STRING_DATATYPES ...
+ 
+ #define LYD_VALIDATE_NO_STATE ...
+ #define LYD_VALIDATE_PRESENT ...
+diff --git a/libyang/context.py b/libyang/context.py
+index 1b1d4cd..2692eb3 100644
+--- a/libyang/context.py
++++ b/libyang/context.py
+@@ -520,6 +520,7 @@ def parse_data(
+         validate_present: bool = False,
+         validate_multi_error: bool = False,
+         store_only: bool = False,
++        json_string_datatypes: bool = False,
+     ) -> Optional[DNode]:
+         if self.cdata is None:
+             raise RuntimeError("context already destroyed")
+@@ -531,6 +532,7 @@ def parse_data(
+             ordered=ordered,
+             strict=strict,
+             store_only=store_only,
++            json_string_datatypes=json_string_datatypes,
+         )
+         validation_flgs = validation_flags(
+             no_state=no_state,
+@@ -589,6 +591,7 @@ def parse_data_mem(
+         validate_present: bool = False,
+         validate_multi_error: bool = False,
+         store_only: bool = False,
++        json_string_datatypes: bool = False,
+     ) -> Optional[DNode]:
+         return self.parse_data(
+             fmt,
+@@ -604,6 +607,7 @@ def parse_data_mem(
+             validate_present=validate_present,
+             validate_multi_error=validate_multi_error,
+             store_only=store_only,
++            json_string_datatypes=json_string_datatypes,
+         )
+ 
+     def parse_data_file(
+@@ -620,6 +624,7 @@ def parse_data_file(
+         validate_present: bool = False,
+         validate_multi_error: bool = False,
+         store_only: bool = False,
++        json_string_datatypes: bool = False,
+     ) -> Optional[DNode]:
+         return self.parse_data(
+             fmt,
+@@ -635,6 +640,7 @@ def parse_data_file(
+             validate_present=validate_present,
+             validate_multi_error=validate_multi_error,
+             store_only=store_only,
++            json_string_datatypes=json_string_datatypes,
+         )
+ 
+     def __iter__(self) -> Iterator[Module]:
+diff --git a/libyang/data.py b/libyang/data.py
+index 19ef0ca..86c26e1 100644
+--- a/libyang/data.py
++++ b/libyang/data.py
+@@ -116,6 +116,7 @@ def parser_flags(
+     ordered: bool = False,
+     strict: bool = False,
+     store_only: bool = False,
++    json_string_datatypes: bool = False,
+ ) -> int:
+     flags = 0
+     if lyb_mod_update:
+@@ -132,6 +133,8 @@ def parser_flags(
+         flags |= lib.LYD_PARSE_STRICT
+     if store_only:
+         flags |= lib.LYD_PARSE_STORE_ONLY
++    if json_string_datatypes:
++        flags |= lib.LYD_PARSE_JSON_STRING_DATATYPES
+     return flags
+ 
+ 
diff --git a/src/libyang3-py3/patch/0003-parse_module-memleak.patch b/src/libyang3-py3/patch/0003-parse_module-memleak.patch
new file mode 100644
index 000000000000..74e1321649b7
--- /dev/null
+++ b/src/libyang3-py3/patch/0003-parse_module-memleak.patch
@@ -0,0 +1,40 @@
+From 6550259f36da08b9b4bc7e9c941b2e13e10661a1 Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Wed, 5 Feb 2025 19:38:43 -0500
+Subject: [PATCH] correct memory leak of `struct ly_in *` objects
+
+`data_load()` returns a reference to `struct ly_in *` which
+must be free'd by `lys_in_free()`.  This is done in one of three
+locations.  This corrects this oversight.
+
+This was found during unit tests of SONiC during porting to
+libyang3 from libyang 1.0.73.
+
+Signed-off-by: Brad House (@bradh352)
+---
+ libyang/context.py | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/libyang/context.py b/libyang/context.py
+index 1b1d4cd..8be232c 100644
+--- a/libyang/context.py
++++ b/libyang/context.py
+@@ -325,7 +325,9 @@ def parse_module(
+ 
+         mod = ffi.new("struct lys_module **")
+         fmt = schema_in_format(fmt)
+-        if lib.lys_parse(self.cdata, data[0], fmt, feat, mod) != lib.LY_SUCCESS:
++        rv  = lib.lys_parse(self.cdata, data[0], fmt, feat, mod)
++        lib.ly_in_free(data[0], 0)
++        if rv != lib.LY_SUCCESS:
+             raise self.error("failed to parse module")
+ 
+         return Module(self, mod[0])
+@@ -489,6 +491,7 @@ def parse_op(
+             par[0] = parent.cdata
+ 
+         ret = lib.lyd_parse_op(self.cdata, par[0], data[0], fmt, dtype, tree, op)
++        lib.ly_in_free(data[0], 0)
+         if ret != lib.LY_SUCCESS:
+             raise self.error("failed to parse input data")
+ 
diff --git a/src/libyang3-py3/patch/0004-pr132-backlinks.patch b/src/libyang3-py3/patch/0004-pr132-backlinks.patch
new file mode 100644
index 000000000000..fb889e8a469a
--- /dev/null
+++ b/src/libyang3-py3/patch/0004-pr132-backlinks.patch
@@ -0,0 +1,322 @@
+From ebb79c5fec9635b5efb7a24daf1537ba91491f6a Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Sun, 16 Feb 2025 11:04:50 -0500
+Subject: [PATCH] schema/context: restore some backlinks support
+
+In libyang v1 the schema nodes had a backlinks member to be able to
+look up dependents of the node.  SONiC depends on this to provide
+functionality it uses and it needs to be exposed via the python
+module.
+
+In theory, exposing the 'dfs' functions could make this work, but
+it would likely be cost prohibitive since walking the tree would
+be expensive to create a python node for evaluation in native
+python.
+
+Instead this PR depends on the this libyang PR:
+https://github.com/CESNET/libyang/pull/2352
+And adds thin wrappers.
+
+This implementation provides 2 python functions:
+ * Context.find_backlinks_paths() - This function can
+   take the path of the base node and find all dependents.  If
+   no path is specified, then it will return all nodes that contain
+   a leafref reference.
+ * Context.find_leafref_path_target_paths() - This function takes
+   an xpath, then returns all target nodes the xpath may reference.
+   Typically only one will be returned, but multiples may be in the
+   case of a union.
+
+A user can build a cache by combining Context.find_backlinks_paths()
+with no path set and building a reverse table using
+Context.find_leafref_path_target_paths()
+
+Signed-off-by: Brad House <brad@brad-house.com>
+---
+ cffi/cdefs.h                                  |  2 +
+ libyang/context.py                            | 98 +++++++++++++++++++
+ tests/test_schema.py                          | 58 +++++++++++
+ .../yang/yolo/yolo-leafref-search-extmod.yang | 39 ++++++++
+ tests/yang/yolo/yolo-leafref-search.yang      | 36 +++++++
+ 5 files changed, 233 insertions(+)
+ create mode 100644 tests/yang/yolo/yolo-leafref-search-extmod.yang
+ create mode 100644 tests/yang/yolo/yolo-leafref-search.yang
+
+diff --git a/cffi/cdefs.h b/cffi/cdefs.h
+index aa75004..e7dc978 100644
+--- a/cffi/cdefs.h
++++ b/cffi/cdefs.h
+@@ -861,6 +861,8 @@ const struct lysc_node* lys_find_child(const struct lysc_node *, const struct ly
+ const struct lysc_node* lysc_node_child(const struct lysc_node *);
+ const struct lysc_node_action* lysc_node_actions(const struct lysc_node *);
+ const struct lysc_node_notif* lysc_node_notifs(const struct lysc_node *);
++LY_ERR lysc_node_lref_targets(const struct lysc_node *, struct ly_set **);
++LY_ERR lysc_node_lref_backlinks(const struct ly_ctx *, const struct lysc_node *, ly_bool, struct ly_set **);
+ 
+ typedef enum {
+     LYD_PATH_STD,
+diff --git a/libyang/context.py b/libyang/context.py
+index fb4a330..d6eb7a3 100644
+--- a/libyang/context.py
++++ b/libyang/context.py
+@@ -646,6 +646,104 @@ def parse_data_file(
+             json_null=json_null,
+         )
+ 
++    def find_leafref_path_target_paths(self, leafref_path: str) -> list[str]:
++        """
++        Fetch all leafref targets of the specified path
++
++        This is an enhanced version of lysc_node_lref_target() which will return
++        a set of leafref target paths retrieved from the specified schema path.
++        While lysc_node_lref_target() will only work on nodetype of LYS_LEAF and
++        LYS_LEAFLIST this function will also evaluate other datatypes that may
++        contain leafrefs such as LYS_UNION.  This does not, however, search for
++        children with leafref targets.
++
++        :arg self
++            This instance on context
++        :arg leafref_path:
++            Path to node to search for leafref targets
++        :returns List of target paths that the leafrefs of the specified node
++                 point to.
++        """
++        if self.cdata is None:
++            raise RuntimeError("context already destroyed")
++        if leafref_path is None:
++            raise RuntimeError("leafref_path must be defined")
++
++        out = []
++
++        node = lib.lys_find_path(self.cdata, ffi.NULL, str2c(leafref_path), 0)
++        if node == ffi.NULL:
++            raise self.error("leafref_path not found")
++
++        node_set = ffi.new("struct ly_set **")
++        if (lib.lysc_node_lref_targets(node, node_set) != lib.LY_SUCCESS or
++            node_set[0] == ffi.NULL or node_set[0].count == 0):
++            raise self.error("leafref_path does not contain any leafref targets")
++
++        node_set = node_set[0]
++        for i in range(node_set.count):
++            path = lib.lysc_path(node_set.snodes[i], lib.LYSC_PATH_DATA, ffi.NULL, 0);
++            out.append(c2str(path))
++            lib.free(path)
++
++        lib.ly_set_free(node_set, ffi.NULL)
++
++        return out
++
++
++    def find_backlinks_paths(self, match_path: str = None, match_ancestors: bool = False) -> list[str]:
++        """
++        Search entire schema for nodes that contain leafrefs and return as a
++        list of schema node paths.
++
++        Perform a complete scan of the schema tree looking for nodes that
++        contain leafref entries. When a node contains a leafref entry, and
++        match_path is specified, determine if reference points to match_path,
++        if so add the node's path to returned list.  If no match_path is
++        specified, the node containing the leafref is always added to the
++        returned set.  When match_ancestors is true, will evaluate if match_path
++        is self or an ansestor of self.
++
++        This does not return the leafref targets, but the actual node that
++        contains a leafref.
++
++        :arg self
++            This instance on context
++        :arg match_path:
++            Target path to use for matching
++        :arg match_ancestors:
++            Whether match_path is a base ancestor or an exact node
++        :returns List of paths.  Exception of match_path is not found or if no
++                 backlinks are found.
++        """
++        if self.cdata is None:
++            raise RuntimeError("context already destroyed")
++        out = []
++
++        match_node = ffi.NULL
++        if match_path is not None and match_path == "/" or match_path == "":
++            match_path = None
++
++        if match_path:
++            match_node = lib.lys_find_path(self.cdata, ffi.NULL, str2c(match_path), 0)
++            if match_node == ffi.NULL:
++                raise self.error("match_path not found")
++
++        node_set = ffi.new("struct ly_set **")
++        if (lib.lysc_node_lref_backlinks(self.cdata, match_node, match_ancestors, node_set)
++            != lib.LY_SUCCESS or node_set[0] == ffi.NULL or node_set[0].count == 0):
++            raise self.error("backlinks not found")
++
++        node_set = node_set[0]
++        for i in range(node_set.count):
++            path = lib.lysc_path(node_set.snodes[i], lib.LYSC_PATH_DATA, ffi.NULL, 0);
++            out.append(c2str(path))
++            lib.free(path)
++
++        lib.ly_set_free(node_set, ffi.NULL)
++
++        return out
++
+     def __iter__(self) -> Iterator[Module]:
+         """
+         Return an iterator that yields all implemented modules from the context
+diff --git a/tests/test_schema.py b/tests/test_schema.py
+index a310aad..4aae73a 100644
+--- a/tests/test_schema.py
++++ b/tests/test_schema.py
+@@ -801,6 +801,64 @@ def test_leaf_list_parsed(self):
+         self.assertFalse(pnode.ordered())
+ 
+ 
++# -------------------------------------------------------------------------------------
++class BacklinksTest(unittest.TestCase):
++    def setUp(self):
++        self.ctx = Context(YANG_DIR)
++        self.ctx.load_module("yolo-leafref-search")
++        self.ctx.load_module("yolo-leafref-search-extmod")
++    def tearDown(self):
++        self.ctx.destroy()
++        self.ctx = None
++    def test_backlinks_all_nodes(self):
++        expected = [
++            "/yolo-leafref-search-extmod:my_extref_list/my_extref",
++            "/yolo-leafref-search:refstr",
++            "/yolo-leafref-search:refnum",
++            "/yolo-leafref-search-extmod:my_extref_list/my_extref_union"
++        ]
++        refs = self.ctx.find_backlinks_paths()
++        expected.sort()
++        refs.sort()
++        self.assertEqual(expected, refs)
++    def test_backlinks_one(self):
++        expected = [
++            "/yolo-leafref-search-extmod:my_extref_list/my_extref",
++            "/yolo-leafref-search:refstr",
++            "/yolo-leafref-search-extmod:my_extref_list/my_extref_union"
++        ]
++        refs = self.ctx.find_backlinks_paths(
++            match_path="/yolo-leafref-search:my_list/my_leaf_string"
++        )
++        expected.sort()
++        refs.sort()
++        self.assertEqual(expected, refs)
++    def test_backlinks_children(self):
++        expected = [
++            "/yolo-leafref-search-extmod:my_extref_list/my_extref",
++            "/yolo-leafref-search:refstr",
++            "/yolo-leafref-search:refnum",
++            "/yolo-leafref-search-extmod:my_extref_list/my_extref_union"
++        ]
++        refs = self.ctx.find_backlinks_paths(
++            match_path="/yolo-leafref-search:my_list",
++            match_ancestors=True
++        )
++        expected.sort()
++        refs.sort()
++        self.assertEqual(expected, refs)
++    def test_backlinks_leafref_target_paths(self):
++        expected = [
++            "/yolo-leafref-search:my_list/my_leaf_string"
++        ]
++        refs = self.ctx.find_leafref_path_target_paths(
++            "/yolo-leafref-search-extmod:my_extref_list/my_extref"
++        )
++        expected.sort()
++        refs.sort()
++        self.assertEqual(expected, refs)
++
++
+ # -------------------------------------------------------------------------------------
+ class ChoiceTest(unittest.TestCase):
+     def setUp(self):
+diff --git a/tests/yang/yolo/yolo-leafref-search-extmod.yang b/tests/yang/yolo/yolo-leafref-search-extmod.yang
+new file mode 100644
+index 0000000..046ceec
+--- /dev/null
++++ b/tests/yang/yolo/yolo-leafref-search-extmod.yang
+@@ -0,0 +1,39 @@
++module yolo-leafref-search-extmod {
++  yang-version 1.1;
++  namespace "urn:yang:yolo:leafref-search-extmod";
++  prefix leafref-search-extmod;
++
++  import wtf-types { prefix types; }
++
++  import yolo-leafref-search {
++    prefix leafref-search;
++  }
++
++  revision 2025-02-11 {
++    description
++      "Initial version.";
++  }
++
++  list my_extref_list {
++    key my_leaf_string;
++    leaf my_leaf_string {
++      type string;
++    }
++    leaf my_extref {
++      type leafref {
++        path "/leafref-search:my_list/leafref-search:my_leaf_string";
++      }
++    }
++    leaf my_extref_union {
++      type union {
++        type leafref {
++          path "/leafref-search:my_list/leafref-search:my_leaf_string";
++        }
++        type leafref {
++          path "/leafref-search:my_list/leafref-search:my_leaf_number";
++        }
++        type types:number;
++      }
++    }
++  }
++}
+diff --git a/tests/yang/yolo/yolo-leafref-search.yang b/tests/yang/yolo/yolo-leafref-search.yang
+new file mode 100644
+index 0000000..5f4af48
+--- /dev/null
++++ b/tests/yang/yolo/yolo-leafref-search.yang
+@@ -0,0 +1,36 @@
++module yolo-leafref-search {
++  yang-version 1.1;
++  namespace "urn:yang:yolo:leafref-search";
++  prefix leafref-search;
++
++  import wtf-types { prefix types; }
++
++  revision 2025-02-11 {
++    description
++      "Initial version.";
++  }
++
++  list my_list {
++    key my_leaf_string;
++    leaf my_leaf_string {
++      type string;
++    }
++    leaf my_leaf_number {
++      description
++        "A number.";
++      type types:number;
++    }
++  }
++
++  leaf refstr {
++    type leafref {
++      path "../my_list/my_leaf_string";
++    }
++  }
++
++  leaf refnum {
++    type leafref {
++      path "../my_list/my_leaf_number";
++    }
++  }
++}
diff --git a/src/libyang3-py3/patch/series b/src/libyang3-py3/patch/series
new file mode 100644
index 000000000000..933279b013b7
--- /dev/null
+++ b/src/libyang3-py3/patch/series
@@ -0,0 +1,4 @@
+0001-debian.patch
+0002-pr134-json-string-datatypes.patch
+0003-parse_module-memleak.patch
+0004-pr132-backlinks.patch
diff --git a/src/libyang1/.gitignore b/src/libyang3/.gitignore
similarity index 58%
rename from src/libyang1/.gitignore
rename to src/libyang3/.gitignore
index a0991ff4402b..644cdde57ba8 100644
--- a/src/libyang1/.gitignore
+++ b/src/libyang3/.gitignore
@@ -1,3 +1,5 @@
 *
 !.gitignore
 !Makefile
+!patch
+!patch/**
diff --git a/src/libyang3/Makefile b/src/libyang3/Makefile
new file mode 100644
index 000000000000..a562daa5a6f5
--- /dev/null
+++ b/src/libyang3/Makefile
@@ -0,0 +1,36 @@
+.ONESHELL:
+SHELL = /bin/bash
+.SHELLFLAGS += -e
+
+LIBYANG_URL = http://debian-archive.trafficmanager.net/debian/pool/main/liby/libyang
+
+MAIN_TARGET = $(LIBYANG3)
+DERIVED_TARGETS = $(LIBYANG3_DEV) $(LIBYANG3_DBG) $(LIBYANG3_TOOLS) $(LIBYANG3_TOOLS_DBG)
+
+$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
+	# Obtaining the libyang
+	rm -fr ./libyang-$(LIBYANG3_VERSION)
+
+	# download debian libyang
+	dget -u $(LIBYANG_URL)/libyang_$(LIBYANG3_FULLVERSION).dsc
+
+	pushd libyang-$(LIBYANG3_VERSION)
+	# The package libyang3.7.8 is taken from debian trixie
+
+	# Enable large file support for 32-bit arch
+	echo 'add_definitions(-D_FILE_OFFSET_BITS=64)' >> CMakeLists.txt
+	[ -d ".pc" ] && rm -rf .pc
+	QUILT_PATCHES=../patch quilt push -a
+
+ifeq ($(CROSS_BUILD_ENVIRON), y)
+	dpkg-buildpackage -rfakeroot -d -b -us -uc -a$(CONFIGURED_ARCH) -Pcross,nocheck -j$(SONIC_CONFIG_MAKE_JOBS)
+else
+	dpkg-buildpackage -rfakeroot -b -us -uc -j$(SONIC_CONFIG_MAKE_JOBS) --admindir $(SONIC_DPKG_ADMINDIR)
+endif
+
+	popd
+
+	# Move the newly-built .deb packages to the destination directory
+	mv $* $(DERIVED_TARGETS) $(DEST)/
+
+$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET)
diff --git a/src/libyang3/patch/0001-pr2344-json-strings.patch b/src/libyang3/patch/0001-pr2344-json-strings.patch
new file mode 100644
index 000000000000..1f46c0b2025e
--- /dev/null
+++ b/src/libyang3/patch/0001-pr2344-json-strings.patch
@@ -0,0 +1,220 @@
+From 9ba71bf71bf77d4c660dabd1b05b5e5cda77cecf Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Fri, 14 Feb 2025 08:54:10 -0500
+Subject: [PATCH] data: option to allow json int/bool as strings
+
+Prior to v1.0.212 the default behavior was to allow numbers
+and boolean values to be in quotes, which is technically a
+violation of the spec.
+
+This adds a new `LYD_PARSE_JSON_STRING_DATATYPES` parse option
+which will restore the prior behavior when enabled.
+
+SONiC is using v1.0.73 currently and has a large installed base which
+may be in violation of the new behavior, so adding such a flag is
+required for this usecase.
+
+Signed-off-by: Brad House <brad@brad-house.com>
+---
+ src/parser_data.h                    |  5 ++++-
+ src/parser_json.c                    | 26 ++++++++++++++++----------
+ src/plugins_types.c                  |  9 ++++++---
+ src/tree_data.h                      |  1 +
+ tests/utests/data/test_parser_json.c | 15 +++++++++++++++
+ 5 files changed, 42 insertions(+), 14 deletions(-)
+
+diff --git a/src/parser_data.h b/src/parser_data.h
+index d7fbe1815..c6371ea4a 100644
+--- a/src/parser_data.h
++++ b/src/parser_data.h
+@@ -179,7 +179,10 @@ struct ly_in;
+ #define LYD_PARSE_JSON_NULL 0x4000000       /**< Allow using JSON empty value 'null' within JSON input, such nodes are
+                                                  silently skipped and treated as non-existent. By default, such values
+                                                  are invalid. */
+-
++#define LYD_PARSE_JSON_STRING_DATATYPES 0x8000000 /*!**< By default, JSON data values are validated to be in the proper format.
++                                                        For instance numbers are expected to not be enclosed in quotes, nor
++                                                        are boolean values.  Setting this option will disable this
++                                                        validation.  Prior to v1.0.212 this was the default behavior. */
+ #define LYD_PARSE_OPTS_MASK 0xFFFF0000      /**< Mask for all the LYD_PARSE_ options. */
+ 
+ /** @} dataparseroptions */
+diff --git a/src/parser_json.c b/src/parser_json.c
+index 5c3171231..83830c0d8 100644
+--- a/src/parser_json.c
++++ b/src/parser_json.c
+@@ -340,7 +340,7 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
+ /**
+  * @brief Get the hint for the data type parsers according to the current JSON parser context.
+  *
+- * @param[in] jsonctx JSON parser context. The context is supposed to be on a value.
++ * @param[in] lydctx JSON data parser context.
+  * @param[in,out] status Pointer to the current context status,
+  * in some circumstances the function manipulates with the context so the status is updated.
+  * @param[out] type_hint_p Pointer to the variable to store the result.
+@@ -348,8 +348,9 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
+  * @return LY_EINVAL in case of invalid context status not referring to a value.
+  */
+ static LY_ERR
+-lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
++lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
+ {
++    struct lyjson_ctx *jsonctx = lydctx->jsonctx;
+     *type_hint_p = 0;
+ 
+     if (*status_p == LYJSON_ARRAY) {
+@@ -383,6 +384,10 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
+         return LY_EINVAL;
+     }
+ 
++    if (lydctx->parse_opts & LYD_PARSE_JSON_STRING_DATATYPES) {
++        *type_hint_p |= LYD_VALHINT_STRING_DATATYPES;
++    }
++
+     return LY_SUCCESS;
+ }
+ 
+@@ -391,15 +396,16 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
+  *
+  * Checks for all the list's keys. Function does not revert the context state.
+  *
+- * @param[in] jsonctx JSON parser context.
++ * @param[in] lydctx JSON data parser context.
+  * @param[in] list List schema node corresponding to the input data object.
+  * @return LY_SUCCESS in case the data are ok for the @p list
+  * @return LY_ENOT in case the input data are not sufficient to fully parse the list instance.
+  */
+ static LY_ERR
+-lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
++lydjson_check_list(struct lyd_json_ctx *lydctx, const struct lysc_node *list)
+ {
+     LY_ERR rc = LY_SUCCESS;
++    struct lyjson_ctx *jsonctx = lydctx->jsonctx;
+     enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
+     struct ly_set key_set = {0};
+     const struct lysc_node *snode;
+@@ -451,7 +457,7 @@ lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
+                         goto cleanup;
+                     }
+ 
+-                    rc = lydjson_value_type_hint(jsonctx, &status, &hints);
++                    rc = lydjson_value_type_hint(lydctx, &status, &hints);
+                     LY_CHECK_GOTO(rc, cleanup);
+                     rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints);
+                     LY_CHECK_GOTO(rc, cleanup);
+@@ -521,7 +527,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
+         case LYS_LEAFLIST:
+         case LYS_LEAF:
+             /* value may not be valid in which case we parse it as an opaque node */
+-            if ((ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p))) {
++            if ((ret = lydjson_value_type_hint(lydctx, &status, type_hint_p))) {
+                 break;
+             }
+ 
+@@ -533,14 +539,14 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
+             break;
+         case LYS_LIST:
+             /* lists may not have all its keys */
+-            if (lydjson_check_list(jsonctx, snode)) {
++            if (lydjson_check_list(lydctx, snode)) {
+                 /* invalid list, parse as opaque if it misses/has invalid some keys */
+                 ret = LY_ENOT;
+             }
+             break;
+         }
+     } else if (snode->nodetype & LYD_NODE_TERM) {
+-        ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p);
++        ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
+     }
+ 
+     /* restore parser */
+@@ -852,7 +858,7 @@ lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, str
+         LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ 
+         /* get value hints */
+-        LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx->jsonctx, &status, &val_hints), cleanup);
++        LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, &status, &val_hints), cleanup);
+ 
+         if (node->schema) {
+             /* create metadata */
+@@ -981,7 +987,7 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l
+         dynamic = lydctx->jsonctx->dynamic;
+         lydctx->jsonctx->dynamic = 0;
+ 
+-        LY_CHECK_RET(lydjson_value_type_hint(lydctx->jsonctx, status_inner_p, &type_hint));
++        LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
+     }
+ 
+     /* get the module name */
+diff --git a/src/plugins_types.c b/src/plugins_types.c
+index d773a8a75..581ef278f 100644
+--- a/src/plugins_types.c
++++ b/src/plugins_types.c
+@@ -685,7 +685,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
+     case LY_TYPE_INT32:
+         LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
+ 
+-        if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) {
++        if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) &&
++            !(hints & LYD_VALHINT_STRING_DATATYPES)) {
+             return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-number-encoded %s value \"%.*s\".",
+                     lys_datatype2str(type), (int)value_len, value);
+         }
+@@ -695,7 +696,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
+     case LY_TYPE_INT64:
+         LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
+ 
+-        if (!(hints & LYD_VALHINT_NUM64)) {
++        if (!(hints & LYD_VALHINT_NUM64) &&
++            !(hints & LYD_VALHINT_STRING_DATATYPES)) {
+             return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-num64-encoded %s value \"%.*s\".",
+                     lys_datatype2str(type), (int)value_len, value);
+         }
+@@ -714,7 +716,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
+         }
+         break;
+     case LY_TYPE_BOOL:
+-        if (!(hints & LYD_VALHINT_BOOLEAN)) {
++        if (!(hints & LYD_VALHINT_BOOLEAN) &&
++            !(hints & LYD_VALHINT_STRING_DATATYPES)) {
+             return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-boolean-encoded %s value \"%.*s\".",
+                     lys_datatype2str(type), (int)value_len, value);
+         }
+diff --git a/src/tree_data.h b/src/tree_data.h
+index 18bc6791b..ed362a198 100644
+--- a/src/tree_data.h
++++ b/src/tree_data.h
+@@ -944,6 +944,7 @@ struct lyd_node_any {
+ #define LYD_VALHINT_NUM64      0x0010 /**< value is allowed to be an int64 or uint64 */
+ #define LYD_VALHINT_BOOLEAN    0x0020 /**< value is allowed to be a boolean */
+ #define LYD_VALHINT_EMPTY      0x0040 /**< value is allowed to be empty */
++#define LYD_VALHINT_STRING_DATATYPES 0x0080 /**< boolean and numeric fields are allowed to be quoted */
+ /**
+  * @} lydvalhints
+  */
+diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
+index 1eff7819a..f752164c6 100644
+--- a/tests/utests/data/test_parser_json.c
++++ b/tests/utests/data/test_parser_json.c
+@@ -168,6 +168,21 @@ test_leaf(void **state)
+     PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Invalid non-string-encoded string value \"\".", "/a:foo", 1);
+     CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree);
+     assert_null(tree);
++
++    /* validate integer in quotes errors out by default */
++    data = "{\"a:foo3\":\"1234\"}";
++    PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
++            "Invalid non-number-encoded uint32 value \"1234\".", "/a:foo3", 1);
++
++    /* validate integers are parsed correctly */
++    data = "{\"a:foo3\":1234}";
++    CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
++    lyd_free_all(tree);
++
++    /* validate LYD_PARSE_JSON_STRING_DATATYPES parser flag allows integers in quotes */
++    data = "{\"a:foo3\":\"1234\"}";
++    CHECK_PARSE_LYD(data, LYD_PARSE_JSON_STRING_DATATYPES, LYD_VALIDATE_PRESENT, tree);
++    lyd_free_all(tree);
+ }
+ 
+ static void
diff --git a/src/libyang3/patch/0002-pr2352-backlinks.patch b/src/libyang3/patch/0002-pr2352-backlinks.patch
new file mode 100644
index 000000000000..6eaab23afbe0
--- /dev/null
+++ b/src/libyang3/patch/0002-pr2352-backlinks.patch
@@ -0,0 +1,554 @@
+From b6ea2d3a0fd74ce9c7323e07aef957c65597ecd0 Mon Sep 17 00:00:00 2001
+From: Michal Vasko <mvasko@cesnet.cz>
+Date: Mon, 17 Feb 2025 15:16:30 +0100
+Subject: [PATCH 1/3] tree schema UPDATE function for getting all leafref
+ targets
+
+Co-authored-by: Brad House <brad@brad-house.com>
+---
+ src/tree_schema.h        | 14 +++++++-
+ src/tree_schema_common.c | 76 +++++++++++++++++++++++++++++++++++-----
+ 2 files changed, 80 insertions(+), 10 deletions(-)
+
+diff --git a/src/tree_schema.h b/src/tree_schema.h
+index 0181bc6f9..c5a464262 100644
+--- a/src/tree_schema.h
++++ b/src/tree_schema.h
+@@ -1908,13 +1908,25 @@ LIBYANG_API_DECL struct lysc_must *lysc_node_musts(const struct lysc_node *node)
+ LIBYANG_API_DECL struct lysc_when **lysc_node_when(const struct lysc_node *node);
+ 
+ /**
+- * @brief Get the target node of a leafref node.
++ * @brief Get the target node of a leafref node. Function ::lysc_node_lref_targets() should be used instead
++ * to get all the leafref targets even for a union node.
+  *
+  * @param[in] node Leafref node.
+  * @return Leafref target, NULL on any error.
+  */
+ LIBYANG_API_DECL const struct lysc_node *lysc_node_lref_target(const struct lysc_node *node);
+ 
++/**
++ * @brief Get the target node(s) of a leafref node or union node with leafrefs.
++ *
++ * @param[in] node Term node to use.
++ * @param[out] set Set with all the leafref targets, may be empty if the node is a different type or the targets
++ * are not found.
++ * @return LY_SUCCESS on success.
++ * @return LY_ERR value on error.
++ */
++LIBYANG_API_DECL LY_ERR lysc_node_lref_targets(const struct lysc_node *node, struct ly_set **set);
++
+ /**
+  * @brief Callback to be called for every schema node in a DFS traversal.
+  *
+diff --git a/src/tree_schema_common.c b/src/tree_schema_common.c
+index e6af579b6..df5e88160 100644
+--- a/src/tree_schema_common.c
++++ b/src/tree_schema_common.c
+@@ -1804,21 +1804,24 @@ lysc_node_when(const struct lysc_node *node)
+     }
+ }
+ 
+-LIBYANG_API_DEF const struct lysc_node *
+-lysc_node_lref_target(const struct lysc_node *node)
++/**
++ * @brief Get the target node of a leafref.
++ *
++ * @param[in] node Context node for the leafref.
++ * @param[in] type Leafref type to resolve.
++ * @return Target schema node;
++ * @return NULL if the tearget is not found.
++ */
++static const struct lysc_node *
++lysc_type_lref_target(const struct lysc_node *node, const struct lysc_type *type)
+ {
+     struct lysc_type_leafref *lref;
+     struct ly_path *p;
+     const struct lysc_node *target;
+ 
+-    if (!node || !(node->nodetype & LYD_NODE_TERM)) {
+-        return NULL;
+-    }
++    assert(type->basetype == LY_TYPE_LEAFREF);
+ 
+-    lref = (struct lysc_type_leafref *)((struct lysc_node_leaf *)node)->type;
+-    if (lref->basetype != LY_TYPE_LEAFREF) {
+-        return NULL;
+-    }
++    lref = (struct lysc_type_leafref *)type;
+ 
+     /* compile the path */
+     if (ly_path_compile_leafref(node->module->ctx, node, NULL, lref->path,
+@@ -1834,6 +1837,61 @@ lysc_node_lref_target(const struct lysc_node *node)
+     return target;
+ }
+ 
++LIBYANG_API_DEF const struct lysc_node *
++lysc_node_lref_target(const struct lysc_node *node)
++{
++    if (!node || !(node->nodetype & LYD_NODE_TERM) || (((struct lysc_node_leaf *)node)->type->basetype != LY_TYPE_LEAFREF)) {
++        return NULL;
++    }
++
++    return lysc_type_lref_target(node, ((struct lysc_node_leaf *)node)->type);
++}
++
++LIBYANG_API_DEF LY_ERR
++lysc_node_lref_targets(const struct lysc_node *node, struct ly_set **set)
++{
++    LY_ERR rc = LY_SUCCESS;
++    struct lysc_type *type;
++    struct lysc_type_union *type_un;
++    const struct lysc_node *target;
++    LY_ARRAY_COUNT_TYPE u;
++
++    LY_CHECK_ARG_RET(NULL, node, (node->nodetype & LYD_NODE_TERM), LY_EINVAL);
++
++    /* allocate return set */
++    LY_CHECK_RET(ly_set_new(set));
++
++    type = ((struct lysc_node_leaf *)node)->type;
++    if (type->basetype == LY_TYPE_UNION) {
++        /* union with possible leafrefs */
++        type_un = (struct lysc_type_union *)type;
++
++        LY_ARRAY_FOR(type_un->types, u) {
++            if (type_un->types[u]->basetype != LY_TYPE_LEAFREF) {
++                continue;
++            }
++
++            target = lysc_type_lref_target(node, type_un->types[u]);
++            if (target) {
++                LY_CHECK_GOTO(rc = ly_set_add(*set, target, 1, NULL), cleanup);
++            }
++        }
++    } else if (type->basetype == LY_TYPE_LEAFREF) {
++        /* leafref */
++        target = lysc_type_lref_target(node, type);
++        if (target) {
++            LY_CHECK_GOTO(rc = ly_set_add(*set, target, 1, NULL), cleanup);
++        }
++    }
++
++cleanup:
++    if (rc) {
++        ly_set_free(*set, NULL);
++        *set = NULL;
++    }
++    return rc;
++}
++
+ enum ly_stmt
+ lysp_match_kw(struct ly_in *in, uint64_t *indent)
+ {
+
+From b7e024d82f63c9d8e278237ca6c65bb21a6bad45 Mon Sep 17 00:00:00 2001
+From: Michal Vasko <mvasko@cesnet.cz>
+Date: Mon, 17 Feb 2025 15:46:59 +0100
+Subject: [PATCH 2/3] tree schema UPDATE function for getting leafref backlinks
+
+Meaning all leafrefs that target a specific node.
+
+Co-authored-by: Brad House <brad@brad-house.com>
+---
+ src/tree_schema.h        |  16 +++++-
+ src/tree_schema_common.c | 103 +++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 118 insertions(+), 1 deletion(-)
+
+diff --git a/src/tree_schema.h b/src/tree_schema.h
+index c5a464262..baeb261c0 100644
+--- a/src/tree_schema.h
++++ b/src/tree_schema.h
+@@ -4,7 +4,7 @@
+  * @author Michal Vasko <mvasko@cesnet.cz>
+  * @brief libyang representation of YANG schema trees.
+  *
+- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
++ * Copyright (c) 2015 - 2025 CESNET, z.s.p.o.
+  *
+  * This source code is licensed under BSD 3-Clause License (the "License").
+  * You may not use this file except in compliance with the License.
+@@ -1927,6 +1927,20 @@ LIBYANG_API_DECL const struct lysc_node *lysc_node_lref_target(const struct lysc
+  */
+ LIBYANG_API_DECL LY_ERR lysc_node_lref_targets(const struct lysc_node *node, struct ly_set **set);
+ 
++/**
++ * @brief Get all the leafref (or union with leafrefs) nodes that target a specific node.
++ *
++ * @param[in] ctx Context to use, may not be set if @p node is.
++ * @param[in] node Leafref target node to use for matching. If not set, all the leafref nodes are just collected.
++ * @param[in] match_ancestors If set, @p node is considered a match not only when a leafref targets it directly but
++ * even when an ancestor (parent) node of @p node is a target of the leafref.
++ * @param[out] set Set of matching leafref nodes.
++ * @return LY_SUCCESS on success.
++ * @return LY_ERR value on error.
++ */
++LIBYANG_API_DECL LY_ERR lysc_node_lref_backlinks(const struct ly_ctx *ctx, const struct lysc_node *node,
++        ly_bool match_ancestors, struct ly_set **set);
++
+ /**
+  * @brief Callback to be called for every schema node in a DFS traversal.
+  *
+diff --git a/src/tree_schema_common.c b/src/tree_schema_common.c
+index df5e88160..d62b01db8 100644
+--- a/src/tree_schema_common.c
++++ b/src/tree_schema_common.c
+@@ -1892,6 +1892,109 @@ lysc_node_lref_targets(const struct lysc_node *node, struct ly_set **set)
+     return rc;
+ }
+ 
++struct lysc_node_lref_backlings_arg {
++    const struct lysc_node *node;
++    ly_bool match_ancestors;
++    struct ly_set *set;
++};
++
++static LY_ERR
++lysc_node_lref_backlinks_clb(struct lysc_node *node, void *data, ly_bool *dfs_continue)
++{
++    LY_ERR rc = LY_SUCCESS;
++    struct lysc_node_lref_backlings_arg *arg = data;
++    struct ly_set *set = NULL;
++    const struct lysc_node *par;
++    uint32_t i;
++
++    (void)dfs_continue;
++
++    if (!(node->nodetype & LYD_NODE_TERM)) {
++        /* skip */
++        goto cleanup;
++    }
++
++    /* get all the leafref targets */
++    LY_CHECK_GOTO(rc = lysc_node_lref_targets(node, &set), cleanup);
++
++    /* ignore node if has no leafref targets */
++    if (!set->count) {
++        goto cleanup;
++    }
++
++    /* if just collecting leafrefs, we are done */
++    if (!arg->node) {
++        rc = ly_set_add(arg->set, node, 1, NULL);
++        goto cleanup;
++    }
++
++    /* check that the node (or the ancestor of) is the target of this leafref */
++    for (i = 0; i < set->count; ++i) {
++        for (par = set->snodes[i]; par; par = par->parent) {
++            if (par == arg->node) {
++                /* match */
++                break;
++            }
++
++            if (!arg->match_ancestors) {
++                /* not a match */
++                par = NULL;
++                break;
++            }
++        }
++
++        if (par) {
++            /* add into the set, matches */
++            LY_CHECK_GOTO(rc = ly_set_add(arg->set, node, 1, NULL), cleanup);
++            break;
++        }
++    }
++
++cleanup:
++    ly_set_free(set, NULL);
++    return rc;
++}
++
++LIBYANG_API_DEF LY_ERR
++lysc_node_lref_backlinks(const struct ly_ctx *ctx, const struct lysc_node *node, ly_bool match_ancestors,
++        struct ly_set **set)
++{
++    LY_ERR rc = LY_SUCCESS;
++    struct lysc_node_lref_backlings_arg arg = {0};
++    uint32_t idx = 0;
++    const struct lys_module *mod;
++
++    LY_CHECK_ARG_RET(NULL, ctx || node, set, LY_EINVAL);
++
++    if (!ctx) {
++        ctx = node->module->ctx;
++    }
++
++    /* allocate return set */
++    LY_CHECK_RET(ly_set_new(set));
++
++    /* prepare the arg */
++    arg.node = node;
++    arg.match_ancestors = match_ancestors;
++    arg.set = *set;
++
++    /* iterate across all loaded modules */
++    while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
++        if (!mod->compiled) {
++            continue;
++        }
++
++        LY_CHECK_GOTO(rc = lysc_module_dfs_full(mod, lysc_node_lref_backlinks_clb, &arg), cleanup);
++    }
++
++cleanup:
++    if (rc) {
++        ly_set_free(*set, NULL);
++        *set = NULL;
++    }
++    return rc;
++}
++
+ enum ly_stmt
+ lysp_match_kw(struct ly_in *in, uint64_t *indent)
+ {
+
+From 2f373f8677023e66c163617da4f11e78b61fa86d Mon Sep 17 00:00:00 2001
+From: Michal Vasko <mvasko@cesnet.cz>
+Date: Mon, 17 Feb 2025 16:02:54 +0100
+Subject: [PATCH 3/3] tests UPDATE new leafref backlinks test
+
+Co-authored-by: Brad House <brad@brad-house.com>
+---
+ tests/utests/schema/test_schema.c | 220 ++++++++++++++++++++++++++++++
+ 1 file changed, 220 insertions(+)
+
+diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c
+index cba2b2d45..45189e4b4 100644
+--- a/tests/utests/schema/test_schema.c
++++ b/tests/utests/schema/test_schema.c
+@@ -1887,6 +1887,225 @@ test_lysc_path(void **state)
+     free(path);
+ }
+ 
++/* TEST */
++static ly_bool
++compare_str_nodeset(struct ly_set *expected, struct ly_set *received)
++{
++    ly_bool is_error = 0;
++    size_t r;
++    size_t e;
++
++    for (e = 0; expected && e < expected->count; e++) {
++        const char *epath = expected->objs[e];
++        ly_bool found = 0;
++
++        for (r = 0; received && (r < received->count); r++) {
++            const char *rpath = received->objs[r];
++
++            if (!strcmp(epath, rpath)) {
++                found = 1;
++                break;
++            }
++        }
++
++        if (!found) {
++            fprintf(stderr, "< %s\n", epath);
++            is_error = 1;
++        }
++    }
++
++    /* If the count was equal and there was no error, no need to scan again */
++    if (expected && received && (expected->count == received->count) && !is_error) {
++        return 1;
++    }
++
++    for (r = 0; received && (r < received->count); r++) {
++        ly_bool found = 0;
++        const char *rpath = received->objs[r];
++
++        for (e = 0; expected && (e < expected->count) && !found; e++) {
++            char *epath = expected->objs[e];
++
++            if (!strcmp(epath, rpath)) {
++                found = 1;
++                break;
++            }
++        }
++        if (!found) {
++            fprintf(stderr, "> %s\n", rpath);
++        }
++    }
++
++    return 0;
++}
++
++static struct ly_set *
++strlist_to_pathset(const char **pathlist)
++{
++    struct ly_set *set = NULL;
++    uint32_t i;
++
++    if (!pathlist || !pathlist[0]) {
++        return NULL;
++    }
++
++    ly_set_new(&set);
++
++    for (i = 0; pathlist[i]; i++) {
++        ly_set_add(set, pathlist[i], 0, NULL);
++    }
++
++    return set;
++}
++
++static struct ly_set *
++lysc_nodeset_to_pathset(struct ly_set *nodeset)
++{
++    struct ly_set *set = NULL;
++    uint32_t i;
++
++    if (!nodeset || !nodeset->count) {
++        return NULL;
++    }
++
++    ly_set_new(&set);
++
++    for (i = 0; i < nodeset->count; i++) {
++        char *path = lysc_path(nodeset->snodes[i], LYSC_PATH_DATA, NULL, 0);
++
++        ly_set_add(set, path, 0, NULL);
++    }
++
++    return set;
++}
++
++static void
++test_lysc_backlinks(void **state)
++{
++    const char *expect1[] = {
++        /* Built-ins, not sure how to exclude those when not limiting by
++         * path */
++        "/ietf-yang-library:yang-library/module-set/module/deviation",
++        "/ietf-yang-library:yang-library/schema/module-set",
++        "/ietf-yang-library:yang-library/datastore/schema",
++        "/ietf-yang-library:yang-library-update/content-id",
++        "/ietf-yang-library:yang-library-change/module-set-id",
++        /* Normal expected */
++        "/b:my_extref_list/my_extref",
++        "/a:refstr",
++        "/a:refnum",
++        "/b:my_extref_union",
++        NULL
++    };
++
++    const char *expect2[] = {
++        "/b:my_extref_list/my_extref",
++        "/a:refstr",
++        "/b:my_extref_union",
++        NULL
++    };
++
++    const char *expect3[] = {
++        "/b:my_extref_list/my_extref",
++        "/a:refstr",
++        "/a:refnum",
++        "/b:my_extref_union",
++        NULL
++    };
++
++    struct {
++        const char *match_path;
++        ly_bool match_ancestors;
++        const char **expected_paths;
++    } tests[] = {
++        {NULL, 0, expect1},
++        {"/a:my_list/my_leaf_string", 0, expect2},
++        {"/a:my_list", 1, expect3}
++    };
++    const char *str;
++    uint32_t i;
++
++    str = "module a {\n"
++            "    namespace urn:a;\n"
++            "    prefix a;\n"
++            "    list my_list {\n"
++            "        key my_leaf_string;\n"
++            "        leaf my_leaf_string {\n"
++            "            type string;\n"
++            "        }\n"
++            "        leaf my_leaf_number {\n"
++            "            type uint32;\n"
++            "        }\n"
++            "    }\n"
++            "    leaf refstr {\n"
++            "        type leafref {\n"
++            "            path \"../my_list/my_leaf_string\";\n"
++            "        }\n"
++            "    }\n"
++            "    leaf refnum {\n"
++            "        type leafref {\n"
++            "            path \"../my_list/my_leaf_number\";\n"
++            "        }\n"
++            "    }\n"
++            "}\n";
++
++    assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
++    CHECK_LOG_CTX(NULL, NULL, 0);
++
++    str = "module b {\n"
++            "    namespace urn:b;\n"
++            "    prefix b;\n"
++            "    import a {\n"
++            "        prefix a;\n"
++            "    }\n"
++            "    list my_extref_list {\n"
++            "        key my_leaf_string;\n"
++            "        leaf my_leaf_string {\n"
++            "            type string;\n"
++            "        }\n"
++            "        leaf my_extref {\n"
++            "            type leafref {\n"
++            "                path \"/a:my_list/a:my_leaf_string\";\n"
++            "            }\n"
++            "        }\n"
++            "    }\n"
++            "    leaf my_extref_union {\n"
++            "        type union {\n"
++            "            type leafref {\n"
++            "                path \"/a:my_list/a:my_leaf_string\";\n"
++            "            }\n"
++            "            type leafref {\n"
++            "                path \"/a:my_list/a:my_leaf_number\";\n"
++            "            }\n"
++            "            type uint32;\n"
++            "         }\n"
++            "    }\n"
++            "}\n";
++
++    assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS);
++    CHECK_LOG_CTX(NULL, NULL, 0);
++
++    for (i = 0; i < sizeof tests / sizeof *tests; i++) {
++        const struct lysc_node *node = NULL;
++        struct ly_set *set = NULL, *expected = NULL, *received = NULL;
++
++        if (tests[i].match_path) {
++            node = lys_find_path(UTEST_LYCTX, NULL, tests[i].match_path, 0);
++            assert_non_null(node);
++        }
++
++        assert_int_equal(LY_SUCCESS, lysc_node_lref_backlinks(UTEST_LYCTX, node, tests[i].match_ancestors, &set));
++
++        expected = strlist_to_pathset(tests[i].expected_paths);
++        received = lysc_nodeset_to_pathset(set);
++        assert_int_equal(1, compare_str_nodeset(expected, received));
++
++        ly_set_free(expected, NULL);
++        ly_set_free(received, free);
++        ly_set_free(set, NULL);
++    }
++}
++
+ int
+ main(void)
+ {
+@@ -1909,6 +2128,7 @@ main(void)
+         UTEST(test_extension_compile),
+         UTEST(test_ext_recursive),
+         UTEST(test_lysc_path),
++        UTEST(test_lysc_backlinks),
+     };
+ 
+     return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/src/libyang3/patch/0003-minmax-errpath-3adb304.patch b/src/libyang3/patch/0003-minmax-errpath-3adb304.patch
new file mode 100644
index 000000000000..cb516fb9b862
--- /dev/null
+++ b/src/libyang3/patch/0003-minmax-errpath-3adb304.patch
@@ -0,0 +1,158 @@
+From 3adb3044f9a3e67f88d8bf3286b6ada8bccb09e8 Mon Sep 17 00:00:00 2001
+From: Michal Vasko <mvasko@cesnet.cz>
+Date: Mon, 24 Feb 2025 10:15:19 +0100
+Subject: [PATCH] validation UPDATE improve list min/max log path
+
+Fixes #2353
+---
+ src/validation.c                    | 66 ++++++++++++++++++++---------
+ tests/utests/data/test_validation.c |  6 +--
+ tests/utests/node/list.c            |  4 +-
+ 3 files changed, 50 insertions(+), 26 deletions(-)
+
+diff --git a/src/validation.c b/src/validation.c
+index bea1047d5..a21d8f034 100644
+--- a/src/validation.c
++++ b/src/validation.c
+@@ -1140,13 +1140,17 @@ static LY_ERR
+ lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
+         uint32_t min, uint32_t max, uint32_t val_opts)
+ {
++    LY_ERR rc = LY_SUCCESS;
+     uint32_t count = 0;
+-    struct lyd_node *iter;
++    struct lyd_node *iter, *last_iter = NULL;
+     const struct lysc_when *disabled;
++    char *log_path;
++    int r;
+ 
+     assert(min || max);
+ 
+     LYD_LIST_FOR_INST(first, snode, iter) {
++        last_iter = iter;
+         ++count;
+ 
+         if (min && (count == min)) {
+@@ -1182,32 +1186,52 @@ lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent,
+         max = 0;
+     }
+ 
+-    if (min) {
+-        if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+-            /* only a warning */
+-            LOG_LOCSET(snode, NULL);
+-            LOGWRN(snode->module->ctx, "Too few \"%s\" instances.", snode->name);
+-            LOG_LOCBACK(1, 0);
++    if (min || max) {
++        /* set log path */
++        if (last_iter) {
++            /* standard data path */
++            LOG_LOCSET(NULL, last_iter);
+         } else {
+-            LOG_LOCSET(snode, NULL);
+-            LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name);
+-            LOG_LOCBACK(1, 0);
+-            return LY_EVALID;
++            /* data path with last schema node name or only the schema node if !parent */
++            if (lyd_node_module(parent) != snode->module) {
++                r = asprintf(&log_path, "/%s:%s", snode->module->name, snode->name);
++            } else {
++                r = asprintf(&log_path, "/%s", snode->name);
++            }
++            if (r == -1) {
++                LOGMEM_RET(snode->module->ctx);
++            }
++            ly_log_location(NULL, parent, log_path, NULL);
++            free(log_path);
+         }
+-    } else if (max) {
+-        if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+-            /* only a warning */
+-            LOG_LOCSET(NULL, iter);
+-            LOGWRN(snode->module->ctx, "Too many \"%s\" instances.", snode->name);
++
++        if (min) {
++            if (val_opts & LYD_VALIDATE_OPERATIONAL) {
++                /* only a warning */
++                LOGWRN(snode->module->ctx, "Too few \"%s\" instances.", snode->name);
++            } else {
++                LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name);
++                rc = LY_EVALID;
++            }
++        } else if (max) {
++            if (val_opts & LYD_VALIDATE_OPERATIONAL) {
++                /* only a warning */
++                LOGWRN(snode->module->ctx, "Too many \"%s\" instances.", snode->name);
++            } else {
++                LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name);
++                rc = LY_EVALID;
++            }
++        }
++
++        /* revert log path */
++        if (last_iter) {
+             LOG_LOCBACK(0, 1);
+         } else {
+-            LOG_LOCSET(NULL, iter);
+-            LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name);
+-            LOG_LOCBACK(0, 1);
+-            return LY_EVALID;
++            ly_log_location_revert(0, parent ? 1 : 0, 1, 0);
+         }
+     }
+-    return LY_SUCCESS;
++
++    return rc;
+ }
+ 
+ /**
+diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c
+index 4a505fe75..694501604 100644
+--- a/tests/utests/data/test_validation.c
++++ b/tests/utests/data/test_validation.c
+@@ -277,12 +277,12 @@ test_minmax(void **state)
+     CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">mate</l>"
+             "<d xmlns=\"urn:tests:c\"/>",
+             LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+-    CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:choic/b/l", 0, "too-few-elements");
++    CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:l[.='mate']", 0, "too-few-elements");
+ 
+     CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">val1</l>"
+             "<l xmlns=\"urn:tests:c\">val2</l>",
+             LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+-    CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:choic/b/l", 0, "too-few-elements");
++    CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:l[.='val2']", 0, "too-few-elements");
+ 
+     LYD_TREE_CREATE("<l xmlns=\"urn:tests:c\">val1</l>"
+             "<l xmlns=\"urn:tests:c\">val2</l>"
+@@ -1259,7 +1259,7 @@ test_multi_error(void **state)
+     CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
+     assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, NULL));
+     lyd_free_tree(tree);
+-    CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "/ii:cont/ll", 0, "too-few-elements");
++    CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "/ii:cont/ll[.='25']", 0, "too-few-elements");
+     CHECK_LOG_CTX_APPTAG("l leaf is not left", "/ii:cont/l3", 0, "not-left");
+     CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "/ii:cont/l2", 0, "must-violation");
+     CHECK_LOG_CTX_APPTAG("Duplicate instance of \"l\".", "/ii:cont/l", 0, NULL);
+diff --git a/tests/utests/node/list.c b/tests/utests/node/list.c
+index 4a69dbbd0..39d026b68 100644
+--- a/tests/utests/node/list.c
++++ b/tests/utests/node/list.c
+@@ -1051,7 +1051,7 @@ test_xml(void **state)
+             "</user>";
+     CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+     assert_null(tree);
+-    CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user", 0);
++    CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user[uid='1']", 0);
+ 
+     data =
+             "<user xmlns=\"urn:tests:T2\">"
+@@ -1347,7 +1347,7 @@ test_json(void **state)
+             "]}";
+     CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+     assert_null(tree);
+-    CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user", 0);
++    CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user[uid='4']", 0);
+ 
+     data =
+             "{\"T2:user\": ["
diff --git a/src/libyang3/patch/0004-union-apptag-529a594.patch b/src/libyang3/patch/0004-union-apptag-529a594.patch
new file mode 100644
index 000000000000..101d10cc10c0
--- /dev/null
+++ b/src/libyang3/patch/0004-union-apptag-529a594.patch
@@ -0,0 +1,56 @@
+From 529a594aafd191bb879e13492014136d9be8ddca Mon Sep 17 00:00:00 2001
+From: Michal Vasko <mvasko@cesnet.cz>
+Date: Tue, 25 Feb 2025 15:46:58 +0100
+Subject: [PATCH] union UPDATE use err-app-tag if suitable
+
+Refs #2356
+
+Co-authored-by: Brad House <brad@brad-house.com>
+---
+ src/plugins_types/union.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
+index 64281c61e..61dde35d9 100644
+--- a/src/plugins_types/union.c
++++ b/src/plugins_types/union.c
+@@ -267,8 +267,9 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
+     LY_ARRAY_COUNT_TYPE u;
+     struct ly_err_item **errs = NULL, *e;
+     uint32_t *prev_lo, temp_lo = 0;
+-    char *msg = NULL;
++    char *msg = NULL, *err_app_tag = NULL;
+     int msg_len = 0;
++    ly_bool use_err_app_tag = 0;
+ 
+     *err = NULL;
+ 
+@@ -306,12 +307,27 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
+                 continue;
+             }
+ 
++            /* use an app-tag if all the types set it or set none */
++            if (errs[u]->apptag) {
++                if (!err_app_tag) {
++                    err_app_tag = strdup(errs[u]->apptag);
++                    LY_CHECK_ERR_GOTO(!err_app_tag, ret = LY_EMEM, cleanup);
++                    use_err_app_tag = 1;
++                } else if (strcmp(errs[u]->apptag, err_app_tag)) {
++                    use_err_app_tag = 0;
++                }
++            }
++
+             msg = ly_realloc(msg, msg_len + 4 + strlen(type_u->types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
+             LY_CHECK_ERR_GOTO(!msg, ret = LY_EMEM, cleanup);
+             msg_len += sprintf(msg + msg_len, "    %s: %s\n", type_u->types[u]->plugin->id, errs[u]->msg);
+         }
+ 
+-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "%s", msg);
++        if (!use_err_app_tag) {
++            free(err_app_tag);
++            err_app_tag = NULL;
++        }
++        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, err_app_tag, "%s", msg);
+     } else if (type_idx) {
+         *type_idx = u;
+     }
diff --git a/src/libyang3/patch/0005-pr2360-validate-union-errors.patch b/src/libyang3/patch/0005-pr2360-validate-union-errors.patch
new file mode 100644
index 000000000000..eee0ef204a50
--- /dev/null
+++ b/src/libyang3/patch/0005-pr2360-validate-union-errors.patch
@@ -0,0 +1,366 @@
+From 7e3672c4b9863d2ea82546751c25d44a066a5ec1 Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Fri, 28 Feb 2025 10:20:07 -0500
+Subject: [PATCH 1/5] validate: unions with leafrefs may return misleading
+ error codes
+
+When a leafref is not found in a union, it should not return an error
+indicating the value for the leafref wouldn't match the restrictions
+for a target but instead just say the leafref target wasn't found.
+
+The only data that should indicate the error message for data type
+restrictions is data associated with the target itself, not leafrefs.
+
+Signed-off-by: Brad House <brad@brad-house.com>
+---
+ src/plugins_types/union.c  | 33 +++++++++++++++++++++++++++++++++
+ tests/utests/types/union.c |  7 ++++---
+ 2 files changed, 37 insertions(+), 3 deletions(-)
+
+diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
+index 61dde35d9..e1bb1a26d 100644
+--- a/src/plugins_types/union.c
++++ b/src/plugins_types/union.c
+@@ -150,6 +150,31 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
+     }
+ }
+ 
++static void
++union_lref_error_rewrite(const struct lyd_node *ctx_node, struct ly_err_item *err, struct lysc_type *type, const void *value, size_t value_len)
++{
++    struct lysc_type_leafref *lref;
++    char *valstr = NULL;
++
++    if ((err == NULL) || (type->basetype != LY_TYPE_LEAFREF)) {
++        return;
++    }
++
++    lref = (struct lysc_type_leafref *)type;
++    free(err->apptag);
++    err->apptag = strdup("instance-required");
++
++    free(err->msg);
++
++    if (lyd_get_value(ctx_node) != NULL) {
++        valstr = strdup(lyd_get_value(ctx_node));
++    } else {
++        valstr = strndup((const char *)value, value_len);
++    }
++    asprintf(&err->msg, LY_ERRMSG_NOLREF_VAL, valstr, lyxp_get_expr(lref->path));
++    free(valstr);
++}
++
+ /**
+  * @brief Store (and validate) subvalue as a specific type.
+  *
+@@ -190,6 +215,9 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+             if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) {
+                 /* clear any leftover/freed garbage */
+                 memset(&subvalue->value, 0, sizeof subvalue->value);
++
++                /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */
++                union_lref_error_rewrite(ctx_node, *err, type_u->types[ti], value, value_len);
+                 return rc;
+             }
+ 
+@@ -228,6 +256,11 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+     if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) {
+         /* clear any leftover/freed garbage */
+         memset(&subvalue->value, 0, sizeof subvalue->value);
++
++        /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */
++        if (type->basetype == LY_TYPE_LEAFREF) {
++            union_lref_error_rewrite(ctx_node, *err, type, value, value_len);
++        }
+         return rc;
+     }
+ 
+diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c
+index c2541aaaf..257776b11 100644
+--- a/tests/utests/types/union.c
++++ b/tests/utests/types/union.c
+@@ -105,8 +105,8 @@ test_data_xml(void **state)
+     TEST_ERROR_XML2("",
+             "defs", "", "un1", "123456789012345678901", LY_EVALID);
+     CHECK_LOG_CTX("Invalid union value \"123456789012345678901\" - no matching subtype found:\n"
+-            "    libyang 2 - leafref, version 1: Invalid type int8 value \"123456789012345678901\".\n"
+-            "    libyang 2 - leafref, version 1: Invalid type int64 value \"123456789012345678901\".\n"
++            "    libyang 2 - leafref, version 1: Invalid leafref value \"123456789012345678901\" - no target instance \"/int8\" with the same value.\n"
++            "    libyang 2 - leafref, version 1: Invalid leafref value \"123456789012345678901\" - no target instance \"/int64\" with the same value.\n"
+             "    libyang 2 - identityref, version 1: Invalid identityref \"123456789012345678901\" value - identity not found in module \"defs\".\n"
+             "    libyang 2 - instance-identifier, version 1: Invalid instance-identifier \"123456789012345678901\" value - syntax error.\n"
+             "    libyang 2 - string, version 1: Unsatisfied length - string \"123456789012345678901\" length is not allowed.\n",
+@@ -295,7 +295,8 @@ test_validation(void **state)
+     assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
+     CHECK_LOG_CTX("Invalid LYB union value - no matching subtype found:\n"
+             "    libyang 2 - leafref, version 1: Invalid leafref value \"one\" - no target instance \"../../a/name\" with the same value.\n"
+-            "    libyang 2 - leafref, version 1: Invalid type uint32 value \"one\".\n", "/lref:test/community[name='test']/view", 0);
++            "    libyang 2 - leafref, version 1: Invalid leafref value \"one\" - no target instance \"../../b/name\" with the same value.\n",
++            "/lref:test/community[name='test']/view", 0);
+ 
+     lyd_free_all(tree);
+ }
+
+From f21bdb7ae93b1076e642bfa22515cc6ddeb3b51a Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Mon, 3 Mar 2025 05:48:58 -0500
+Subject: [PATCH 2/5] fixes as per @michalvasko
+
+---
+ src/plugins_types/union.c | 21 ++++++++++++++-------
+ 1 file changed, 14 insertions(+), 7 deletions(-)
+
+diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
+index e1bb1a26d..0adf5ab2b 100644
+--- a/src/plugins_types/union.c
++++ b/src/plugins_types/union.c
+@@ -150,17 +150,26 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
+     }
+ }
+ 
++/**
++ * @brief For leafref failures, ensure the appropriate error is propagated, not a type validation failure.
++ *
++ * @param[in] ctx_node Context node for prefix resolution.
++ * @param[in,out] err Error record to be updated
++ * @param[in] type leafref type used to extract target path
++ * @param[in] value value attempting to be stored
++ * @param[in] value_len Length of value that was attempted to be stored.
++ */
+ static void
+-union_lref_error_rewrite(const struct lyd_node *ctx_node, struct ly_err_item *err, struct lysc_type *type, const void *value, size_t value_len)
++union_update_lref_err(const struct lyd_node *ctx_node, struct ly_err_item *err, const struct lysc_type *type, const void *value, size_t value_len)
+ {
+-    struct lysc_type_leafref *lref;
++    const struct lysc_type_leafref *lref;
+     char *valstr = NULL;
+ 
+     if ((err == NULL) || (type->basetype != LY_TYPE_LEAFREF)) {
+         return;
+     }
+ 
+-    lref = (struct lysc_type_leafref *)type;
++    lref = (const struct lysc_type_leafref *)type;
+     free(err->apptag);
+     err->apptag = strdup("instance-required");
+ 
+@@ -217,7 +226,7 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+                 memset(&subvalue->value, 0, sizeof subvalue->value);
+ 
+                 /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */
+-                union_lref_error_rewrite(ctx_node, *err, type_u->types[ti], value, value_len);
++                union_update_lref_err(ctx_node, *err, type_u->types[ti], value, value_len);
+                 return rc;
+             }
+ 
+@@ -258,9 +267,7 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+         memset(&subvalue->value, 0, sizeof subvalue->value);
+ 
+         /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */
+-        if (type->basetype == LY_TYPE_LEAFREF) {
+-            union_lref_error_rewrite(ctx_node, *err, type, value, value_len);
+-        }
++        union_update_lref_err(ctx_node, *err, type, value, value_len);
+         return rc;
+     }
+ 
+
+From d2752709457bbff4dc12eb36dfd67a034e557a6d Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Mon, 3 Mar 2025 06:51:55 -0500
+Subject: [PATCH 3/5] do not use lyd_value()
+
+---
+ src/plugins_types/union.c | 25 ++++++++++---------------
+ 1 file changed, 10 insertions(+), 15 deletions(-)
+
+diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
+index 0adf5ab2b..0c126f6ae 100644
+--- a/src/plugins_types/union.c
++++ b/src/plugins_types/union.c
+@@ -153,14 +153,13 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
+ /**
+  * @brief For leafref failures, ensure the appropriate error is propagated, not a type validation failure.
+  *
+- * @param[in] ctx_node Context node for prefix resolution.
+  * @param[in,out] err Error record to be updated
+  * @param[in] type leafref type used to extract target path
+  * @param[in] value value attempting to be stored
+  * @param[in] value_len Length of value that was attempted to be stored.
+  */
+ static void
+-union_update_lref_err(const struct lyd_node *ctx_node, struct ly_err_item *err, const struct lysc_type *type, const void *value, size_t value_len)
++union_update_lref_err(struct ly_err_item *err, const struct lysc_type *type, const void *value, size_t value_len)
+ {
+     const struct lysc_type_leafref *lref;
+     char *valstr = NULL;
+@@ -174,12 +173,7 @@ union_update_lref_err(const struct lyd_node *ctx_node, struct ly_err_item *err,
+     err->apptag = strdup("instance-required");
+ 
+     free(err->msg);
+-
+-    if (lyd_get_value(ctx_node) != NULL) {
+-        valstr = strdup(lyd_get_value(ctx_node));
+-    } else {
+-        valstr = strndup((const char *)value, value_len);
+-    }
++    valstr = strndup((const char *)value, value_len);
+     asprintf(&err->msg, LY_ERRMSG_NOLREF_VAL, valstr, lyxp_get_expr(lref->path));
+     free(valstr);
+ }
+@@ -226,8 +220,8 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+                 memset(&subvalue->value, 0, sizeof subvalue->value);
+ 
+                 /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */
+-                union_update_lref_err(ctx_node, *err, type_u->types[ti], value, value_len);
+-                return rc;
++                union_update_lref_err(*err, type_u->types[ti], value, value_len);
++                goto cleanup;
+             }
+ 
+             assert(subvalue->value.realtype);
+@@ -256,9 +250,6 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+     if (options & LYPLG_TYPE_STORE_ONLY) {
+         opts |= LYPLG_TYPE_STORE_ONLY;
+     }
+-    if (dynamic) {
+-        opts |= LYPLG_TYPE_STORE_DYNAMIC;
+-    }
+ 
+     rc = type->plugin->store(ctx, type, value, value_len, opts, format, prefix_data, subvalue->hints,
+             subvalue->ctx_node, &subvalue->value, unres, err);
+@@ -267,8 +258,8 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+         memset(&subvalue->value, 0, sizeof subvalue->value);
+ 
+         /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */
+-        union_update_lref_err(ctx_node, *err, type, value, value_len);
+-        return rc;
++        union_update_lref_err(*err, type, value, value_len);
++        goto cleanup;
+     }
+ 
+     if (validate && (rc == LY_EINCOMPLETE)) {
+@@ -280,6 +271,10 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+         }
+     }
+ 
++cleanup:
++    if (dynamic) {
++        free((void *)value);
++    }
+     return rc;
+ }
+ 
+
+From 400b42a9075fe67b54b4bd6c6751e24cb46a0b36 Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Mon, 3 Mar 2025 06:57:03 -0500
+Subject: [PATCH 4/5] reference RFC in doxygen
+
+---
+ src/plugins_types/union.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
+index 0c126f6ae..1f60d8c43 100644
+--- a/src/plugins_types/union.c
++++ b/src/plugins_types/union.c
+@@ -153,6 +153,8 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
+ /**
+  * @brief For leafref failures, ensure the appropriate error is propagated, not a type validation failure.
+  *
++ * RFC7950 Section 15.5 defines the appropriate error app tag of "require-instance".
++ *
+  * @param[in,out] err Error record to be updated
+  * @param[in] type leafref type used to extract target path
+  * @param[in] value value attempting to be stored
+
+From e3cd01a4985d3eb5ed1c35793d1742c3960108ca Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Mon, 3 Mar 2025 07:02:41 -0500
+Subject: [PATCH 5/5] error checking for OOM
+
+---
+ src/plugins_types/union.c | 32 ++++++++++++++++++++++++++------
+ 1 file changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
+index 1f60d8c43..0da238235 100644
+--- a/src/plugins_types/union.c
++++ b/src/plugins_types/union.c
+@@ -159,25 +159,39 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
+  * @param[in] type leafref type used to extract target path
+  * @param[in] value value attempting to be stored
+  * @param[in] value_len Length of value that was attempted to be stored.
++ * @return LY_ERR value. Only possible errors are LY_SUCCESS and LY_EMEM.
+  */
+-static void
++static LY_ERR
+ union_update_lref_err(struct ly_err_item *err, const struct lysc_type *type, const void *value, size_t value_len)
+ {
+     const struct lysc_type_leafref *lref;
+     char *valstr = NULL;
++    int msg_len;
+ 
+     if ((err == NULL) || (type->basetype != LY_TYPE_LEAFREF)) {
+-        return;
++        return LY_SUCCESS;
+     }
+ 
+     lref = (const struct lysc_type_leafref *)type;
+     free(err->apptag);
+     err->apptag = strdup("instance-required");
++    if (err->apptag == NULL) {
++        return LY_EMEM;
++    }
+ 
+     free(err->msg);
++    err->msg = NULL;
+     valstr = strndup((const char *)value, value_len);
+-    asprintf(&err->msg, LY_ERRMSG_NOLREF_VAL, valstr, lyxp_get_expr(lref->path));
++    if (valstr == NULL) {
++        return LY_EMEM;
++    }
++
++    msg_len = asprintf(&err->msg, LY_ERRMSG_NOLREF_VAL, valstr, lyxp_get_expr(lref->path));
+     free(valstr);
++    if (msg_len == -1) {
++        return LY_EMEM;
++    }
++    return LY_SUCCESS;
+ }
+ 
+ /**
+@@ -200,7 +214,7 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+         uint32_t options, ly_bool validate, const struct lyd_node *ctx_node, const struct lyd_node *tree,
+         struct lys_glob_unres *unres, struct ly_err_item **err)
+ {
+-    LY_ERR rc = LY_SUCCESS;
++    LY_ERR urc, rc = LY_SUCCESS;
+     struct lysc_type *type = type_u->types[type_idx];
+     const void *value = NULL;
+     size_t value_len = 0;
+@@ -222,7 +236,10 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+                 memset(&subvalue->value, 0, sizeof subvalue->value);
+ 
+                 /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */
+-                union_update_lref_err(*err, type_u->types[ti], value, value_len);
++                urc = union_update_lref_err(*err, type_u->types[ti], value, value_len);
++                if (urc != LY_SUCCESS) {
++                    rc = urc;
++                }
+                 goto cleanup;
+             }
+ 
+@@ -260,7 +277,10 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3
+         memset(&subvalue->value, 0, sizeof subvalue->value);
+ 
+         /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */
+-        union_update_lref_err(*err, type, value, value_len);
++        urc = union_update_lref_err(*err, type, value, value_len);
++        if (urc != LY_SUCCESS) {
++            rc = urc;
++        }
+         goto cleanup;
+     }
+ 
diff --git a/src/libyang3/patch/0006-pr2361-union-sort-assert.patch b/src/libyang3/patch/0006-pr2361-union-sort-assert.patch
new file mode 100644
index 000000000000..a72cdb9e10d5
--- /dev/null
+++ b/src/libyang3/patch/0006-pr2361-union-sort-assert.patch
@@ -0,0 +1,45 @@
+From 18dd3a625f408e0b22258fcf30be25a94abf7d3e Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Mon, 3 Mar 2025 06:16:41 -0500
+Subject: [PATCH] union: fix assert in sort
+
+An assert was being hit in lyplg_type_sort_union() when evaluating
+leafrefs due to not using the realtype node.
+
+Signed-off-by: Michal Vasko <mvasko@cesnet.cz>
+Signed-off-by: Brad House <brad@brad-house.com>
+---
+ src/plugins_types/union.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
+index 61dde35d9..74ac0d64f 100644
+--- a/src/plugins_types/union.c
++++ b/src/plugins_types/union.c
+@@ -515,6 +515,7 @@ lyplg_type_sort_union(const struct ly_ctx *ctx, const struct lyd_value *val1, co
+     int rc = LY_SUCCESS;
+     LY_ARRAY_COUNT_TYPE u;
+     struct lysc_type **types;
++    struct lysc_type *type;
+ 
+     if (val1->subvalue->value.realtype == val2->subvalue->value.realtype) {
+         return val1->subvalue->value.realtype->plugin->sort(ctx, &val1->subvalue->value, &val2->subvalue->value);
+@@ -523,10 +524,16 @@ lyplg_type_sort_union(const struct ly_ctx *ctx, const struct lyd_value *val1, co
+     /* compare according to the order of types */
+     types = ((struct lysc_type_union *)val1->realtype)->types;
+     LY_ARRAY_FOR(types, u) {
+-        if (types[u] == val1->subvalue->value.realtype) {
++        if (types[u]->basetype == LY_TYPE_LEAFREF) {
++            type = ((struct lysc_type_leafref *)types[u])->realtype;
++        } else {
++            type = types[u];
++        }
++
++        if (type == val1->subvalue->value.realtype) {
+             rc = 1;
+             break;
+-        } else if (types[u] == val2->subvalue->value.realtype) {
++        } else if (type == val2->subvalue->value.realtype) {
+             rc = -1;
+             break;
+         }
diff --git a/src/libyang3/patch/series b/src/libyang3/patch/series
new file mode 100644
index 000000000000..c519c189b856
--- /dev/null
+++ b/src/libyang3/patch/series
@@ -0,0 +1,6 @@
+0001-pr2344-json-strings.patch
+0002-pr2352-backlinks.patch
+0003-minmax-errpath-3adb304.patch
+0004-union-apptag-529a594.patch
+0005-pr2360-validate-union-errors.patch
+0006-pr2361-union-sort-assert.patch
diff --git a/src/sonic-frr/patch/0081-backport-libyang3.patch b/src/sonic-frr/patch/0081-backport-libyang3.patch
new file mode 100644
index 000000000000..ad61de12eb84
--- /dev/null
+++ b/src/sonic-frr/patch/0081-backport-libyang3.patch
@@ -0,0 +1,230 @@
+diff --git a/debian/control b/debian/control
+index 4784e6b23..ef8895a41 100644
+--- a/debian/control
++++ b/debian/control
+@@ -23,7 +23,7 @@ Build-Depends: bison,
+                librtr-dev (>= 0.8.0~) <!pkg.frr.nortrlib>,
+                libsnmp-dev,
+                libssh-dev <!pkg.frr.nortrlib>,
+-               libyang2-dev (>= 2.1.128),
++               libyang2-dev (>= 2.1.128) | libyang-dev ( >= 3.0.3),
+                lsb-base,
+                pkg-config,
+                protobuf-c-compiler,
+diff --git a/lib/vty.c b/lib/vty.c
+index 1c9cff478..d3e617f88 100644
+--- a/lib/vty.c
++++ b/lib/vty.c
+@@ -39,6 +39,7 @@
+ #include "libfrr.h"
+ #include "frrstr.h"
+ #include "lib_errors.h"
++#include <libyang/version.h>
+ #include "northbound_cli.h"
+ #include "printfrr.h"
+ #include "json.h"
+@@ -3668,15 +3669,24 @@ static ssize_t vty_mgmt_libyang_print(void *user_data, const void *buf,
+ }
+ 
+ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
+-			       struct ly_err_item *ei)
++			       const struct ly_err_item *ei)
+ {
++#if (LY_VERSION_MAJOR < 3)
++#define data_path path
++#else
++#define data_path data_path
++#endif
+ 	bool have_apptag = ei->apptag && ei->apptag[0] != 0;
+-	bool have_path = ei->path && ei->path[0] != 0;
++	bool have_path = ei->data_path && ei->data_path[0] != 0;
+ 	bool have_msg = ei->msg && ei->msg[0] != 0;
+ 	const char *severity = NULL;
+ 	const char *evalid = NULL;
+ 	const char *ecode = NULL;
++#if (LY_VERSION_MAJOR < 3)
+ 	LY_ERR err = ei->no;
++#else
++	LY_ERR err = ei->err;
++#endif
+ 
+ 	if (ei->level == LY_LLERR)
+ 		severity = "error";
+@@ -3701,7 +3711,8 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
+ 			vty_out(vty, "<error-validation>%s</error-validation>\n",
+ 				evalid);
+ 		if (have_path)
+-			vty_out(vty, "<error-path>%s</error-path>\n", ei->path);
++			vty_out(vty, "<error-path>%s</error-path>\n",
++				ei->data_path);
+ 		if (have_apptag)
+ 			vty_out(vty, "<error-app-tag>%s</error-app-tag>\n",
+ 				ei->apptag);
+@@ -3720,7 +3731,7 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
+ 		if (evalid)
+ 			vty_out(vty, ", \"error-validation\": \"%s\"", evalid);
+ 		if (have_path)
+-			vty_out(vty, ", \"error-path\": \"%s\"", ei->path);
++			vty_out(vty, ", \"error-path\": \"%s\"", ei->data_path);
+ 		if (have_apptag)
+ 			vty_out(vty, ", \"error-app-tag\": \"%s\"", ei->apptag);
+ 		if (have_msg)
+@@ -3737,18 +3748,19 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
+ 		if (evalid)
+ 			vty_out(vty, " invalid: %s", evalid);
+ 		if (have_path)
+-			vty_out(vty, " path: %s", ei->path);
++			vty_out(vty, " path: %s", ei->data_path);
+ 		if (have_apptag)
+ 			vty_out(vty, " app-tag: %s", ei->apptag);
+ 		if (have_msg)
+ 			vty_out(vty, " msg: %s", ei->msg);
+ 		break;
+ 	}
++#undef data_path
+ }
+ 
+ static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format)
+ {
+-	struct ly_err_item *ei = ly_err_first(ly_native_ctx);
++	const struct ly_err_item *ei = ly_err_first(ly_native_ctx);
+ 	uint count;
+ 
+ 	if (!ei)
+diff --git a/lib/yang.c b/lib/yang.c
+index 03044fc29..2c09d37ba 100644
+--- a/lib/yang.c
++++ b/lib/yang.c
+@@ -11,6 +11,7 @@
+ #include "lib_errors.h"
+ #include "yang.h"
+ #include "yang_translator.h"
++#include <libyang/version.h>
+ #include "northbound.h"
+ 
+ #include "lib/config_paths.h"
+@@ -18,6 +19,17 @@
+ DEFINE_MTYPE_STATIC(LIB, YANG_MODULE, "YANG module");
+ DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure");
+ 
++/* Safe to remove after libyang 2.2.8 */
++#if (LY_VERSION_MAJOR < 3)
++#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \
++			     set)                                              \
++	lyd_find_xpath3(ctx_node, tree, xpath, vars, set)
++#else
++#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \
++			     set)                                              \
++	lyd_find_xpath3(ctx_node, tree, xpath, LY_VALUE_JSON, NULL, vars, set)
++#endif
++
+ /* libyang container. */
+ struct ly_ctx *ly_native_ctx;
+ 
+@@ -691,7 +703,12 @@ struct yang_data *yang_data_list_find(const struct list *list,
+ }
+ 
+ /* Make libyang log its errors using FRR logging infrastructure. */
+-static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
++static void ly_zlog_cb(LY_LOG_LEVEL level, const char *msg, const char *data_path
++#if !(LY_VERSION_MAJOR < 3)
++		       ,
++		       const char *schema_path, uint64_t line
++#endif
++)
+ {
+ 	int priority = LOG_ERR;
+ 
+@@ -708,8 +725,14 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
+ 		break;
+ 	}
+ 
+-	if (path)
+-		zlog(priority, "libyang: %s (%s)", msg, path);
++	if (data_path)
++		zlog(priority, "libyang: %s (%s)", msg, data_path);
++#if !(LY_VERSION_MAJOR < 3)
++	else if (schema_path)
++		zlog(priority, "libyang %s (%s)\n", msg, schema_path);
++	else if (line)
++		zlog(priority, "libyang %s (line %" PRIu64 ")\n", msg, line);
++#endif
+ 	else
+ 		zlog(priority, "libyang: %s", msg);
+ }
+@@ -736,7 +759,8 @@ LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
+ 		return err;
+ 	}
+ 
+-	err = lyd_find_xpath3(NULL, tree, xpath, NULL, &set);
++	err = yang_lyd_find_xpath3(NULL, tree, xpath, LY_VALUE_JSON, NULL, NULL,
++				   &set);
+ 	if (err) {
+ 		zlog_err("Failed to parse notification: %s", ly_last_errmsg());
+ 		lyd_free_all(tree);
+@@ -795,7 +819,7 @@ char *yang_convert_lyd_format(const char *data, size_t data_len,
+ 
+ 	assert(out_format != LYD_LYB);
+ 
+-	if (in_format != LYD_LYB && !MGMT_MSG_VALIDATE_NUL_TERM(data, data_len)) {
++	if (in_format != LYD_LYB && (!data_len || data[data_len - 1] != 0)) {
+ 		zlog_err("Corrupt input data, no NUL terminating byte");
+ 		return NULL;
+ 	}
+@@ -829,23 +853,29 @@ char *yang_convert_lyd_format(const char *data, size_t data_len,
+ 
+ const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len)
+ {
+-	struct ly_err_item *ei;
++	const struct ly_err_item *ei;
+ 
+ 	ei = ly_err_first(ly_ctx);
+ 	if (!ei)
+ 		return "";
+ 
+ 	strlcpy(buf, "YANG error(s):\n", buf_len);
++#if (LY_VERSION_MAJOR < 3)
++#define data_path path
++#else
++#define data_path data_path
++#endif
+ 	for (; ei; ei = ei->next) {
+-		if (ei->path) {
++		if (ei->data_path) {
+ 			strlcat(buf, " Path: ", buf_len);
+-			strlcat(buf, ei->path, buf_len);
++			strlcat(buf, ei->data_path, buf_len);
+ 			strlcat(buf, "\n", buf_len);
+ 		}
+ 		strlcat(buf, " Error: ", buf_len);
+ 		strlcat(buf, ei->msg, buf_len);
+ 		strlcat(buf, "\n", buf_len);
+ 	}
++#undef data_path
+ 
+ 	ly_err_clean(ly_ctx, NULL);
+ 
+@@ -897,7 +927,12 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile)
+ void yang_init(bool embedded_modules, bool defer_compile)
+ {
+ 	/* Initialize libyang global parameters that affect all containers. */
+-	ly_set_log_clb(ly_log_cb, 1);
++	ly_set_log_clb(ly_zlog_cb
++#if (LY_VERSION_MAJOR < 3)
++		       ,
++		       1
++#endif
++	);
+ 	ly_log_options(LY_LOLOG | LY_LOSTORE);
+ 
+ 	/* Initialize libyang container for native models. */
+@@ -1209,7 +1244,8 @@ LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath)
+ 
+ 	*root = lyd_first_sibling(*root);
+ 
+-	err = lyd_find_xpath3(NULL, *root, xpath, NULL, &set);
++	err = yang_lyd_find_xpath3(NULL, *root, xpath, LY_VALUE_JSON, NULL,
++				   NULL, &set);
+ 	if (err) {
+ 		flog_err_sys(EC_LIB_LIBYANG,
+ 			     "cannot obtain specific result for xpath \"%s\": %s",
diff --git a/src/sonic-frr/patch/series b/src/sonic-frr/patch/series
index c18d0159c6c6..e7c69db36456 100644
--- a/src/sonic-frr/patch/series
+++ b/src/sonic-frr/patch/series
@@ -61,5 +61,6 @@
 0078-vtysh-de-conditionalize-and-reorder-install-node.patch
 0079-staticd-add-support-for-srv6.patch
 0080-SRv6-vpn-route-and-sidlist-install.patch
+0081-backport-libyang3.patch
 0082-Revert-bgpd-upon-if-event-evaluate-bnc-with-matching.patch
 0083-staticd-add-cli-to-support-steering-of-ipv4-traffic-over-srv6-sid-list.patch
diff --git a/src/sonic-yang-models/tests/yang_model_tests/test_yang_model.py b/src/sonic-yang-models/tests/yang_model_tests/test_yang_model.py
index 22cad816b115..46a2f63ef4ce 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/test_yang_model.py
+++ b/src/sonic-yang-models/tests/yang_model_tests/test_yang_model.py
@@ -39,17 +39,27 @@ class Test_yang_models:
     def initTest(self):
         self.defaultYANGFailure = {
             'Must': ['Must condition', 'not satisfied'],
-            'InvalidValue': ['Invalid value'],
-            'LeafRef': ['Leafref', 'non-existing'],
+            'InvalidValue': ['Invalid value'],  # libyang3: ['Invalid', 'value', 'Data path']
+            'LeafRef': ['Leafref', 'non-existing'], #libyang3: ['Invalid leafref', 'no target instance']
             'When': ['When condition', 'not satisfied'],
-            'Pattern': ['pattern', 'does not satisfy'],
-            'Mandatory': ['required element', 'Missing'],
+            'Pattern': ['pattern', 'does not satisfy'], #libyang3: ['pattern', 'Unsatisfied pattern']
+            'Mandatory': ['required element', 'Missing'], #libyang3: ['Mandatory node', 'does not exist']
             'Verify': ['verified'],
-            'Range': ['does not satisfy', 'range'],
+            'Range': ['does not satisfy', 'range'], #libyang3: ['Unsatisfied range']
             'MinElements': ['Too few'],
             'MaxElements': ['Too many'],
             'UnknownElement': ['Unknown element'],
-            'None': []
+            'None': [],
+# New keys are needed for libyang3's messages which are different.  Go
+# ahead and add them now with the libyang1 values (which are duplicates
+# of the above).  This will make migrating to libyang3 easier with less
+# code review.
+            'Length': ['does not satisfy', 'range'], # libyang3: ['Unsatisfied length']
+            'DecimalFractionExceed': ['Invalid value'], # libyang3: ['Value', 'exceeds defined number', 'fraction digits']
+            'Bounds': ['Invalid value'], #libyang3 ['Value', 'out of type', 'min/max bounds'],
+            'ListKey': ['Missing required element'], #libyang3 ['List instance is missing its key']
+            'DateTime': ['pattern', 'does not satisfy'], #libyang3: ['Invalid date-and-time']
+            'IPv4': ['pattern', 'does not satisfy'], #libyang3 ['Failed to convert IPv4 address'],
         }
 
         self.ExceptionTests = { }
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/asic-sensors.json b/src/sonic-yang-models/tests/yang_model_tests/tests/asic-sensors.json
index 227904f40085..d94587f6ac7c 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/asic-sensors.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/asic-sensors.json
@@ -4,7 +4,7 @@
     },
     "ASIC_SENSORS_INVALID_POLLER_INTERVAL": {
         "desc": "Configure an invalid ASIC Sensors polling interval",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
     "ASIC_SENSORS_INVALID_POLLER_ADMIN_STATUS": {
         "desc": "Configure an invalid ASIC Sensors polling admin status",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/auto_techsupport.json b/src/sonic-yang-models/tests/yang_model_tests/tests/auto_techsupport.json
index a335dcfd389c..dc6af878a9e4 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/auto_techsupport.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/auto_techsupport.json
@@ -12,14 +12,14 @@
     },
     "AUTO_TECHSUPPORT_OUT_OF_RANGE_DECIMAL": {
             "desc" : "Configure a value for core-uage outside the range [0, 100)",
-            "eStr": "Value \"100.00\" does not satisfy the constraint \"0..99.99\" (range, length, or pattern)."
+            "eStrKey": "Range"
     },
     "AUTO_TECHSUPPORT_VALID_DECIMAL_VALUE": {
             "desc" : "Configure a value for max_techsupport_size inside the range [0, 100)"
     },
     "AUTO_TECHSUPPORT_INVALID_FRACTION_DIGITS": {
             "desc" : "Configure a value for max_techsupport_size inside the range [0, 100) but with 3 fractional digits",
-            "eStrKey": "InvalidValue"
+            "eStrKey": "DecimalFractionExceed"
     },
     "AUTO_TECHSUPPORT_RATE_LIMIT_INTERVAL_TEST": {
             "desc" : "Configure and test the valid configuration"
@@ -29,13 +29,13 @@
     },
     "AUTO_TECHSUPPORT_INVALID_AVAILABLE_MEM_THRESHOLD": {
             "desc" : "Configure a value for available_mem_threshold inside the range [0, 100) but with 3 fractional digits",
-            "eStrKey": "InvalidValue"
+            "eStrKey": "DecimalFractionExceed"
     },
     "AUTO_TECHSUPPORT_GLOBAL_MEM_THRESHOLD_VALID": {
             "desc" : "Configure and test the valid configuration"
     },
     "AUTO_TECHSUPPORT_GLOBAL_MEM_THRESHOLD_INVALID_THRESHOLD": {
             "desc" : "Configure a value for available_mem_threshold inside the range [0, 100) but with 3 fractional digits",
-            "eStrKey": "InvalidValue"
+            "eStrKey": "DecimalFractionExceed"
     }
 }
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/bgp.json b/src/sonic-yang-models/tests/yang_model_tests/tests/bgp.json
index 476c9c2d47cc..0118a5f1b1a8 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/bgp.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/bgp.json
@@ -51,8 +51,7 @@
     },
     "BGP_NEIGHBOR_NEG_INVALID_NAME": {
         "desc": "Incorrect neighbor name.",
-        "eStrKey": "InvalidValue",
-        "eStr": ["neighbor"]
+        "eStrKey": "InvalidValue"
     },
     "BGP_NEIGHBOR_NEG_INVALID_ASN": {
         "desc": "Invalid local AS number.",
@@ -78,7 +77,7 @@
     },
     "BGP_NEIGHBOR_AF_NEG_INVALID_MAXTHRESHOLD": {
         "desc": "Invalid maximum prefix warning threshold in neighbor AF.",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
     "BGP_NEIGHBOR_AF_NEG_INVALID_SOFT_RECFG_IN": {
         "desc": "Invalid boolean value for soft reconfiguration-in in neighbor AF.",
@@ -118,11 +117,11 @@
     },
     "BGP_PEERGROUP_INVALID_HOPS": {
         "desc": "Invalid TTL hops for peergroup.",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
     "BGP_PEERGROUP_AF_INVALID_MAXTHRESHOLD": {
         "desc": "Invalid maximum prefix warning threshold in peergroup AF.",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
     "BGP_PEERGROUP_AF_INVALID_SOFT_RECFG_IN": {
         "desc": "Invalid boolean value for soft reconfiguration-in in peergroup AF.",
@@ -157,7 +156,7 @@
     },
     "BGP_NEIGHBOR_NEG_INVALID_HOPS": {
         "desc": "Invalid TTL hops for unnumbered interface neigbhor.",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
     "BGP_MONITORS_ALL_VALID": {
         "desc": "Configure BGP monitor table."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/buffer_pool.json b/src/sonic-yang-models/tests/yang_model_tests/tests/buffer_pool.json
index ff35fc7357d1..f8557fb25031 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/buffer_pool.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/buffer_pool.json
@@ -48,7 +48,7 @@
     },
     "BUFFER_POOL_WRONG_PERCENTAGE_NEGATIVE_VALUE": {
         "desc": "BUFFER_POOL_WRONG_PERCENTAGE_NEGATIVE_VALUE pattern failure.",
-        "eStr": "Invalid value"
+        "eStrKey": "Bounds"
     },
     "BUFFER_POOL_WRONG_PERCENTAGE_NOT_A_NUMBER_VALUE": {
         "desc": "BUFFER_POOL_WRONG_PERCENTAGE_NOT_A_NUMBER_VALUE pattern failure.",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/device_metadata.json b/src/sonic-yang-models/tests/yang_model_tests/tests/device_metadata.json
index 6ca674f7e554..aff9dd38e3fc 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/device_metadata.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/device_metadata.json
@@ -80,7 +80,7 @@
     },
     "DEVICE_METADATA_INCORRECT_BUFFER_MODEL_PATTERN": {
         "desc": "DEVICE_METADATA wrong value for BUFFER_MODEL field.",
-        "eStr": ["pattern", "does not satisfy"]
+        "eStrKey": "Pattern"
     },
     "DEVICE_METADATA_FRR_MGMT_FWK_CONFIG": {
         "desc": "Verifying FRR MGMT framework configuration."
@@ -121,7 +121,7 @@
     },
     "DEVICE_METADATA_INVALID_PEER_SWITCH": {
         "desc": "Verifying test fails with hostname that is too long",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "DEVICE_METADATA_VALID_STORAGE_DEVICE": {
         "desc": "Verifying valid storage device value"
@@ -172,7 +172,7 @@
     },
     "DEVICE_METADATA_INVALID_TIMEZONE": {
         "desc": "Verifying invalid timezone value",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "DEVICE_METADATA_VALID_CREATE_ONLY_CONFIG_DB_BUFFERS": {
         "desc": "Verifying the create_only_config_db_buffers value"
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/dhcp_server_ipv4.json b/src/sonic-yang-models/tests/yang_model_tests/tests/dhcp_server_ipv4.json
index 3e7829b41b08..0f99827112a4 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/dhcp_server_ipv4.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/dhcp_server_ipv4.json
@@ -12,7 +12,7 @@
     },
     "DHCP_SERVER_IPV4_INCORRECT_NETMASK": {
         "desc": "Add netmask which is not in correct ip-prefix format.",
-        "eStrKey": "Pattern"
+        "eStrKey": "IPv4"
     },
     "DHCP_SERVER_IPV4_STATE_WRONG_VALUE": {
         "desc": "Configure wrong value for state.",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/fine-grained-ecmp.json b/src/sonic-yang-models/tests/yang_model_tests/tests/fine-grained-ecmp.json
index 2eed9fb8f34f..e81189ebea4e 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/fine-grained-ecmp.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/fine-grained-ecmp.json
@@ -38,15 +38,15 @@
     },
     "FG_NHG_MEMBER_TEST_MISSING_FG_NHG_REF": {
         "desc": "Fine-grained ECMP next-hop member configuration with missing FG_NHG reference in FG_NHG_MEMBER_LIST table.",
-        "eStr": "Missing required element \"FG_NHG\" in \"FG_NHG_MEMBER_LIST\". "
+        "eStrKey": "Mandatory"
     },
     "FG_NHG_PREFIX_TEST_MISSING_FG_NHG_REF": {
         "desc": "Fine-grained ECMP prefix configuration with missing FG_NHG reference in FG_NHG_PREFIX_LIST table.",
-        "eStr": "Missing required element \"FG_NHG\" in \"FG_NHG_PREFIX_LIST\". "
+        "eStrKey": "Mandatory"
     },
     "FG_NHG_MEMBER_TEST_MISSING_BANK": {
         "desc": "Fine-grained ECMP next-hop member configuration with missing bank in FG_NHG_MEMBER_LIST table.",
-        "eStr": "Missing required element \"bank\""
+        "eStrKey": "Mandatory"
     },
     "FG_NHG_PREFIX_TEST_DUPLICATE_ENTRY": {
         "desc": "Fine-grained ECMP prefix configuration with duplicate entry in FG_NHG_PREFIX_LIST table.",
@@ -56,4 +56,4 @@
         "desc": "Fine-grained ECMP next-hop member configuration with duplicate member in FG_NHG_MEMBER_LIST table.",
         "eStr": "Duplicated instance of \"FG_NHG_MEMBER_LIST\" list."
     }
-}
\ No newline at end of file
+}
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/flex_counter.json b/src/sonic-yang-models/tests/yang_model_tests/tests/flex_counter.json
index a508400f26d5..ea3e7ded392b 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/flex_counter.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/flex_counter.json
@@ -4,16 +4,14 @@
     },
     "FLEX_COUNTER_TABLE_WITH_INVALID_POLL_INTERVAL": {
         "desc": "Out of range poll interval.",
-        "eStrKey": "Range",
-        "eStr": "100..4294967295"
+        "eStrKey": "Range"
     },
     "FLEX_COUNTER_TABLE_WITH_VALID_BULK_CHUNK_SIZE": {
         "desc": "FLEX_COUNTER_TABLE_WITH_VALID_BULK_CHUNK_SIZE no failure."
     },
     "FLEX_COUNTER_TABLE_WITH_INVALID_BULK_CHUNK_SIZE": {
         "desc": "Out of range bulk chunk size.",
-        "eStrKey": "Range",
-        "eStr": "1..4294967295"
+        "eStrKey": "Range"
     },
     "FLOW_COUNTER_ROUTE_PATTERN_TABLE_WITH_VRF": {
         "desc": "FLOW_COUNTER_ROUTE_PATTERN_TABLE_WITH_VRF no failure."
@@ -23,7 +21,6 @@
     },
     "FLOW_COUNTER_ROUTE_PATTERN_TABLE_WITH_INVALID_MAX_MATCH_COUNT": {
         "desc": "Out of range max_match_count.",
-        "eStrKey": "Range",
-        "eStr": "1..50"
+        "eStrKey": "Range"
     }
 }
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/kdump.json b/src/sonic-yang-models/tests/yang_model_tests/tests/kdump.json
index 9f9525c2231d..8f065dc4f256 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/kdump.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/kdump.json
@@ -10,11 +10,11 @@
     },
     "KDUMP_WITH_INVALID_NUM_DUMPS": {
         "desc": "Configuring kdump config with a invalid number of allowed kdumps.",
-        "eStr": ["pattern", "does not satisfy"]
+        "eStrKey": "Range"
     },
     "KDUMP_WITH_INVALID_MEMORY": {
         "desc": "Configuring kdump config with invalid memory config.",
-        "eStr": ["pattern", "does not satisfy"]
+        "eStrKey": "Pattern"
     },
     "KDUMP_WITH_VALID_REMOTE_VALUES": {
         "desc": "Configuring the kdump with valid remote ssh values."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/kubernetes_master.json b/src/sonic-yang-models/tests/yang_model_tests/tests/kubernetes_master.json
index 6518f425da16..832798db21bc 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/kubernetes_master.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/kubernetes_master.json
@@ -12,7 +12,7 @@
     },
     "KUBERNETES_MASTER_INVALID_PORT": {
         "desc": "Configure invalid PORT in kubernetes_master.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "KUBERNETES_MASTER_INVALID_IP" : {
         "desc": "Configure invalid IP in kubernetes_master.",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/memory_statistics.json b/src/sonic-yang-models/tests/yang_model_tests/tests/memory_statistics.json
index e466e9cf92c6..2faaa4d4a4e7 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/memory_statistics.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/memory_statistics.json
@@ -4,15 +4,13 @@
     },
     "MEMORY_STATISTICS_WITH_INVALID_SAMPLING_INTERVAL": {
         "desc": "Configuring memory statistics with an invalid sampling_interval ( out of acceptable range).",
-        "eStrKey": "Range",
-        "eStr": "3..15"
+        "eStrKey": "Range"
     },
     "MEMORY_STATISTICS_WITH_INVALID_RETENTION_PERIOD": {
         "desc": "Configuring memory statistics with an invalid retention_period (out of acceptable range).",
-        "eStrKey": "Range",
-        "eStr": "1..30"
+        "eStrKey": "Range"
     },
     "MEMORY_STATISTICS_WITH_ENABLE_FEATURE": {
         "desc": "Enabling memory statistics feature with valid values."
     }
-}
\ No newline at end of file
+}
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/mgmt_port.json b/src/sonic-yang-models/tests/yang_model_tests/tests/mgmt_port.json
index 3530c7754b51..e279875b62b4 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/mgmt_port.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/mgmt_port.json
@@ -15,13 +15,11 @@
     },
     "MGMT_PORT_INVALID_SPEED": {
         "desc": "INVALID SPEED",
-        "eStrKey": "Range",
-        "eStr": ["10|100|1000"]
+        "eStrKey": "Range"
     },
     "MGMT_PORT_INVALID_MTU": {
         "desc": "INVALID MTU",
-        "eStrKey": "Range",
-        "eStr": "1500..9216"
+        "eStrKey": "Range"
     },
     "MGMT_PORT_DEFAULT_MTU": {
         "desc": "VALIDATE DEFAULT MTU",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/mirror_session.json b/src/sonic-yang-models/tests/yang_model_tests/tests/mirror_session.json
index 5bc6b54a0443..239b6a724376 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/mirror_session.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/mirror_session.json
@@ -60,7 +60,7 @@
     },
     "MIRROR_ERSPAN_ENTRY_WRONG_TTL": {
         "desc": "Configuring ERSPAN entry with invalid ttl",
-        "eStr": "Invalid ttl value"
+        "eStrKey": "Bounds"
     },
     "MIRROR_ERSPAN_ENTRY_WRONG_TTL_TYPE": {
         "desc": "Configuring ERSPAN entry with invalid ttl",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/nat.json b/src/sonic-yang-models/tests/yang_model_tests/tests/nat.json
index 2d69dacb7379..9b211c110c38 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/nat.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/nat.json
@@ -13,8 +13,7 @@
     },
     "STATIC_NAPT_ENTRY_WRONG_GLOBAL_L4_PORT": {
         "desc": "Configuring a wrong global l4 port in Static NAPT table.",
-        "eStrKey": "InvalidValue",
-        "eStr": ["global_l4_port"]
+        "eStrKey": "Bounds"
     },
     "STATIC_NAPT_ENTRY_WITHOUT_LOCAL_IP": {
         "desc": "Configuring a Static NAPT table without local ip.",
@@ -26,7 +25,7 @@
     },
     "STATIC_NAPT_ENTRY_INVALID_TWICE_NAT_ID": {
         "desc": "Configuring a invalid twice nat id in Static NAPT table.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "STATIC_NAT_ENTRY_WITH_VALID_VALUES": {
         "desc": "Configuring the Static NAT table with valid values."
@@ -41,7 +40,7 @@
     },
     "STATIC_NAT_ENTRY_INVALID_TWICE_NAT_ID": {
         "desc": "Configuring a invalid twice nat id in Static NAT table.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "NAT_GLOBAL_WITH_VALID_VALUES": {
         "desc": "Configuring a NAT Global table with valid values."
@@ -92,7 +91,7 @@
     },
     "NAT_BINDING_WITH_INVALID_TWICE_NAT_ID": {
         "desc": "Configuring a invalid twice nat id in NAT Binding table.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "NAT_BINDING_WITHOUT_ACL_TABLE": {
         "desc": "Configuring a NAT Binding table without acl."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/neigh.json b/src/sonic-yang-models/tests/yang_model_tests/tests/neigh.json
index 33f87167d680..6d041311ac10 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/neigh.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/neigh.json
@@ -5,11 +5,11 @@
 
     "NEIGH_MISSING_IP": {
         "desc": "Load NEIGH missing IP address",
-        "eStr": ["Invalid JSON data"]
+        "eStrKey": "ListKey"
     },
 
     "NEIGH_INVALID_VLAN": {
         "desc": "Load NEIGH missing VLAN",
-        "eStr": ["does not satisfy the constraint"]
+        "eStrKey": "Pattern"
     }
 }
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/ntp.json b/src/sonic-yang-models/tests/yang_model_tests/tests/ntp.json
index f380162b83db..1be07b0225f5 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/ntp.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/ntp.json
@@ -122,7 +122,7 @@
     },
     "NTP_KEY_ID_INVALID": {
         "desc": "NTP authentication keys invalid key id",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "NTP_KEY_TRUSTED_INVALID": {
         "desc": "NTP authentication keys invalid trustiness",
@@ -134,6 +134,6 @@
     },
     "NTP_KEY_VALUE_INVALID": {
         "desc": "NTP authentication keys bad key value",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     }
 }
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/nvgre.json b/src/sonic-yang-models/tests/yang_model_tests/tests/nvgre.json
index e776b0d9064f..a1eccae226a8 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/nvgre.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/nvgre.json
@@ -15,11 +15,11 @@
 
     "NVGRE_TUNNEL_MAP_INVALID_VLAN_ID": {
         "desc": "Invalid VLAN ID",
-        "eStrKey": "Pattern"
+        "eStrKey": "Range"
     },
 
     "NVGRE_TUNNEL_MAP_INVALID_VSID": {
         "desc": "INVALID VSID value for NVGRE_TUNNEL_MAP",
-        "eStrKey": "Pattern"
+        "eStrKey": "Range"
     }
-}
\ No newline at end of file
+}
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/password_hardening.json b/src/sonic-yang-models/tests/yang_model_tests/tests/password_hardening.json
index 4bcde92849f1..fe5d9ea88b31 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/password_hardening.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/password_hardening.json
@@ -15,18 +15,18 @@
     },
     "PASSWORD_BAD_HISTORY_CNT": {
         "desc": "Configure password history_cnt with out of range value",
-        "eStrKey" : "InvalidValue"
+        "eStrKey" : "Bounds"
     },
     "PASSWORD_BAD_LEN_MIN": {
         "desc": "Configure password len-min with out of range value",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
     "PASSWORD_BAD_EXPIRATION": {
         "desc": "Configure password expiration with out of range value",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
     "PASSWORD_BAD_EXPIRATION_WARN": {
         "desc": "Configure password expiration-warning with out of range value",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     }
-}
\ No newline at end of file
+}
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/pbh.json b/src/sonic-yang-models/tests/yang_model_tests/tests/pbh.json
index cb3a0a99fae4..51c2982d2d20 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/pbh.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/pbh.json
@@ -12,7 +12,7 @@
     },
     "PBH_TABLE_INVALID_DESCRIPTION": {
         "desc": "Configure invalid DESCRIPTION in PBH_TABLE.",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "PBH_RULE_INVALID_TABLE": {
         "desc": "Configure non-existing PBH_TABLE in PBH_RULE.",
@@ -20,7 +20,7 @@
     },
     "PBH_RULE_INVALID_PRIORITY": {
         "desc": "Configure invalid PRIORITY in PBH_RULE.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "PBH_RULE_INVALID_GRE_KEY": {
         "desc": "Configure invalid GRE_KEY in PBH_RULE.",
@@ -102,6 +102,6 @@
     },
     "PBH_HASH_FIELD_INVALID_SEQUENCE_ID": {
         "desc": "Configure invalid SEQUENCE_ID in PBH_HASH_FIELD.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     }
 }
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/peer-switch.json b/src/sonic-yang-models/tests/yang_model_tests/tests/peer-switch.json
index b8ee10dbdf9c..a4df44f176e5 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/peer-switch.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/peer-switch.json
@@ -4,7 +4,7 @@
     },
     "PEER_SWITCH_MISSING_DEVICE__NAME": {
         "desc": "Load PEER_SWITCH missing PEER Device name.",
-        "eStrKey": "Mandatory"
+        "eStrKey": "ListKey"
     },
     "PEER_SWITCH_INVALID_IP_ADDRESS": {
         "desc": "Load PEER_SWITCH with invalid IPv4 Address.",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/port.json b/src/sonic-yang-models/tests/yang_model_tests/tests/port.json
index 3c0d59cc247d..db68bd865b62 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/port.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/port.json
@@ -36,15 +36,14 @@
     },
     "PORT_INVALID_AUTONEG_TEST": {
         "desc": "PORT_INVALID_AUTONEG_TEST must condition failure.",
-        "eStrKey" : "Pattern",
-        "eStr": ["on|off"]
+        "eStrKey" : "Pattern"
     },
     "PORT_VALID_SPEEDS_TEST_1": {
         "desc": "PORT_VALID_SPEEDS_TEST_1 no failure."
     },
     "PORT_INVALID_SPEEDS_TEST_1": {
         "desc": "PORT_INVALID_SPEEDS_TEST_1 InvalidValue condition failure.",
-        "eStr": ["pattern", "does not satisfy"]
+        "eStrKey": "Range"
     },
     "PORT_VALID_ADVSPEEDS_TEST_1": {
         "desc": "PORT_VALID_ADVSPEEDS_TEST_1 no failure."
@@ -117,8 +116,7 @@
     },
     "PORT_INVALID_LINK_TRAINING_TEST": {
         "desc": "PORT_INVALID_LINK_TRAINING_TEST must condition failure.",
-        "eStrKey" : "Pattern",
-        "eStr": ["on|off"]
+        "eStrKey" : "Pattern"
     },
     "PORT_INVALID_TPID_TEST": {
         "desc": "PORT_INVALID_TPID_TEST invalid tpid value failure.",
@@ -144,24 +142,21 @@
      },
      "PORT_INVALID_SUBPORT_NUMBER": {
          "desc": "Out of range subport number",
-         "eStrKey": "Range",
-         "eStr": "0..8"
+         "eStrKey": "Range"
      },
      "PORT_VALID_DOM_POLLING": {
         "desc": "PORT_VALID_DOM_POLLING no failure."
     },
     "PORT_INVALID_DOM_POLLING": {
         "desc": "PORT_INVALID_DOM_POLLING invalid condition failure.",
-        "eStrKey" : "InvalidValue",
-        "eStr": ["dom_polling"]
+        "eStrKey" : "InvalidValue"
     },
     "PORT_AUTO_FEC_TEST": {
         "desc": "PORT_AUTO_FEC_TEST validate auto mode in fec."
     },
     "PORT_NO_LANES_NEGATIVE_TEST": {
         "desc": "PORT_NO_LANES_NEGATIVE_TEST no lanes.",
-        "eStrKey": "Mandatory",
-        "eStr": ["Missing"]
+        "eStrKey": "Mandatory"
     },
     "PORT_VOQ_CHASSIS_WITH_NO_LANES": {
         "desc": "PORT_VOQ_CHASSIS_WITH_NO_LANES no failure."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/portchannel.json b/src/sonic-yang-models/tests/yang_model_tests/tests/portchannel.json
index b2044e02d134..ac1cdd4d5dc5 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/portchannel.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/portchannel.json
@@ -7,7 +7,7 @@
     },
     "PORT_CHANNEL_OUT_OF_RANGE_MIN_LINKS": {
         "desc": "Configure PortChannel with greater than maximum valid value of min-links.",
-        "eStr": ["Value", "does not satisfy the constraint"]
+        "eStrKey": "Range"
     },
     "PORT_CHANNEL_WRONG_PATTERN": {
         "desc": "INCORRECT PORTCHANNEL_NAME IN PORT_CHANNEL TABLE.",
@@ -59,13 +59,11 @@
     },
     "PORTCHANNEL_INTERFACE_LIST_VRF_TEST_ON_NON_EXIST_PO": {
         "desc": "Configure vrf name on a non existent PortChannel.",
-        "eStrKey": "LeafRef",
-        "eStr": ["sonic-portchannel:name"]
+        "eStrKey": "LeafRef"
     },
     "PORTCHANNEL_INTERFACE_LIST_NON_EXIST_VRF_TEST": {
         "desc": "Configure a non existent Vrf in PORTCHANNEL_INTERFACE table.",
-        "eStrKey": "LeafRef",
-        "eStr": ["sonic-vrf:name"]
+        "eStrKey": "LeafRef"
     },
     "PORTCHANNEL_INTERFACE_WRONG_NAT_ZONE_RANGE": {
         "desc": "Configure wrong value for nat zone.",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/qos.json b/src/sonic-yang-models/tests/yang_model_tests/tests/qos.json
index 3b0199106472..c43190898442 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/qos.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/qos.json
@@ -11,7 +11,7 @@
 
     "SCHEDULER_INVALID_WEIGHT": {
         "desc": "Configure outof range weight in SCHEDULER table.",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
 
     "SCHEDULER_INVALID_METER_TYPE": {
@@ -49,7 +49,7 @@
 
     "WRED_PROFILE_INVALID_DROP_PROBABILITY": {
         "desc": "Configure invalid drop probability in WRED profile.",
-        "eStrKey" : "Pattern"
+        "eStrKey" : "Range"
     },
 
     "WRED_PROFILE_INVALID_GREEN_THRESHOLD": {
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/restapi.json b/src/sonic-yang-models/tests/yang_model_tests/tests/restapi.json
index 42ce64ba330f..22f44f211cf0 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/restapi.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/restapi.json
@@ -1,11 +1,11 @@
 {
     "RESTAPI_TABLE_WITH_INCORRECT_CERT": {
         "desc": "RESTAPI TABLE_WITH_INCORRECT_CERT failure.",
-        "eStr": ["Value", "does not satisfy the constraint"]
+        "eStrKey": "Pattern"
     },
     "RESTAPI_TABLE_WITH_INCORRECT_CLIENT": {
         "desc": "RESTAPI TABLE_WITH_INCORRECT_CLIENT failure.",
-        "eStr": ["Value", "does not satisfy the constraint"]
+        "eStrKey": "Pattern"
     },
     "RESTAPI_TABLE_WITH_VALID_CONFIG": {
         "desc": "RESTAPI TABLE WITH VALID CONFIG."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/route_filter.json b/src/sonic-yang-models/tests/yang_model_tests/tests/route_filter.json
index a1993ff52316..36c9d6e3ac98 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/route_filter.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/route_filter.json
@@ -17,8 +17,7 @@
     },
     "ROUTE_MAP_INVALID_STMT": {
         "desc": "Configure route map table with wrong value for stmt_name.",
-        "eStrKey": "InvalidValue",
-        "eStr": ["stmt_name"]
+        "eStrKey": "Bounds"
     },
     "ROUTE_MAP_INVALID_OPERATION_TYPE": {
         "desc": "Configure route map table with invalid route operation type.",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/serial_console.json b/src/sonic-yang-models/tests/yang_model_tests/tests/serial_console.json
index 76e39bb17944..c4634a8320f9 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/serial_console.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/serial_console.json
@@ -4,7 +4,7 @@
     },
     "SERIAL_CONSOLE_INVALID_INACTIVITY_TIMEOUT": {
         "desc": "SERIAL_CONSOLE attribute 'inactivity_timeout' set to invalid value (out of allowed range of [0, 35000] minutes).",
-        "eStr": "does not satisfy the constraint \"0..35000\""
+        "eStrKey": "Range"
     },
     "SERIAL_CONSOLE_INVALID_SYSRQ" : {
         "desc": "SERIAL_CONSOLE attribute 'sysrq' set to invalid value",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/sflow.json b/src/sonic-yang-models/tests/yang_model_tests/tests/sflow.json
index b98cc85fd390..598906e0388a 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/sflow.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/sflow.json
@@ -4,7 +4,7 @@
     },
     "SFLOW_COLLECTOR_WRONG_NAME_TEST": {
         "desc": "Configure a collector with incorrect name in SFLOW_COLLECTOR table.",
-        "eStr" : ["not", "satisfy", "the", "constraint", "1..64"]
+        "eStrKey" : "Length"
     },
     "SFLOW_TEST_WITHOUT_COLLECTOR_IP": {
         "desc": "Configure a collector without collector IP attribute in SFLOW_COLLECTOR table.",
@@ -12,7 +12,7 @@
     },
     "SFLOW_TEST_EXCEEDING_MAX_ELEMENTS": {
         "desc": "Configure collectors above the specified limit in SFLOW_COLLECTOR table.",
-        "eStr":	["Too many \"SFLOW_COLLECTOR_LIST\" elements"]
+        "eStrKey": "MaxElements"
     },
     "SFLOW_TEST_WITH_COLLECTOR_DEFAULT_VRF": {
         "desc": "Configure a collector in SFLOW_COLLECTOR table with default VRF."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/snmp.json b/src/sonic-yang-models/tests/yang_model_tests/tests/snmp.json
index 95e9b7ba9f80..b00e48e71f90 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/snmp.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/snmp.json
@@ -4,22 +4,22 @@
     },
     "SNMP_SYSTEM_CONTACT_NEG_TEST": {
         "desc": "Load SNMP sysContact with empty string",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "SNMP_SYSTEM_LOCATION_NEG_TEST": {
         "desc": "Load SNMP sysContact with empty string",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "SNMP_COMMUNITY_TEST": {
         "desc": "Load SNMP community string."
     },
     "SNMP_COMMUNITY_MIN_NEG_TEST": {
         "desc": "Load SNMP community string of length < 3.",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "SNMP_COMMUNITY_MAX_NEG_TEST": {
         "desc": "Load SNMP community string of lenth > 32.",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "SNMP_COMMUNITY_WRONG_TYPE_TEST": {
         "desc": "Load SNMP community string with un supported type.",
@@ -33,7 +33,7 @@
     },
     "SNMP_USER_NAME_MIN_NEG_TEST": {
         "desc": "Load SNMP user with name < 4",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "SNMP_USER_NAME_INVALID_NEG_TEST": {
         "desc": "Load SNMP user with invalid value",
@@ -112,7 +112,7 @@
     },
     "SNMP_USER_PRIV_LONG_ENCRYPT_PASS_NEG_TEST": {
         "desc": "Load SNMP user with user type Priv with long encryption password",
-        "eStrKey": "Range"
+        "eStrKey": "Length"
     },
     "SNMP_AGENT_ADDRESS_CONFIG": {
         "desc": "Load SNMP agent address config"
@@ -128,11 +128,11 @@
     },
     "SNMP_AGENT_ADDRESS_CONFIG_NO_VRF": {
         "desc": "Load SNMP agent address config with no vrf",
-	"eStr": ["Missing required element"]
+        "eStrKey": "ListKey"
     },
     "SNMP_AGENT_ADDRESS_CONFIG_INVALID_PORT": {
         "desc": "Load SNMP agent address config with invalid port",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "SNMP_AGENT_ADDRESS_CONFIG_DUPLICATE_IP_PORT": {
         "desc": "Load two SNMP agent address config same ip and port",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-bgp.json b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-bgp.json
index 6749860b2f7f..edf2e74264a4 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-bgp.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-bgp.json
@@ -9,7 +9,7 @@
 	},
 	"SONIC_EVENTS_BGP_BGP_STATE_INCORRECT_TIMESTAMP": {
 		"desc": "BGP_STATE_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_BGP_BGP_STATE_VALID": {
 		"desc": "VALID BGP STATE EVENT."
@@ -32,14 +32,14 @@
 	},
 	"SONIC_EVENTS_BGP_NOTIFICATION_INCORRECT_TIMESTAMP": {
 		"desc": "BGP_NOTIFICATION_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_BGP_NOTIFICATION_VALID": {
 		"desc": "VALID BGP NOTIFICATION."
 	},
 	"SONIC_EVENTS_BGP_ZEBRA_NO_BUFF_INCORRECT_TIMESTAMP": {
 		"desc": "ZEBRA_NO_BUFF_EVENT_INCORRECT_TIMESTAMP.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_BGP_ZEBRA_NO_BUFF_VALID": {
 		"desc": "VALID ZEBRA_NO_BUFF EVENT."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-dhcp-relay.json b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-dhcp-relay.json
index a4f17ae9e841..8ed60f46b7a0 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-dhcp-relay.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-dhcp-relay.json
@@ -5,7 +5,7 @@
 	},
 	"SONIC_EVENTS_DHCP_RELAY_DHCP_RELAY_DISCARD_INCORRECT_TIMESTAMP": {
 		"desc": "DHCP_RELAY_DISCARD_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_DHCP_RELAY_DHCP_RELAY_DISCARD_VALID": {
 		"desc": "VALID DHCP_RELAY_DISCARD EVENT."
@@ -20,7 +20,7 @@
 	},
 	"SONIC_EVENTS_DHCP_RELAY_DHCP_RELAY_DISPARITY_INCORRECT_TIMESTAMP": {
 		"desc": "DHCP_RELAY_DISPARITY_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_DHCP_RELAY_DHCP_RELAY_DISPARITY_VALID": {
 		"desc": "VALID DHCP_RELAY_DISPARITY EVENT."
@@ -35,7 +35,7 @@
 	},
 	"SONIC_EVENTS_DHCP_RELAY_DHCP_RELAY_BIND_FAILURE_INCORRECT_TIMESTAMP": {
 		"desc": "DHCP_RELAY_BIND_FAILURE_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_DHCP_RELAY_DHCP_RELAY_BIND_FAILURE_VALID": {
 		"desc": "VALID DHCP_RELAY_BIND_FAILURE EVENT."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-host.json b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-host.json
index b26d8e68a531..ad18f6cc1c5e 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-host.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-host.json
@@ -9,7 +9,7 @@
 	},
 	"SONIC_EVENTS_HOST_DISK_USAGE_INCORRECT_TIMESTAMP": {
 		"desc": "DISK_USAGE_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_DISK_USAGE_VALID": {
 		"desc": "VALID DISK_USAGE EVENT."
@@ -24,7 +24,7 @@
 	},
 	"SONIC_EVENTS_HOST_MEMORY_USAGE_INCORRECT_TIMESTAMP": {
 		"desc": "MEMORY_USAGE_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_MEMORY_USAGE_VALID": {
 		"desc": "VALID MEMORY_USAGE EVENT."
@@ -39,14 +39,14 @@
 	},
 	"SONIC_EVENTS_HOST_CPU_USAGE_INCORRECT_TIMESTAMP": {
 		"desc": "CPU_USAGE_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_CPU_USAGE_VALID": {
 		"desc": "VALID CPU_USAGE EVENT."
 	},
 	"SONIC_EVENTS_HOST_EVENT_SSHD_INCORRECT_TIMESTAMP": {
 		"desc": "EVENT_SSHD_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_EVENT_SSHD_VALID": {
 		"desc": "VALID EVENT_SSHD EVENT."
@@ -57,7 +57,7 @@
 	},
 	"SONIC_EVENTS_HOST_EVENT_DISK_INCORRECT_TIMESTAMP": {
 		"desc": "EVENT_DISK_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_EVENT_DISK_VALID": {
 		"desc": "VALID EVENT_DISK EVENT."
@@ -68,21 +68,21 @@
 	},
 	"SONIC_EVENTS_HOST_EVENT_KERNEL_INCORRECT_TIMESTAMP": {
 		"desc": "EVENT_KERNEL_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_EVENT_KERNEL_VALID": {
 		"desc": "VALID EVENT_KERNEL EVENT."
 	},
 	"SONIC_EVENTS_HOST_EVENT_DOWN_CTR_INCORRECT_TIMESTAMP": {
 		"desc": "EVENT_DOWN_CTR_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_EVENT_DOWN_CTR_VALID": {
 		"desc": "VALID EVENT_DOWN_CTR EVENT."
 	},
 	"SONIC_EVENTS_HOST_EVENT_STOPPED_CTR_INCORRECT_TIMESTAMP": {
 		"desc": "EVENT_STOPPED_CTR_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_EVENT_STOPPED_CTR_VALID": {
 		"desc": "VALID EVENT_STOPPED_CTR EVENT."
@@ -93,21 +93,21 @@
 	},
 	"SONIC_EVENTS_HOST_WATCHDOG_TIMEOUT_INCORRECT_TIMESTAMP": {
 		"desc": "WATCHDOG_TIMEOUT_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_WATCHDOG_TIMEOUT_VALID": {
 		"desc": "VALID WATCHDOG_TIMEOUT EVENT."
 	},
 	"SONIC_EVENTS_HOST_EVENT_SEU_INCORRECT_TIMESTAMP": {
 		"desc": "EVENT_SEU_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_EVENT_SEU_VALID": {
 		"desc": "VALID EVENT_SEU EVENT."
 	},
 	"SONIC_EVENTS_HOST_INVALID_FREELIST_INCORRECT_TIMESTAMP": {
 		"desc": "INVALID_FREELIST_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_INVALID_FREELIST_VALID": {
 		"desc": "VALID INVALID_FREELIST EVENT."
@@ -132,7 +132,7 @@
 	},
         "SONIC_EVENTS_HOST_MEM_THRESHOLD_EXCEEDED_INCORRECT_TIMESTAMP": {
 		"desc": "MEM_THRESHOLD_EXCEEDED_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_MEM_THRESHOLD_EXCEEDED_VALID": {
 		"desc": "VALID MEM_THRESHOLD_EXCEEDED EVENT."
@@ -155,7 +155,7 @@
 	},
 	"SONIC_EVENTS_HOST_PROCESS_EXITED_UNEXPECTEDLY_INCORRECT_TIMESTAMP": {
 		"desc": "PROCESS_EXITED_UNEXPECTEDLY_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_PROCESS_EXITED_UNEXPECTEDLY_VALID": {
 		"desc": "VALID_PROCESS_EXITED_UNEXPECTEDLY EVENT."
@@ -178,7 +178,7 @@
 	},
 	"SONIC_EVENTS_HOST_PROCESS_NOT_RUNNING_INCORRECT_TIMESTAMP": {
 		"desc": "PROCESS_NOT_RUNNING_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_HOST_PROCESS_NOT_RUNNING_VALID": {
 		"desc": "VALID_PROCESS_NOT_RUNNING EVENT."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-swss.json b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-swss.json
index a6c3d9332c08..693789ceb52b 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-swss.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-swss.json
@@ -9,7 +9,7 @@
 	},
 	"SONIC_EVENTS_SWSS_IF_STATE_INCORRECT_TIMESTAMP": {
 		"desc": "IF_STATE_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_SWSS_IF_STATE_VALID": {
 		"desc": "VALID IF_STATE EVENT."
@@ -32,7 +32,7 @@
 	},
 	"SONIC_EVENTS_SWSS_PFC_STORM_INCORRECT_TIMESTAMP": {
 		"desc": "PFC_STORM_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_SWSS_PFC_STORM_VALID": {
 		"desc": "VALID IF_STATE EVENT."
@@ -65,7 +65,7 @@
 	},
 	"SONIC_EVENTS_SWSS_CHK_CRM_THRESHOLD_INCORRECT_TIMESTAMP": {
 		"desc": "CHK_CRM_THRESHOLD_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_SWSS_CHK_CRM_THRESHOLD_VALID": {
 		"desc": "VALID CHK_CRM_THRESHOLD EVENT."
@@ -88,7 +88,7 @@
 	},
 	"SONIC_EVENTS_SWSS_SELECT_OPERATION_FAILURE_INCORRECT_TIMESTAMP": {
 		"desc": "SELECT_OPERATION_FAILURE_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_SWSS_SELECT_OPERATION_FAILURE_VALID": {
 		"desc": "VALID SELECT_OPERATION_FAILURE EVENT."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-syncd.json b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-syncd.json
index daa50cc1a086..d72750766afe 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-syncd.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/sonic-events-syncd.json
@@ -5,7 +5,7 @@
 	},
 	"SONIC_EVENTS_SYNCD_SYNCD_FAILURE_INCORRECT_TIMESTAMP": {
 		"desc": "SYNCD_FAILURE_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_SYNCD_SYNCD_FAILURE_VALID": {
 		"desc": "VALID SYNCD_FAILURE EVENT."
@@ -20,7 +20,7 @@
 	},
 	"SONIC_EVENTS_SYNCD_ALPM_PARITY_ERROR_INCORRECT_TIMESTAMP": {
 		"desc": "ALPM_PARITY_ERROR_EVENT_INCORRECT_TIMESTAMP failure.",
-		"eStrKey": "Pattern"
+		"eStrKey": "DateTime"
 	},
 	"SONIC_EVENTS_SYNCD_ALPM_PARITY_ERROR_VALID": {
 		"desc": "VALID ALPM_PARITY_ERROR_EVENT."
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/ssh-server.json b/src/sonic-yang-models/tests/yang_model_tests/tests/ssh-server.json
index e7bc0af10acb..b67c7b66edb2 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/ssh-server.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/ssh-server.json
@@ -7,13 +7,11 @@
     },
     "SSH_SERVER_INVALID_AUTH_RETRIES": {
         "desc": "Configure invalid number of authentication retries in SSH_SERVER.",
-        "eStrKey" : "Pattern",
-        "eStr": ["1..100"]
+        "eStrKey" : "Range"
     },
     "SSH_SERVER_INVALID_LOGIN_TIMEOUT": {
         "desc": "Configure invalid login timeout value in SSH_SERVER.",
-        "eStrKey" : "Pattern",
-        "eStr": ["1..600"]
+        "eStrKey" : "Range"
     },
     "SSH_SERVER_INVALID_PORTS_1": {
         "desc": "Configure invalid port value in SSH_SERVER.",
@@ -25,10 +23,10 @@
     },
     "SSH_SERVER_INVALID_INACTIVITY_TIMEOUT": {
         "desc": "Configure invalid inactivity_timeout value in SSH_SERVER.",
-        "eStr": "does not satisfy the constraint \"0..35000\""
+        "eStrKey": "Range"
     },
     "SSH_SERVER_INVALID_MAX_SESSIONS": {
         "desc": "Configure invalid max_sessions value in SSH_SERVER.",
-        "eStr": "does not satisfy the constraint \"0..100\""
+        "eStrKey": "Range"
     }
 }
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/stormond.json b/src/sonic-yang-models/tests/yang_model_tests/tests/stormond.json
index de8c699c49d0..ac73bedfcfb6 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/stormond.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/stormond.json
@@ -4,7 +4,7 @@
     },
     "STORMOND_INVALID_POLLING_INTERVAL": {
         "desc": "Configure an invalid daemon polling interval",
-        "eStrKey" : "InvalidValue"
+        "eStrKey" : "Bounds"
     },
     "STORMOND_INVALID_SYNC_INTERVAL": {
         "desc": "Configure an invalid fsstats file sync interval",
@@ -26,4 +26,4 @@
         "desc": "Configure an empty file sync interval",
         "eStrKey" : "InvalidValue"
     }
-}
\ No newline at end of file
+}
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/suppress_asic_sdk_health_event.json b/src/sonic-yang-models/tests/yang_model_tests/tests/suppress_asic_sdk_health_event.json
index a96a95deb280..2f2c0d468e97 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/suppress_asic_sdk_health_event.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/suppress_asic_sdk_health_event.json
@@ -14,7 +14,7 @@
     },
     "SUPPRESS_ASIC_SDK_HEALTH_EVENT_LIST_NEGATIVE_MAX_EVENTS": {
         "desc": "Load suppress ASIC/SDK health event list missing name.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "SUPPRESS_ASIC_SDK_HEALTH_EVENT_LIST_INVALID_MAX_EVENTS": {
         "desc": "Load suppress ASIC/SDK health event list missing name.",
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/syslog.json b/src/sonic-yang-models/tests/yang_model_tests/tests/syslog.json
index 4b2eb0ae1da2..eff13fca7ce0 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/syslog.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/syslog.json
@@ -12,7 +12,7 @@
     },
     "SYSLOG_SERVER_INVALID_PORT": {
         "desc": "Configure invalid PORT in SYSLOG_SERVER.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "SYSLOG_SERVER_INVALID_VRF": {
         "desc": "Configure invalid VRF in SYSLOG_SERVER.",
@@ -38,11 +38,11 @@
     },
     "SYSLOG_CONFIG_INVALID_INTERVAL": {
         "desc": "Configure invalid rate limit interval in SYSLOG_CONFIG.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "SYSLOG_CONFIG_INVALID_BURST": {
         "desc": "Configure invalid rate limit burst in SYSLOG_CONFIG.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "SYSLOG_CONFIG_FEATURE_VALID": {
         "desc": "Configure SYSLOG_CONFIG_FEATURE."
@@ -53,11 +53,11 @@
     },
     "SYSLOG_CONFIG_FEATURE_INVALID_INTERVAL": {
         "desc": "Configure invalid rate_limit_interval in SYSLOG_CONFIG_FEATURE.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "SYSLOG_CONFIG_FEATURE_INVALID_BURST": {
         "desc": "Configure invalid rate_limit_burst in SYSLOG_CONFIG_FEATURE.",
-        "eStrKey": "InvalidValue"
+        "eStrKey": "Bounds"
     },
     "SYSLOG_SERVER_HOSTNAME": {
         "desc": "Load syslog server table with hostname"
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/system_port.json b/src/sonic-yang-models/tests/yang_model_tests/tests/system_port.json
index 727373ade25a..43e40666f23a 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/system_port.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/system_port.json
@@ -4,6 +4,6 @@
     },
     "SYSTEM_PORT_WRONG_SPEED_PATTERN": {
         "desc": "Configure SYSTEM_PORT wrong speed.",
-        "eStr": ["pattern", "does not satisfy"]
+        "eStrKey": "Range"
     }
 }
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/tunnel.json b/src/sonic-yang-models/tests/yang_model_tests/tests/tunnel.json
index b414b639aa35..268aea1aae13 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/tunnel.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/tunnel.json
@@ -4,15 +4,15 @@
     },
     "TUNNEL_INVALID_ADDR": {
         "desc": "Load TUNNEL with invalid IPv4 Address.",
-        "eStr": ["does not satisfy the constraint"]
+        "eStrKey": "Pattern"
     },
     "TUNNEL_SRC_IP_NOT_PEER_SWITCH": {
         "desc": "Load TUNNEL with wrong IPv4 Address.",
-        "eStr": ["points to a non-existing leaf."]
+        "eStrKey": "LeafRef"
     },
     "TUNNEL_MISSING_MUX_TUNNEL": {
         "desc": "Load MUX_TUNNEL missing name.",
-        "eStr": ["Missing required element"]
+        "eStrKey": "ListKey"
     }
 }
 
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests/vnet.json b/src/sonic-yang-models/tests/yang_model_tests/tests/vnet.json
index 555ceea7d28a..c91cc9999347 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests/vnet.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests/vnet.json
@@ -7,22 +7,22 @@
     },
     "VNET_INVALID_VALUE_TEST1": {
         "desc": "Invalid values in parameters.",
-        "eStr": [ "Invalid VRF name" ]
+        "eStr": "Invalid VRF name"
     },
     "VNET_INVALID_VALUE_TEST2": {
         "desc": "Invalid values in parameters.",
-        "eStr": [ "Invalid value" ]
+        "eStrKey": "Bounds"
     },
     "VNET_INVALID_VALUE_TEST3": {
         "desc": "Invalid values in parameters.",
-        "eStr": [ "does not satisfy the constraint" ]
+        "eStrKey": "Pattern"
     },
     "VNET_INVALID_VALUE_TEST4": {
         "desc": "Invalid values in parameters.",
-        "eStr": [ "does not satisfy the constraint" ]
+        "eStrKey": "Pattern"
     },
     "VNET_INVALID_VXLAN_VTEP": {
         "desc": "Missing Vxlan_TUNNEL configuration",
-        "eStr" : [ "points to a non-existing leaf" ]
+        "eStrKey" : "LeafRef"
     }
-}
\ No newline at end of file
+}
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests_config/neigh.json b/src/sonic-yang-models/tests/yang_model_tests/tests_config/neigh.json
index 165bbb6b8649..26112109f0a1 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests_config/neigh.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests_config/neigh.json
@@ -28,7 +28,7 @@
         }
     },
 
-    "NEIGH_MISSING_IPV4": {
+    "NEIGH_MISSING_IP": {
         "sonic-vlan:sonic-vlan": {
             "sonic-vlan:VLAN": {
                 "VLAN_LIST": [
diff --git a/src/sonic-yang-models/tests/yang_model_tests/tests_config/port.json b/src/sonic-yang-models/tests/yang_model_tests/tests_config/port.json
index 005961c1daae..fb49b38e8fb7 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/tests_config/port.json
+++ b/src/sonic-yang-models/tests/yang_model_tests/tests_config/port.json
@@ -180,7 +180,7 @@
                         "lanes": "65",
                         "speed": 25000,
                         "tpid": "0x8100",
-                        "autoneg": 0,
+                        "autoneg": "invalid",
                         "mode":"trunk"
                     }
                 ]
@@ -583,7 +583,7 @@
                         "lanes": "65",
                         "speed": 25000,
                         "tpid": "0x8100",
-                        "link_training": 0
+                        "link_training": "invalid"
                     }
                 ]
             }

From 071b6445c5e4c09b63f98c6ba4b65e071ff7819c Mon Sep 17 00:00:00 2001
From: Brad House <brad@brad-house.com>
Date: Wed, 5 Feb 2025 21:14:24 -0500
Subject: [PATCH 2/7] IGNORE THIS COMMIT: merge libyang3 step 2 (PR #21719)

---
 rules/sonic-yang-models-py3.mk                |  4 +-
 .../tests/yang_model_pytests/conftest.py      | 23 ++---
 .../tests/yang_model_pytests/test_crm.py      |  6 +-
 .../tests/yang_model_pytests/test_dash_crm.py |  6 +-
 .../yang_model_pytests/test_smart_switch.py   | 20 ++---
 .../tests/yang_model_tests/test_yang_model.py | 90 ++++++++++++-------
 .../yang-models/sonic-bgp-allowed-prefix.yang |  8 ++
 .../yang-models/sonic-bgp-common.yang         |  2 +
 .../yang-models/sonic-bgp-global.yang         |  1 +
 .../yang-models/sonic-bgp-peerrange.yang      |  1 +
 .../yang-models/sonic-bgp-sentinel.yang       |  1 +
 ...sonic-buffer-port-egress-profile-list.yang |  1 +
 ...onic-buffer-port-ingress-profile-list.yang |  1 +
 .../yang-models/sonic-dash.yang               |  6 ++
 .../yang-models/sonic-device_metadata.yang    | 31 +++----
 .../yang-models/sonic-dhcp-server-ipv4.yang   |  4 +
 .../yang-models/sonic-dhcpv6-relay.yang       |  1 +
 .../yang-models/sonic-hash.yang               |  2 +
 .../yang-models/sonic-mgmt_interface.yang     |  2 +-
 .../yang-models/sonic-ntp.yang                |  2 +-
 .../yang-models/sonic-pbh.yang                |  2 +
 .../yang-models/sonic-port.yang               |  2 +
 .../yang-models/sonic-route-common.yang       |  1 +
 .../yang-models/sonic-route-map.yang          |  4 +
 .../sonic-routing-policy-sets.yang            |  3 +
 .../sonic-suppress-asic-sdk-health-event.yang |  1 +
 .../yang-models/sonic-vlan.yang               |  2 +
 .../yang-templates/sonic-acl.yang.j2          |  5 ++
 .../yang-templates/sonic-policer.yang.j2      |  1 -
 29 files changed, 152 insertions(+), 81 deletions(-)

diff --git a/rules/sonic-yang-models-py3.mk b/rules/sonic-yang-models-py3.mk
index d5e64ac07584..8125b1d580c0 100644
--- a/rules/sonic-yang-models-py3.mk
+++ b/rules/sonic-yang-models-py3.mk
@@ -1,9 +1,7 @@
 SONIC_YANG_MODELS_PY3 = sonic_yang_models-1.0-py3-none-any.whl
 $(SONIC_YANG_MODELS_PY3)_SRC_PATH = $(SRC_PATH)/sonic-yang-models
 $(SONIC_YANG_MODELS_PY3)_PYTHON_VERSION = 3
-$(SONIC_YANG_MODELS_PY3)_DEBS_DEPENDS = $(LIBYANG) $(LIBYANG_CPP) \
-                                        $(LIBYANG_PY3) \
-                                        $(LIBYANG3) \
+$(SONIC_YANG_MODELS_PY3)_DEBS_DEPENDS = $(LIBYANG3) \
                                         $(LIBYANG3_PY3)
 
 SONIC_PYTHON_WHEELS += $(SONIC_YANG_MODELS_PY3)
diff --git a/src/sonic-yang-models/tests/yang_model_pytests/conftest.py b/src/sonic-yang-models/tests/yang_model_pytests/conftest.py
index 59d55ebfbb8c..8116cc7102de 100644
--- a/src/sonic-yang-models/tests/yang_model_pytests/conftest.py
+++ b/src/sonic-yang-models/tests/yang_model_pytests/conftest.py
@@ -1,37 +1,38 @@
 import os
 import pytest
-import yang as ly
+import libyang as ly
 from json import dumps
 from glob import glob
 
-
 class YangModel:
 
     def __init__(self) -> None:
         cur_dir = os.path.dirname(os.path.abspath(__file__))
         project_root = os.path.abspath(os.path.join(cur_dir, '..', '..'))
         self.model_dir = os.path.join(project_root, 'yang-models')
-
         self._load_model()
 
+    def __del__(self) -> None:
+        self.ctx.destroy()
+        self.ctx = None
+
     def _load_model(self) -> None:
         self.ctx = ly.Context(self.model_dir)
         yang_files = glob(self.model_dir +"/*.yang")
-
         for file in yang_files:
-            m = self.ctx.parse_module_path(file, ly.LYS_IN_YANG)
-            if not m:
-                raise RuntimeError("Failed to parse '{file}' model")
+            with open(file, 'r') as f:
+                m = self.ctx.parse_module_file(f, "yang")
+                if not m:
+                    raise RuntimeError("Failed to parse '{file}' model")
 
     def _load_data(self, data) -> None:
-        self.ctx.parse_data_mem(dumps(data), ly.LYD_JSON,
-                                ly.LYD_OPT_CONFIG | ly.LYD_OPT_STRICT)
+        dnode = self.ctx.parse_data_mem(dumps(data), "json", strict=True, no_state=True, json_string_datatypes=True)
+        dnode.free()
 
     def load_data(self, data, expected_error=None) -> None:
         if expected_error:
-            with pytest.raises(RuntimeError) as exc_info:
+            with pytest.raises(Exception) as exc_info:
                 self._load_data(data)
-
             assert expected_error in str(exc_info)
         else:
             self._load_data(data)
diff --git a/src/sonic-yang-models/tests/yang_model_pytests/test_crm.py b/src/sonic-yang-models/tests/yang_model_pytests/test_crm.py
index ada390c618b9..0d0f84815147 100644
--- a/src/sonic-yang-models/tests/yang_model_pytests/test_crm.py
+++ b/src/sonic-yang-models/tests/yang_model_pytests/test_crm.py
@@ -61,8 +61,8 @@ def test_crm_valid_data(self, yang_model, resource, threshold):
 
     @pytest.mark.parametrize(
         "high, low, error_message", [
-            (-1, 70, 'Invalid value "-1"'),
-            (100, -70, 'Invalid value "-70"'),
+            (-1, 70, 'Value "-1" is out of type uint16 min/max bounds'),
+            (100, -70, 'Value "-70" is out of type uint16 min/max bounds'),
             (10, 70, 'high_threshold should be more than low_threshold')]
         )
     def test_crm_thresholds(self, yang_model, resource, threshold, high, low, error_message):
@@ -82,7 +82,7 @@ def test_crm_thresholds(self, yang_model, resource, threshold, high, low, error_
 
     @pytest.mark.parametrize(
         "high, low, th_type, error_message", [
-            (100, 70, 'wrong', 'Value "wrong" does not satisfy the constraint'),
+            (100, 70, 'wrong', 'Unsatisfied pattern'),
             (110, 20, 'percentage', 'Must condition')]
         )
     def test_crm_threshold_type(self, yang_model, resource, high, low, th_type, error_message):
diff --git a/src/sonic-yang-models/tests/yang_model_pytests/test_dash_crm.py b/src/sonic-yang-models/tests/yang_model_pytests/test_dash_crm.py
index ead37b98fdd9..6d634371b0ac 100644
--- a/src/sonic-yang-models/tests/yang_model_pytests/test_dash_crm.py
+++ b/src/sonic-yang-models/tests/yang_model_pytests/test_dash_crm.py
@@ -68,8 +68,8 @@ def test_dash_crm_valid_data(self, yang_model, data, resource, threshold):
 
     @pytest.mark.parametrize(
         "high, low, error_message", [
-            (-1, 70, 'Invalid value "-1"'),
-            (100, -70, 'Invalid value "-70"'),
+            (-1, 70, 'Value "-1" is out of type uint16 min/max bounds'),
+            (100, -70, 'Value "-70" is out of type uint16 min/max bounds'),
             (10, 70, 'high_threshold should be more than low_threshold')]
         )
     def test_dash_crm_thresholds(self, yang_model, data, resource, threshold, high, low, error_message):
@@ -87,7 +87,7 @@ def test_dash_crm_thresholds(self, yang_model, data, resource, threshold, high,
 
     @pytest.mark.parametrize(
         "high, low, th_type, error_message", [
-            (100, 70, 'wrong', 'Value "wrong" does not satisfy the constraint'),
+            (100, 70, 'wrong', 'Unsatisfied pattern'),
             (110, 20, 'percentage', 'Must condition')]
         )
     def test_dash_crm_threshold_type(self, yang_model, data, resource, high, low, th_type, error_message):
diff --git a/src/sonic-yang-models/tests/yang_model_pytests/test_smart_switch.py b/src/sonic-yang-models/tests/yang_model_pytests/test_smart_switch.py
index 4258e5e80641..4bcbc7f55d04 100644
--- a/src/sonic-yang-models/tests/yang_model_pytests/test_smart_switch.py
+++ b/src/sonic-yang-models/tests/yang_model_pytests/test_smart_switch.py
@@ -32,7 +32,7 @@ def test_valid_data(self, yang_model):
     @pytest.mark.parametrize(
         "bridge_name, error_message", [
             ("bridge-midplane", None),
-            ("wrong_name", 'Value "wrong_name" does not satisfy the constraint "bridge-midplane"')]
+            ("wrong_name", 'Unsatisfied pattern')]
         )
     def test_bridge_name(self, yang_model, bridge_name, error_message):
         data = {
@@ -51,7 +51,7 @@ def test_bridge_name(self, yang_model, bridge_name, error_message):
     @pytest.mark.parametrize(
         "ip_prefix, error_message", [
             ("169.254.200.254/24", None),
-            ("169.254.xyz.254/24", 'Value "169.254.xyz.254/24" does not satisfy the constraint')]
+            ("169.254.xyz.254/24", 'Unsatisfied pattern')]
         )
     def test_bridge_ip_prefix(self, yang_model, ip_prefix, error_message):
         data = {
@@ -70,7 +70,7 @@ def test_bridge_ip_prefix(self, yang_model, ip_prefix, error_message):
     @pytest.mark.parametrize(
         "dpu_name, error_message", [
             ("dpu0", None),
-            ("xyz", 'Value "xyz" does not satisfy the constraint "dpu[0-9]+')]
+            ("xyz", 'Unsatisfied pattern')]
         )
     def test_dpu_name(self, yang_model, dpu_name, error_message):
         data = {
@@ -91,7 +91,7 @@ def test_dpu_name(self, yang_model, dpu_name, error_message):
     @pytest.mark.parametrize(
         "midplane_interface, error_message", [
             ("dpu0", None),
-            ("xyz", 'Value "xyz" does not satisfy the constraint "dpu[0-9]+')]
+            ("xyz", 'Unsatisfied pattern')]
         )
     def test_dpu_midplane_interface(self, yang_model, midplane_interface, error_message):
         data = {
@@ -112,7 +112,7 @@ def test_dpu_midplane_interface(self, yang_model, midplane_interface, error_mess
     @pytest.mark.parametrize(
         "port_name, error_message", [
             ("dpu0", None),
-            ("dp0rt0", 'Value "dp0rt0" does not satisfy the constraint "[a-zA-Z]+[0-9]+"')]
+            ("dp0rt0", 'Unsatisfied pattern')]
         )
     def test_dpu_port_name(self, yang_model, port_name, error_message):
         data = {
@@ -139,7 +139,7 @@ def test_dpu_port_name(self, yang_model, port_name, error_message):
     @pytest.mark.parametrize(
         "vip_ipv4, error_message", [
             ("192.168.1.1", None),
-            ("192.168.1.xyz", 'Value "192.168.1.xyz" does not satisfy the constraint')]
+            ("192.168.1.xyz", 'Unsatisfied pattern')]
         )
     def test_dpu_port_vip_ipv4(self, yang_model, vip_ipv4, error_message):
         data = {
@@ -166,7 +166,7 @@ def test_dpu_port_vip_ipv4(self, yang_model, vip_ipv4, error_message):
     @pytest.mark.parametrize(
         "vip_ipv6, error_message", [
             ("2001:db8::1", None),
-            ("2001:db8::xyz", 'Value "2001:db8::xyz" does not satisfy the constraint')]
+            ("2001:db8::xyz", 'Unsatisfied pattern')]
         )
     def test_dpu_port_vip_ipv6(self, yang_model, vip_ipv6, error_message):
         data = {
@@ -193,7 +193,7 @@ def test_dpu_port_vip_ipv6(self, yang_model, vip_ipv6, error_message):
     @pytest.mark.parametrize(
         "pa_ipv4, error_message", [
             ("192.168.1.2", None),
-            ("192.168.1.xyz", 'Value "192.168.1.xyz" does not satisfy the constraint')]
+            ("192.168.1.xyz", 'Unsatisfied pattern')]
         )
     def test_dpu_port_pa_ipv4(self, yang_model, pa_ipv4, error_message):
         data = {
@@ -220,7 +220,7 @@ def test_dpu_port_pa_ipv4(self, yang_model, pa_ipv4, error_message):
     @pytest.mark.parametrize(
         "pa_ipv6, error_message", [
             ("2001:db8::2", None),
-            ("2001:db8::xyz", 'Value "2001:db8::xyz" does not satisfy the constraint')]
+            ("2001:db8::xyz", 'Unsatisfied pattern')]
         )
     def test_dpu_port_pa_ipv6(self, yang_model, pa_ipv6, error_message):
         data = {
@@ -247,7 +247,7 @@ def test_dpu_port_pa_ipv6(self, yang_model, pa_ipv6, error_message):
     @pytest.mark.parametrize(
         "gnmi_port, error_message", [
             (8080, None),
-            (99999, 'Invalid value "99999" in "gnmi_port" element.')]
+            (99999, 'Value "99999" is out of type uint16 min/max bounds')]
         )
     def test_dpu_port_gnmi(self, yang_model, gnmi_port, error_message):
         data = {
diff --git a/src/sonic-yang-models/tests/yang_model_tests/test_yang_model.py b/src/sonic-yang-models/tests/yang_model_tests/test_yang_model.py
index 46a2f63ef4ce..7467a7c6772b 100644
--- a/src/sonic-yang-models/tests/yang_model_tests/test_yang_model.py
+++ b/src/sonic-yang-models/tests/yang_model_tests/test_yang_model.py
@@ -1,11 +1,12 @@
 # This script is used to
 
-import yang as ly
+import libyang as ly
 import logging
 import argparse
 import sys
 import ijson
 import json
+import pytest
 #import sonic_yang as sy
 from glob import glob
 from os import listdir
@@ -39,30 +40,27 @@ class Test_yang_models:
     def initTest(self):
         self.defaultYANGFailure = {
             'Must': ['Must condition', 'not satisfied'],
-            'InvalidValue': ['Invalid value'],  # libyang3: ['Invalid', 'value', 'Data path']
-            'LeafRef': ['Leafref', 'non-existing'], #libyang3: ['Invalid leafref', 'no target instance']
+            'InvalidValue': ['Invalid', 'value', 'Data path'],
+            'LeafRef': ['Invalid leafref', 'no target instance'],
             'When': ['When condition', 'not satisfied'],
-            'Pattern': ['pattern', 'does not satisfy'], #libyang3: ['pattern', 'Unsatisfied pattern']
-            'Mandatory': ['required element', 'Missing'], #libyang3: ['Mandatory node', 'does not exist']
+            'Pattern': ['pattern', 'Unsatisfied pattern'],
+            'Mandatory': ['Mandatory node', 'does not exist'],
             'Verify': ['verified'],
-            'Range': ['does not satisfy', 'range'], #libyang3: ['Unsatisfied range']
+            'Range': ['Unsatisfied range'],
             'MinElements': ['Too few'],
             'MaxElements': ['Too many'],
-            'UnknownElement': ['Unknown element'],
+            'UnknownElement': ['not found as a child'],
             'None': [],
-# New keys are needed for libyang3's messages which are different.  Go
-# ahead and add them now with the libyang1 values (which are duplicates
-# of the above).  This will make migrating to libyang3 easier with less
-# code review.
-            'Length': ['does not satisfy', 'range'], # libyang3: ['Unsatisfied length']
-            'DecimalFractionExceed': ['Invalid value'], # libyang3: ['Value', 'exceeds defined number', 'fraction digits']
-            'Bounds': ['Invalid value'], #libyang3 ['Value', 'out of type', 'min/max bounds'],
-            'ListKey': ['Missing required element'], #libyang3 ['List instance is missing its key']
-            'DateTime': ['pattern', 'does not satisfy'], #libyang3: ['Invalid date-and-time']
-            'IPv4': ['pattern', 'does not satisfy'], #libyang3 ['Failed to convert IPv4 address'],
+            'Length': ['Unsatisfied length'],
+            'DecimalFractionExceed': ['Value', 'exceeds defined number', 'fraction digits'],
+            'Bounds': ['Value', 'out of type', 'min/max bounds'],
+            'ListKey': ['List instance is missing its key'],
+            'DateTime': ['Invalid date-and-time'],
+            'IPv4': ['Failed to convert IPv4 address'],
         }
 
         self.ExceptionTests = { }
+        self.FailedTests = []
 
         for test_file in glob("./tests/yang_model_tests/tests/*.json"):
             try:
@@ -122,11 +120,12 @@ def loadYangModel(self, yangDir):
             # load yang modules
             for file in yangFiles:
                 log.debug(file)
-                m = self.ctx.parse_module_path(file, ly.LYS_IN_YANG)
-                if m is not None:
-                    log.info("module: {} is loaded successfully".format(m.name()))
-                else:
-                    log.info("Could not load module: {}".format(file))
+                with open(file, 'r') as f:
+                    m = self.ctx.parse_module_file(f, "yang")
+                    if m is not None:
+                        log.info("module: {} is loaded successfully".format(m.name()))
+                    else:
+                        log.info("Could not load module: {}".format(file))
 
         except Exception as e:
             printExceptionDetails()
@@ -178,26 +177,35 @@ def logStartTest(self, desc):
     """
     def loadConfigData(self, jInput, verify=None):
         s = ""
+        node = None
+        try:
+            node = self.ctx.parse_data_mem(jInput, "json", strict=True, no_state=True, json_string_datatypes=True)
+        except Exception as e:
+            printExceptionDetails()
+            s = str(e)
+            log.info(s)
+            return s
+
         try:
-            node = self.ctx.parse_data_mem(jInput, ly.LYD_JSON, \
-            ly.LYD_OPT_CONFIG | ly.LYD_OPT_STRICT)
             # verify the data tree if asked
             if verify is not None:
                 xpath = verify['xpath']
                 log.info("Verify xpath: {}".format(xpath))
-                set = node.find_path(xpath)
-                for dnode in set.data():
+                nodes = node.find_all(xpath)
+                for dnode in nodes:
                     if (xpath == dnode.path()):
                         log.info("Verify dnode: {}".format(dnode.path()))
-                        data = dnode.print_mem(ly.LYD_JSON, ly.LYP_WITHSIBLINGS \
-                            | ly.LYP_FORMAT | ly.LYP_WD_ALL)
+                        data = dnode.print_mem("json", with_siblings=True, pretty=True, include_implicit_defaults=True)
                         data = json.loads(data)
-                        log.info("Verify data: {}".format(data))
+                        log.info("Verify path value {} is {} in {}".format(verify['key'], verify['value'], data))
+                        assert (verify['key'] in data)
                         assert (data[verify['key']] == verify['value'])
                         s = 'verified'
         except Exception as e:
-            s = str(e)
-            log.info(s)
+            printExceptionDetails()
+
+        node.free()
+
         return s
 
     """
@@ -221,9 +229,12 @@ def runExceptionTest(self, test):
                 log.info(desc + " Passed\n")
                 return PASS
             else:
-                raise Exception("Mismatch {} and {}".format(eStr, s))
+                errstr = "{}: Mismatch {} and {}".format(test, eStr, s)
+                self.FailedTests.append(errstr)
+                raise Exception(errstr)
         except Exception as e:
             printExceptionDetails()
+
         log.info(desc + " Failed\n")
         return FAIL
 
@@ -253,7 +264,9 @@ def runVlanSpecialTest(self, test):
                 log.debug(jInput)
                 s = self.loadConfigData(json.dumps(jInput))
                 if s!="":
-                    raise Exception("{} in not empty".format(s))
+                    errstr="{}[{}]: {} in not empty".format(test,i,s)
+                    self.FailedTests.append(errstr)
+                    raise Exception(errstr)
             return PASS
         except Exception as e:
             printExceptionDetails()
@@ -282,6 +295,17 @@ def test_run_tests(self):
             ret = FAIL * len(self.tests)
             printExceptionDetails()
 
+        if len(self.FailedTests):
+            print("{} Failures:".format(len(self.FailedTests)))
+            log.error("{} Failures:".format(len(self.FailedTests)))
+            for x in self.FailedTests:
+                print(x)
+                log.error(x)
+
+        if self.ctx:
+            self.ctx.destroy()
+            self.ctx = None
+
         assert ret == 0
         return
 # End of Class
diff --git a/src/sonic-yang-models/yang-models/sonic-bgp-allowed-prefix.yang b/src/sonic-yang-models/yang-models/sonic-bgp-allowed-prefix.yang
index ace3ddc2f1d0..02ab19474264 100644
--- a/src/sonic-yang-models/yang-models/sonic-bgp-allowed-prefix.yang
+++ b/src/sonic-yang-models/yang-models/sonic-bgp-allowed-prefix.yang
@@ -96,11 +96,13 @@ module sonic-bgp-allowed-prefix {
 
                 leaf-list prefixes_v4 {
                     type bgp-allowed-ipv4-prefix;
+                    ordered-by user;
                     description "BGP V4 allowed prefix list";
                 }
 
                 leaf-list prefixes_v6 {
                     type bgp-allowed-ipv6-prefix;
+                    ordered-by user;
                     description "BGP V6 allowed prefix list";
                 }
             }
@@ -139,11 +141,13 @@ module sonic-bgp-allowed-prefix {
 
                 leaf-list prefixes_v4 {
                     type bgp-allowed-ipv4-prefix;
+                    ordered-by user;
                     description "BGP V4 allowed prefix list";
                 }
 
                 leaf-list prefixes_v6 {
                     type bgp-allowed-ipv6-prefix;
+                    ordered-by user;
                     description "BGP V6 allowed prefix list";
                 }
             }
@@ -175,11 +179,13 @@ module sonic-bgp-allowed-prefix {
 
                 leaf-list prefixes_v4 {
                     type bgp-allowed-ipv4-prefix;
+                    ordered-by user;
                     description "BGP V4 allowed prefix list";
                 }
 
                 leaf-list prefixes_v6 {
                     type bgp-allowed-ipv6-prefix;
+                    ordered-by user;
                     description "BGP V6 allowed prefix list";
                 }
             }
@@ -223,11 +229,13 @@ module sonic-bgp-allowed-prefix {
 
                 leaf-list prefixes_v4 {
                     type bgp-allowed-ipv4-prefix;
+                    ordered-by user;
                     description "BGP V4 allowed prefix list";
                 }
 
                 leaf-list prefixes_v6 {
                     type bgp-allowed-ipv6-prefix;
+                    ordered-by user;
                     description "BGP V6 allowed prefix list";
                 }
             }
diff --git a/src/sonic-yang-models/yang-models/sonic-bgp-common.yang b/src/sonic-yang-models/yang-models/sonic-bgp-common.yang
index e035c1be261c..84c6a8ff7457 100644
--- a/src/sonic-yang-models/yang-models/sonic-bgp-common.yang
+++ b/src/sonic-yang-models/yang-models/sonic-bgp-common.yang
@@ -386,6 +386,7 @@ module sonic-bgp-common {
             type leafref {
                 path "/rmap:sonic-route-map/rmap:ROUTE_MAP_SET/rmap:ROUTE_MAP_SET_LIST/rmap:name";
             }
+            ordered-by user;
             description "Route-map filter for incoming routes";
             max-elements 1;
         }
@@ -394,6 +395,7 @@ module sonic-bgp-common {
             type leafref {
                 path "/rmap:sonic-route-map/rmap:ROUTE_MAP_SET/rmap:ROUTE_MAP_SET_LIST/rmap:name";
             }
+            ordered-by user;
             description "Route-map filter for outgoing routes";
             max-elements 1;
         }
diff --git a/src/sonic-yang-models/yang-models/sonic-bgp-global.yang b/src/sonic-yang-models/yang-models/sonic-bgp-global.yang
index d717803184fc..efd4d5515579 100644
--- a/src/sonic-yang-models/yang-models/sonic-bgp-global.yang
+++ b/src/sonic-yang-models/yang-models/sonic-bgp-global.yang
@@ -304,6 +304,7 @@ module sonic-bgp-global {
                     type uint32 {
                         range "1..4294967295";
                     }
+                    ordered-by user;
                     description
                         "Peer ASs in BGP confederation";
                 }
diff --git a/src/sonic-yang-models/yang-models/sonic-bgp-peerrange.yang b/src/sonic-yang-models/yang-models/sonic-bgp-peerrange.yang
index 01348096ccab..6e7a098a6c3b 100644
--- a/src/sonic-yang-models/yang-models/sonic-bgp-peerrange.yang
+++ b/src/sonic-yang-models/yang-models/sonic-bgp-peerrange.yang
@@ -56,6 +56,7 @@ module sonic-bgp-peerrange {
 
                 leaf-list ip_range {
                     type stypes:sonic-ip-prefix;
+                    ordered-by user;
                     description "A range of addresses";
                 }
             }
diff --git a/src/sonic-yang-models/yang-models/sonic-bgp-sentinel.yang b/src/sonic-yang-models/yang-models/sonic-bgp-sentinel.yang
index 747aadd2f89e..c9ce07f8fb9b 100644
--- a/src/sonic-yang-models/yang-models/sonic-bgp-sentinel.yang
+++ b/src/sonic-yang-models/yang-models/sonic-bgp-sentinel.yang
@@ -50,6 +50,7 @@ module sonic-bgp-sentinel {
 
                 leaf-list ip_range {
                     type stypes:sonic-ip-prefix;
+                    ordered-by user;
                     description "A range of addresses";
                 }
             }
diff --git a/src/sonic-yang-models/yang-models/sonic-buffer-port-egress-profile-list.yang b/src/sonic-yang-models/yang-models/sonic-buffer-port-egress-profile-list.yang
index 6c1a75fbfd30..45c1584d0e90 100644
--- a/src/sonic-yang-models/yang-models/sonic-buffer-port-egress-profile-list.yang
+++ b/src/sonic-yang-models/yang-models/sonic-buffer-port-egress-profile-list.yang
@@ -46,6 +46,7 @@ module sonic-buffer-port-egress-profile-list {
                     type leafref {
                         path "/bpf:sonic-buffer-profile/bpf:BUFFER_PROFILE/bpf:BUFFER_PROFILE_LIST/bpf:name";
                     }
+                    ordered-by user;
                     description "a list of references to BUFFER_PROFILE_TABLE object for a port";
                 }
 
diff --git a/src/sonic-yang-models/yang-models/sonic-buffer-port-ingress-profile-list.yang b/src/sonic-yang-models/yang-models/sonic-buffer-port-ingress-profile-list.yang
index 01a7f6bb6e21..0094ac73480c 100644
--- a/src/sonic-yang-models/yang-models/sonic-buffer-port-ingress-profile-list.yang
+++ b/src/sonic-yang-models/yang-models/sonic-buffer-port-ingress-profile-list.yang
@@ -46,6 +46,7 @@ module sonic-buffer-port-ingress-profile-list {
                     type leafref {
                         path "/bpf:sonic-buffer-profile/bpf:BUFFER_PROFILE/bpf:BUFFER_PROFILE_LIST/bpf:name";
                     }
+                    ordered-by user;
                     description "a list of references to BUFFER_PROFILE_TABLE object for a port";
                 }
 
diff --git a/src/sonic-yang-models/yang-models/sonic-dash.yang b/src/sonic-yang-models/yang-models/sonic-dash.yang
index 806a05e881f3..0c6574865f9b 100644
--- a/src/sonic-yang-models/yang-models/sonic-dash.yang
+++ b/src/sonic-yang-models/yang-models/sonic-dash.yang
@@ -64,6 +64,7 @@ module sonic-dash {
 
                 leaf-list address_spaces {
                      type stypes:sonic-ip-prefix;
+                     ordered-by user;
                 }
 
             } /* end of list DASH_VNET_LIST */
@@ -270,14 +271,17 @@ module sonic-dash {
                 leaf-list ip_protocol {
                     description "IP Protocol (tcp or udp or icmp etc)";
                     type stypes:ip-protocol-type;
+                    ordered-by user;
                 }
 
                 leaf-list src_addr {
                     type stypes:sonic-ip-prefix;
+                    ordered-by user;
                 }
 
                 leaf-list dst_addr {
                     type stypes:sonic-ip-prefix;
+                    ordered-by user;
                 }
 
                 leaf-list src_port {
@@ -285,6 +289,7 @@ module sonic-dash {
                      type string {
                          pattern '([0-9]{1,4}|[0-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-2][0-9]{2}|[6][5][3][0-5]{2}|[6][5][3][6][0-5])-([0-9]{1,4}|[0-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-2][0-9]{2}|[6][5][3][0-5]{2}|[6][5][3][6][0-5])';
                      }
+                     ordered-by user;
                 }
 
                 leaf-list dst_port {
@@ -292,6 +297,7 @@ module sonic-dash {
                      type string {
                          pattern '([0-9]{1,4}|[0-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-2][0-9]{2}|[6][5][3][0-5]{2}|[6][5][3][6][0-5])-([0-9]{1,4}|[0-5][0-9]{4}|[6][0-4][0-9]{3}|[6][5][0-2][0-9]{2}|[6][5][3][0-5]{2}|[6][5][3][6][0-5])';
                      }
+                     ordered-by user;
                 }
 
             } /* end of list DASH_ACL_RULE_LIST */
diff --git a/src/sonic-yang-models/yang-models/sonic-device_metadata.yang b/src/sonic-yang-models/yang-models/sonic-device_metadata.yang
index 7a423b54a6de..34a5e959d642 100644
--- a/src/sonic-yang-models/yang-models/sonic-device_metadata.yang
+++ b/src/sonic-yang-models/yang-models/sonic-device_metadata.yang
@@ -34,7 +34,7 @@ module sonic-device_metadata {
 
             description "DEVICE_METADATA part of config_db.json";
 
-            container localhost{
+            container localhost {
 
                 leaf hwsku {
                     type stypes:hwsku;
@@ -47,6 +47,20 @@ module sonic-device_metadata {
                     description "asic_id is unique identifier of the asic used by SAI for initialization.";
                 }
 
+                leaf hostname {
+                    type stypes:hostname;
+                }
+
+                leaf platform {
+                    type string {
+                        length 1..255;
+                    }
+                }
+
+                leaf mac {
+                    type yang:mac-address;
+                }
+
                 leaf default_bgp_status {
                     type enumeration {
                         enum up;
@@ -67,20 +81,6 @@ module sonic-device_metadata {
                     default "unified";
                 }
 
-                leaf hostname {
-                    type stypes:hostname;
-                }
-
-                leaf platform {
-                    type string {
-                        length 1..255;
-                    }
-                }
-
-                leaf mac {
-                    type yang:mac-address;
-                }
-
                 leaf default_pfcwd_status {
                     type enumeration {
                         enum disable;
@@ -257,6 +257,7 @@ module sonic-device_metadata {
 
                 leaf-list supporting_bulk_counter_groups {
                     type string;
+                    ordered-by user;
                     description "This field contains a list of counter groups that support bulk operation.";
                 }
 
diff --git a/src/sonic-yang-models/yang-models/sonic-dhcp-server-ipv4.yang b/src/sonic-yang-models/yang-models/sonic-dhcp-server-ipv4.yang
index d3aa89862686..fd1e4233deee 100644
--- a/src/sonic-yang-models/yang-models/sonic-dhcp-server-ipv4.yang
+++ b/src/sonic-yang-models/yang-models/sonic-dhcp-server-ipv4.yang
@@ -96,6 +96,7 @@ module sonic-dhcp-server-ipv4 {
                     type leafref {
                         path "/dhcp-server-ipv4:sonic-dhcp-server-ipv4/dhcp-server-ipv4:DHCP_SERVER_IPV4_CUSTOMIZED_OPTIONS/dhcp-server-ipv4:DHCP_SERVER_IPV4_CUSTOMIZED_OPTIONS_LIST/dhcp-server-ipv4:name";
                     }
+                    ordered-by user;
                 }
 
                 leaf state {
@@ -186,6 +187,7 @@ module sonic-dhcp-server-ipv4 {
                 leaf-list range {
                     description "Range of IPs";
                     type inet:ipv4-address;
+                    ordered-by user;
                 }
 
                 must "((count(range) <= 2) and (count(range) >= 1))";
@@ -232,6 +234,7 @@ module sonic-dhcp-server-ipv4 {
                         error-message "Statement of 'ips' and 'ranges' cannot both exist";
                     }
                     type inet:ipv4-address;
+                    ordered-by user;
                 }
 
                 leaf-list ranges {
@@ -242,6 +245,7 @@ module sonic-dhcp-server-ipv4 {
                     type leafref {
                         path "/dhcp-server-ipv4:sonic-dhcp-server-ipv4/dhcp-server-ipv4:DHCP_SERVER_IPV4_RANGE/dhcp-server-ipv4:DHCP_SERVER_IPV4_RANGE_LIST/dhcp-server-ipv4:name";
                     }
+                    ordered-by user;
                 }
             }
             /* end of DHCP_SERVER_IPV4_PORT_LIST */
diff --git a/src/sonic-yang-models/yang-models/sonic-dhcpv6-relay.yang b/src/sonic-yang-models/yang-models/sonic-dhcpv6-relay.yang
index cd32ca96662f..eae5ec5acdc1 100644
--- a/src/sonic-yang-models/yang-models/sonic-dhcpv6-relay.yang
+++ b/src/sonic-yang-models/yang-models/sonic-dhcpv6-relay.yang
@@ -37,6 +37,7 @@ module sonic-dhcpv6-relay {
 				leaf-list dhcpv6_servers {
 					description "Configure the dhcp v6 servers";
 					type inet:ipv6-address;
+					ordered-by user;
 				}
 
 				leaf rfc6939_support {
diff --git a/src/sonic-yang-models/yang-models/sonic-hash.yang b/src/sonic-yang-models/yang-models/sonic-hash.yang
index d95f82406927..45f0f9b59167 100644
--- a/src/sonic-yang-models/yang-models/sonic-hash.yang
+++ b/src/sonic-yang-models/yang-models/sonic-hash.yang
@@ -54,11 +54,13 @@ module sonic-hash {
                 leaf-list ecmp_hash {
                     description "Hash fields for hashing packets going through ECMP";
                     type hash:hash-field;
+                    ordered-by user;
                 }
 
                 leaf-list lag_hash  {
                     description "Hash fields for hashing packets going through LAG";
                     type hash:hash-field;
+                    ordered-by user;
                 }
 
                 leaf ecmp_hash_algorithm {
diff --git a/src/sonic-yang-models/yang-models/sonic-mgmt_interface.yang b/src/sonic-yang-models/yang-models/sonic-mgmt_interface.yang
index d45c40327031..aec3f85b2110 100644
--- a/src/sonic-yang-models/yang-models/sonic-mgmt_interface.yang
+++ b/src/sonic-yang-models/yang-models/sonic-mgmt_interface.yang
@@ -55,7 +55,7 @@ module sonic-mgmt_interface {
                         type stypes:sonic-ip-prefix;
                         type inet:ip-address;
                     }
-
+                    ordered-by user;
                     description
                         "This configuration allows addtional routes to be added to default VRF table
                          or mgmt VRF table, based on if Management VRF is configured.
diff --git a/src/sonic-yang-models/yang-models/sonic-ntp.yang b/src/sonic-yang-models/yang-models/sonic-ntp.yang
index 2591f8c7a58b..dce4cd4675b9 100644
--- a/src/sonic-yang-models/yang-models/sonic-ntp.yang
+++ b/src/sonic-yang-models/yang-models/sonic-ntp.yang
@@ -109,7 +109,7 @@ module sonic-ntp {
                             pattern 'eth0';
                         }
                     }
-
+                    ordered-by user;
                     description
                         "This is the interface whose IP address is used as the source IP address for
                          generating NTP traffic. User is required to make sure that the NTP server
diff --git a/src/sonic-yang-models/yang-models/sonic-pbh.yang b/src/sonic-yang-models/yang-models/sonic-pbh.yang
index d0c395d4e6a3..af754972f476 100644
--- a/src/sonic-yang-models/yang-models/sonic-pbh.yang
+++ b/src/sonic-yang-models/yang-models/sonic-pbh.yang
@@ -120,6 +120,7 @@ module sonic-pbh {
 					type leafref {
 						path "/pbh:sonic-pbh/pbh:PBH_HASH_FIELD/pbh:PBH_HASH_FIELD_LIST/pbh:hash_field_name";
 					}
+					ordered-by user;
 				}
 
 			}
@@ -248,6 +249,7 @@ module sonic-pbh {
 							path "/lag:sonic-portchannel/lag:PORTCHANNEL/lag:PORTCHANNEL_LIST/lag:name";
 						}
 					}
+					ordered-by user;
 				}
 
 				leaf description {
diff --git a/src/sonic-yang-models/yang-models/sonic-port.yang b/src/sonic-yang-models/yang-models/sonic-port.yang
index 528f661b8fcf..d166c638918e 100644
--- a/src/sonic-yang-models/yang-models/sonic-port.yang
+++ b/src/sonic-yang-models/yang-models/sonic-port.yang
@@ -136,6 +136,7 @@ module sonic-port{
 							pattern "all";
 						}
 					}
+					ordered-by user;
 				}
 
 				must "(count(adv_speeds[text()='all']) = 0) or (count(adv_speeds) = 1)";
@@ -155,6 +156,7 @@ module sonic-port{
 							pattern "all";
 						}
 					}
+					ordered-by user;
 				}
 
 				must "(count(adv_interface_types[text()='all']) = 0) or (count(adv_interface_types) = 1)";
diff --git a/src/sonic-yang-models/yang-models/sonic-route-common.yang b/src/sonic-yang-models/yang-models/sonic-route-common.yang
index df0eae8f4ea9..5f16138611de 100644
--- a/src/sonic-yang-models/yang-models/sonic-route-common.yang
+++ b/src/sonic-yang-models/yang-models/sonic-route-common.yang
@@ -61,6 +61,7 @@ module sonic-route-common {
                     type leafref {
                         path "/rmap:sonic-route-map/rmap:ROUTE_MAP_SET/rmap:ROUTE_MAP_SET_LIST/rmap:name";
                     }
+                    ordered-by user;
                     max-elements 1;
                     description "Router filter to apply while redistributing the routes from another protocol.";
                 }
diff --git a/src/sonic-yang-models/yang-models/sonic-route-map.yang b/src/sonic-yang-models/yang-models/sonic-route-map.yang
index 3a5d1f256fd3..fd88227a3bb7 100644
--- a/src/sonic-yang-models/yang-models/sonic-route-map.yang
+++ b/src/sonic-yang-models/yang-models/sonic-route-map.yang
@@ -210,6 +210,7 @@ module sonic-route-map {
                             pattern 'Vlan([0-9]{1,3}|[1-3][0-9]{3}|[4][0][0-8][0-9]|[4][0][9][0-4])';
                         }
                     }
+                    ordered-by user;
                     description
                         "IP addresse or interface for match operation.";
                     max-elements 1;
@@ -217,6 +218,7 @@ module sonic-route-map {
 
                 leaf-list match_tag {
                     type uint32;
+                    ordered-by user;
                     description
                         "Value of the tag match member";
                     max-elements 1;
@@ -326,6 +328,7 @@ module sonic-route-map {
 
                 leaf-list set_community_inline {
                     type string;
+                    ordered-by user;
                     description "Set community string";
                 }
 
@@ -338,6 +341,7 @@ module sonic-route-map {
 
                 leaf-list set_ext_community_inline {
                     type string;
+                    ordered-by user;
                     description "Set extended community string";
                 }
 
diff --git a/src/sonic-yang-models/yang-models/sonic-routing-policy-sets.yang b/src/sonic-yang-models/yang-models/sonic-routing-policy-sets.yang
index cf469730f680..362211048034 100644
--- a/src/sonic-yang-models/yang-models/sonic-routing-policy-sets.yang
+++ b/src/sonic-yang-models/yang-models/sonic-routing-policy-sets.yang
@@ -150,6 +150,7 @@ module sonic-routing-policy-sets {
 
                 leaf-list community_member {
                     type string;
+                    ordered-by user;
                     description
                         "members of the community set.";
                 }
@@ -190,6 +191,7 @@ module sonic-routing-policy-sets {
 
                 leaf-list community_member {
                     type string;
+                    ordered-by user;
                     description
                         "members of the community set.";
                 }
@@ -214,6 +216,7 @@ module sonic-routing-policy-sets {
 
                 leaf-list as_path_set_member {
                     type string;
+                    ordered-by user;
                     description
                         "AS path expression -- list of ASes in the set";
                 }
diff --git a/src/sonic-yang-models/yang-models/sonic-suppress-asic-sdk-health-event.yang b/src/sonic-yang-models/yang-models/sonic-suppress-asic-sdk-health-event.yang
index 650911cc8261..fe6cb43c16e1 100644
--- a/src/sonic-yang-models/yang-models/sonic-suppress-asic-sdk-health-event.yang
+++ b/src/sonic-yang-models/yang-models/sonic-suppress-asic-sdk-health-event.yang
@@ -51,6 +51,7 @@ module sonic-suppress-asic-sdk-health-event {
                         enum cpu_hw;
                         enum asic_hw;
                     }
+                    ordered-by user;
                     description "Category of the ASIC/SDK health event to suppress";
                 }
             }
diff --git a/src/sonic-yang-models/yang-models/sonic-vlan.yang b/src/sonic-yang-models/yang-models/sonic-vlan.yang
index 5d6f898c768c..92ea9085e895 100644
--- a/src/sonic-yang-models/yang-models/sonic-vlan.yang
+++ b/src/sonic-yang-models/yang-models/sonic-vlan.yang
@@ -218,11 +218,13 @@ module sonic-vlan {
 				leaf-list dhcp_servers {
 					description "Configure the dhcp v4 servers";
 					type inet:ip-address;
+					ordered-by user;
 				}
 
 				leaf-list dhcpv6_servers {
 					description "Configure the dhcp v6 servers";
 					type inet:ipv6-address;
+					ordered-by user;
 				}
 
 				leaf mtu {
diff --git a/src/sonic-yang-models/yang-templates/sonic-acl.yang.j2 b/src/sonic-yang-models/yang-templates/sonic-acl.yang.j2
index f1a0629f3222..621b9ff2ca6f 100644
--- a/src/sonic-yang-models/yang-templates/sonic-acl.yang.j2
+++ b/src/sonic-yang-models/yang-templates/sonic-acl.yang.j2
@@ -302,11 +302,13 @@ module sonic-acl {
 
 				leaf-list MATCHES {
 					type string;
+					ordered-by user;
 					min-elements 1;
 				}
 
 				leaf-list ACTIONS {
 					type string;
+					ordered-by user;
 					default "";
 				}
 
@@ -315,6 +317,7 @@ module sonic-acl {
 						enum PORT;
 						enum PORTCHANNEL;
 					}
+					ordered-by user;
 					min-elements 1;
 				}
 			}
@@ -357,6 +360,7 @@ module sonic-acl {
 
 				leaf-list services {
 					type string;
+					ordered-by user;
 				}
 
 				/* Validating 'services' exist if ACL type is 'CTRLPLANE' */
@@ -375,6 +379,7 @@ module sonic-acl {
 							pattern "";
 						}
 					}
+					ordered-by user;
 					/* Today in SONiC, we do not delete the list once
 					 * created, instead we set to empty list. Due to that
 					 * below default values are needed.
diff --git a/src/sonic-yang-models/yang-templates/sonic-policer.yang.j2 b/src/sonic-yang-models/yang-templates/sonic-policer.yang.j2
index 07b40ea742c1..0b65d2c1f4cd 100644
--- a/src/sonic-yang-models/yang-templates/sonic-policer.yang.j2
+++ b/src/sonic-yang-models/yang-templates/sonic-policer.yang.j2
@@ -66,7 +66,6 @@ module sonic-policer {
 						error-message "cbs must be greater than or equal to cir";
 					}
 					type uint64;
-					default 0;
 					description
 						"Committed burst size for the dual-rate token bucket
 						policer.  This value represents the depth of the token

From e26017c3169b92d5a5b7ada8e42b13f2d50e77b2 Mon Sep 17 00:00:00 2001
From: Brad House <brad@brad-house.com>
Date: Tue, 11 Feb 2025 08:26:28 -0500
Subject: [PATCH 3/7] IGNORE THIS COMMIT: merge libyang3 step 3 (PR
 sonic-net#21716)

---
 rules/sonic-yang-mgmt-py3.mk                  |   6 +-
 src/sonic-yang-mgmt/sonic_yang.py             | 363 ++++++++----------
 src/sonic-yang-mgmt/sonic_yang_ext.py         |  17 +-
 .../libyang-python-tests/config_data.json     |   4 +-
 .../test-yang-structure.yang                  |   2 +-
 .../libyang-python-tests/test_SonicYang.json  | 102 +++--
 .../libyang-python-tests/test_sonic_yang.py   |  20 +-
 7 files changed, 260 insertions(+), 254 deletions(-)

diff --git a/rules/sonic-yang-mgmt-py3.mk b/rules/sonic-yang-mgmt-py3.mk
index 877fc6de3952..9d1760c05b26 100644
--- a/rules/sonic-yang-mgmt-py3.mk
+++ b/rules/sonic-yang-mgmt-py3.mk
@@ -3,9 +3,9 @@
 SONIC_YANG_MGMT_PY3 = sonic_yang_mgmt-1.0-py3-none-any.whl
 $(SONIC_YANG_MGMT_PY3)_SRC_PATH = $(SRC_PATH)/sonic-yang-mgmt
 $(SONIC_YANG_MGMT_PY3)_PYTHON_VERSION = 3
-$(SONIC_YANG_MGMT_PY3)_DEBS_DEPENDS = $(LIBYANG) $(LIBYANG_CPP) $(LIBYANG_PY3)
+$(SONIC_YANG_MGMT_PY3)_DEBS_DEPENDS = $(LIBYANG3) $(LIBYANG3_PY3)
 $(SONIC_YANG_MGMT_PY3)_DEPENDS =  $(SONIC_YANG_MODELS_PY3)
-$(SONIC_YANG_MGMT_PY3)_RDEPENDS = $(SONIC_YANG_MODELS_PY3) $(LIBYANG) \
-                                 $(LIBYANG_CPP) $(LIBYANG_PY3)
+$(SONIC_YANG_MGMT_PY3)_RDEPENDS = $(SONIC_YANG_MODELS_PY3) $(LIBYANG3) \
+                                  $(LIBYANG3_PY3)
 
 SONIC_PYTHON_WHEELS += $(SONIC_YANG_MGMT_PY3)
diff --git a/src/sonic-yang-mgmt/sonic_yang.py b/src/sonic-yang-mgmt/sonic_yang.py
index 9af9217ad34b..a354ee7fad2c 100644
--- a/src/sonic-yang-mgmt/sonic_yang.py
+++ b/src/sonic-yang-mgmt/sonic_yang.py
@@ -1,4 +1,4 @@
-import yang as ly
+import libyang as ly
 import syslog
 
 from json import dump
@@ -12,7 +12,7 @@
 """
 class SonicYang(SonicYangExtMixin):
 
-    def __init__(self, yang_dir, debug=False, print_log_enabled=True, sonic_yang_options=0):
+    def __init__(self, yang_dir, debug=False, print_log_enabled=True):
         self.yang_dir = yang_dir
         self.ctx = None
         self.module = None
@@ -22,10 +22,6 @@ def __init__(self, yang_dir, debug=False, print_log_enabled=True, sonic_yang_opt
         self.SYSLOG_IDENTIFIER = "sonic_yang"
         self.DEBUG = debug
         self.print_log_enabled = print_log_enabled
-        if not print_log_enabled:
-            # The default libyang log options are ly.LY_LOLOG|ly.LY_LOSTORE_LAST.
-            # Removing ly.LY_LOLOG will stop libyang from printing the logs.
-            ly.set_log_options(ly.LY_LOSTORE_LAST)
 
         # yang model files, need this map it to module
         self.yangFiles = list()
@@ -48,19 +44,26 @@ def __init__(self, yang_dir, debug=False, print_log_enabled=True, sonic_yang_opt
         # ['PORT', 'Ethernet0', 'speed']
         self.elementPath = []
         try:
-            self.ctx = ly.Context(yang_dir, sonic_yang_options)
+            self.ctx = ly.Context(yang_dir)
         except Exception as e:
             self.fail(e)
 
         return
 
     def __del__(self):
-        pass
+        if self.root:
+            self.root.free()
+            self.root = None
+        if self.ctx:
+            self.ctx.destroy()
+            self.ctx = None
 
     def sysLog(self, debug=syslog.LOG_INFO, msg=None, doPrint=False):
         # log debug only if enabled
         if self.DEBUG == False and debug == syslog.LOG_DEBUG:
             return
+        if msg is None:
+            return
         if doPrint and self.print_log_enabled:
             print("{}({}):{}".format(self.SYSLOG_IDENTIFIER, debug, msg))
         syslog.openlog(self.SYSLOG_IDENTIFIER)
@@ -80,7 +83,8 @@ def fail(self, e):
     """
     def _load_schema_module(self, yang_file):
         try:
-            return self.ctx.parse_module_path(yang_file, ly.LYS_IN_YANG)
+            with open(yang_file, 'r') as f:
+                return self.ctx.parse_module_file(f, "yang")
         except Exception as e:
             self.sysLog(msg="Failed to load yang module file: " + yang_file, debug=syslog.LOG_ERR, doPrint=True)
             self.fail(e)
@@ -110,27 +114,6 @@ def _load_schema_modules(self, yang_dir):
             except Exception as e:
                 self.fail(e)
 
-    """
-    load_schema_modules_ctx(): load all Yang model files in the directory to context: ctx
-    input:    yang_dir,  context
-    returns:  Exception if error, returrns context object if no error
-    """
-    def _load_schema_modules_ctx(self, yang_dir=None):
-        if not yang_dir:
-            yang_dir = self.yang_dir
-
-        ctx = ly.Context(yang_dir)
-
-        py = glob(yang_dir+"/*.yang")
-        for file in py:
-            try:
-                ctx.parse_module_path(str(file), ly.LYS_IN_YANG)
-            except Exception as e:
-                self.sysLog(msg="Failed to parse yang module file: " + file, debug=syslog.LOG_ERR, doPrint=True)
-                self.fail(e)
-
-        return ctx
-
     """
     load_data_file(): load a Yang data json file
     input:    data_file - the full path of the yang json data file to be loaded
@@ -138,7 +121,8 @@ def _load_schema_modules_ctx(self, yang_dir=None):
     """
     def _load_data_file(self, data_file):
        try:
-           data_node = self.ctx.parse_data_path(data_file, ly.LYD_JSON, ly.LYD_OPT_CONFIG | ly.LYD_OPT_STRICT)
+           with open(data_file, 'r') as f:
+               data_node = self.ctx.parse_data_file(f, "json", no_state=True, strict=True, json_string_datatypes=True)
        except Exception as e:
            self.sysLog(msg="Failed to load data file: " + str(data_file), debug=syslog.LOG_ERR, doPrint=True)
            self.fail(e)
@@ -169,8 +153,8 @@ def _get_module(self, module_name):
     returns: returns (context, root) if no error,  or Exception if failed
     """
     def _load_data_model(self, yang_dir, yang_files, data_files, output=None):
-        if (self.ctx is None):
-            self.ctx = ly.Context(yang_dir)
+        if not self.ctx:
+            raise Exception('ctx not initialized')
 
         try:
             self._load_schema_module_list(yang_files)
@@ -197,9 +181,9 @@ def _load_data_model(self, yang_dir, yang_files, data_files, output=None):
     """
     def _print_data_mem(self, option):
         if (option == "JSON"):
-            mem = self.root.print_mem(ly.LYD_JSON, ly.LYP_WITHSIBLINGS | ly.LYP_FORMAT)
+            mem = self.root.print_mem("json", with_siblings=True, pretty=True)
         else:
-            mem = self.root.print_mem(ly.LYD_XML, ly.LYP_WITHSIBLINGS | ly.LYP_FORMAT)
+            mem = self.root.print_mem("yang", with_siblings=True, pretty=True)
 
         return mem
 
@@ -208,7 +192,7 @@ def _print_data_mem(self, option):
     input: outfile - full path of the file to save the data tree to
     """
     def _save_data_file_json(self, outfile):
-        mem = self.root.print_mem(ly.LYD_JSON, ly.LYP_FORMAT)
+        mem = self.root.print_mem("json", pretty=True)
         with open(outfile, 'w') as out:
             dump(mem, out, indent=4)
 
@@ -228,10 +212,9 @@ def _get_module_tree(self, module_name, format):
         else:
             if (module is not None):
                 if (format == "XML"):
-                    #libyang bug with format
-                    result = module.print_mem(ly.LYD_JSON, ly.LYP_FORMAT)
+                    result = module.print_mem("yin")
                 else:
-                    result = module.print_mem(ly.LYD_XML, ly.LYP_FORMAT)
+                    result = module.print_mem("json")
 
         return result
 
@@ -250,7 +233,7 @@ def _validate_data(self, node=None, ctx=None):
             ctx = self.ctx
 
         try:
-            node.validate(ly.LYD_OPT_CONFIG, ctx)
+            node.validate(no_state=True)
         except Exception as e:
             self.fail(e)
 
@@ -314,7 +297,7 @@ def _get_parent_data_xpath(self, data_xpath):
     def _new_data_node(self, xpath, value):
         val = str(value)
         try:
-            data_node = self.root.new_path(self.ctx, xpath, val, 0, 0)
+            data_node = self.ctx.create_data_path(xpath, parent=self.root, value=val, update=False, force_return_value=False)
         except Exception as e:
             self.sysLog(msg="Failed to add data node for path: " + str(xpath), debug=syslog.LOG_ERR, doPrint=True)
             self.fail(e)
@@ -330,13 +313,13 @@ def _new_data_node(self, xpath, value):
     """
     def _find_data_node(self, data_xpath):
         try:
-            set = self.root.find_path(data_xpath)
+            set = self.root.find_all(data_xpath)
         except Exception as e:
             self.sysLog(msg="Failed to find data node from xpath: " + str(data_xpath), debug=syslog.LOG_ERR, doPrint=True)
             self.fail(e)
         else:
             if set is not None:
-                for data_node in set.data():
+                for data_node in set:
                     if (data_xpath == data_node.path()):
                         return data_node
             return None
@@ -350,17 +333,13 @@ def _find_data_node(self, data_xpath):
     def _find_schema_node(self, schema_xpath):
         try:
             schema_set = self.ctx.find_path(schema_xpath)
-            for schema_node in schema_set.schema():
-                if (schema_xpath == schema_node.path()):
+            for schema_node in schema_set:
+                if (schema_xpath == schema_node.schema_path()):
                     return schema_node
         except Exception as e:
              self.fail(e)
              return None
-        else:
-             for schema_node in schema_set.schema():
-                 if schema_xapth == schema_node.path():
-                     return schema_node
-             return None
+        return None
     """
     find_data_node_schema_xpath(): find the xpath of the schema node from data xpath
       data xpath example:
@@ -372,14 +351,14 @@ def _find_schema_node(self, schema_xpath):
     def _find_data_node_schema_xpath(self, data_xpath):
         path = ""
         try:
-            set = self.root.find_path(data_xpath)
+            data_node = self._find_data_node(data_xpath)
+            if data_node != None:
+                path = data_node.schema().schema_path()
+
+            return path
         except Exception as e:
             self.fail(e)
-        else:
-            for data_node in set.data():
-                if data_xpath == data_node.path():
-                    return data_node.schema().path()
-            return path
+            return None
 
     """
     add_node(): add a node to Yang schema or data tree
@@ -397,22 +376,22 @@ def _add_data_node(self, data_xpath, value):
 
     """
     merge_data(): merge a data file to the existing data tree
-    input:    yang model directory and full path of the data json file to be merged
+    input:    full path of the data json file to be merged into the existing root
     returns:  Exception if failed
     """
-    def _merge_data(self, data_file, yang_dir=None):
-        #load all yang models to ctx
-        if not yang_dir:
-            yang_dir = self.yang_dir
+    def _merge_data(self, data_file):
+        if not self.ctx:
+            raise Exception('ctx not initialized')
 
-        try:
-            ctx = self._load_schema_modules_ctx(yang_dir)
+        if not self.root:
+            raise Exception('no root initialized')
 
+        try:
             #source data node
-            source_node = ctx.parse_data_path(str(data_file), ly.LYD_JSON, ly.LYD_OPT_CONFIG | ly.LYD_OPT_STRICT)
-
-            #merge
-            self.root.merge(source_node, 0)
+            with open(str(data_file), 'r') as f:
+                source_node = self.ctx.parse_data_file(f, "json", no_state=True, strict=True, json_string_datatypes=True)
+                #merge
+                self.root.merge(source_node, destruct=True, with_siblings=True)
         except Exception as e:
             self.fail(e)
 
@@ -421,22 +400,13 @@ def _merge_data(self, data_file, yang_dir=None):
     input:    xpath of the schema/data node
     returns:  True - success   False - failed
     """
-    def _deleteNode(self, xpath=None, node=None):
-        if node is None:
-            node = self._find_data_node(xpath)
-
-        if (node):
-            node.unlink()
-            dnode = self._find_data_node(xpath)
-            if (dnode is None):
-                #deleted node not found
-                return True
-            else:
-                self.sysLog(msg='Could not delete Node', debug=syslog.LOG_ERR, doPrint=True)
-                return False
-        else:
-            self.sysLog(msg="failed to find node, xpath: " + xpath, debug=syslog.LOG_ERR, doPrint=True)
+    def _deleteNode(self, xpath):
+        dnode = self._find_data_node(xpath)
+        if (dnode):
+            dnode.unlink()
+            return True
 
+        self.sysLog(msg='Could not delete Node: {}'.format(xpath), debug=syslog.LOG_ERR, doPrint=True)
         return False
 
     """
@@ -453,9 +423,9 @@ def _find_data_node_value(self, data_xpath):
             self.fail(e)
         else:
             if (data_node is not None):
-                subtype = data_node.subtype()
+                subtype = data_node.schema().type()
                 if (subtype is not None):
-                    value = subtype.value_str()
+                    value = data_node.value()
                     return value
             return output
 
@@ -466,7 +436,7 @@ def _find_data_node_value(self, data_xpath):
     """
     def _set_data_node_value(self, data_xpath, value):
         try:
-            self.root.new_path(self.ctx, data_xpath, str(value), ly.LYD_ANYDATA_STRING, ly.LYD_PATH_OPT_UPDATE)
+            self.ctx.create_data_path(data_xpath, parent=self.root, value=str(value), update=True, force_return_value=False)
         except Exception as e:
             self.sysLog(msg="set data node value failed for xpath: " + str(data_xpath), debug=syslog.LOG_ERR, doPrint=True)
             self.fail(e)
@@ -478,75 +448,114 @@ def _set_data_node_value(self, data_xpath, value):
     """
     def _find_data_nodes(self, data_xpath):
         list = []
-        node = self.root.child()
+        node = next(self.root.children())
         try:
-            node_set = node.find_path(data_xpath);
+            node_set = node.find_all(data_xpath);
         except Exception as e:
             self.fail(e)
         else:
             if node_set is None:
                 raise Exception('data node not found')
 
-            for data_set in node_set.data():
-                data_set.schema()
+            for data_set in node_set:
                 list.append(data_set.path())
             return list
 
+
     """
     find_schema_dependencies():  find the schema dependencies from schema xpath
-    input:    schema_xpath of the schema node
+    input:    match_path         target node path to use as a filter
+              match_ancestors    whether or not to treat the specified path as
+                                 an ancestor rather than a full path.
     returns:  - list of xpath of the dependencies
-              - Exception if schema node not found
     """
-    def _find_schema_dependencies(self, schema_xpath):
-        ref_list = []
+    def _find_schema_dependencies(self, match_path, match_ancestors: bool=False):
+        return self.ctx.find_backlinks_paths(match_path, match_ancestors=match_ancestors)
+
+    """
+    load_module_str_name(): load a module based on the provided string and return
+                            the loaded module name.  This is needed by
+                            sonic-yang-modules to prevent direct dependency on
+                            libyang.
+    input: yang_module_str yang-formatted module
+    returns: module name on success, exception on failure
+    """
+    def load_module_str_name(yang_module_str):
         try:
-            schema_node = self._find_schema_node(schema_xpath)
+            module = self.ctx.parse_module_str(yang_module_str)
         except Exception as e:
-            self.sysLog(msg="Cound not find the schema node from xpath: " + str(schema_xpath), debug=syslog.LOG_ERR, doPrint=True)
-            self.fail(e)
-            return ref_list
-
-        schema_node = ly.Schema_Node_Leaf(schema_node)
-        backlinks = schema_node.backlinks()
-        if backlinks.number() > 0:
-            for link in backlinks.schema():
-                self.sysLog(msg="backlink schema: {}".format(link.path()), doPrint=True)
-                ref_list.append(link.path())
-        return ref_list
+            self.fail(e);
+        else:
+            return module.name()
+
+        return None
 
     """
-    find_data_dependencies():   find the data dependencies from data xpath
-    input:    data_xpath - xpath of data node. (Public)
+    find_data_dependencies(): find the data dependencies from data xpath  (Public)
+    input:    data_xpath - xpath to search.  If it references an exact data node
+                           only the references to that data node will be returned.
+                           If a path contains multiple data nodes, then all references
+                           to all child nodes will be returned.  If set to None (or "" or "/"),
+                           will return all references, globally.
     returns:  - list of xpath
               - Exception if error
     """
     def find_data_dependencies(self, data_xpath):
         ref_list = []
+        required_value = None
+        base_dnode = None
+        nodes = []
         node = self.root
-        try:
-            data_node = self._find_data_node(data_xpath)
-        except Exception as e:
-            self.sysLog(msg="find_data_dependencies(): Failed to find data node from xpath: {}".format(data_xapth), debug=syslog.LOG_ERR, doPrint=True)
-            return ref_list
 
+        if data_xpath is not None and (len(data_xpath) == 0 or data_xpath == "/"):
+            data_xpath = None
+
+        if data_xpath is not None:
+            try:
+                dnode_list = list(self.root.find_all(data_xpath))
+            except Exception as e:
+                self.sysLog(msg="find_data_dependencies(): Failed to find data node from xpath: {}".format(data_xapth), debug=syslog.LOG_ERR, doPrint=True)
+                return ref_list
+
+            if len(dnode_list) == 0:
+                raise Exception("no data nodes found for xpath specified")
+
+            # If exactly 1 node and its a data node, we need to match the value.
+            if len(dnode_list) == 1:
+                base_dnode = dnode_list[0]
+                try:
+                    required_value = self._find_data_node_value(data_xpath)
+                except Exception as e:
+                    pass
+
+        # Get a list of all schema leafrefs pointing to this node (or these data nodes).
+        lreflist = []
         try:
-            value = str(self._find_data_node_value(data_xpath))
-
-            schema_node = ly.Schema_Node_Leaf(data_node.schema())
-            backlinks = schema_node.backlinks()
-            if backlinks is not None and backlinks.number() > 0:
-                for link in backlinks.schema():
-                     node_set = node.find_path(link.path())
-                     for data_set in node_set.data():
-                          data_set.schema()
-                          casted = data_set.subtype()
-                          if value == casted.value_str():
-                              ref_list.append(data_set.path())
+            search_xpath = data_xpath
+            match_ancestors = True
+            if required_value is not None:
+                search_xpath = base_dnode.schema().schema_path()
+                match_ancestors = False
+
+            lreflist = self._find_schema_dependencies(match_path=search_xpath, match_ancestors=match_ancestors)
+            if lreflist is None:
+                raise Exception("no schema backlinks found")
         except Exception as e:
             self.sysLog(msg='Failed to find node or dependencies for {}'.format(data_xpath), debug=syslog.LOG_ERR, doPrint=True)
-            raise SonicYangException("Failed to find node or dependencies for \
-                {}\n{}".format(data_xpath, str(e)))
+            lreflist = []
+            # Exception not expected by existing tests if backlinks not found, so don't raise.
+            # raise SonicYangException("Failed to find node or dependencies for {}\n{}".format(data_xpath, str(e)))
+
+        # For all found data nodes, emit the path to the data node.  If we need to
+        # restrict to a value, do so.
+        for lref in lreflist:
+            try:
+                data_set = self.root.find_all(lref)
+                for dnode in data_set:
+                    if required_value is None or dnode.value() == required_value:
+                        ref_list.append(dnode.path())
+            except Exception as e:
+                pass
 
         return ref_list
 
@@ -565,53 +574,13 @@ def _get_module_prefix(self, module_name):
         else:
             return module.prefix()
 
-    """
-    str_to_type:   map string to type of node
-    input:    string
-    output:   type
-    """
-    def _str_to_type(self, type_str):
-           mapped_type = {
-                "LY_TYPE_DER":ly.LY_TYPE_DER,
-                "LY_TYPE_BINARY":ly.LY_TYPE_BINARY,
-                "LY_TYPE_BITS":ly.LY_TYPE_BITS,
-                "LY_TYPE_BOOL":ly.LY_TYPE_BOOL,
-                "LY_TYPE_DEC64":ly.LY_TYPE_DEC64,
-                "LY_TYPE_EMPTY":ly.LY_TYPE_EMPTY,
-                "LY_TYPE_ENUM":ly.LY_TYPE_ENUM,
-                "LY_TYPE_IDENT":ly.LY_TYPE_IDENT,
-                "LY_TYPE_INST":ly.LY_TYPE_INST,
-                "LY_TYPE_LEAFREF":ly.LY_TYPE_LEAFREF,
-                "LY_TYPE_STRING":ly.LY_TYPE_STRING,
-                "LY_TYPE_UNION":ly.LY_TYPE_UNION,
-                "LY_TYPE_INT8":ly.LY_TYPE_INT8,
-                "LY_TYPE_UINT8":ly.LY_TYPE_UINT8,
-                "LY_TYPE_INT16":ly.LY_TYPE_INT16,
-                "LY_TYPE_UINT16":ly.LY_TYPE_UINT16,
-                "LY_TYPE_INT32":ly.LY_TYPE_INT32,
-                "LY_TYPE_UINT32":ly.LY_TYPE_UINT32,
-                "LY_TYPE_INT64":ly.LY_TYPE_INT64,
-                "LY_TYPE_UINT64":ly.LY_TYPE_UINT64,
-                "LY_TYPE_UNKNOWN":ly.LY_TYPE_UNKNOWN
-           }
-
-           if type_str not in mapped_type:
-               return ly.LY_TYPE_UNKNOWN
-
-           return mapped_type[type_str]
-
     def _get_data_type(self, schema_xpath):
-        try:
-            schema_node = self._find_schema_node(schema_xpath)
-        except Exception as e:
-            self.sysLog(msg="get_data_type(): Failed to find schema node from xpath: {}".format(schema_xpath), debug=syslog.LOG_ERR, doPrint=True)
-            self.fail(e)
-            return None
+        schema_node = self._find_schema_node(schema_xpath)
 
-        if (schema_node is not None):
-           return schema_node.subtype().type().base()
+        if (schema_node is None):
+            return None
 
-        return ly.LY_TYPE_UNKNOWN
+        return schema_node.type().basename()
 
     """
     get_leafref_type:   find the type of node that leafref references to
@@ -620,16 +589,15 @@ def _get_data_type(self, schema_xpath):
     """
     def _get_leafref_type(self, data_xpath):
         data_node = self._find_data_node(data_xpath)
-        if (data_node is not None):
-            subtype = data_node.subtype()
-            if (subtype is not None):
-                if data_node.schema().subtype().type().base() != ly.LY_TYPE_LEAFREF:
+        if data_node is not None:
+            if data_node.schema() is not None:
+                if data_node.schema().type().base() != ly.Type.LEAFREF:
                     self.sysLog(msg="get_leafref_type() node type for data xpath: {} is not LEAFREF".format(data_xpath), debug=syslog.LOG_ERR, doPrint=True)
-                    return ly.LY_TYPE_UNKNOWN
+                    return None
                 else:
-                    return subtype.value_type()
+                    return data_node.schema().type().leafref_type().basename()
 
-        return ly.LY_TYPE_UNKNOWN
+        return None
 
     """
     get_leafref_path():   find the leafref path
@@ -637,16 +605,16 @@ def _get_leafref_type(self, data_xpath):
     output:   path value of the leafref node
     """
     def _get_leafref_path(self, schema_xpath):
-        schema_node = self._find_schema_node(schema_xpath)
-        if (schema_node is not None):
-            subtype = schema_node.subtype()
-            if (subtype is not None):
-                if subtype.type().base() != ly.LY_TYPE_LEAFREF:
-                    return None
-                else:
-                    return subtype.type().info().lref().path()
+        try:
+            schemas = self.ctx.find_path(schema_xpath)
 
-        return None
+            for schema_node in schemas:
+                if schema_node.type().base() == ly.Type.LEAFREF:
+                    leafref_path = schema_node.type().leafref_path()
+                    return leafref_path
+        except Exception as e:
+             self.fail(e)
+             return None
 
     """
     get_leafref_type_schema:   find the type of node that leafref references to
@@ -655,16 +623,15 @@ def _get_leafref_path(self, schema_xpath):
     """
     def _get_leafref_type_schema(self, schema_xpath):
         schema_node = self._find_schema_node(schema_xpath)
-        if (schema_node is not None):
-            subtype = schema_node.subtype()
-            if (subtype is not None):
-                if subtype.type().base() != ly.LY_TYPE_LEAFREF:
-                    return None
-                else:
-                    subtype.type().info().lref().path()
-                    target = subtype.type().info().lref().target()
-                    target_path = target.path()
-                    target_type = self._get_data_type(target_path)
-                    return target_type
+        if (schema_node is None):
+            return None
 
-        return None
+
+        subtype = schema_node.type()
+        if (subtype is None):
+            return None
+
+        if subtype.base() != ly.Type.LEAFREF:
+            return None
+
+        return subtype.leafref_type().basename()
diff --git a/src/sonic-yang-mgmt/sonic_yang_ext.py b/src/sonic-yang-mgmt/sonic_yang_ext.py
index bb8163265987..cbc2797d3a53 100644
--- a/src/sonic-yang-mgmt/sonic_yang_ext.py
+++ b/src/sonic-yang-mgmt/sonic_yang_ext.py
@@ -2,7 +2,7 @@
 # class sonic_yang. A separate file is used to avoid a single large file.
 
 from __future__ import print_function
-import yang as ly
+import libyang as ly
 import syslog
 from json import dump, dumps, loads
 from xmltodict import parse
@@ -86,7 +86,7 @@ def _loadJsonYangModel(self):
             for f in self.yangFiles:
                 m = self.ctx.get_module(f)
                 if m is not None:
-                    xml = m.print_mem(ly.LYD_JSON, ly.LYP_FORMAT)
+                    xml = m.print_mem("yin")
                     self.yJson.append(parse(xml))
                     self.sysLog(msg="Parsed Json for {}".format(m.name()))
         except Exception as e:
@@ -328,7 +328,7 @@ def _fillLeafDictUses(self, uses_s, table, leafDict):
             Parameters:
                 uses_s (str): uses statement in yang module.
                 table (str): config DB table, this table is being translated.
-                leafDict (dict): dict with leaf(s) information for List\Container
+                leafDict (dict): dict with leaf(s) information for List Container
                     corresponding to config DB table.
 
             Returns:
@@ -369,7 +369,7 @@ def _createLeafDict(self, model, table):
                 table (str): config DB table, this table is being translated.
 
             Returns:
-                 leafDict (dict): dict with leaf(s) information for List\Container
+                 leafDict (dict): dict with leaf(s) information for List Container
                     corresponding to config DB table.
         '''
         leafDict = dict()
@@ -1174,8 +1174,7 @@ def loadData(self, configdbJson, debug=False):
           self._xlateConfigDB(xlateFile=xlateFile)
           #print(self.xlateJson)
           self.sysLog(msg="Try to load Data in the tree")
-          self.root = self.ctx.parse_data_mem(dumps(self.xlateJson), \
-                        ly.LYD_JSON, ly.LYD_OPT_CONFIG|ly.LYD_OPT_STRICT)
+          self.root = self.ctx.parse_data_mem(dumps(self.xlateJson), "json", no_state=True, strict=True, json_string_datatypes=True)
 
        except Exception as e:
            self.root = None
@@ -1195,7 +1194,7 @@ def getData(self, debug=False):
             revXlateFile = None
             if debug:
                 revXlateFile = "revXlateConfig.json"
-            self.xlateJson = loads(self._print_data_mem('JSON'))
+            self.xlateJson = loads(self._print_data_mem("JSON"))
             # reset reverse xlate
             self.revXlateJson = dict()
             # result will be stored self.revXlateJson
@@ -1229,13 +1228,13 @@ def deleteNode(self, xpath):
                     # try to delete parent
                     nodeP = self._find_parent_data_node(xpath)
                     xpathP = nodeP.path()
-                    if self._deleteNode(xpath=xpathP, node=nodeP) == False:
+                    if self._deleteNode(xpath=xpathP) == False:
                         raise Exception('_deleteNode failed')
                     else:
                         return True
 
             # delete non key element
-            if self._deleteNode(xpath=xpath, node=node) == False:
+            if self._deleteNode(xpath=xpath) == False:
                 raise Exception('_deleteNode failed')
         except Exception as e:
             self.sysLog(msg="deleteNode:{}".format(str(e)), \
diff --git a/src/sonic-yang-mgmt/tests/libyang-python-tests/config_data.json b/src/sonic-yang-mgmt/tests/libyang-python-tests/config_data.json
index 154ef21b494d..9da360e84d7e 100644
--- a/src/sonic-yang-mgmt/tests/libyang-python-tests/config_data.json
+++ b/src/sonic-yang-mgmt/tests/libyang-python-tests/config_data.json
@@ -267,7 +267,7 @@
 						"fc02:2000::2"
 					],
 					"container-in-list-test": {
-						"leaf-1": true,
+						"leaf-1": "up",
 						"leaf-2": "test1",
 						"mc-case-leaf-1": 55,
 						"mc-case-leaf-3": 1234
@@ -281,7 +281,7 @@
 						"2002:2001::2"
 					],
 					"container-in-list-test": {
-						"leaf-1": false,
+						"leaf-1": "down",
 						"leaf-2": "test2",
 						"mc-case-leaf-2": 77,
 						"mc-case-leaf-3": 4321
diff --git a/src/sonic-yang-mgmt/tests/libyang-python-tests/sample-yang-models/test-yang-structure.yang b/src/sonic-yang-mgmt/tests/libyang-python-tests/sample-yang-models/test-yang-structure.yang
index 1f57ae337a47..43d4309cc3e2 100755
--- a/src/sonic-yang-mgmt/tests/libyang-python-tests/sample-yang-models/test-yang-structure.yang
+++ b/src/sonic-yang-mgmt/tests/libyang-python-tests/sample-yang-models/test-yang-structure.yang
@@ -45,7 +45,7 @@ module test-yang-structure {
           leaf leaf-1 {
             description "test leaf in container";
             type string {
-              pattern "false|true";
+              pattern "down|up";
             }
           }
 
diff --git a/src/sonic-yang-mgmt/tests/libyang-python-tests/test_SonicYang.json b/src/sonic-yang-mgmt/tests/libyang-python-tests/test_SonicYang.json
index 2376c357b757..eddaf49f8b91 100644
--- a/src/sonic-yang-mgmt/tests/libyang-python-tests/test_SonicYang.json
+++ b/src/sonic-yang-mgmt/tests/libyang-python-tests/test_SonicYang.json
@@ -25,25 +25,25 @@
    ],
    "data_type" : [
       {
-         "data_type" : "LY_TYPE_STRING",
-         "xpath" : "/test-port:port/test-port:PORT/test-port:PORT_LIST/test-port:port_name"
+         "data_type" : "string",
+         "xpath" : "/test-port:port/PORT/PORT_LIST/port_name"
       },
       {
-         "data_type" : "LY_TYPE_LEAFREF",
-         "xpath" : "/test-vlan:vlan/test-vlan:VLAN_INTERFACE/test-vlan:VLAN_INTERFACE_LIST/test-vlan:vlanid"
+         "data_type" : "leafref",
+         "xpath" : "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST/vlanid"
       }
    ],
    "delete_nodes" : [
       {
-         "valid" : "False",
+         "valid" : false,
          "xpath" : "/test-port:port/PORT/PORT_LIST[port_name='Ethernet10']/speed"
       },
       {
-         "valid" : "True",
+         "valid" : true,
          "xpath" : "/test-port:port/PORT/PORT_LIST[port_name='Ethernet9']/mtu"
       },
       {
-         "valid" : "False",
+         "valid" : false,
          "xpath" : "/test-port:port/PORT/PORT_LIST[port_name='Ethernet20']/mtu"
       }
    ],
@@ -56,6 +56,52 @@
          ],
          "xpath" : "/test-port:port/PORT/PORT_LIST[port_name='Ethernet8']/port_name"
       },
+      {
+         "dependencies" : [
+            "/test-acl:acl/ACL_RULE/ACL_RULE_LIST[ACL_TABLE_NAME='PACL-V6'][RULE_NAME='Rule_20']/ACL_TABLE_NAME",
+            "/test-acl:acl/ACL_RULE/ACL_RULE_LIST[ACL_TABLE_NAME='PACL-test'][RULE_NAME='rule_20']/ACL_TABLE_NAME",
+            "/test-acl:acl/ACL_RULE/ACL_RULE_LIST[ACL_TABLE_NAME='PACL-V4'][RULE_NAME='Rule_20']/ACL_TABLE_NAME",
+            "/test-acl:acl/ACL_RULE/ACL_RULE_LIST[ACL_TABLE_NAME='PACL-V4'][RULE_NAME='Rule_40']/ACL_TABLE_NAME"
+         ],
+         "xpath" : "/test-acl:acl/ACL_TABLE"
+      },
+      {
+         "dependencies" : [
+            "/test-acl:acl/ACL_TABLE/ACL_TABLE_LIST[ACL_TABLE_NAME='PACL-V6']/ports[.='Ethernet8']",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet2']/port",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet3']/vlanid",
+            "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST[vlanid='555'][ip-prefix='fe80::/10']/vlanid",
+            "/test-interface:interface/INTERFACE/INTERFACE_LIST[interface='Ethernet8'][ip-prefix='10.1.1.64/26']/interface",
+            "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST[vlanid='111'][ip-prefix='2000:f500:45:6709::/64']/vlanid",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet5']/vlanid",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet1']/port",
+            "/test-acl:acl/ACL_TABLE/ACL_TABLE_LIST[ACL_TABLE_NAME='PACL-V4']/ports[.='Ethernet0']",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet6']/port",
+            "/test-acl:acl/ACL_TABLE/ACL_TABLE_LIST[ACL_TABLE_NAME='PACL-V4']/ports[.='Ethernet2']",
+            "/test-acl:acl/ACL_TABLE/ACL_TABLE_LIST[ACL_TABLE_NAME='PACL-V6']/ports[.='Ethernet7']",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet3']/port",
+            "/test-interface:interface/INTERFACE/INTERFACE_LIST[interface='Ethernet8'][ip-prefix='2000:f500:40:a749::/126']/interface",
+            "/test-acl:acl/ACL_RULE/ACL_RULE_LIST[ACL_TABLE_NAME='PACL-test'][RULE_NAME='rule_20']/ACL_TABLE_NAME",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet0']/port",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet5']/port",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet1']/vlanid",
+            "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST[vlanid='111'][ip-prefix='fe80::/10']/vlanid",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet4']/port",
+            "/test-acl:acl/ACL_RULE/ACL_RULE_LIST[ACL_TABLE_NAME='PACL-V4'][RULE_NAME='Rule_20']/ACL_TABLE_NAME",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet2']/vlanid",
+            "/test-acl:acl/ACL_TABLE/ACL_TABLE_LIST[ACL_TABLE_NAME='PACL-V6']/ports[.='Ethernet9']",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet6']/vlanid",
+            "/test-acl:acl/ACL_RULE/ACL_RULE_LIST[ACL_TABLE_NAME='PACL-V4'][RULE_NAME='Rule_40']/ACL_TABLE_NAME",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet0']/vlanid",
+            "/test-acl:acl/ACL_RULE/ACL_RULE_LIST[ACL_TABLE_NAME='PACL-V6'][RULE_NAME='Rule_20']/ACL_TABLE_NAME",
+            "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST[vlanid='111'][ip-prefix='10.1.1.64/26']/vlanid",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet4']/vlanid",
+            "/test-acl:acl/ACL_TABLE/ACL_TABLE_LIST[ACL_TABLE_NAME='PACL-V4']/ports[.='Ethernet1']",
+            "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST[vlanid='555'][ip-prefix='2000:f500:41:4e9::/64']/vlanid",
+            "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST[vlanid='555'][ip-prefix='10.1.5.64/26']/vlanid"
+         ],
+         "xpath" : "/"
+      },
       {
          "dependencies" : [],
          "xpath" : "/test-port:port/PORT/PORT_LIST[port_name='Ethernet8']/alias"
@@ -67,11 +113,11 @@
          "xpath" : "/test-vlan:vlan/test-vlan:VLAN_INTERFACE/test-vlan:VLAN_INTERFACE_LIST/test-vlan:vlanid"
       },
       {
-         "leafref_path" : "/test-port:port/test-port:PORT/test-port:PORT_LIST/test-port:port_name",
+         "leafref_path" : "/port:port/port:PORT/port:PORT_LIST/port:port_name",
          "xpath" : "/test-interface:interface/test-interface:INTERFACE/test-interface:INTERFACE_LIST/test-interface:interface"
       },
       {
-         "leafref_path" : "/test-port:port/test-port:PORT/test-port:PORT_LIST/test-port:port_name",
+         "leafref_path" : "/port:port/port:PORT/port:PORT_LIST/port:port_name",
          "xpath" : "/test-vlan:vlan/test-vlan:VLAN_MEMBER/test-vlan:VLAN_MEMBER_LIST/test-vlan:port"
       },
       {
@@ -81,38 +127,38 @@
    ],
    "leafref_type" : [
       {
-         "data_type" : "LY_TYPE_UINT16",
+         "data_type" : "uint16",
          "xpath" : "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST[vlanid='111'][ip-prefix='2000:f500:45:6709::/64']/vlanid"
       },
       {
-         "data_type" : "LY_TYPE_STRING",
+         "data_type" : "string",
          "xpath" : "/test-interface:interface/INTERFACE/INTERFACE_LIST[interface='Ethernet8'][ip-prefix='2000:f500:40:a749::/126']/interface"
       },
       {
-         "data_type" : "LY_TYPE_STRING",
+         "data_type" : "string",
          "xpath" : "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet0']/port"
       },
       {
-         "data_type" : "LY_TYPE_UINT16",
+         "data_type" : "uint16",
          "xpath" : "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST[vlanid='111'][port='Ethernet0']/vlanid"
       }
    ],
    "leafref_type_schema" : [
       {
-         "data_type" : "LY_TYPE_UINT16",
-         "xpath" : "/test-vlan:vlan/test-vlan:VLAN_INTERFACE/test-vlan:VLAN_INTERFACE_LIST/test-vlan:vlanid"
+         "data_type" : "uint16",
+         "xpath" : "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST/vlanid"
       },
       {
-         "data_type" : "LY_TYPE_STRING",
-         "xpath" : "/test-interface:interface/test-interface:INTERFACE/test-interface:INTERFACE_LIST/test-interface:interface"
+         "data_type" : "string",
+         "xpath" : "/test-interface:interface/INTERFACE/INTERFACE_LIST/interface"
       },
       {
-         "data_type" : "LY_TYPE_STRING",
-         "xpath" : "/test-vlan:vlan/test-vlan:VLAN_MEMBER/test-vlan:VLAN_MEMBER_LIST/test-vlan:port"
+         "data_type" : "string",
+         "xpath" : "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST/port"
       },
       {
-         "data_type" : "LY_TYPE_UINT16",
-         "xpath" : "/test-vlan:vlan/test-vlan:VLAN_MEMBER/test-vlan:VLAN_MEMBER_LIST/test-vlan:vlanid"
+         "data_type" : "uint16",
+         "xpath" : "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST/vlanid"
       }
    ],
    "members" : [
@@ -257,21 +303,21 @@
    "schema_dependencies" : [
       {
          "schema_dependencies" : [
-            "/test-acl:acl/test-acl:ACL_TABLE/test-acl:ACL_TABLE_LIST/test-acl:ports",
-            "/test-portchannel:portchannel/test-portchannel:PORTCHANNEL/test-portchannel:PORTCHANNEL_LIST/test-portchannel:members",
-            "/test-interface:interface/test-interface:INTERFACE/test-interface:INTERFACE_LIST/test-interface:interface",
-            "/test-vlan:vlan/test-vlan:VLAN_MEMBER/test-vlan:VLAN_MEMBER_LIST/test-vlan:port"
+            "/test-acl:acl/ACL_TABLE/ACL_TABLE_LIST/ports",
+            "/test-portchannel:portchannel/PORTCHANNEL/PORTCHANNEL_LIST/members",
+            "/test-interface:interface/INTERFACE/INTERFACE_LIST/interface",
+            "/test-vlan:vlan/VLAN_MEMBER/VLAN_MEMBER_LIST/port"
          ],
-         "xpath" : "/test-port:port/test-port:PORT/test-port:PORT_LIST/test-port:port_name"
+         "xpath" : "/test-port:port/PORT/PORT_LIST/port_name"
       }
    ],
    "schema_nodes" : [
       {
-         "value" : "/test-vlan:vlan/test-vlan:VLAN_INTERFACE/test-vlan:VLAN_INTERFACE_LIST/test-vlan:family",
+         "value" : "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST/family",
          "xpath" : "/test-vlan:vlan/VLAN_INTERFACE/VLAN_INTERFACE_LIST[vlanid='111'][ip-prefix='10.1.1.64/26']/family"
       },
       {
-         "value" : "/test-port:port/test-port:PORT/test-port:PORT_LIST/test-port:speed",
+         "value" : "/test-port:port/PORT/PORT_LIST/speed",
          "xpath" : "/test-port:port/PORT/PORT_LIST[port_name='Ethernet9']/speed"
       }
    ],
diff --git a/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py b/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py
index 86b27ef174e5..c8f76a1f2088 100644
--- a/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py
+++ b/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py
@@ -153,8 +153,6 @@ def test_find_data_node_value(self, data, yang_s):
        for node in data['node_values']:
             xpath = str(node['xpath'])
             value = str(node['value'])
-            print(xpath)
-            print(value)
             val = yang_s._find_data_node_value(xpath)
             assert str(val) == str(value)
 
@@ -162,7 +160,8 @@ def test_find_data_node_value(self, data, yang_s):
     def test_delete_node(self, data, yang_s):
         for node in data['delete_nodes']:
             xpath = str(node['xpath'])
-            yang_s._deleteNode(xpath)
+            rv = yang_s._deleteNode(xpath)
+            assert rv == node['valid']
 
     #test set node's value
     def test_set_datanode_value(self, data, yang_s):
@@ -218,8 +217,7 @@ def test_find_schema_dependencies(self, yang_s, data):
     def test_merge_data_tree(self, data, yang_s):
         data_merge_file = data['data_merge_file']
         yang_dir = str(data['yang_dir'])
-        yang_s._merge_data(data_merge_file, yang_dir)
-        #yang_s.root.print_mem(ly.LYD_JSON, ly.LYP_FORMAT)
+        yang_s._merge_data(data_merge_file)
 
     #test get module prefix
     def test_get_module_prefix(self, yang_s, data):
@@ -234,17 +232,15 @@ def test_get_data_type(self, yang_s, data):
         for node in data['data_type']:
             xpath = str(node['xpath'])
             expected = node['data_type']
-            expected_type = yang_s._str_to_type(expected)
             data_type = yang_s._get_data_type(xpath)
-            assert expected_type == data_type
+            assert expected == data_type
 
     def test_get_leafref_type(self, yang_s, data):
         for node in data['leafref_type']:
             xpath = str(node['xpath'])
             expected = node['data_type']
-            expected_type = yang_s._str_to_type(expected)
             data_type = yang_s._get_leafref_type(xpath)
-            assert expected_type == data_type
+            assert expected == data_type
 
     def test_get_leafref_path(self, yang_s, data):
         for node in data['leafref_path']:
@@ -257,9 +253,8 @@ def test_get_leafref_type_schema(self, yang_s, data):
         for node in data['leafref_type_schema']:
             xpath = str(node['xpath'])
             expected = node['data_type']
-            expected_type = yang_s._str_to_type(expected)
             data_type = yang_s._get_leafref_type_schema(xpath)
-            assert expected_type == data_type
+            assert expected == data_type
 
     """
     This is helper function to load YANG models for tests cases, which works
@@ -345,8 +340,7 @@ def test_xlate_rev_xlate(self, sonic_yang_data):
             # print for better debugging, in case of failure.
             from jsondiff import diff
             print(diff(syc.jIn, syc.revXlateJson, syntax='symmetric'))
-            # make it fail
-            assert False == True
+            raise Exception("Xlate and Rev Xlate failed")
 
         return
 

From 01f25acf6f9aba701dd49bb0e0588db8458e1e32 Mon Sep 17 00:00:00 2001
From: Brad House <brad@brad-house.com>
Date: Wed, 12 Feb 2025 11:03:16 -0500
Subject: [PATCH 4/7] IGNORE THIS COMMIT: merge libyang3 step 4 (PR #21718)

---
 dockers/docker-config-engine-bookworm/Dockerfile.j2 | 3 ++-
 dockers/docker-config-engine-bullseye/Dockerfile.j2 | 3 ++-
 dockers/docker-config-engine-buster/Dockerfile.j2   | 3 ++-
 dockers/docker-config-engine/Dockerfile.j2          | 2 +-
 files/build_templates/sonic_debian_extension.j2     | 2 +-
 platform/vs/docker-sonic-vs.mk                      | 2 ++
 rules/docker-bmp.mk                                 | 5 +++--
 rules/docker-config-engine-bookworm.mk              | 2 ++
 rules/docker-config-engine-bullseye.mk              | 2 ++
 rules/docker-config-engine-buster.mk                | 2 ++
 rules/docker-macsec.mk                              | 2 +-
 rules/sonic-config.mk                               | 7 +++----
 rules/sonic-mgmt-common.mk                          | 4 ++--
 rules/sonic-utilities.mk                            | 2 ++
 rules/sonic_bgpcfgd.mk                              | 2 ++
 rules/swss-common.mk                                | 5 +++--
 16 files changed, 32 insertions(+), 16 deletions(-)

diff --git a/dockers/docker-config-engine-bookworm/Dockerfile.j2 b/dockers/docker-config-engine-bookworm/Dockerfile.j2
index c7c1d9cdc825..d437b7c91794 100644
--- a/dockers/docker-config-engine-bookworm/Dockerfile.j2
+++ b/dockers/docker-config-engine-bookworm/Dockerfile.j2
@@ -9,7 +9,8 @@ RUN apt-get update         && \
         apt-utils             \
         build-essential       \
         python3-dev           \
-        python3-yaml
+        python3-yaml          \
+        python3-cffi
 
 {%- if CONFIGURED_ARCH == "armhf" or CONFIGURED_ARCH == "arm64" %}
 RUN apt-get install -y        \
diff --git a/dockers/docker-config-engine-bullseye/Dockerfile.j2 b/dockers/docker-config-engine-bullseye/Dockerfile.j2
index 700af660a91d..9bf79443c33d 100644
--- a/dockers/docker-config-engine-bullseye/Dockerfile.j2
+++ b/dockers/docker-config-engine-bullseye/Dockerfile.j2
@@ -8,7 +8,8 @@ RUN apt-get update         && \
     apt-get install -y        \
         apt-utils             \
         build-essential       \
-        python3-dev
+        python3-dev           \
+        python3-cffi
 
 {%- if CONFIGURED_ARCH == "armhf" or CONFIGURED_ARCH == "arm64" %}
 RUN apt-get install -y        \
diff --git a/dockers/docker-config-engine-buster/Dockerfile.j2 b/dockers/docker-config-engine-buster/Dockerfile.j2
index cfa61bc0ba8c..df713b3d1b7b 100644
--- a/dockers/docker-config-engine-buster/Dockerfile.j2
+++ b/dockers/docker-config-engine-buster/Dockerfile.j2
@@ -8,7 +8,8 @@ RUN apt-get update         && \
     apt-get install -y        \
         apt-utils             \
         build-essential       \
-        python3-dev
+        python3-dev           \
+        python3-cffi
 
 {%- if CONFIGURED_ARCH == "armhf" or CONFIGURED_ARCH == "arm64" %}
 RUN apt-get install -y        \
diff --git a/dockers/docker-config-engine/Dockerfile.j2 b/dockers/docker-config-engine/Dockerfile.j2
index c470102fa4c4..0f78efeed481 100644
--- a/dockers/docker-config-engine/Dockerfile.j2
+++ b/dockers/docker-config-engine/Dockerfile.j2
@@ -6,7 +6,7 @@ ENV DEBIAN_FRONTEND=noninteractive
 RUN apt-get update
 
 # Dependencies for sonic-cfggen
-RUN apt-get install -y build-essential python-dev
+RUN apt-get install -y build-essential python-dev python3-cffi
 
 # Install python-redis
 RUN pip install redis>=3.5.3
diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2
index e959fb24f931..ac7ea98396f8 100644
--- a/files/build_templates/sonic_debian_extension.j2
+++ b/files/build_templates/sonic_debian_extension.j2
@@ -162,7 +162,7 @@ if [[ $CONFIGURED_ARCH == armhf || $CONFIGURED_ARCH == arm64 ]]; then
 fi
 
 # Install sonic-yang-models Python 3 package, install dependencies
-sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/libyang_*.deb $debs_path/libyang-cpp_*.deb $debs_path/python3-yang_*.deb || \
+sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/libyang_*.deb $debs_path/libyang-cpp_*.deb $debs_path/python3-yang_*.deb $debs_path/libyang3_*.deb $debs_path/python3-libyang_*.deb || \
     sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f
 SONIC_YANG_MODEL_PY3_WHEEL_NAME=$(basename {{sonic_yang_models_py3_wheel_path}})
 sudo cp {{sonic_yang_models_py3_wheel_path}} $FILESYSTEM_ROOT/$SONIC_YANG_MODEL_PY3_WHEEL_NAME
diff --git a/platform/vs/docker-sonic-vs.mk b/platform/vs/docker-sonic-vs.mk
index 3e66e94546ad..878163078a60 100644
--- a/platform/vs/docker-sonic-vs.mk
+++ b/platform/vs/docker-sonic-vs.mk
@@ -10,6 +10,8 @@ $(DOCKER_SONIC_VS)_DEPENDS += $(SYNCD_VS) \
                               $(LIBYANG) \
                               $(LIBYANG_CPP) \
                               $(LIBYANG_PY3) \
+                              $(LIBYANG3) \
+                              $(LIBYANG3_PY3) \
                               $(SONIC_UTILITIES_DATA) \
                               $(SONIC_HOST_SERVICES_DATA)
 
diff --git a/rules/docker-bmp.mk b/rules/docker-bmp.mk
index 95ad79799e65..48501efcfa68 100644
--- a/rules/docker-bmp.mk
+++ b/rules/docker-bmp.mk
@@ -13,7 +13,8 @@ $(DOCKER_BMP)_PYTHON_WHEELS = $(SONIC_BMPCFGD)
 $(DOCKER_BMP)_INSTALL_DEBS = $(LIBSWSSCOMMON) \
                              $(SONIC_BMPD) \
                              $(PYTHON3_SWSSCOMMON) \
-                             $(LIBYANG_PY3)
+                             $(LIBYANG_PY3) \
+                             $(LIBYANG3_PY3)
 
 $(DOCKER_BMP)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_BOOKWORM)_DBG_DEPENDS)
 
@@ -43,4 +44,4 @@ $(DOCKER_BMP)_RUN_OPT += -v /etc/localtime:/etc/localtime:ro
 $(DOCKER_BMP)_RUN_OPT += -v /var/run/dbus:/var/run/dbus:rw
 
 $(DOCKER_BMP)_FILES += $(SUPERVISOR_PROC_EXIT_LISTENER_SCRIPT)
-$(DOCKER_BMP)_BASE_IMAGE_FILES += monit_bmp:/etc/monit/conf.d
\ No newline at end of file
+$(DOCKER_BMP)_BASE_IMAGE_FILES += monit_bmp:/etc/monit/conf.d
diff --git a/rules/docker-config-engine-bookworm.mk b/rules/docker-config-engine-bookworm.mk
index 35145d890614..60606ab70b6c 100644
--- a/rules/docker-config-engine-bookworm.mk
+++ b/rules/docker-config-engine-bookworm.mk
@@ -7,6 +7,8 @@ $(DOCKER_CONFIG_ENGINE_BOOKWORM)_DEPENDS += $(LIBSWSSCOMMON) \
                                           $(LIBYANG) \
                                           $(LIBYANG_CPP) \
                                           $(LIBYANG_PY3) \
+                                          $(LIBYANG3) \
+                                          $(LIBYANG3_PY3) \
                                           $(PYTHON3_SWSSCOMMON) \
                                           $(SONIC_DB_CLI) \
                                           $(SONIC_EVENTD)
diff --git a/rules/docker-config-engine-bullseye.mk b/rules/docker-config-engine-bullseye.mk
index aa91a56279ce..2211519c873a 100644
--- a/rules/docker-config-engine-bullseye.mk
+++ b/rules/docker-config-engine-bullseye.mk
@@ -7,6 +7,8 @@ $(DOCKER_CONFIG_ENGINE_BULLSEYE)_DEPENDS += $(LIBSWSSCOMMON) \
                                           $(LIBYANG) \
                                           $(LIBYANG_CPP) \
                                           $(LIBYANG_PY3) \
+                                          $(LIBYANG3) \
+                                          $(LIBYANG3_PY3) \
                                           $(PYTHON3_SWSSCOMMON) \
                                           $(SONIC_DB_CLI) \
                                           $(SONIC_EVENTD)
diff --git a/rules/docker-config-engine-buster.mk b/rules/docker-config-engine-buster.mk
index 9f4035d6aa24..1f9b2f533b4e 100644
--- a/rules/docker-config-engine-buster.mk
+++ b/rules/docker-config-engine-buster.mk
@@ -7,6 +7,8 @@ $(DOCKER_CONFIG_ENGINE_BUSTER)_DEPENDS += $(LIBSWSSCOMMON) \
                                           $(LIBYANG) \
                                           $(LIBYANG_CPP) \
                                           $(LIBYANG_PY3) \
+                                          $(LIBYANG3) \
+                                          $(LIBYANG3_PY3) \
                                           $(PYTHON3_SWSSCOMMON) \
                                           $(SONIC_DB_CLI)
 $(DOCKER_CONFIG_ENGINE_BUSTER)_PYTHON_WHEELS += $(SONIC_PY_COMMON_PY3) \
diff --git a/rules/docker-macsec.mk b/rules/docker-macsec.mk
index 682ef619e9be..f7f319cbbb95 100644
--- a/rules/docker-macsec.mk
+++ b/rules/docker-macsec.mk
@@ -15,7 +15,7 @@ $(DOCKER_MACSEC)_DBG_IMAGE_PACKAGES = $($(DOCKER_SWSS_LAYER_BOOKWORM)_DBG_IMAGE_
 $(DOCKER_MACSEC)_LOAD_DOCKERS += $(DOCKER_SWSS_LAYER_BOOKWORM)
 
 $(DOCKER_MACSEC)_INSTALL_PYTHON_WHEELS = $(SONIC_UTILITIES_PY3)
-$(DOCKER_MACSEC)_INSTALL_DEBS = $(PYTHON3_SWSSCOMMON) $(LIBYANG_PY3)
+$(DOCKER_MACSEC)_INSTALL_DEBS = $(PYTHON3_SWSSCOMMON) $(LIBYANG_PY3) $(LIBYANG3_PY3)
 
 SONIC_DOCKER_IMAGES += $(DOCKER_MACSEC)
 SONIC_BOOKWORM_DOCKERS += $(DOCKER_MACSEC)
diff --git a/rules/sonic-config.mk b/rules/sonic-config.mk
index ba146df70577..d65805bc96c5 100644
--- a/rules/sonic-config.mk
+++ b/rules/sonic-config.mk
@@ -16,10 +16,9 @@ $(SONIC_CONFIG_ENGINE_PY3)_SRC_PATH = $(SRC_PATH)/sonic-config-engine
 $(SONIC_CONFIG_ENGINE_PY3)_DEPENDS += $(SONIC_PY_COMMON_PY3) \
                                       $(SONIC_YANG_MGMT_PY3) \
                                       $(SONIC_YANG_MODELS_PY3)
-$(SONIC_CONFIG_ENGINE_PY3)_DEBS_DEPENDS += $(LIBYANG) \
-                                           $(LIBYANG_CPP) \
-                                           $(LIBYANG_PY3) \
-	                                       $(PYTHON3_SWSSCOMMON)
+$(SONIC_CONFIG_ENGINE_PY3)_DEBS_DEPENDS += $(LIBYANG3) \
+                                           $(LIBYANG3_PY3) \
+                                           $(PYTHON3_SWSSCOMMON)
 ifeq ($(ENABLE_PY2_MODULES), y)
     # Synthetic dependency to avoid building the Python 2 and 3 packages
     # simultaneously and any potential conflicts which may arise
diff --git a/rules/sonic-mgmt-common.mk b/rules/sonic-mgmt-common.mk
index 41441ab3ee94..1764e423175b 100644
--- a/rules/sonic-mgmt-common.mk
+++ b/rules/sonic-mgmt-common.mk
@@ -3,8 +3,8 @@
 MGMT_COMMON_VERSION = 1.0.0
 SONIC_MGMT_COMMON = sonic-mgmt-common_$(MGMT_COMMON_VERSION)_$(CONFIGURED_ARCH).deb
 $(SONIC_MGMT_COMMON)_SRC_PATH = $(SRC_PATH)/sonic-mgmt-common
-$(SONIC_MGMT_COMMON)_DEPENDS  = $(LIBYANG_DEV) $(LIBYANG)
-$(SONIC_MGMT_COMMON)_RDEPENDS = $(LIBYANG)
+$(SONIC_MGMT_COMMON)_DEPENDS  = $(LIBYANG_DEV) $(LIBYANG) $(LIBYANG3)
+$(SONIC_MGMT_COMMON)_RDEPENDS = $(LIBYANG) $(LIBYANG3)
 $(SONIC_MGMT_COMMON)_WHEEL_DEPENDS = $(SONIC_YANG_MODELS_PY3)
 SONIC_DPKG_DEBS += $(SONIC_MGMT_COMMON)
 
diff --git a/rules/sonic-utilities.mk b/rules/sonic-utilities.mk
index d0dec73e90e5..1217c7cd951a 100644
--- a/rules/sonic-utilities.mk
+++ b/rules/sonic-utilities.mk
@@ -19,6 +19,8 @@ $(SONIC_UTILITIES_PY3)_DEPENDS += $(SONIC_PY_COMMON_PY3) \
 $(SONIC_UTILITIES_PY3)_DEBS_DEPENDS = $(LIBYANG) \
                                       $(LIBYANG_CPP) \
                                       $(LIBYANG_PY3) \
+                                      $(LIBYANG3) \
+                                      $(LIBYANG3_PY3) \
                                       $(LIBSWSSCOMMON) \
                                       $(PYTHON3_SWSSCOMMON)
 ifeq ($(CONFIGURED_PLATFORM),nvidia-bluefield)
diff --git a/rules/sonic_bgpcfgd.mk b/rules/sonic_bgpcfgd.mk
index 9abab06800c8..8881fb67caf6 100644
--- a/rules/sonic_bgpcfgd.mk
+++ b/rules/sonic_bgpcfgd.mk
@@ -13,6 +13,8 @@ $(SONIC_BGPCFGD)_DEPENDS += $(SONIC_CONFIG_ENGINE_PY3) \
 $(SONIC_BGPCFGD)_DEBS_DEPENDS += $(LIBYANG) \
                                  $(LIBYANG_CPP) \
                                  $(LIBYANG_PY3) \
+                                 $(LIBYANG3) \
+                                 $(LIBYANG3_PY3) \
                                  $(PYTHON3_SWSSCOMMON)
 $(SONIC_BGPCFGD)_PYTHON_VERSION = 3
 SONIC_PYTHON_WHEELS += $(SONIC_BGPCFGD)
diff --git a/rules/swss-common.mk b/rules/swss-common.mk
index 5d657d5e500b..12a10b3186ee 100644
--- a/rules/swss-common.mk
+++ b/rules/swss-common.mk
@@ -9,9 +9,10 @@ $(LIBSWSSCOMMON)_VERSION = $(LIBSWSSCOMMON_VERSION)
 $(LIBSWSSCOMMON)_NAME = $(LIBSWSSCOMMON_NAME)
 $(LIBSWSSCOMMON)_DEPENDS += $(LIBNL3_DEV) $(LIBNL_GENL3_DEV) \
                             $(LIBNL_ROUTE3_DEV) $(LIBNL_NF3_DEV) \
-                            $(LIBNL_CLI_DEV) $(LIBYANG_DEV) $(LIBYANG) 
+                            $(LIBNL_CLI_DEV) $(LIBYANG_DEV) $(LIBYANG) $(LIBYANG3)
 $(LIBSWSSCOMMON)_RDEPENDS += $(LIBNL3) $(LIBNL_GENL3) \
-                             $(LIBNL_ROUTE3) $(LIBNL_NF3) $(LIBNL_CLI) $(LIBYANG) 
+                             $(LIBNL_ROUTE3) $(LIBNL_NF3) $(LIBNL_CLI) $(LIBYANG) \
+                             $(LIBYANG3)
 SONIC_DPKG_DEBS += $(LIBSWSSCOMMON)
 
 LIBSWSSCOMMON_DEV = $(LIBSWSSCOMMON_NAME)-dev_$(LIBSWSSCOMMON_VERSION)_$(CONFIGURED_ARCH).deb

From 576b9ecd1de171d53c1578bcc80087c5c18a89c6 Mon Sep 17 00:00:00 2001
From: Brad House <brad@brad-house.com>
Date: Fri, 14 Feb 2025 19:49:28 -0500
Subject: [PATCH 5/7] IGNORE THIS COMMIT: merge libyang3 step 5 (PR #21752)

---
 rules/swss-common.mk | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/rules/swss-common.mk b/rules/swss-common.mk
index 12a10b3186ee..65c7caf8e014 100644
--- a/rules/swss-common.mk
+++ b/rules/swss-common.mk
@@ -9,10 +9,9 @@ $(LIBSWSSCOMMON)_VERSION = $(LIBSWSSCOMMON_VERSION)
 $(LIBSWSSCOMMON)_NAME = $(LIBSWSSCOMMON_NAME)
 $(LIBSWSSCOMMON)_DEPENDS += $(LIBNL3_DEV) $(LIBNL_GENL3_DEV) \
                             $(LIBNL_ROUTE3_DEV) $(LIBNL_NF3_DEV) \
-                            $(LIBNL_CLI_DEV) $(LIBYANG_DEV) $(LIBYANG) $(LIBYANG3)
+                            $(LIBNL_CLI_DEV) $(LIBYANG3_DEV) $(LIBYANG3)
 $(LIBSWSSCOMMON)_RDEPENDS += $(LIBNL3) $(LIBNL_GENL3) \
-                             $(LIBNL_ROUTE3) $(LIBNL_NF3) $(LIBNL_CLI) $(LIBYANG) \
-                             $(LIBYANG3)
+                             $(LIBNL_ROUTE3) $(LIBNL_NF3) $(LIBNL_CLI) $(LIBYANG3)
 SONIC_DPKG_DEBS += $(LIBSWSSCOMMON)
 
 LIBSWSSCOMMON_DEV = $(LIBSWSSCOMMON_NAME)-dev_$(LIBSWSSCOMMON_VERSION)_$(CONFIGURED_ARCH).deb

From 85707bbb24033a18309d8725ab887864a9875ada Mon Sep 17 00:00:00 2001
From: Brad House <brad@brad-house.com>
Date: Wed, 19 Feb 2025 10:23:58 -0500
Subject: [PATCH 6/7] libyang: upgrade to v3 step 6 -- sonic-mgmt-common

---
 rules/sonic-mgmt-common.mk | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/rules/sonic-mgmt-common.mk b/rules/sonic-mgmt-common.mk
index 1764e423175b..84cdc5d45ca8 100644
--- a/rules/sonic-mgmt-common.mk
+++ b/rules/sonic-mgmt-common.mk
@@ -3,8 +3,8 @@
 MGMT_COMMON_VERSION = 1.0.0
 SONIC_MGMT_COMMON = sonic-mgmt-common_$(MGMT_COMMON_VERSION)_$(CONFIGURED_ARCH).deb
 $(SONIC_MGMT_COMMON)_SRC_PATH = $(SRC_PATH)/sonic-mgmt-common
-$(SONIC_MGMT_COMMON)_DEPENDS  = $(LIBYANG_DEV) $(LIBYANG) $(LIBYANG3)
-$(SONIC_MGMT_COMMON)_RDEPENDS = $(LIBYANG) $(LIBYANG3)
+$(SONIC_MGMT_COMMON)_DEPENDS  = $(LIBYANG3_DEV) $(LIBYANG3)
+$(SONIC_MGMT_COMMON)_RDEPENDS = $(LIBYANG3)
 $(SONIC_MGMT_COMMON)_WHEEL_DEPENDS = $(SONIC_YANG_MODELS_PY3)
 SONIC_DPKG_DEBS += $(SONIC_MGMT_COMMON)
 

From 4db1da8a6871729d5a1ca34158b09591f8402ac8 Mon Sep 17 00:00:00 2001
From: Brad House <brad@brad-house.com>
Date: Fri, 28 Feb 2025 21:39:41 -0500
Subject: [PATCH 7/7] libyang3: patch: add removed feature
 LYD_VALIDATE_NOEXTDEPS

---
 .../0007-pr2362-lyd_validate_noextdeps.patch  | 345 ++++++++++++++++++
 src/libyang3/patch/series                     |   1 +
 2 files changed, 346 insertions(+)
 create mode 100644 src/libyang3/patch/0007-pr2362-lyd_validate_noextdeps.patch

diff --git a/src/libyang3/patch/0007-pr2362-lyd_validate_noextdeps.patch b/src/libyang3/patch/0007-pr2362-lyd_validate_noextdeps.patch
new file mode 100644
index 000000000000..ddbb968650d0
--- /dev/null
+++ b/src/libyang3/patch/0007-pr2362-lyd_validate_noextdeps.patch
@@ -0,0 +1,345 @@
+From cfc94cc0d66524f453ccd781e9d252e8fc3ae711 Mon Sep 17 00:00:00 2001
+From: Brad House <brad@brad-house.com>
+Date: Sun, 23 Feb 2025 12:25:04 -0500
+Subject: [PATCH] validation: Add LYD_VALIDATE_NOEXTDEPS to bypass
+ leafref/when/must
+
+In libyang v1, there was an LYD_OPT_NOEXTDEPS flag.  This was
+removed, and this patch re-adds a flag with similar functionality.
+---
+ src/parser_data.h |  3 +-
+ src/validation.c  | 85 ++++++++++++++++++++++++++---------------------
+ 2 files changed, 49 insertions(+), 39 deletions(-)
+
+diff --git a/src/parser_data.h b/src/parser_data.h
+index ddb22781e..292eda86f 100644
+--- a/src/parser_data.h
++++ b/src/parser_data.h
+@@ -222,7 +222,8 @@ struct ly_in;
+ #define LYD_VALIDATE_NOT_FINAL 0x0020       /**< Skip final validation tasks that require for all the data nodes to
+                                                  either exist or not, based on the YANG constraints. Once the data
+                                                  satisfy this requirement, the final validation should be performed. */
+-
++#define LYD_VALIDATE_NOEXTDEPS 0x0040       /**< Allow external dependencies (external leafrefs, instance-identifiers,
++                                                 must, and when) to not be resolved/satisfied during validation. */
+ #define LYD_VALIDATE_OPTS_MASK  0x0000FFFF  /**< Mask for all the LYD_VALIDATE_* options. */
+ 
+ /** @} datavalidationoptions */
+diff --git a/src/validation.c b/src/validation.c
+index a436816fe..ac1822ae9 100644
+--- a/src/validation.c
++++ b/src/validation.c
+@@ -41,18 +41,22 @@
+ #include "xpath.h"
+ 
+ /**
+- * @brief Check validation error taking into account multi-error validation.
++ * @brief Check validation error taking into account multi-error validation and
++ *        possible skipping of external dependency validation.
+  *
+  * @param[in] r Local return value.
+  * @param[in] err_cmd Command to perform on any error.
++ * @param[in] err_item Optional, may be NULL. Full error to evaluate.
+  * @param[in] val_opts Validation options.
+  * @param[in] label Label to go to on fatal error.
+  */
+-#define LY_VAL_ERR_GOTO(r, err_cmd, val_opts, label) \
++#define LY_VAL_ERR_GOTO(r, err_cmd, err_item, val_opts, label) \
+         if (r) { \
+-            err_cmd; \
+-            if ((r != LY_EVALID) || !(val_opts & LYD_VALIDATE_MULTI_ERROR)) { \
+-                goto label; \
++            if (!(val_opts & LYD_VALIDATE_NOEXTDEPS) || (r != LY_EVALID) || ((err_item) == NULL) || ((err_item)->apptag == NULL) || (strcmp((err_item)->apptag, "instance-required") != 0)) { \
++                err_cmd; \
++                if ((r != LY_EVALID) || !(val_opts & LYD_VALIDATE_MULTI_ERROR)) { \
++                    goto label; \
++                } \
+             } \
+         }
+ 
+@@ -412,7 +416,7 @@ lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, st
+                     /* invalid data */
+                     LOGVAL(LYD_CTX(node), LY_VCODE_NOWHEN, disabled->cond->expr);
+                     r = LY_EVALID;
+-                    LY_VAL_ERR_GOTO(r, rc = r, val_opts, error);
++                    LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(node)), val_opts, error);
+                 }
+             } else {
+                 /* when true */
+@@ -423,7 +427,7 @@ lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, st
+             ly_set_rm_index_ordered(node_when, i, NULL);
+         } else if (r != LY_EINCOMPLETE) {
+             /* error */
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, error);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(node)), val_opts, error);
+         }
+ 
+         LOG_LOCBACK(1, 1);
+@@ -454,7 +458,7 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
+ 
+             /* validate extension data */
+             r = ext_v->ext->def->plugin->validate(ext_v->ext, ext_v->sibling, *tree, data_type, val_opts, diff);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*tree)), val_opts, cleanup);
+ 
+             /* remove this item from the set */
+             ly_set_rm_index(ext_val, i, free);
+@@ -471,21 +475,21 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
+ 
+             /* validate the node */
+             r = ext_n->ext->def->plugin->node(ext_n->ext, ext_n->node, val_opts);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*tree)), val_opts, cleanup);
+ 
+             /* remove this item from the set */
+             ly_set_rm_index(ext_node, i, free);
+         } while (i);
+     }
+ 
+-    if (node_when) {
++    if (node_when && !(val_opts & LYD_VALIDATE_NOEXTDEPS)) {
+         /* evaluate all when conditions */
+         uint32_t prev_count;
+ 
+         do {
+             prev_count = node_when->count;
+             r = lyd_validate_unres_when(tree, mod, node_when, val_opts, when_xp_opts, node_types, diff);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*tree)), val_opts, cleanup);
+ 
+             /* there must have been some when conditions resolved */
+         } while (prev_count > node_when->count);
+@@ -513,7 +517,7 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
+             LOG_LOCSET(NULL, &node->node);
+             r = lyd_value_validate_incomplete(LYD_CTX(node), type, &node->value, &node->node, *tree);
+             LOG_LOCBACK(0, 1);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(node)), val_opts, cleanup);
+ 
+             /* remove this node from the set */
+             ly_set_rm_index(node_types, i, NULL);
+@@ -532,7 +536,7 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
+             /* validate and store the value of the metadata */
+             lyplg_ext_get_storage(meta->annotation, LY_STMT_TYPE, sizeof type, (const void **)&type);
+             r = lyd_value_validate_incomplete(LYD_CTX(meta->parent), type, &meta->value, meta->parent, *tree);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(meta->parent)), val_opts, cleanup);
+ 
+             /* remove this attr from the set */
+             ly_set_rm_index(meta_types, i, NULL);
+@@ -926,11 +930,11 @@ lyd_validate_choice_r(struct lyd_node **first, const struct lysc_node *sparent,
+     for (i = 0; *first && choices[i]; ++i) {
+         /* check case duplicites */
+         r = lyd_validate_cases(first, mod, (struct lysc_node_choice *)choices[i], diff);
+-        LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++        LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*first)), val_opts, cleanup);
+ 
+         /* check for nested choice */
+         r = lyd_validate_choice_r(first, choices[i], mod, ext, val_opts, int_opts, getnext_ht, diff);
+-        LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++        LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*first)), val_opts, cleanup);
+     }
+ 
+ cleanup:
+@@ -950,7 +954,7 @@ lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const
+ 
+     /* validate choices */
+     r = lyd_validate_choice_r(first, sparent, mod, ext, val_opts, int_opts, getnext_ht, diff);
+-    LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++    LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*first)), val_opts, cleanup);
+ 
+     node = *first;
+     while (node) {
+@@ -982,7 +986,7 @@ lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const
+         if (node->flags & LYD_NEW) {
+             /* then check new node instance duplicities */
+             r = lyd_validate_duplicates(*first, node, val_opts);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*first)), val_opts, cleanup);
+ 
+             /* this node is valid */
+             node->flags &= ~LYD_NEW;
+@@ -1100,7 +1104,7 @@ lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *pare
+     }
+ 
+     disabled = NULL;
+-    if (lysc_has_when(snode)) {
++    if (lysc_has_when(snode) && !(val_opts & LYD_VALIDATE_NOEXTDEPS)) {
+         /* if there are any when conditions, they must be true for a validation error */
+         LY_CHECK_RET(lyd_validate_dummy_when(first, parent, snode, &disabled));
+     }
+@@ -1177,7 +1181,7 @@ lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent,
+         assert(count < min);
+ 
+         disabled = NULL;
+-        if (lysc_has_when(snode)) {
++        if (lysc_has_when(snode) && !(val_opts & LYD_VALIDATE_NOEXTDEPS)) {
+             /* if there are any when conditions, they must be true for a validation error */
+             LY_CHECK_RET(lyd_validate_dummy_when(first, parent, snode, &disabled));
+         }
+@@ -1553,7 +1557,7 @@ lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_no
+         if (snode->flags & LYS_MAND_TRUE) {
+             /* check generic mandatory existence */
+             r = lyd_validate_mandatory(first, parent, snode, val_opts);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(first)), val_opts, cleanup);
+         }
+ 
+         /* find the existing case, if any */
+@@ -1561,7 +1565,7 @@ lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_no
+             if (lys_getnext_data(NULL, first, NULL, scase, NULL)) {
+                 /* validate only this case */
+                 r = lyd_validate_siblings_schema_r(first, parent, scase, mod, ext, val_opts, int_opts, getnext_ht);
+-                LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++                LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(first)), val_opts, cleanup);
+                 break;
+             }
+         }
+@@ -1580,25 +1584,25 @@ lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_no
+             slist = (struct lysc_node_list *)snode;
+             if (slist->min || (slist->max < UINT32_MAX)) {
+                 r = lyd_validate_minmax(first, parent, snode, slist->min, slist->max, val_opts);
+-                LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++                LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(first)), val_opts, cleanup);
+             }
+ 
+             /* check unique */
+             if (slist->uniques) {
+                 r = lyd_validate_unique(first, snode, (const struct lysc_node_leaf ***)slist->uniques, val_opts);
+-                LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++                LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(first)), val_opts, cleanup);
+             }
+         } else if (snode->nodetype == LYS_LEAFLIST) {
+             sllist = (struct lysc_node_leaflist *)snode;
+             if (sllist->min || (sllist->max < UINT32_MAX)) {
+                 r = lyd_validate_minmax(first, parent, snode, sllist->min, sllist->max, val_opts);
+-                LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++                LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(first)), val_opts, cleanup);
+             }
+ 
+         } else if (snode->flags & LYS_MAND_TRUE) {
+             /* check generic mandatory existence */
+             r = lyd_validate_mandatory(first, parent, snode, val_opts);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(first)), val_opts, cleanup);
+         }
+     }
+ 
+@@ -1649,6 +1653,11 @@ lyd_validate_must(const struct lyd_node *node, uint32_t val_opts, uint32_t int_o
+     const char *emsg, *eapptag;
+     LY_ARRAY_COUNT_TYPE u;
+ 
++    /* Must validation has been bypassed */
++    if (val_opts & LYD_VALIDATE_NOEXTDEPS) {
++        return LY_SUCCESS;
++    }
++
+     assert((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) != (LYD_INTOPT_RPC | LYD_INTOPT_REPLY));
+     assert((int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) != (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY));
+ 
+@@ -1710,7 +1719,7 @@ lyd_validate_must(const struct lyd_node *node, uint32_t val_opts, uint32_t int_o
+                 }
+                 LOG_LOCBACK(0, 1);
+                 r = LY_EVALID;
+-                LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++                LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(node)), val_opts, cleanup);
+             }
+         }
+     }
+@@ -1794,12 +1803,12 @@ lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, cons
+         /* node value was checked by plugins */
+ 
+ next_iter:
+-        LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++        LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(node)), val_opts, cleanup);
+     }
+ 
+     /* validate schema-based restrictions */
+     r = lyd_validate_siblings_schema_r(first, parent, sparent, mod, ext, val_opts, int_opts, getnext_ht);
+-    LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++    LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(first)), val_opts, cleanup);
+ 
+     LY_LIST_FOR(first, node) {
+         if (!node->schema || (!node->parent && mod && (lyd_owner_module(node) != mod))) {
+@@ -1810,7 +1819,7 @@ lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, cons
+         /* validate all children recursively */
+         r = lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, NULL, val_opts, int_opts, must_xp_opts,
+                 getnext_ht);
+-        LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++        LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(node)), val_opts, cleanup);
+ 
+         /* set default for containers */
+         lyd_np_cont_dflt_set(node);
+@@ -1947,7 +1956,7 @@ lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_
+         } else if (node->schema->nodetype & LYD_NODE_INNER) {
+             /* new node validation, autodelete */
+             r = lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, NULL, val_opts, int_opts, getnext_ht, diff);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(root)), val_opts, cleanup);
+ 
+             /* add nested defaults */
+             impl_opts = 0;
+@@ -2027,7 +2036,7 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
+         /* validate new top-level nodes of this module, autodelete */
+         r = lyd_validate_new(first2, *first2 ? lysc_data_parent((*first2)->schema) : NULL, mod, NULL, val_opts, 0,
+                 getnext_ht, diff);
+-        LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++        LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(ctx), val_opts, cleanup);
+ 
+         /* add all top-level defaults for this module, if going to validate subtree, do not add into unres sets
+          * (lyd_validate_subtree() adds all the nodes in that case) */
+@@ -2066,19 +2075,19 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
+ 
+                 r = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
+                         val_opts, 0, getnext_ht, diff);
+-                LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++                LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(ctx), val_opts, cleanup);
+             }
+         }
+ 
+         /* finish incompletely validated terminal values/attributes and when conditions */
+         r = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
+                 ext_node_p, ext_val_p, val_opts, diff);
+-        LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++        LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(ctx), val_opts, cleanup);
+ 
+         if (!(val_opts & LYD_VALIDATE_NOT_FINAL)) {
+             /* perform final validation that assumes the data tree is final */
+             r = lyd_validate_final_r(*first2, NULL, NULL, mod, NULL, val_opts, 0, 0, getnext_ht);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(ctx), val_opts, cleanup);
+         }
+ 
+         /* free the getnext hash table */
+@@ -2127,19 +2136,19 @@ lyd_validate_ext(struct lyd_node **tree, const struct lysc_ext_instance *ext, ui
+         LY_LIST_FOR(*tree, iter) {
+             r = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
+                     val_opts, 0, getnext_ht, diff);
+-            LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++            LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(iter)), val_opts, cleanup);
+         }
+     }
+ 
+     /* finish incompletely validated terminal values/attributes and when conditions */
+     r = lyd_validate_unres(tree, NULL, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
+             ext_node_p, ext_val_p, val_opts, diff);
+-    LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++    LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*tree)), val_opts, cleanup);
+ 
+     if (!(val_opts & LYD_VALIDATE_NOT_FINAL)) {
+         /* perform final validation that assumes the data tree is final */
+         r = lyd_validate_final_r(*tree, NULL, NULL, NULL, ext, val_opts, 0, 0, getnext_ht);
+-        LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++        LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(*tree)), val_opts, cleanup);
+     }
+ 
+ cleanup:
+@@ -2201,7 +2210,7 @@ lyd_validate_module_final(struct lyd_node *tree, const struct lys_module *module
+ 
+     /* perform final validation that assumes the data tree is final */
+     r = lyd_validate_final_r(first, NULL, NULL, mod, NULL, val_opts, 0, 0, getnext_ht);
+-    LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
++    LY_VAL_ERR_GOTO(r, rc = r, ly_err_last(LYD_CTX(tree)), val_opts, cleanup);
+ 
+ cleanup:
+     lyd_val_getnext_ht_free(getnext_ht);
diff --git a/src/libyang3/patch/series b/src/libyang3/patch/series
index c519c189b856..00c5c5175d55 100644
--- a/src/libyang3/patch/series
+++ b/src/libyang3/patch/series
@@ -4,3 +4,4 @@
 0004-union-apptag-529a594.patch
 0005-pr2360-validate-union-errors.patch
 0006-pr2361-union-sort-assert.patch
+0007-pr2362-lyd_validate_noextdeps.patch