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/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-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/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/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-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-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/rules/sonic-yang-models-py3.mk b/rules/sonic-yang-models-py3.mk index f5a606c28c3a..8125b1d580c0 100644 --- a/rules/sonic-yang-models-py3.mk +++ b/rules/sonic-yang-models-py3.mk @@ -1,8 +1,8 @@ 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) +$(SONIC_YANG_MODELS_PY3)_DEBS_DEPENDS = $(LIBYANG3) \ + $(LIBYANG3_PY3) SONIC_PYTHON_WHEELS += $(SONIC_YANG_MODELS_PY3) export SONIC_YANG_MODELS_PY3 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 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 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 ++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 +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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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 + * @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 +Date: Mon, 17 Feb 2025 16:02:54 +0100 +Subject: [PATCH 3/3] tests UPDATE new leafref backlinks test + +Co-authored-by: Brad House +--- + 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 +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("mate" + "", + 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("val1" + "val2", + 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("val1" + "val2" +@@ -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) + ""; + 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 = + "" +@@ -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 +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 +--- + 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 +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 +--- + 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 +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 +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 +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 +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 +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 +Signed-off-by: Brad House +--- + 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~) , + libsnmp-dev, + libssh-dev , +- 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 + #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, "%s\n", + evalid); + if (have_path) +- vty_out(vty, "%s\n", ei->path); ++ vty_out(vty, "%s\n", ++ ei->data_path); + if (have_apptag) + vty_out(vty, "%s\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 + #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-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 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 22cad816b115..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,20 +40,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', 'Data path'], + 'LeafRef': ['Invalid leafref', 'no target instance'], 'When': ['When condition', 'not satisfied'], - 'Pattern': ['pattern', 'does not satisfy'], - 'Mandatory': ['required element', 'Missing'], + 'Pattern': ['pattern', 'Unsatisfied pattern'], + 'Mandatory': ['Mandatory node', 'does not exist'], 'Verify': ['verified'], - 'Range': ['does not satisfy', 'range'], + 'Range': ['Unsatisfied range'], 'MinElements': ['Too few'], 'MaxElements': ['Too many'], - 'UnknownElement': ['Unknown element'], - 'None': [] + 'UnknownElement': ['not found as a child'], + 'None': [], + '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: @@ -112,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() @@ -168,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 """ @@ -211,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 @@ -243,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() @@ -272,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/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" } ] } 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