diff --git a/utils/SConscript b/utils/SConscript
index 11a0767af..2c4e42f67 100644
--- a/utils/SConscript
+++ b/utils/SConscript
@@ -18,7 +18,7 @@ env.Append(CCFLAGS = '-g')
if 'install' in COMMAND_LINE_TARGETS:
VRutilsEnv.Append(SRC_INSTALL_TARGET = '/utils/')
-subdirs = ['dkms']
+subdirs = ['dkms', 'vtest']
for sdir in subdirs:
env.SConscript(sdir + '/SConscript',
exports='VRutilsEnv',
diff --git a/utils/vtest/README.txt b/utils/vtest/README.txt
new file mode 100644
index 000000000..6796dff1a
--- /dev/null
+++ b/utils/vtest/README.txt
@@ -0,0 +1,225 @@
+The xml file specification
+-------------------------
+
+
+
+ Interface test
+
+
+ Add
+ Virtual
+ 4
+ 0
+ 00:01:02:03:04:05
+ 1514
+
+ 0
+
+
+
+
+
+ Get
+ 4
+
+
+ 0
+
+
+ Virtual
+ 4
+ 0
+ 00:01:02:03:04:05
+ 1514
+
+
+
+
+
+
+The generated code from sandesh file processing
+-----------------------------------------------
+
+There is a sandesh compiler that parses the sandesh file and autogenerates
+code to read values from specification file and assign it to corresponding
+variables of the sandesh structure. It takes care of all known data types
+of sandesh message (including list and string). As of now, the compiler
+generates 'vt_gen_sandesh.c' (that contains the above described functionality)
+, 'vt_gen_message_modules.c' (that contains the list of nodes under the message
+module of vtest), 'vt_gen_message_modules.h' (a header file that contains all
+the required declarations) and 'vt_gen_expect.c' that sanitizes the received
+message with expected values from the specification file.
+
+The generator is still evolving. More functionality will be added to generator
+on need basis.
+
+
+Snap of code generated in 'vt_gen_sandesh.c'
+-------------------------------------------
+
+....
+
+void *
+vr_nexthop_req_node(xmlNodePtr node, struct vtest *test)
+{
+ unsigned int list_size;
+ vr_nexthop_req *req;
+
+ req = calloc(sizeof(*req), 1);
+ if (!req)
+ return NULL;
+
+ node = node->xmlChildrenNode;
+ while (node) {
+ if (!node->content || !strlen(node->content)) {
+ return NULL;
+ }
+
+ if (!strncmp(node->name, "h_op", strlen(node->content))) {
+ req->h_op = vt_gen_op(node->content);
+ } else if (!strncmp(node->name, "nhr_type", strlen(node->content))) {
+ req->nhr_type = strtoul(node->content, NULL, 0);
+ } else if (!strncmp(node->name, "nhr_family", strlen(node->content))) {
+
+....
+
+ } else if (!strncmp(node->name, "nhr_label_list", strlen(node->content))) {
+ req->nhr_label_list = vt_gen_list(node->content, GEN_TYPE_U32, &list_size);
+ req->nhr_label_list_size = list_size;
+ }
+ node = node->next;
+ }
+
+ return (void *)req;
+}
+
+Snap of code generated in 'vt_gen_message_modules.c'
+----------------------------------------------------
+
+/*
+ * Auto generated file
+ */
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+struct vt_message_module vt_message_modules[] = {
+ {
+ .vmm_name = "vr_nexthop_req",
+ .vmm_node = vr_nexthop_req_node,
+ .vmm_expect = vr_nexthop_req_expect,
+ .vmm_size = sizeof(vr_nexthop_req),
+ },
+ {
+ .vmm_name = "vr_interface_req",
+ .vmm_node = vr_interface_req_node,
+
+....
+....
+
+ {
+ .vmm_name = "expect",
+ .vmm_node = vt_expect_node,
+ .vmm_size = 0,
+ },
+};
+
+unsigned int vt_message_modules_num =
+ sizeof(vt_message_modules) / sizeof(vt_message_modules[0]);
+
+
+Snap of code generated in 'vt_gen_message_modules.h'
+---------------------------------------------------
+
+/*
+ * Auto generated file
+ */
+#ifndef __VT_GEN_MESSAGE_MODULES_H__
+#define __VT_GEN_MESSAGE_MODULES_H__
+
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+struct vt_message_module {
+ char *vmm_name;
+ void *(*vmm_node)(xmlNodePtr, struct vtest *);
+ bool (*vmm_expect)(xmlNodePtr, struct vtest *, void *);
+ unsigned int vmm_size;
+};
+
+extern void *vr_nexthop_req_node(xmlNodePtr, struct vtest *);
+extern bool vr_nexthop_req_expect(xmlNodePtr, struct vtest *, void *);
+
+....
+....
+
+extern void *vt_return_node(xmlNodePtr, struct vtest *);
+extern void *vt_expect_node(xmlNodePtr, struct vtest *);
+
+#endif
+
+Snap of code generated in 'vt_gen_sandesh_expect.c'
+--------------------------------------------------
+
+/*
+ * Auto generated file
+ */
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+bool
+vr_nexthop_req_expect(xmlNodePtr node, struct vtest *test, void *buf)
+{
+ bool result = true;
+ unsigned int list_size;
+ vr_nexthop_req *req = (vr_nexthop_req *)buf;
+
+ node = node->xmlChildrenNode;
+ while (node) {
+ if (!node->content || !strlen(node->content)) {
+ return NULL;
+ }
+
+....
+....
+
+ } else if (!strncmp(node->name, "nhr_label_list", strlen(node->content))) {
+ result = vt_gen_list_compare(req->nhr_label_list,
+ req->nhr_label_list_size, node->content, GEN_TYPE_U32);
+ }
+
+ if (!result)
+ return result;
+
+ node = node->next;
+ }
+
+ return result;
+}
+
+
diff --git a/utils/vtest/SConscript b/utils/vtest/SConscript
new file mode 100644
index 000000000..6ca32e52d
--- /dev/null
+++ b/utils/vtest/SConscript
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
+#
+import os
+Import('VRutilsEnv')
+env = VRutilsEnv.Clone()
+VTestEnv = env;
+
+src_root = Dir('#').srcnode().abspath
+build_root = Dir(env['TOP']).abspath
+sandesh_file = src_root + '/vrouter/sandesh/vr.sandesh'
+build_dir = build_root + '/vrouter/utils/vtest/'
+
+system_header_path = GetOption('system-header-path')
+if system_header_path:
+ env.Append(CPPPATH = system_header_path + '/include/')
+
+env.Append(CPPPATH = ['./include', build_dir, '/usr/include/libxml2'])
+xml_cpppath = os.popen('xml2-config --cflags | sed \'s/\-I//g\'').read()
+#env.Append(CPPPATH = xml_cpppath)
+
+# CFLAGS
+env.Append(CCFLAGS = '-g')
+
+env.Replace(LIBPATH = env['TOP_LIB'])
+env.Append(LIBPATH = ['.', '../../sandesh'])
+env.Replace(LIBS = ['sandesh-c', 'dp_sandesh_c', 'xml2'])
+
+sandesh_gen = env.Program('sandesh_gen.c')
+
+sandesh_gen_cmd = build_root + '/vrouter/utils/vtest/sandesh_gen' + ' ' + sandesh_file
+sandesh_gen_output = env.Command('sandesh_gen_output', None,
+ sandesh_gen_cmd, chdir=build_dir)
+
+vtest_src = [
+ 'vt_main.c',
+ 'vt_message.c',
+ 'vt_gen_lib.c',
+ 'vt_gen_message_modules.c',
+ 'vt_gen_sandesh.c',
+ 'vt_gen_sandesh_expect.c',
+ ]
+
+
+vtest = env.Program('vtest', vtest_src)
+env.Requires(vtest, sandesh_gen_output)
+
+# Local Variables:
+# mode: python
+# End:
diff --git a/utils/vtest/example.xml b/utils/vtest/example.xml
new file mode 100644
index 000000000..2296baeb7
--- /dev/null
+++ b/utils/vtest/example.xml
@@ -0,0 +1,36 @@
+
+
+ Interface test
+
+
+ Add
+ Virtual
+ 4
+ 0
+ 00:01:02:03:04:05
+ 1514
+
+ 0
+
+
+
+
+
+ Get
+ 4
+
+
+ 0
+
+
+ Virtual
+ 4
+ 0
+ 00:01:02:03:04:05
+ 1514
+
+
+
+
+
+
diff --git a/utils/vtest/include/vt_gen_lib.h b/utils/vtest/include/vt_gen_lib.h
new file mode 100644
index 000000000..611aeb602
--- /dev/null
+++ b/utils/vtest/include/vt_gen_lib.h
@@ -0,0 +1,33 @@
+/*
+ * gen_lib.h --
+ *
+ * Copyright (c) 2015, Juniper Networks, Inc.
+ * All rights reserved
+ */
+
+#ifndef __GEN_LIB_H__
+#define __GEN_LIB_H__
+
+enum gen_types {
+ GEN_TYPE_U8,
+ GEN_TYPE_U16,
+ GEN_TYPE_U32,
+ GEN_TYPE_U64,
+};
+
+unsigned char *vt_gen_skip_space(unsigned char *);
+unsigned char *vt_gen_reach_char(unsigned char *, unsigned char);
+unsigned char *vt_gen_reach_space(unsigned char *);
+bool vt_gen_byte_compare(uint8_t, uint8_t);
+bool vt_gen_short_compare(uint16_t, uint16_t);
+bool vt_gen_int_compare(unsigned int, unsigned int);
+bool vt_gen_int64_compare(uint64_t, uint64_t);
+bool vt_gen_flow_op_compare(int, unsigned char *);
+int vt_gen_flow_op(unsigned char *);
+bool vt_gen_op_compare(int, unsigned char *);
+int vt_gen_op(unsigned char *);
+void *vt_gen_list(unsigned char *, unsigned int, unsigned int *);
+bool vt_gen_list_compare(void *, unsigned int, unsigned char *, unsigned int);
+void *vt_gen_string(char *);
+
+#endif /* __GEN_LIB_H__ */
diff --git a/utils/vtest/include/vtest.h b/utils/vtest/include/vtest.h
new file mode 100644
index 000000000..3e2070c2f
--- /dev/null
+++ b/utils/vtest/include/vtest.h
@@ -0,0 +1,30 @@
+/*
+ * vtest.h --
+ *
+ * Copyright (c) 2015, Juniper Networks, Inc.
+ * All rights reserved
+ */
+#ifndef __VTEST_H__
+#define __VTEST_H__
+
+#define VT_PROG_NAME "vtest"
+#define VT_MAX_TEST_NAME_LEN 128
+#define VT_MAX_TEST_MODULE_NAME_LEN 128
+
+struct vtest {
+ int vtest_return;
+ int vtest_iteration;
+ bool vtest_break;
+ unsigned char *vtest_name;
+ unsigned char *vtest_error_module;
+};
+
+struct vtest_module {
+ unsigned char *vt_name;
+ int (*vt_node)(xmlNodePtr, struct vtest *);
+ int (*vt_init)(void);
+};
+
+extern int vt_message(xmlNodePtr, struct vtest *);
+
+#endif /* __VTEST_H__ */
diff --git a/utils/vtest/sandesh_gen.c b/utils/vtest/sandesh_gen.c
new file mode 100644
index 000000000..9765863a4
--- /dev/null
+++ b/utils/vtest/sandesh_gen.c
@@ -0,0 +1,611 @@
+/*
+ * sandesh_gen.c -- parse the sandesh file and generate code that takes
+ * the xml node and assigns the value to the corresponding member of
+ * the structure
+ *
+ * Copyright (c) 2015, Juniper Networks, Inc.
+ * All rights reserved
+ */
+#include
+#include
+#include
+#include
+
+#define CHARS 80
+
+static char line[CHARS];
+static char nest[] = " ";
+
+static char header[] = {"\
+/*\n\
+ * Auto generated file\n\
+ */\n"
+};
+
+static char includes[] = {"\
+#include \n\n\
+#include \n\n\
+\
+#include \n\
+#include \n\n\
+\
+#include \n\
+#include \n\
+#include \n\n"
+};
+
+static char message_gen[] = {"\
+#include \n\n\
+struct vt_message_module vt_message_modules[] = {\n\
+"};
+
+static char vt_message_module_gen[] = {"\
+struct vt_message_module {\n\
+ char *vmm_name;\n\
+ void *(*vmm_node)(xmlNodePtr, struct vtest *);\n\
+ bool (*vmm_expect)(xmlNodePtr, struct vtest *, void *);\n\
+ unsigned int vmm_size;\n\
+};\n\n"
+};
+
+static char part_gen[] = {"\
+ req = calloc(sizeof(*req), 1);\n\
+ if (!req)\n\
+ return NULL;\n\n\
+ node = node->xmlChildrenNode;\n\
+ while (node) {\n\
+ if (!node->content || !strlen(node->content)) {\n\
+ return NULL;\n\
+ }\n\n\
+"
+};
+
+static char expect_gen[] = {"\
+ node = node->xmlChildrenNode;\n\
+ while (node) {\n\
+ if (!node->content || !strlen(node->content)) {\n\
+ return NULL;\n\
+ }\n\n\
+"
+};
+
+static unsigned char *
+gen_skip_space(unsigned char *string)
+{
+ unsigned int i = 0, len;
+
+ if (!string)
+ return string;
+
+ len = strlen(string);
+ if (!len)
+ return NULL;
+
+ while ((i < len) && isspace(string[i])) {
+ i++;
+ }
+
+ if (i == len)
+ return NULL;
+
+ return &string[i];
+}
+
+static unsigned char *
+gen_reach_char(unsigned char *string, unsigned char c)
+{
+ unsigned int i = 0, len;
+
+ if (!string)
+ return string;
+
+ len = strlen(string);
+ if (!len)
+ return string;
+
+ while ((i < len) && (string[i] != c)) {
+ i++;
+ }
+
+ return &string[i];
+}
+
+static unsigned char *
+gen_reach_space(unsigned char *string)
+{
+ return gen_reach_char(string, ' ');
+}
+
+
+static void
+gen_write(FILE *ofp, unsigned int nesting, unsigned char *string)
+{
+ unsigned int i;
+
+ for (i = 0; i < nesting; i++)
+ fwrite(nest, 1, strlen(nest), ofp);
+
+ fwrite(string, 1, strlen(string), ofp);
+ return;
+}
+
+static void
+gen_raw_write(FILE *ofp, unsigned int nesting,
+ unsigned char *string, unsigned int len)
+{
+ unsigned int i;
+
+ for (i = 0; i < nesting; i++)
+ fwrite(nest, 1, strlen(nest), ofp);
+
+ fwrite(string, 1, len, ofp);
+ return;
+}
+
+static void
+gen_close(FILE *fp, bool header_file)
+{
+ if (header_file) {
+ gen_write(fp, 0, "\n#endif\n");
+ }
+
+ fclose(fp);
+ return;
+}
+
+static FILE *
+gen_open(unsigned char *name, bool header_file)
+{
+ unsigned char c[105];
+ unsigned int i = 0, j = 0;
+ FILE *fp;
+
+ if (header_file && (strlen(name) >= 100)) {
+ perror(name);
+ return NULL;
+ }
+
+ fp = fopen(name, "w+");
+ if (!fp)
+ return fp;
+
+ gen_write(fp, 0, header);
+
+ if (header_file) {
+ gen_write(fp, 0, "#ifndef ");
+ c[i++] = '_';
+ c[i++] = '_';
+ while ((c[i] = name[j]) != '\0') {
+ if (c[i] == '.') {
+ c[i] = '_';
+ } else {
+ c[i] = toupper(c[i]);
+ }
+ i++, j++;
+ }
+ c[i++] = '_';
+ c[i++] = '_';
+
+ c[i] = '\0';
+
+ gen_write(fp, 0, c);
+ gen_write(fp, 0, "\n");
+ gen_write(fp, 0, "#define ");
+ gen_write(fp, 0, c);
+ gen_write(fp, 0, "\n\n");
+ }
+
+ gen_write(fp, 0, includes);
+
+ return fp;
+}
+
+static int
+gen(FILE *fp)
+{
+ bool need_else = false;
+
+ unsigned int len, start = 0, end = 0;
+ unsigned int type_len, sub_type_len = 0, var_len;
+ unsigned int nesting = 0;
+
+ char *marker, *type, *var, *sub_type = NULL;
+ FILE *ofp, *fp_expect, *fp_message, *fp_message_hdr;
+
+ ofp = gen_open("vt_gen_sandesh.c", false);
+ if (!ofp) {
+ perror("vt_gen_sandesh.c");
+ return errno;
+ }
+
+ fp_expect = gen_open("vt_gen_sandesh_expect.c", false);
+ if (!fp_expect) {
+ perror("vt_gen_sandesh_expect.c");
+ return errno;
+ }
+
+ fp_message = gen_open("vt_gen_message_modules.c", false);
+ if (!fp_message) {
+ perror("vt_message_modules.c");
+ return errno;
+ }
+ gen_write(fp_message, 0, message_gen);
+
+ fp_message_hdr = gen_open("vt_gen_message_modules.h", true);
+ if (!fp_message_hdr) {
+ perror("vt_gen_message_modules.h");
+ return errno;
+ }
+ gen_write(fp_message_hdr, 0, vt_message_module_gen);
+
+ while (fgets(line, sizeof(line), fp)) {
+ if (!strncmp("buffer sandesh", line, strlen("buffer sandesh"))) {
+ len = strlen("buffer sandesh");
+ marker = gen_skip_space(&line[len]);
+ if (!marker)
+ break;
+
+ start = marker - line;
+ marker = gen_reach_space(marker);
+ end = marker - line;
+
+ gen_write(fp_message, 1, "{\n");
+ gen_write(fp_message, 2, ".vmm_name");
+ gen_write(fp_message, 2, "=");
+ gen_write(fp_message, 2, "\"");
+ gen_raw_write(fp_message, 0, &line[start], end - start);
+ gen_write(fp_message, 0, "\",\n");
+
+ gen_write(fp_message, 2, ".vmm_node");
+ gen_write(fp_message, 2, "=");
+ gen_raw_write(fp_message, 2, &line[start], end - start);
+ gen_write(fp_message, 0, "_node,\n");
+
+ gen_write(fp_message, 2, ".vmm_expect");
+ gen_write(fp_message, 2, "=");
+ gen_raw_write(fp_message, 2, &line[start], end - start);
+ gen_write(fp_message, 0, "_expect,\n");
+
+ gen_write(fp_message, 2, ".vmm_size");
+ gen_write(fp_message, 2, "=");
+ gen_write(fp_message, 2, "sizeof(");
+ gen_raw_write(fp_message, 0, &line[start], end - start);
+ gen_write(fp_message, 0, "),\n");
+ gen_write(fp_message, 1, "},\n");
+
+ gen_write(fp_message_hdr, 0, "extern void *");
+ gen_raw_write(fp_message_hdr, 0, &line[start], end - start);
+ gen_write(fp_message_hdr, 0, "_node(xmlNodePtr, struct vtest *);\n");
+
+ gen_write(ofp, nesting, "void *\n");
+ gen_raw_write(ofp, nesting, &line[start], end - start);
+ gen_write(ofp, nesting, "_node(xmlNodePtr node, struct vtest *test)\n");
+ gen_write(ofp, nesting, "{\n");
+
+ gen_write(fp_message_hdr, 0, "extern bool ");
+ gen_raw_write(fp_message_hdr, 0, &line[start], end - start);
+ gen_write(fp_message_hdr, 0, "_expect(xmlNodePtr, struct vtest *, void *);\n");
+
+ gen_write(fp_expect, nesting, "bool\n");
+ gen_raw_write(fp_expect, nesting, &line[start], end - start);
+ gen_write(fp_expect, nesting, "_expect(xmlNodePtr node, struct vtest *test, void *buf)\n");
+ gen_write(fp_expect, nesting, "{\n");
+
+ gen_write(ofp, ++nesting, "unsigned int list_size;\n");
+ gen_raw_write(ofp, nesting, &line[start], end - start);
+ gen_write(ofp, 0, " *req;\n\n");
+ gen_write(ofp, 0, part_gen);
+
+ gen_write(fp_expect, nesting, "bool result = true;\n");
+ gen_write(fp_expect, nesting, "unsigned int list_size;\n");
+ gen_raw_write(fp_expect, nesting, &line[start], end - start);
+ gen_write(fp_expect, 0, " *req = (");
+ gen_raw_write(fp_expect, 0, &line[start], end - start);
+ gen_write(fp_expect, 0, " *)buf;\n\n");
+ gen_write(fp_expect, 0, expect_gen);
+
+ /* account for nesting inside part_gen */
+ nesting++;
+ continue;
+ }
+
+ if (start) {
+ if (!strncmp("}", line, strlen("}"))) {
+ start = end = 0;
+ gen_write(ofp, 0, "\n");
+ gen_write(ofp, nesting, "node = node->next;\n");
+
+ gen_write(fp_expect, 0, "\n\n");
+ gen_write(fp_expect, nesting, "if (!result)\n");
+ gen_write(fp_expect, nesting + 1, "return result;\n\n");
+ gen_write(fp_expect, nesting, "node = node->next;\n");
+
+ gen_write(ofp, --nesting, "}\n\n");
+ gen_write(ofp, nesting, "return (void *)req;\n}\n\n");
+
+ gen_write(fp_expect, nesting, "}\n\n");
+ gen_write(fp_expect, nesting, "return result;\n}\n\n");
+
+ --nesting;
+ need_else = false;
+ continue;
+ }
+
+ marker = strchr(line, ':');
+ if (!marker) {
+ return EINVAL;
+ }
+
+ marker = gen_skip_space(++marker);
+ if (!marker) {
+ return EINVAL;
+ }
+
+ type = marker;
+ marker = gen_reach_space(marker);
+ if (!marker) {
+ return EINVAL;
+ }
+ type_len = marker - type;
+
+ if (!strncmp(type, "list", strlen("list"))) {
+ marker = type + strlen("list");;
+ marker = gen_skip_space(marker);
+ if (!marker) {
+ return EINVAL;
+ }
+
+ if (*marker != '<') {
+ return EINVAL;
+ }
+
+ sub_type = ++marker;
+ marker = gen_reach_char(marker, '>');
+ if (!marker) {
+ return EINVAL;
+ }
+ sub_type_len = marker - sub_type;
+ marker = gen_reach_space(marker);
+ if (!marker) {
+ return EINVAL;
+ }
+ }
+
+ marker = gen_skip_space(marker);
+ if (!marker) {
+ return EINVAL;
+ }
+
+ var = marker;
+ marker = gen_reach_char(marker, ';');
+ if (!marker) {
+ return EINVAL;
+ }
+ var_len = marker - var;
+
+ if (need_else) {
+ gen_write(ofp, 0, " else ");
+ gen_write(fp_expect, 0, " else ");
+ } else {
+ gen_write(ofp, nesting, "");
+ gen_write(fp_expect, nesting, "");
+ }
+
+ gen_write(ofp, 0, "if (!strncmp(node->name, ");
+ gen_write(ofp, 0, "\"");
+ gen_raw_write(ofp, 0, var, var_len);
+ gen_write(ofp, 0, "\"");
+ gen_write(ofp, 0, ", strlen(node->content))) {\n");
+
+ gen_write(fp_expect, 0, "if (!strncmp(node->name, ");
+ gen_write(fp_expect, 0, "\"");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, "\"");
+ gen_write(fp_expect, 0, ", strlen(node->content))) {\n");
+
+ gen_write(ofp, ++nesting, "req->");
+ gen_raw_write(ofp, 0, var, var_len);
+ gen_write(ofp, 0, " = ");
+
+ gen_write(fp_expect, nesting, "");
+
+ if (!strncmp(type, "i32", strlen("i32"))) {
+ gen_write(ofp, 0, "strtoul(node->content, NULL, 0);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_int_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 4, "strtoul(node->content, NULL, 0)");
+ gen_write(fp_expect, 0, ");\n");
+
+ } else if (!strncmp(type, "u32", strlen("u32"))) {
+ gen_write(ofp, 0, "strtoul(node->content, NULL, 0);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_int_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 4, "strtoul(node->content, NULL, 0)");
+ gen_write(fp_expect, 0, ");\n");
+
+ } else if (!strncmp(type, "i64", strlen("i64"))) {
+ gen_write(ofp, 0, "strtoull(node->content, NULL, 0);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_int64_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 4, "strtoul(node->content, NULL, 0)");
+ gen_write(fp_expect, 0, ");\n");
+
+ } else if (!strncmp(type, "i16", strlen("i16"))) {
+ gen_write(ofp, 0, "strtoul(node->content, NULL, 0);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_short_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 4, "strtoul(node->content, NULL, 0));\n");
+
+ } else if (!strncmp(type, "byte", strlen("byte"))) {
+ gen_write(ofp, 0, "strtoul(node->content, NULL, 0);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_byte_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 4, "strtoul(node->content, NULL, 0)");
+ gen_write(fp_expect, 0, ");\n");
+
+ } else if (!strncmp(type, "sandesh_op", strlen("sandesh_op"))) {
+ gen_write(ofp, 0, "vt_gen_op(node->content);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_op_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ", node->content);\n");
+
+ } else if (!strncmp(type, "flow_op", strlen("flow_op"))) {
+ gen_write(ofp, 0, "vt_gen_flow_op(node->content);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_flow_op_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ", node->content);\n");
+
+ } else if (!strncmp(type, "list", strlen("list"))) {
+ if (!strncmp(sub_type, "byte", strlen("byte"))) {
+ gen_write(ofp, 0, "vt_gen_list(node->content, GEN_TYPE_U8, &list_size);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_list_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 2, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, "_size");
+ gen_write(fp_expect, 0, ", node->content, GEN_TYPE_U8);\n");
+
+ } else if (!strncmp(sub_type, "i16", strlen("i32"))) {
+ gen_write(ofp, 0, "vt_gen_list(node->content, GEN_TYPE_U16, &list_size);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_list_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 2, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, "_size");
+ gen_write(fp_expect, 0, ", node->content, GEN_TYPE_U16);\n");
+
+ } else if (!strncmp(sub_type, "i32", strlen("i32"))) {
+ gen_write(ofp, 0, "vt_gen_list(node->content, GEN_TYPE_U32, &list_size);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_list_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 2, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, "_size");
+ gen_write(fp_expect, 0, ", node->content, GEN_TYPE_U32);\n");
+
+ } else if (!strncmp(sub_type, "i64", strlen("i32"))) {
+ gen_write(ofp, 0, "vt_gen_list(node->content, GEN_TYPE_U64, &list_size);\n");
+
+ gen_write(fp_expect, 0, "result = vt_gen_list_compare(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ",\n");
+ gen_write(fp_expect, nesting + 2, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, "_size");
+ gen_write(fp_expect, 0, ", node->content, GEN_TYPE_U64);\n");
+
+ }
+
+ gen_write(ofp, nesting, "req->");
+ gen_raw_write(ofp, 0, var, var_len);
+ gen_write(ofp, 0, "_size = list_size;\n");
+ } else if (!strncmp(type, "string", strlen("string"))) {
+ gen_write(ofp, 0, "vt_gen_string(node->content);\n");
+
+ gen_write(fp_expect, 0, "result = strcmp(");
+ gen_write(fp_expect, 0, "req->");
+ gen_raw_write(fp_expect, 0, var, var_len);
+ gen_write(fp_expect, 0, ", node->content);\n");
+
+ }
+
+ gen_write(ofp, --nesting, "}");
+ gen_write(fp_expect, nesting, "}");
+ need_else = true;
+ }
+ }
+
+ gen_write(fp_message, 1, "{\n");
+ gen_write(fp_message, 2, ".vmm_name");
+ gen_write(fp_message, 2, "=");
+ gen_write(fp_message, 2, "\"return\",\n");
+ gen_write(fp_message, 2, ".vmm_node");
+ gen_write(fp_message, 2, "=");
+ gen_write(fp_message, 2, "vt_return_node,\n");
+ gen_write(fp_message, 2, ".vmm_size");
+ gen_write(fp_message, 2, "=");
+ gen_write(fp_message, 2, "0,\n");
+ gen_write(fp_message, 1, "},\n");
+ gen_write(fp_message_hdr, 0, "extern void *");
+ gen_write(fp_message_hdr, 0, "vt_return_node(xmlNodePtr, struct vtest *);\n");
+
+ gen_write(fp_message, 1, "{\n");
+ gen_write(fp_message, 2, ".vmm_name");
+ gen_write(fp_message, 2, "=");
+ gen_write(fp_message, 2, "\"expect\",\n");
+ gen_write(fp_message, 2, ".vmm_node");
+ gen_write(fp_message, 2, "=");
+ gen_write(fp_message, 2, "vt_expect_node,\n");
+ gen_write(fp_message, 2, ".vmm_size");
+ gen_write(fp_message, 2, "=");
+ gen_write(fp_message, 2, "0,\n");
+ gen_write(fp_message, 1, "},\n");
+ gen_write(fp_message_hdr, 0, "extern void *");
+ gen_write(fp_message_hdr, 0, "vt_expect_node(xmlNodePtr, struct vtest *);\n");
+
+ gen_write(fp_message, 0, "};\n\n");
+ gen_write(fp_message, 0,
+ "unsigned int vt_message_modules_num = \n");
+ gen_write(fp_message, 2, "sizeof(vt_message_modules) / sizeof(vt_message_modules[0]);\n\n");
+ gen_close(fp_message, false);
+ gen_close(fp_message_hdr, true);
+
+ return 0;
+}
+
+static void
+Usage(void)
+{
+ printf("Usage: sandesh_gen \n");
+ return;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ret;
+ FILE *fp;
+
+ if (argc != 2) {
+ Usage();
+ return EINVAL;
+ }
+
+ fp = fopen(argv[1], "r");
+ if (!fp) {
+ perror(argv[1]);
+ return errno;
+ }
+
+ ret = gen(fp);
+ return ret;
+}
diff --git a/utils/vtest/vt_gen_lib.c b/utils/vtest/vt_gen_lib.c
new file mode 100644
index 000000000..efe8b5d31
--- /dev/null
+++ b/utils/vtest/vt_gen_lib.c
@@ -0,0 +1,298 @@
+/*
+ * vt_gen_lib.c --
+ *
+ * Copyright(c) 2015, Juniper Networks, Inc.
+ * All rights reserved
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+unsigned char *
+vt_gen_skip_space(unsigned char *string)
+{
+ unsigned int i = 0, len;
+
+ if (!string)
+ return string;
+
+ len = strlen(string);
+ if (!len)
+ return string;
+
+ while ((i < len) && isspace(string[i])) {
+ i++;
+ }
+
+ if (i == len)
+ return NULL;
+
+ return &string[i];
+}
+
+unsigned char *
+vt_gen_reach_char(unsigned char *string, unsigned char c)
+{
+ unsigned int i = 0, len;
+
+ if (!string)
+ return string;
+
+ len = strlen(string);
+ if (!len)
+ return string;
+
+ while ((i < len) && (string[i] != c)) {
+ i++;
+ }
+
+ return &string[i];
+}
+
+unsigned char *
+vt_gen_reach_space(unsigned char *string)
+{
+ return vt_gen_reach_char(string, ' ');
+}
+
+bool
+vt_gen_byte_compare(uint8_t one, uint8_t two)
+{
+ return one == two;
+}
+
+bool
+vt_gen_short_compare(uint16_t one, uint16_t two)
+{
+ return one == two;
+}
+
+bool
+vt_gen_int_compare(unsigned int one, unsigned int two)
+{
+ return one == two;
+}
+
+bool
+vt_gen_int64_compare(uint64_t one, uint64_t two)
+{
+ return one == two;
+}
+
+bool
+vt_gen_flow_op_compare(int op, unsigned char *string)
+{
+ int expected_op = 0;
+
+ if (!string)
+ return -1;
+
+ if (!strncasecmp(string, "flow_set", strlen("flow_set"))) {
+ expected_op = FLOW_OP_FLOW_SET;
+ } else if (!strncasecmp(string, "flow_table_get",
+ strlen("flow_table_get"))) {
+ expected_op = FLOW_OP_FLOW_TABLE_GET;
+ }
+
+ return op == expected_op;
+}
+
+int
+vt_gen_flow_op(unsigned char *string)
+{
+ if (!string)
+ return -1;
+
+ if (!strncasecmp(string, "flow_set", strlen("flow_set"))) {
+ return FLOW_OP_FLOW_SET;
+ } else if (!strncasecmp(string, "flow_table_get",
+ strlen("flow_table_get"))) {
+ return FLOW_OP_FLOW_TABLE_GET;
+ }
+
+ return -1;
+}
+
+bool
+vt_gen_op_compare(int op, unsigned char *string)
+{
+ int expected_op = 0;
+
+ if (!strncasecmp(string, "Add", strlen("Add"))) {
+ expected_op = SANDESH_OP_ADD;
+ } else if (!strncasecmp(string, "Get", strlen("Get"))) {
+ expected_op = SANDESH_OP_GET;
+ } else if (!strncasecmp(string, "Delete", strlen("Delete"))) {
+ expected_op = SANDESH_OP_DELETE;
+ } else if (!strncasecmp(string, "Dump", strlen("Dump"))) {
+ expected_op = SANDESH_OP_DUMP;
+ } else if (!strncasecmp(string, "Reset", strlen("Reset"))) {
+ expected_op = SANDESH_OP_RESET;
+ }
+
+ return op == expected_op;
+}
+
+int
+vt_gen_op(unsigned char *string)
+{
+ if (!string)
+ return -1;
+
+ if (!strncasecmp(string, "Add", strlen("Add"))) {
+ return SANDESH_OP_ADD;
+ } else if (!strncasecmp(string, "Get", strlen("Get"))) {
+ return SANDESH_OP_GET;
+ } else if (!strncasecmp(string, "Delete", strlen("Delete"))) {
+ return SANDESH_OP_DELETE;
+ } else if (!strncasecmp(string, "Dump", strlen("Dump"))) {
+ return SANDESH_OP_DUMP;
+ } else if (!strncasecmp(string, "Reset", strlen("Reset"))) {
+ return SANDESH_OP_RESET;
+ }
+
+ return -1;
+}
+
+void *
+vt_gen_list(unsigned char *list, unsigned int type, unsigned int *list_size)
+{
+ unsigned int i = 0, j = 0, type_size;
+ unsigned int string_size;
+ unsigned char *local_list = list, *end;
+ char *tmp;
+
+ unsigned char *store;
+ uint8_t *store_b;
+ uint16_t *store_i16;
+ uint32_t *store_i32;
+ uint64_t *store_i64;
+
+ *list_size = 0;
+ if (!list)
+ return NULL;
+
+ switch (type) {
+ case GEN_TYPE_U8:
+ type_size = 1;
+ break;
+
+ case GEN_TYPE_U16:
+ type_size = 2;
+ break;
+
+ case GEN_TYPE_U32:
+ type_size = 4;
+ break;
+
+ case GEN_TYPE_U64:
+ type_size = 8;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ string_size = strlen(list);
+ end = list + string_size;
+
+ while (1) {
+ local_list = vt_gen_skip_space(local_list);
+ if (!local_list)
+ break;
+
+ j++;
+
+ local_list = vt_gen_reach_space(local_list);
+ if (!local_list)
+ break;
+ }
+
+
+ store = calloc(j, type_size);
+ if (!store)
+ return NULL;
+
+ *list_size = type_size * j;
+
+ store_b = (uint8_t *)store;
+ store_i16 = (uint16_t *)store;
+ store_i32 = (uint32_t *)store;
+ store_i64 = (uint64_t *)store;
+
+ local_list = list;
+ for (i = 0; i < j; i++) {
+ switch (type) {
+ case GEN_TYPE_U8:
+ store_b[i] = strtoul(local_list, &tmp, 0);
+ break;
+
+ case GEN_TYPE_U16:
+ store_i16[i] = strtoul(local_list, &tmp, 0);
+ break;
+
+ case GEN_TYPE_U32:
+ store_i32[i] = strtoul(local_list, &tmp, 0);
+ break;
+
+ case GEN_TYPE_U64:
+ store_i64[i] = strtoul(local_list, &tmp, 0);
+ break;
+ }
+
+ if (errno == ERANGE) {
+ free(store);
+ return NULL;
+ }
+
+ local_list = tmp;
+ local_list = vt_gen_reach_space(local_list);
+ if (!local_list)
+ break;
+ }
+
+ return store;
+}
+
+bool
+vt_gen_list_compare(void *list, unsigned int list_size,
+ unsigned char *string, unsigned int type)
+{
+ unsigned int buf_size;
+ void *buf;
+
+ buf = vt_gen_list(string, type, &buf_size);
+ if (!buf)
+ return false;
+
+ if (buf_size != list_size)
+ return false;
+
+ if (memcmp(list, buf, list_size))
+ return false;
+
+ return true;
+}
+
+void *
+vt_gen_string(char *string)
+{
+ unsigned int string_len;
+ char *store;
+
+ string_len = strlen(string);
+ store = malloc(string_len + 1);
+ if (!store)
+ return NULL;
+
+ strcpy(store, string);
+
+ return store;
+}
+
diff --git a/utils/vtest/vt_main.c b/utils/vtest/vt_main.c
new file mode 100644
index 000000000..e3719fddd
--- /dev/null
+++ b/utils/vtest/vt_main.c
@@ -0,0 +1,214 @@
+/*
+ * vt_main.c -- test main function
+ *
+ * Copyright (c) 2015, Juniper Networks, Inc.
+ * All rights reserved
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+static int vt_test_name(xmlNodePtr, struct vtest *);
+
+struct vtest_module vt_modules[] = {
+ { .vt_name = "test_name",
+ .vt_node = vt_test_name,
+ },
+ {
+ .vt_name = "message",
+ .vt_node = vt_message,
+ },
+};
+
+#define VTEST_NUM_MODULES sizeof(vt_modules) / sizeof(vt_modules[0])
+
+void
+vt_error(unsigned char *module, struct vtest *test, int ret)
+{
+ test->vtest_return = ret;
+ test->vtest_break = true;
+ test->vtest_error_module = malloc(strlen(module) + 1);
+ if (!test->vtest_error_module) {
+ printf("(Unrelated)Internal metadata allocation failure\n");
+ return;
+ }
+ strcpy(test->vtest_error_module, module);
+
+ return;
+}
+
+static int
+vt_test_name(xmlNodePtr node, struct vtest *test)
+{
+ xmlNodePtr child;
+
+ child = node->xmlChildrenNode;
+ if (!child || !child->content || !strlen(child->content))
+ return;
+
+ printf("Running \"%s\"\n", (char *)child->content);
+
+ return 0;
+}
+
+static int
+vt_process_node(xmlNodePtr node, struct vtest *test)
+{
+ unsigned int i;
+
+ struct vtest_module *vt;
+
+ for (i = 0; i < VTEST_NUM_MODULES; i++) {
+ if (!strncmp((char *)node->name, vt_modules[i].vt_name,
+ strlen(vt_modules[i].vt_name))) {
+ return vt_modules[i].vt_node(node, test);
+ }
+ }
+
+ if (i == VTEST_NUM_MODULES) {
+ printf("Unrecognized node %s in xml\n", node->name);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+vt_tree_traverse(xmlNodePtr node, struct vtest *test)
+{
+ int ret;
+ xmlNodePtr child;
+
+ while (node) {
+ if (node->type == XML_ELEMENT_NODE) {
+ ret = vt_process_node(node, test);
+ if (ret)
+ return ret;
+ }
+
+ node = node->next;
+ }
+
+ return;
+}
+
+static int
+vt_parse_file(char *file, struct vtest *test)
+{
+ xmlDocPtr doc;
+ xmlNodePtr node;
+
+ doc = xmlParseFile(file);
+ if (!doc) {
+ printf("xmlParseFile %s failed\n", file);
+ return EINVAL;
+ }
+
+ node = xmlDocGetRootElement(doc);
+ if (!node) {
+ printf("NULL Root Element\n");
+ return EINVAL;
+ }
+
+ vt_tree_traverse(node->xmlChildrenNode, test);
+
+ return 0;
+}
+
+static int
+vt_init(struct vtest *test)
+{
+ int error;
+
+ memset(test, 0, sizeof(*test));
+
+ test->vtest_return = 0;
+ test->vtest_iteration = 0;
+ test->vtest_break = 0;
+ test->vtest_name = calloc(VT_MAX_TEST_NAME_LEN, 1);
+ if (!test->vtest_name) {
+ error = ENOMEM;
+ goto error;
+ }
+
+ test->vtest_error_module = calloc(VT_MAX_TEST_MODULE_NAME_LEN, 1);
+ if (!test->vtest_error_module) {
+ error = ENOMEM;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (test->vtest_name) {
+ free(test->vtest_name);
+ test->vtest_name = NULL;
+ }
+
+ if (test->vtest_error_module) {
+ free(test->vtest_error_module);
+ test->vtest_error_module = NULL;
+ }
+
+ return error;
+}
+
+static void
+vt_Usage(void)
+{
+ printf("Usage: %s \n",
+ VT_PROG_NAME);
+ return;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ret;
+ unsigned int i;
+ char *xml_file;
+
+ struct stat stat_buf;
+ struct vtest vtest;
+
+ if (argc != 2) {
+ vt_Usage();
+ return EINVAL;
+ }
+
+ xml_file = argv[1];
+ ret = stat(xml_file, &stat_buf);
+ if (ret) {
+ perror(xml_file);
+ return errno;
+ }
+
+ vt_init(&vtest);
+
+ for (i = 0; i < VTEST_NUM_MODULES; i++) {
+ if (vt_modules[i].vt_init) {
+ ret = vt_modules[i].vt_init();
+ if (ret) {
+ printf("%s: %s init failed\n", VT_PROG_NAME,
+ vt_modules[i].vt_name);
+ return ret;
+ }
+ }
+ }
+
+ vt_parse_file(xml_file, &vtest);
+
+ return 0;
+}
diff --git a/utils/vtest/vt_message.c b/utils/vtest/vt_message.c
new file mode 100644
index 000000000..b9cba7d98
--- /dev/null
+++ b/utils/vtest/vt_message.c
@@ -0,0 +1,92 @@
+/*
+ * vt_message.c -- the messaging module
+ *
+ * Copyright (c) 2015, Juniper Networks, Inc.
+ * All rights reserved
+ */
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+#define RETURN "message.return"
+#define EXPECT "message.expect"
+#define MESSAGE "message"
+
+extern struct vt_message_module *vt_message_modules;
+extern unsigned int vt_message_modules_num;
+
+void *
+vt_expect_node(xmlNodePtr node, struct vtest *test)
+{
+ unsigned int i;
+ bool result;
+ /* TODO */
+ void *buf = NULL;
+
+ node = node->xmlChildrenNode;
+ while (node) {
+ for (i = 0; i < vt_message_modules_num; i++) {
+ if (!strncmp(node->name, vt_message_modules[i].vmm_name,
+ strlen(vt_message_modules[i].vmm_name))) {
+ result = vt_message_modules[i].vmm_expect(node, test, buf);
+ if (!result) {
+ vt_error(EXPECT, test, -EINVAL);
+ break;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void *
+vt_return_node(xmlNodePtr node, struct vtest *test)
+{
+ int exp_ret;
+
+ exp_ret = strtoul(node->content, NULL, 0);
+ if (exp_ret != test->vtest_return) {
+ printf("Expected return %d. Actual return %d\n",
+ exp_ret, test->vtest_return);
+ vt_error(RETURN, test, -EINVAL);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+int
+vt_message(xmlNodePtr node, struct vtest *test)
+{
+ int ret = 0;
+ unsigned int i;
+
+ void *buf;
+
+ node = node->xmlChildrenNode;
+ while (node) {
+ for (i = 0; i < vt_message_modules_num; i++) {
+ if (!strncmp(node->name, vt_message_modules[i].vmm_name,
+ strlen(vt_message_modules[i].vmm_name))) {
+ buf = vt_message_modules[i].vmm_node(node, test);
+ if (!buf && vt_message_modules[i].vmm_size) {
+ return -ENOMEM;
+ }
+ }
+ }
+
+ if (test->vtest_break)
+ return 0;
+
+ node = node->next;
+ }
+
+ return -1;
+}
+