diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d0b16b7d..fd5d381a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,6 +6,7 @@ stages: - static_code_analysis - unit_test - functional_test + - libbbf_ubus_test - memory_test - uspd @@ -55,6 +56,23 @@ run_functional_api_test: - timestamp.log - functional-api-test-coverage.xml +run_libbbf_ubus_test: + stage: libbbf_ubus_test + image: iopsys/code-analysis:latest + allow_failure: false + script: + - "./gitlab-ci/setup.sh" + - "./gitlab-ci/libbbf_ubus_api_test.sh" + + artifacts: + when: always + reports: + junit: ./report/tap.xml + paths: + - timestamp.log + - memory-report.xml + - api-result.log + run_tools_test: stage: unit_test image: iopsys/code-analysis:latest diff --git a/Makefile.am b/Makefile.am index fa5187da..950975af 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,9 +2,11 @@ MAINTAINERCLEANFILES = Makefile.in libbbfdm_includedir = $(includedir)/libbbfdm libbbf_api_includedir = $(includedir)/libbbf_api +libbbf_ubus_includedir = $(includedir)/libbbf_ubus libbbfdm_tr181_includedir = $(libbbfdm_includedir) libbbfdm_include_HEADERS = *.h libbbf_api_include_HEADERS = libbbf_api/*.h +libbbf_ubus_include_HEADERS = include/*.h -SUBDIRS = bin +SUBDIRS = bin libbbf_ubus diff --git a/README.md b/README.md index 1ab3434d..9913d6e5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ # BroadBand Forum Data Models (BBFDM) `bbfdm` is a data model library implementation which includes a list of objects, parameters and operates used for CPE management through remote control protocols such as [TR-069/CWMP](https://cwmp-data-models.broadband-forum.org/) or [TR-369/USP](https://usp.technology/). +This package comprises of the below libraries: + +| Library | Description | +| ------- | ------------------------------------------------- | +| libbbfdm | This provides the mechanism to add new parameters or extend the existing DM tree using json plugin or shared library plugin. | +| libbbf_api | This provides the APIs for UCI, Ubus, JSON, CLI and memory management. | +| libbbf_ubus | This library helps to expose the datamodel directly over ubus. Application can expose any datamodel(need not be part of "Device."/TR-181) using this library. | + +Note: Applications that use libbbf_ubus to expose datamodel, not required to use libbbfdm. ## Design of bbfdm @@ -19,6 +28,7 @@ │   ├── openwrt │   └── vendor.h ├── libbbf_api +├── libbbf_ubus ├── scripts └── tools ``` @@ -37,6 +47,8 @@ - `libbbf_api` folder which contains the source code of all API functions (UCI, Ubus, JSON, CLI and memory management) +- `libbbf_ubus` folder which contains the source code of all API functions helps in exposing datamodel directly over ubus + - `scripts` folder which contains the Diagnostics scripts - `tools` folder which contains some tools to generate Data Model in C, JSON, XML and Excel format @@ -1034,9 +1046,21 @@ The input json file should be defined as follow: - For more examples of tools input json file, you can see this link: [tools_input.json](./devel/tools/tools_input.json) -## Dependencies +# How to expose datamodel over ubus directly with the help of libbbf APIs -To successfully build libbbfdm, the following libraries are needed: +`Libbbf_ubus` is the library that helps in exposing the datamodel over ubus directly using libbbf_api. +Application using `libbbf_ubus`, shall not use the `libbbfdm` library because all needed operations from `libbbfdm` library has been internally handled in `libbbf_ubus`. + +To identify the mechanism of exposing datamodel directly over ubus please refer to the sample code [dmtest.c](./test/dynamicdm_ubus_test/bbf_ubus.c) + +For more info you can see the schemas at: + +- Raw schema [link](./schemas/dmtest.json) +- Markdown schema [link](./docs/api/dmtest.md) + +## Dependencies of of libbbfdm and libbbf_ubus + +To successfully build libbbfdm or libbbf_ubus, the following libraries are needed: | Dependency | Link | License | | ----------- | ------------------------------------------- | -------------- | @@ -1047,3 +1071,4 @@ To successfully build libbbfdm, the following libraries are needed: | libcurl | https://dl.uxnr.de/mirror/curl | MIT | | libtrace | https://github.com/apietila/libtrace.git | GPLv2 | | libbbf_api | https://dev.iopsys.eu/iopsys/bbf.git | LGPL 2.1 | + diff --git a/configure.ac b/configure.ac index abd88a43..a8d1c07c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,10 @@ AC_INIT([libbbfdm], [0.1], [mohamed.kallel@pivasoftware.com]) +AC_CONFIG_MACRO_DIR([m4]) + AM_INIT_AUTOMAKE([foreign subdir-objects]) +AC_CONFIG_SUBDIRS([libbbf_ubus]) + AC_ARG_ENABLE(tr181, [AS_HELP_STRING([--enable-tr181], [enable tr181 device feature])], AC_DEFINE(BBF_TR181),) AM_CONDITIONAL([BBF_TR181],[test "x$enable_tr181" = "xyes"]) diff --git a/dmentry.c b/dmentry.c index b0ba0df9..896b5e59 100644 --- a/dmentry.c +++ b/dmentry.c @@ -16,7 +16,11 @@ #include "dmdynamicjson.h" #include "dmdynamiclibrary.h" #include "dmdynamicvendor.h" + +#ifdef BBF_TR181 #include "device.h" +#endif /* BBF_TR181 */ + #include "dmbbfcommon.h" LIST_HEAD(head_package_change); @@ -129,7 +133,7 @@ int usp_fault_map(int fault) return out_fault; } -static int dm_ctx_init_custom(struct dmctx *ctx, unsigned int instance_mode, int custom) +static int dm_ctx_init_custom(struct dmctx *ctx, unsigned int instance_mode, DMOBJ *tEntryObj, int custom) { if (custom == CTX_INIT_ALL) bbf_uci_init(); @@ -138,7 +142,7 @@ static int dm_ctx_init_custom(struct dmctx *ctx, unsigned int instance_mode, int INIT_LIST_HEAD(&ctx->set_list_tmp); INIT_LIST_HEAD(&ctx->list_fault_param); ctx->instance_mode = instance_mode; - ctx->dm_entryobj = tEntry181Obj; + ctx->dm_entryobj = tEntryObj; ctx->dm_version = DEFAULT_DMVERSION; ctx->end_session_flag = 0; return 0; @@ -162,10 +166,19 @@ void dm_config_ubus(struct ubus_context *ctx) dmubus_configure(ctx); } +int dm_ctx_init_entry(struct dmctx *ctx, DMOBJ *tEntryObj, unsigned int instance_mode) +{ + return dm_ctx_init_custom(ctx, instance_mode, tEntryObj, CTX_INIT_ALL); +} + int dm_ctx_init(struct dmctx *ctx, unsigned int instance_mode) { +#ifdef BBF_TR181 dmubus_clean_endlife_entries(); - return dm_ctx_init_custom(ctx, instance_mode, CTX_INIT_ALL); + return dm_ctx_init_custom(ctx, instance_mode, tEntry181Obj, CTX_INIT_ALL); +#else + return 0; +#endif /* BBF_TR181 */ } int dm_ctx_clean(struct dmctx *ctx) @@ -182,7 +195,11 @@ int dm_ctx_init_cache(int time) int dm_ctx_init_sub(struct dmctx *ctx, unsigned int instance_mode) { - return dm_ctx_init_custom(ctx, instance_mode, CTX_INIT_SUB); +#ifdef BBF_TR181 + return dm_ctx_init_custom(ctx, instance_mode, tEntry181Obj, CTX_INIT_SUB); +#else + return 0; +#endif /* BBF_TR181 */ } int dm_ctx_clean_sub(struct dmctx *ctx) @@ -197,11 +214,12 @@ int dm_get_supported_dm(struct dmctx *ctx, char *path, bool first_level, schema_ // Load dynamic objects and parameters load_dynamic_arrays(ctx); - if (strlen(path) == 0) - path = "Device."; - - if (path[strlen(path) - 1] != '.') - return usp_fault_map(USP_FAULT_INVALID_PATH); + if (strlen(path) == 0) { + path = ""; + } else { + if (path[strlen(path) - 1] != '.') + return usp_fault_map(USP_FAULT_INVALID_PATH); + } ctx->in_param = path; @@ -545,7 +563,9 @@ static void load_dynamic_arrays(struct dmctx *ctx) static void free_dynamic_arrays(void) { +#ifdef BBF_TR181 DMOBJ *root = tEntry181Obj; + DMNODE node = {.current_object = ""}; #ifdef BBFDM_ENABLE_JSON_PLUGIN @@ -560,6 +580,7 @@ static void free_dynamic_arrays(void) free_vendor_dynamic_arrays(tEntry181Obj); #endif free_dm_browse_node_dynamic_object_tree(&node, root); +#endif /* BBF_TR181 */ } void bbf_dm_cleanup(void) @@ -568,3 +589,11 @@ void bbf_dm_cleanup(void) dm_dynamic_cleanmem(&main_memhead); free_dynamic_arrays(); } + +void dm_cleanup_dynamic_entry(DMOBJ *root) +{ + DMNODE node = {.current_object = ""}; + + dm_dynamic_cleanmem(&main_memhead); + free_dm_browse_node_dynamic_object_tree(&node, root); +} diff --git a/dmentry.h b/dmentry.h index 5cf2e602..d5fd46ac 100644 --- a/dmentry.h +++ b/dmentry.h @@ -33,6 +33,7 @@ typedef enum { int dm_ctx_init(struct dmctx *ctx, unsigned int instance_mode); int dm_ctx_init_sub(struct dmctx *ctx, unsigned int instance_mode); +int dm_ctx_init_entry(struct dmctx *ctx, DMOBJ tEntryObj[], unsigned int instance_mode); int dm_entry_param_method(struct dmctx *ctx, int cmd, char *inparam, char *arg1, char *arg2); int dm_entry_apply(struct dmctx *ctx, int cmd, char *arg1); int dm_entry_restart_services(void); @@ -62,5 +63,6 @@ void bbf_dm_cleanup(void); * object illegal access. */ int dm_debug_browse_path(char *buff, size_t len); +void dm_cleanup_dynamic_entry(DMOBJ *root); #endif diff --git a/docs/api/dmtest.md b/docs/api/dmtest.md new file mode 100644 index 00000000..957cb79f --- /dev/null +++ b/docs/api/dmtest.md @@ -0,0 +1,1937 @@ +# dmtest Schema + +``` +https://dev.iopsys.eu/iopsys/uspd/-/blob/devel/docs/api/dmtest.json +``` + +| Custom Properties | Additional Properties | +| ----------------- | --------------------- | +| Forbidden | Forbidden | + +# dmtest + +| List of Methods | +| ----------------------------------------- | +| [add_object](#add_object) | Method | dmtest (this schema) | +| [del_object](#del_object) | Method | dmtest (this schema) | +| [get](#get) | Method | dmtest (this schema) | +| [get_supported_dm](#get_supported_dm) | Method | dmtest (this schema) | +| [operate](#operate) | Method | dmtest (this schema) | +| [set](#set) | Method | dmtest (this schema) | +| [transaction_abort](#transaction_abort) | Method | dmtest (this schema) | +| [transaction_commit](#transaction_commit) | Method | dmtest (this schema) | +| [transaction_start](#transaction_start) | Method | dmtest (this schema) | + +## add_object + +### Add a new object instance + +Add a new object in multi instance object + +`add_object` + +- type: `Method` + +### add_object Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | **Required** | +| `output` | | **Required** | + +#### input + +`input` + +- is **required** +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | Default | +| -------- | ------ | ------------ | -------- | +| `path` | string | **Required** | | +| `proto` | string | Optional | `"both"` | + +#### path + +Complete object element path as per TR181 + +`path` + +- is **required** +- type: reference + +##### path Type + +`string` + +- minimum length: 6 characters +- maximum length: 1024 characters + +##### path Examples + +```json +Device. +``` + +```json +Device.DeviceInfo.Manufacturer +``` + +```json +Device.WiFi.SSID.1. +``` + +```json +Device.WiFi. +``` + +#### proto + +`proto` + +- is optional +- type: reference +- default: `"both"` + +##### proto Type + +`string` + +The value of this property **must** be equal to one of the [known values below](#add_object-known-values). + +##### proto Known Values + +| Value | +| ----- | +| usp | +| cwmp | +| both | + +### Ubus CLI Example + +``` +ubus call dmtest add_object {"path":"Lorem aliqua laboris","proto":"cwmp"} +``` + +### JSONRPC Example + +```json +{ + "jsonrpc": "2.0", + "id": 0, + "method": "call", + "params": ["", "dmtest", "add_object", { "path": "Lorem aliqua laboris", "proto": "cwmp" }] +} +``` + +#### output + +`output` + +- is **required** +- type: complex + +##### output Type + +Unknown type ``. + +```json +{ + "oneof": [ + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "required": ["parameters"], + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "required": ["parameter", "status"], + "properties": { + "parameter": { + "$ref": "#/definitions/path_t" + }, + "status": { + "type": "boolean" + }, + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be determined by fault code" + }, + "instance": { + "type": "string" + } + } + } + ] + } + } + } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { + "type": "string", + "enum": ["0", "1"] + }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { + "type": "string", + "enum": ["async", "sync"] + }, + "instance_t": { + "description": "Multi object instances", + "type": "string", + "minLength": 6, + "maxLength": 256 + }, + "proto_t": { + "type": "string", + "default": "both", + "enum": ["usp", "cwmp", "both"] + }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { + "type": "integer", + "minimum": 7000, + "maximum": 9050 + } + }, + "out": "{\"oneof\":[{\"fault\":7961},{\"parameters\":[{\"parameter\":\"aliquip ut laborum tempor\",\"status\":true,\"fault\":7103,\"instance\":\"a\"}]}],\"definitions\":{\"path_t\":{\"description\":\"Complete object element path as per TR181\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.1.\",\"Device.WiFi.\"]},\"schema_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.Bridging.Bridge.{i}.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.{i}.SSID\"]},\"boolean_t\":{\"type\":\"string\",\"enum\":[\"0\",\"1\"]},\"operate_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.DHCPv4.Client.{i}.Renew()\",\"Device.FactoryReset()\"]},\"operate_type_t\":{\"type\":\"string\",\"enum\":[\"async\",\"sync\"]},\"instance_t\":{\"description\":\"Multi object instances\",\"type\":\"string\",\"minLength\":6,\"maxLength\":256},\"proto_t\":{\"type\":\"string\",\"default\":\"both\",\"enum\":[\"usp\",\"cwmp\",\"both\"]},\"type_t\":{\"type\":\"string\",\"enum\":[\"xsd:string\",\"xsd:unsignedInt\",\"xsd:int\",\"xsd:unsignedLong\",\"xsd:long\",\"xsd:boolean\",\"xsd:dateTime\",\"xsd:hexBinary\",\"xsd:object\",\"xsd:command\",\"xsd:event\"]},\"fault_t\":{\"type\":\"integer\",\"minimum\":7000,\"maximum\":9050}}}", + "simpletype": "complex" +} +``` + +### Output Example + +```json +{ + "oneof": [ + { "fault": 7961 }, + { "parameters": [{ "parameter": "aliquip ut laborum tempor", "status": true, "fault": 7103, "instance": "a" }] } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { "type": "string", "enum": ["0", "1"] }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { "type": "string", "enum": ["async", "sync"] }, + "instance_t": { "description": "Multi object instances", "type": "string", "minLength": 6, "maxLength": 256 }, + "proto_t": { "type": "string", "default": "both", "enum": ["usp", "cwmp", "both"] }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { "type": "integer", "minimum": 7000, "maximum": 9050 } + } +} +``` + +## del_object + +### Delete object instance + +Delete a object instance from multi instance object + +`del_object` + +- type: `Method` + +### del_object Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | **Required** | +| `output` | | **Required** | + +#### input + +`input` + +- is **required** +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | Default | +| -------- | ------ | ------------ | -------- | +| `path` | string | **Required** | | +| `proto` | string | Optional | `"both"` | + +#### path + +Complete object element path as per TR181 + +`path` + +- is **required** +- type: reference + +##### path Type + +`string` + +- minimum length: 6 characters +- maximum length: 1024 characters + +##### path Examples + +```json +Device. +``` + +```json +Device.DeviceInfo.Manufacturer +``` + +```json +Device.WiFi.SSID.1. +``` + +```json +Device.WiFi. +``` + +#### proto + +`proto` + +- is optional +- type: reference +- default: `"both"` + +##### proto Type + +`string` + +The value of this property **must** be equal to one of the [known values below](#del_object-known-values). + +##### proto Known Values + +| Value | +| ----- | +| usp | +| cwmp | +| both | + +### Ubus CLI Example + +``` +ubus call dmtest del_object {"path":"ut officia pariatur","proto":"cwmp"} +``` + +### JSONRPC Example + +```json +{ + "jsonrpc": "2.0", + "id": 0, + "method": "call", + "params": ["", "dmtest", "del_object", { "path": "ut officia pariatur", "proto": "cwmp" }] +} +``` + +#### output + +`output` + +- is **required** +- type: complex + +##### output Type + +Unknown type ``. + +```json +{ + "oneof": [ + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "required": ["parameters"], + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "required": ["parameter", "status"], + "properties": { + "parameter": { + "$ref": "#/definitions/path_t" + }, + "status": { + "type": "boolean" + }, + "fault": { + "$ref": "#/definitions/fault_t" + } + } + } + ] + } + } + } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { + "type": "string", + "enum": ["0", "1"] + }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { + "type": "string", + "enum": ["async", "sync"] + }, + "instance_t": { + "description": "Multi object instances", + "type": "string", + "minLength": 6, + "maxLength": 256 + }, + "proto_t": { + "type": "string", + "default": "both", + "enum": ["usp", "cwmp", "both"] + }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { + "type": "integer", + "minimum": 7000, + "maximum": 9050 + } + }, + "out": "{\"oneof\":[{\"fault\":7423},{\"parameters\":[{\"parameter\":\"laborum consequat elit in\",\"status\":true,\"fault\":8381}]}],\"definitions\":{\"path_t\":{\"description\":\"Complete object element path as per TR181\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.1.\",\"Device.WiFi.\"]},\"schema_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.Bridging.Bridge.{i}.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.{i}.SSID\"]},\"boolean_t\":{\"type\":\"string\",\"enum\":[\"0\",\"1\"]},\"operate_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.DHCPv4.Client.{i}.Renew()\",\"Device.FactoryReset()\"]},\"operate_type_t\":{\"type\":\"string\",\"enum\":[\"async\",\"sync\"]},\"instance_t\":{\"description\":\"Multi object instances\",\"type\":\"string\",\"minLength\":6,\"maxLength\":256},\"proto_t\":{\"type\":\"string\",\"default\":\"both\",\"enum\":[\"usp\",\"cwmp\",\"both\"]},\"type_t\":{\"type\":\"string\",\"enum\":[\"xsd:string\",\"xsd:unsignedInt\",\"xsd:int\",\"xsd:unsignedLong\",\"xsd:long\",\"xsd:boolean\",\"xsd:dateTime\",\"xsd:hexBinary\",\"xsd:object\",\"xsd:command\",\"xsd:event\"]},\"fault_t\":{\"type\":\"integer\",\"minimum\":7000,\"maximum\":9050}}}", + "simpletype": "complex" +} +``` + +### Output Example + +```json +{ + "oneof": [ + { "fault": 7423 }, + { "parameters": [{ "parameter": "laborum consequat elit in", "status": true, "fault": 8381 }] } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { "type": "string", "enum": ["0", "1"] }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { "type": "string", "enum": ["async", "sync"] }, + "instance_t": { "description": "Multi object instances", "type": "string", "minLength": 6, "maxLength": 256 }, + "proto_t": { "type": "string", "default": "both", "enum": ["usp", "cwmp", "both"] }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { "type": "integer", "minimum": 7000, "maximum": 9050 } + } +} +``` + +## get + +### Get handler + +Query the datamodel object + +`get` + +- type: `Method` + +### get Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | **Required** | +| `output` | | **Required** | + +#### input + +`input` + +- is **required** +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | Default | +| -------- | ------ | ------------ | -------- | +| `path` | string | **Required** | | +| `proto` | string | Optional | `"both"` | + +#### path + +Complete object element path as per TR181 + +`path` + +- is **required** +- type: reference + +##### path Type + +`string` + +- minimum length: 6 characters +- maximum length: 1024 characters + +##### path Examples + +```json +Device. +``` + +```json +Device.DeviceInfo.Manufacturer +``` + +```json +Device.WiFi.SSID.1. +``` + +```json +Device.WiFi. +``` + +#### proto + +`proto` + +- is optional +- type: reference +- default: `"both"` + +##### proto Type + +`string` + +The value of this property **must** be equal to one of the [known values below](#get-known-values). + +##### proto Known Values + +| Value | +| ----- | +| usp | +| cwmp | +| both | + +### Ubus CLI Example + +``` +ubus call dmtest get {"path":"elit in dolore enim culpa","proto":"both"} +``` + +### JSONRPC Example + +```json +{ + "jsonrpc": "2.0", + "id": 0, + "method": "call", + "params": ["", "dmtest", "get", { "path": "elit in dolore enim culpa", "proto": "both" }] +} +``` + +#### output + +`output` + +- is **required** +- type: complex + +##### output Type + +Unknown type ``. + +```json +{ + "oneof": [ + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "required": ["parameter", "value", "type"], + "properties": { + "parameter": { + "$ref": "#/definitions/path_t" + }, + "value": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/type_t" + } + } + } + ] + } + } + } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { + "type": "string", + "enum": ["0", "1"] + }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { + "type": "string", + "enum": ["async", "sync"] + }, + "instance_t": { + "description": "Multi object instances", + "type": "string", + "minLength": 6, + "maxLength": 256 + }, + "proto_t": { + "type": "string", + "default": "both", + "enum": ["usp", "cwmp", "both"] + }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { + "type": "integer", + "minimum": 7000, + "maximum": 9050 + } + }, + "out": "{\"oneof\":[{\"fault\":8800},{\"parameters\":[{\"parameter\":\"cupidatat\",\"value\":\"dolor in pariatur\",\"type\":\"xsd:event\"}]}],\"definitions\":{\"path_t\":{\"description\":\"Complete object element path as per TR181\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.1.\",\"Device.WiFi.\"]},\"schema_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.Bridging.Bridge.{i}.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.{i}.SSID\"]},\"boolean_t\":{\"type\":\"string\",\"enum\":[\"0\",\"1\"]},\"operate_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.DHCPv4.Client.{i}.Renew()\",\"Device.FactoryReset()\"]},\"operate_type_t\":{\"type\":\"string\",\"enum\":[\"async\",\"sync\"]},\"instance_t\":{\"description\":\"Multi object instances\",\"type\":\"string\",\"minLength\":6,\"maxLength\":256},\"proto_t\":{\"type\":\"string\",\"default\":\"both\",\"enum\":[\"usp\",\"cwmp\",\"both\"]},\"type_t\":{\"type\":\"string\",\"enum\":[\"xsd:string\",\"xsd:unsignedInt\",\"xsd:int\",\"xsd:unsignedLong\",\"xsd:long\",\"xsd:boolean\",\"xsd:dateTime\",\"xsd:hexBinary\",\"xsd:object\",\"xsd:command\",\"xsd:event\"]},\"fault_t\":{\"type\":\"integer\",\"minimum\":7000,\"maximum\":9050}}}", + "simpletype": "complex" +} +``` + +### Output Example + +```json +{ + "oneof": [ + { "fault": 8800 }, + { "parameters": [{ "parameter": "cupidatat", "value": "dolor in pariatur", "type": "xsd:event" }] } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { "type": "string", "enum": ["0", "1"] }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { "type": "string", "enum": ["async", "sync"] }, + "instance_t": { "description": "Multi object instances", "type": "string", "minLength": 6, "maxLength": 256 }, + "proto_t": { "type": "string", "default": "both", "enum": ["usp", "cwmp", "both"] }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { "type": "integer", "minimum": 7000, "maximum": 9050 } + } +} +``` + +## get_supported_dm + +### Get list of supported datamodel parameters + +Schema will have all the nodes/objects supported by libbbf + +`get_supported_dm` + +- type: `Method` + +### get_supported_dm Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | Optional | +| `output` | | **Required** | + +#### input + +`input` + +- is optional +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | Default | +| ------------- | ------- | -------- | -------- | +| `next-level` | boolean | Optional | | +| `path` | string | Optional | | +| `proto` | string | Optional | `"both"` | +| `schema_type` | integer | Optional | | + +#### next-level + +gets only next level objects if true + +`next-level` + +- is optional +- type: `boolean` + +##### next-level Type + +`boolean` + +#### path + +Complete object element path as per TR181 + +`path` + +- is optional +- type: reference + +##### path Type + +`string` + +- minimum length: 6 characters +- maximum length: 1024 characters + +##### path Examples + +```json +Device. +``` + +```json +Device.DeviceInfo.Manufacturer +``` + +```json +Device.WiFi.SSID.1. +``` + +```json +Device.WiFi. +``` + +#### proto + +`proto` + +- is optional +- type: reference +- default: `"both"` + +##### proto Type + +`string` + +The value of this property **must** be equal to one of the [known values below](#get_supported_dm-known-values). + +##### proto Known Values + +| Value | +| ----- | +| usp | +| cwmp | +| both | + +#### schema_type + +0-All, 1-Parameter only 2- Event only 3- operate only + +`schema_type` + +- is optional +- type: `integer` + +##### schema_type Type + +`integer` + +- minimum value: `0` +- maximum value: `3` + +### Ubus CLI Example + +``` +ubus call dmtest get_supported_dm {"path":"ut nostrud do e","proto":"usp","next-level":false,"schema_type":2} +``` + +### JSONRPC Example + +```json +{ + "jsonrpc": "2.0", + "id": 0, + "method": "call", + "params": [ + "", + "dmtest", + "get_supported_dm", + { "path": "ut nostrud do e", "proto": "usp", "next-level": false, "schema_type": 2 } + ] +} +``` + +#### output + +`output` + +- is **required** +- type: complex + +##### output Type + +Unknown type ``. + +```json +{ + "oneof": [ + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "parameter": { + "$ref": "#/definitions/schema_path_t" + }, + "writable": { + "$ref": "#/definitions/boolean_t" + }, + "type": { + "$ref": "#/definitions/type_t" + }, + "cmd_type": { + "$ref": "#/definitions/operate_type_t" + }, + "in": { + "type": "array", + "uniqueItems": true, + "items": [ + { + "type": "string" + } + ] + }, + "out": { + "type": "array", + "uniqueItems": true, + "items": [ + { + "type": "string" + } + ] + } + }, + "required": ["parameter", "type"] + } + ] + } + } + } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { + "type": "string", + "enum": ["0", "1"] + }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { + "type": "string", + "enum": ["async", "sync"] + }, + "instance_t": { + "description": "Multi object instances", + "type": "string", + "minLength": 6, + "maxLength": 256 + }, + "proto_t": { + "type": "string", + "default": "both", + "enum": ["usp", "cwmp", "both"] + }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { + "type": "integer", + "minimum": 7000, + "maximum": 9050 + } + }, + "out": "{\"oneof\":[{\"fault\":8801},{\"parameters\":[{\"parameter\":\"qui irure au\",\"type\":\"xsd:dateTime\",\"writable\":\"1\",\"cmd_type\":\"async\",\"in\":[\"ipsum occaecat labore Ut\"],\"out\":[\"Excepteur dolore cupidatat aliquip\"]}]}],\"definitions\":{\"path_t\":{\"description\":\"Complete object element path as per TR181\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.1.\",\"Device.WiFi.\"]},\"schema_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.Bridging.Bridge.{i}.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.{i}.SSID\"]},\"boolean_t\":{\"type\":\"string\",\"enum\":[\"0\",\"1\"]},\"operate_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.DHCPv4.Client.{i}.Renew()\",\"Device.FactoryReset()\"]},\"operate_type_t\":{\"type\":\"string\",\"enum\":[\"async\",\"sync\"]},\"instance_t\":{\"description\":\"Multi object instances\",\"type\":\"string\",\"minLength\":6,\"maxLength\":256},\"proto_t\":{\"type\":\"string\",\"default\":\"both\",\"enum\":[\"usp\",\"cwmp\",\"both\"]},\"type_t\":{\"type\":\"string\",\"enum\":[\"xsd:string\",\"xsd:unsignedInt\",\"xsd:int\",\"xsd:unsignedLong\",\"xsd:long\",\"xsd:boolean\",\"xsd:dateTime\",\"xsd:hexBinary\",\"xsd:object\",\"xsd:command\",\"xsd:event\"]},\"fault_t\":{\"type\":\"integer\",\"minimum\":7000,\"maximum\":9050}}}", + "simpletype": "complex" +} +``` + +### Output Example + +```json +{ + "oneof": [ + { "fault": 8801 }, + { + "parameters": [ + { + "parameter": "qui irure au", + "type": "xsd:dateTime", + "writable": "1", + "cmd_type": "async", + "in": ["ipsum occaecat labore Ut"], + "out": ["Excepteur dolore cupidatat aliquip"] + } + ] + } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { "type": "string", "enum": ["0", "1"] }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { "type": "string", "enum": ["async", "sync"] }, + "instance_t": { "description": "Multi object instances", "type": "string", "minLength": 6, "maxLength": 256 }, + "proto_t": { "type": "string", "default": "both", "enum": ["usp", "cwmp", "both"] }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { "type": "integer", "minimum": 7000, "maximum": 9050 } + } +} +``` + +## operate + +### Operate handler + +Operate on object element provided in path + +`operate` + +- type: `Method` + +### operate Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | **Required** | +| `output` | object | **Required** | + +#### input + +`input` + +- is **required** +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | Optional | +| `path` | string | **Required** | + +#### input + +Input arguments for the operate command as defined in TR-181-2.13 + +`input` + +- is optional +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ---- | -------- | +| None | None | None | + +##### input Example + +```json +{ "path": "Device.IP.Diagnostics.IPPing()", "input": { "Host": "iopsys.eu" } } +``` + +#### path + +Datamodel object schema path + +`path` + +- is **required** +- type: reference + +##### path Type + +`string` + +- minimum length: 6 characters +- maximum length: 1024 characters + +##### path Examples + +```json +Device.DHCPv4.Client.{i}.Renew() +``` + +```json +Device.FactoryReset() +``` + +### Ubus CLI Example + +``` +ubus call dmtest operate {"path":"nonesse nostrud anim","input":{}} +``` + +### JSONRPC Example + +```json +{ + "jsonrpc": "2.0", + "id": 0, + "method": "call", + "params": ["", "dmtest", "operate", { "path": "nonesse nostrud anim", "input": {} }] +} +``` + +#### output + +`output` + +- is **required** +- type: `object` + +##### output Type + +`object` with following properties: + +| Property | Type | Required | +| --------- | ----- | ------------ | +| `Results` | array | **Required** | + +#### Results + +`Results` + +- is **required** +- type: `array` + +##### Results Type + +Array type: `array` + +All items must be of the type: Unknown type ``. + +```json +{ + "type": "array", + "items": [ + { + "type": "object", + "required": ["path"], + "properties": { + "path": { + "$ref": "#/definitions/path_t" + }, + "parameters": { + "description": "Output will have status for sync commands and for async commands parameters as defined in TR-181-2.13", + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "parameter": { + "type": "string" + }, + "value": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/type_t" + }, + "fault": { + "$ref": "#/definitions/fault_t" + } + } + } + ], + "examples": [ + "{\n\t\"status\": true}", + "{\n\t\"AverageResponseTime\": \"0\",\n\t\"AverageResponseTimeDetailed\": \"130\",\n\t\"FailureCount\": \"0\",\n\t\"MaximumResponseTime\": \"0\",\n\t\"MaximumResponseTimeDetailed\": \"140\",\n\t\"MinimumResponseTime\": \"0\",\n\t\"MinimumResponseTimeDetailed\": \"120\",\n\t\"SuccessCount\": \"3\"}" + ] + } + } + } + ], + "simpletype": "`array`" +} +``` + +### Output Example + +```json +{ + "Results": [ + { + "path": "velit sit veniam magna et", + "parameters": [ + { "parameter": "ullamco Duis do eiusmod eu", "value": "aute velit sit", "type": "xsd:event", "fault": 7524 } + ] + } + ] +} +``` + +## set + +### Set handler + +Set values of datamodel object element + +`set` + +- type: `Method` + +### set Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | **Required** | +| `output` | | **Required** | + +#### input + +`input` + +- is **required** +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | Default | +| -------- | ------ | ------------ | -------- | +| `path` | string | **Required** | | +| `proto` | string | Optional | `"both"` | +| `value` | string | **Required** | | + +#### path + +Complete object element path as per TR181 + +`path` + +- is **required** +- type: reference + +##### path Type + +`string` + +- minimum length: 6 characters +- maximum length: 1024 characters + +##### path Examples + +```json +Device. +``` + +```json +Device.DeviceInfo.Manufacturer +``` + +```json +Device.WiFi.SSID.1. +``` + +```json +Device.WiFi. +``` + +#### proto + +`proto` + +- is optional +- type: reference +- default: `"both"` + +##### proto Type + +`string` + +The value of this property **must** be equal to one of the [known values below](#set-known-values). + +##### proto Known Values + +| Value | +| ----- | +| usp | +| cwmp | +| both | + +#### value + +value of the object element provided in path, path should contains valid writable object element + +`value` + +- is **required** +- type: `string` + +##### value Type + +`string` + +##### value Examples + +```json +{ "path": "Device.WiFi.SSID.1.SSID", "value": "test_ssid" } +``` + +```json +{ "path": "Device.WiFi.SSID.2.Enable", "value": "true" } +``` + +```json +{ "path": "Device.WiFi.SSID.1.Enable", "value": "0" } +``` + +### Ubus CLI Example + +``` +ubus call dmtest set {"path":"ea consecte","value":"et ipsum velit","proto":"both"} +``` + +### JSONRPC Example + +```json +{ + "jsonrpc": "2.0", + "id": 0, + "method": "call", + "params": ["", "dmtest", "set", { "path": "ea consecte", "value": "et ipsum velit", "proto": "both" }] +} +``` + +#### output + +`output` + +- is **required** +- type: complex + +##### output Type + +Unknown type ``. + +```json +{ + "oneof": [ + { + "type": "object", + "properties": { + "status": { + "const": "1" + } + } + }, + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "required": ["parameters"], + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "required": ["parameter"], + "properties": { + "parameter": { + "$ref": "#/definitions/path_t" + }, + "status": { + "type": "boolean" + }, + "fault": { + "$ref": "#/definitions/fault_t" + } + } + } + ] + } + } + } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { + "type": "string", + "enum": ["0", "1"] + }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { + "type": "string", + "enum": ["async", "sync"] + }, + "instance_t": { + "description": "Multi object instances", + "type": "string", + "minLength": 6, + "maxLength": 256 + }, + "proto_t": { + "type": "string", + "default": "both", + "enum": ["usp", "cwmp", "both"] + }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { + "type": "integer", + "minimum": 7000, + "maximum": 9050 + } + }, + "out": "{\"oneof\":[{\"status\":\"1\"},{\"fault\":7434},{\"parameters\":[{\"parameter\":\"consequat in et cillum voluptate\",\"status\":true,\"fault\":7297}]}],\"definitions\":{\"path_t\":{\"description\":\"Complete object element path as per TR181\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.1.\",\"Device.WiFi.\"]},\"schema_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.Bridging.Bridge.{i}.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.{i}.SSID\"]},\"boolean_t\":{\"type\":\"string\",\"enum\":[\"0\",\"1\"]},\"operate_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.DHCPv4.Client.{i}.Renew()\",\"Device.FactoryReset()\"]},\"operate_type_t\":{\"type\":\"string\",\"enum\":[\"async\",\"sync\"]},\"instance_t\":{\"description\":\"Multi object instances\",\"type\":\"string\",\"minLength\":6,\"maxLength\":256},\"proto_t\":{\"type\":\"string\",\"default\":\"both\",\"enum\":[\"usp\",\"cwmp\",\"both\"]},\"type_t\":{\"type\":\"string\",\"enum\":[\"xsd:string\",\"xsd:unsignedInt\",\"xsd:int\",\"xsd:unsignedLong\",\"xsd:long\",\"xsd:boolean\",\"xsd:dateTime\",\"xsd:hexBinary\",\"xsd:object\",\"xsd:command\",\"xsd:event\"]},\"fault_t\":{\"type\":\"integer\",\"minimum\":7000,\"maximum\":9050}}}", + "simpletype": "complex" +} +``` + +### Output Example + +```json +{ + "oneof": [ + { "status": "1" }, + { "fault": 7434 }, + { "parameters": [{ "parameter": "consequat in et cillum voluptate", "status": true, "fault": 7297 }] } + ], + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"] + }, + "boolean_t": { "type": "string", "enum": ["0", "1"] }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": ["Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"] + }, + "operate_type_t": { "type": "string", "enum": ["async", "sync"] }, + "instance_t": { "description": "Multi object instances", "type": "string", "minLength": 6, "maxLength": 256 }, + "proto_t": { "type": "string", "default": "both", "enum": ["usp", "cwmp", "both"] }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { "type": "integer", "minimum": 7000, "maximum": 9050 } + } +} +``` + +## transaction_abort + +### Aborts an on-going transaction + +`transaction_abort` + +- type: `Method` + +### transaction_abort Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | Optional | +| `output` | object | **Required** | + +#### input + +`input` + +- is optional +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ---- | -------- | +| None | None | None | + +### Ubus CLI Example + +``` +ubus call dmtest transaction_abort {} +``` + +### JSONRPC Example + +```json +{ "jsonrpc": "2.0", "id": 0, "method": "call", "params": ["", "dmtest", "transaction_abort", {}] } +``` + +#### output + +`output` + +- is **required** +- type: `object` + +##### output Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------- | ------------ | +| `status` | boolean | **Required** | + +#### status + +`status` + +- is **required** +- type: `boolean` + +##### status Type + +`boolean` + +### Output Example + +```json +{ "status": true } +``` + +## transaction_commit + +### Commits an on-going transaction + +`transaction_commit` + +- type: `Method` + +### transaction_commit Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | Optional | +| `output` | object | **Required** | + +#### input + +`input` + +- is optional +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ---- | -------- | +| None | None | None | + +### Ubus CLI Example + +``` +ubus call dmtest transaction_commit {} +``` + +### JSONRPC Example + +```json +{ "jsonrpc": "2.0", "id": 0, "method": "call", "params": ["", "dmtest", "transaction_commit", {}] } +``` + +#### output + +`output` + +- is **required** +- type: `object` + +##### output Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------- | ------------ | +| `status` | boolean | **Required** | + +#### status + +`status` + +- is **required** +- type: `boolean` + +##### status Type + +`boolean` + +### Output Example + +```json +{ "status": false } +``` + +## transaction_start + +### Start a transaction before set/add/del operations + +`transaction_start` + +- type: `Method` + +### transaction_start Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------ | ------------ | +| `input` | object | Optional | +| `output` | object | **Required** | + +#### input + +`input` + +- is optional +- type: `object` + +##### input Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ---- | -------- | +| None | None | None | + +### Ubus CLI Example + +``` +ubus call dmtest transaction_start {} +``` + +### JSONRPC Example + +```json +{ "jsonrpc": "2.0", "id": 0, "method": "call", "params": ["", "dmtest", "transaction_start", {}] } +``` + +#### output + +`output` + +- is **required** +- type: `object` + +##### output Type + +`object` with following properties: + +| Property | Type | Required | +| -------- | ------- | ------------ | +| `status` | boolean | **Required** | + +#### status + +`status` + +- is **required** +- type: `boolean` + +##### status Type + +`boolean` + +### Output Example + +```json +{ "status": false } +``` diff --git a/gitlab-ci/iopsys-supervisord.conf b/gitlab-ci/iopsys-supervisord.conf index 61335c0d..92b43a4d 100755 --- a/gitlab-ci/iopsys-supervisord.conf +++ b/gitlab-ci/iopsys-supervisord.conf @@ -9,3 +9,9 @@ autorestart=false startretries=0 priority=2 command=/bin/bash -c "/usr/sbin/rpcd" + +[program:bbf_ubus] +autorestart=false +priority=3 +stdout_logfile=/builds/iopsys/bbf/log_file +command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/builds/iopsys/bbf/memory-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /builds/iopsys/bbf/test/dynamicdm_ubus_test/bbf_ubus" diff --git a/gitlab-ci/libbbf_ubus_api_test.sh b/gitlab-ci/libbbf_ubus_api_test.sh new file mode 100755 index 00000000..8f71ac97 --- /dev/null +++ b/gitlab-ci/libbbf_ubus_api_test.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +echo "$0 preparation script" +pwd + +source ./gitlab-ci/shared.sh + +# clean and make +# compile and install libbbf +install_libbbf + +#compile and install libbbf_test dynamic extension library +install_libbbf_test + +supervisorctl update +supervisorctl restart all +supervisorctl status all +exec_cmd ubus wait_for dmtest +supervisorctl status all + +# debug logging +echo "Checking ubus status [$(date '+%d/%m/%Y %H:%M:%S')]" +ubus list +ubus -v list dmtest + +echo "Checking system resources" +free -h +df -h +sleep 5 + +# run functional on dmtest object validation +if [ -f "/usr/share/rpcd/schemas/dmtest.json" ]; then + rm /usr/share/rpcd/schemas/dmtest.json +fi + +cp -r ./schemas/dmtest.json /usr/share/rpcd/schemas +ubus-api-validator -t 5 -f ./test/api/json/dmtest.validation.json >> ./api-result.log +check_ret $? + +supervisorctl status all +supervisorctl stop all +supervisorctl status + +#report part +date +%s > timestamp.log +exec_cmd tap-junit --input ./api-result.log --output report + +echo "Checking memory leaks..." +grep -q "Leak" memory-report.xml +error_on_zero $? + +echo "Functional libbbf_ubus API test :: PASS" diff --git a/gitlab-ci/shared.sh b/gitlab-ci/shared.sh index cb4f72e4..71acaabf 100755 --- a/gitlab-ci/shared.sh +++ b/gitlab-ci/shared.sh @@ -74,6 +74,15 @@ function install_libbbf_test() echo "installing libbbf_test" cp -f test/bbf_test/libbbf_test.so /usr/lib/bbfdm + + echo "pre-installation for libbbf_ubus_test" + cp -f test/bbf_test/libbbf_test.so /usr/local/lib + ldconfig + + # compile and install libbbf_ubus_test + echo "Compiling libbbf_ubus_test" + make clean -C test/dynamicdm_ubus_test/ + make -C test/dynamicdm_ubus_test/ } function install_libbulkdata() @@ -101,3 +110,14 @@ function install_libperiodicstats() echo "installing libperiodicstats" cp -f /opt/dev/periodicstats/libperiodicstats.so /usr/lib/bbfdm } + +function error_on_zero() +{ + ret=$1 + if [ "$ret" -eq 0 ]; then + echo "Validation of last command failed, ret(${ret})" + exit $ret + fi + +} + diff --git a/include/libbbf_ubus.h b/include/libbbf_ubus.h new file mode 100644 index 00000000..08fbf0fe --- /dev/null +++ b/include/libbbf_ubus.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 Iopsys Software Solutions AB + * + * Author: Suvendhu Hansa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +/** + * \file libbbf_ubus.h + * + * This Library provides APIs to expose the datamodel constructed with the help + * of libbbf API over the ubus directly. + * This library has an external dependency on libbbf_api + */ + +#ifndef __LIBBBF_UBUS_H__ +#define __LIBBBF_UBUS_H__ + +#include +#include "libbbf_api/dmbbf.h" + +/*********************************************************************//** +** +** dynamicdm_init +** +** This API is to register the predefined ubus methods to work on provided +** `DMOBJ` tree. +** +** NOTE: dynamicdm_free should be called to deregister and free the allocated +** resources used in this API. +** +** \param ctx - pre-allocated ubus context, should not be NULL +** \param ubus_name - name of the ubus object on which pre-defined usus methods will be registered. +** It should not be NULL or Empty +** \param entry - Object which points to the root node of the tree. More details available in +** libbbf_api documentation. +** +** \return 0 if ubus methods are registered with the given tree, -1 otherwise +** +**************************************************************************/ +int dynamicdm_init(struct ubus_context *ctx, char *ubus_name, DMOBJ *entry); + +/*********************************************************************//** +** +** dynamicdm_init_plugin_object +** +** This API is to register the predefined ubus methods to work on provided +** `DM_MAP_OBJ` tree. +** +** NOTE: dynamicdm_free_plugin_object should be called to deregister and free +** the allocated resources used in this API. +** This API is for developer purpose and can register a tree with intermediate +** node. +** +** \param ctx - pre-allocated ubus context, should not be NULL +** \param ubus_name - name of the ubus object on which pre-defined usus methods will be registered. +** It should not be NULL or Empty +** \param entry - Object which points to the root node of the tree. More details available in +** libbbf_api documentation. +** +** \return 0 if ubus methods are registered with the given tree, -1 otherwise +** +**************************************************************************/ +int dynamicdm_init_plugin_object(struct ubus_context *ctx, char *ubus_name, DM_MAP_OBJ *entry); + +/*********************************************************************//** +** +** dynamicdm_free +** +** This is the API responsible to deregister/remove the allocated resources +** used in dynamicdm_init +** +** NOTE: It's the responsibility of the application to call this API before +** termination in order to free the resources if dynamicdm_init has been used. +** +** \param ctx - pre-allocated ubus context, should not be NULL +** \param ubus_name - name of the ubus object on which pre-defined usus methods are registered. +** It should not be NULL or Empty +** +** \return None +** +**************************************************************************/ +void dynamicdm_free(struct ubus_context *ctx, const char *ubus_name); + +/*********************************************************************//** +** +** dynamicdm_free +** +** This is the API responsible to deregister/remove the allocated resources +** used in dynamicdm_init_plugin_object +** +** NOTE: It's the responsibility of the application to call this API before +** termination in order to free the resources if dynamicdm_init_plugin_object +** has been used. +** +** \param ctx - pre-allocated ubus context, should not be NULL +** \param ubus_name - name of the ubus object on which pre-defined usus methods are registered. +** It should not be NULL or Empty +** +** \return None +** +**************************************************************************/ +void dynamicdm_free_plugin_object(struct ubus_context *ctx, const char *ubus_name); + +#endif //__LIBBBF_UBUS_H__ diff --git a/libbbf_ubus/Makefile.am b/libbbf_ubus/Makefile.am new file mode 100644 index 00000000..9b6def43 --- /dev/null +++ b/libbbf_ubus/Makefile.am @@ -0,0 +1,38 @@ +MAINTAINERCLEANFILES = Makefile.in +ACLOCAL_AMFLAGS = -I m4 + +lib_LTLIBRARIES = libbbf_ubus.la + +libbbf_ubus_la_SOURCES = \ + ../dmentry.c \ + ../dmbbfcommon.c \ + libbbf_ubus.c + +libbbf_ubus_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBUCI_CFLAGS) \ + $(LIBUBOX_CFLAGS) \ + $(LIBUBUS_CFLAGS) + +libbbf_ubus_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(LIBUCI_LDFLAGS) \ + $(LIBUBOX_LDFLAGS) \ + $(LIBUBUS_LDFLAGS) + +libbbf_ubus_la_LIBADD = \ + $(AM_LIBS) \ + $(LIBUCI_LIBS) \ + $(LIBUBOX_LIBS) \ + $(LIBUBUS_LIBS) \ + $(LIBJSON_LIBS) \ + $(LIBTRACE_LIBS) \ + $(LBLOBMSG_LIBS) \ + $(LIBDLOPEN_LIBS) \ + $(LIBCURL_LIBS) \ + $(LIBOPENSSL_LIBS) \ + $(LIBCRYPTO_LIBS) \ + -L../bin/ -lbbf_api + +libbbf_ubus_la_CFLAGS+=-I../ +libbbf_ubus_la_CFLAGS+=-I../include diff --git a/libbbf_ubus/configure.ac b/libbbf_ubus/configure.ac new file mode 100644 index 00000000..5305121b --- /dev/null +++ b/libbbf_ubus/configure.ac @@ -0,0 +1,71 @@ +AC_INIT([libbbf_ubus], [0.1], [suvendhu.hansa@iopsys.eu]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) + +AC_PROG_CC +AC_PROG_CC_C_O +AC_ENABLE_SHARED +LT_INIT + +LIBJSON_LIBS='-ljson-c' +AC_SUBST([LIBJSON_LIBS]) + +AC_ARG_WITH([uci-include-path], + [AS_HELP_STRING([--with-uci-include-path], + [location of the uci library headers])], + [LIBUCI_CFLAGS="-I$withval"]) +AC_SUBST([LIBUCI_CFLAGS]) + +AC_ARG_WITH([uci-lib-path], + [AS_HELP_STRING([--with-uci-lib-path], [location of the uci library])], [LIBUCI_LDFLAGS="-L$withval"]) +AC_SUBST([LIBUCI_LDFLAGS]) + +LIBUCI_LIBS='-luci' +AC_SUBST([LIBUCI_LIBS]) + +LIBTRACE_LIBS='-ltrace' +AC_SUBST([LIBTRACE_LIBS]) + +AC_ARG_WITH([libubox-include-path], + [AS_HELP_STRING([--with-libubox-include-path], + [location of the libubox library headers])], + [LIBUBOX_CFLAGS="-I$withval"]) +AC_SUBST([LIBUBOX_CFLAGS]) + +AC_ARG_WITH([libubox-lib-path], + [AS_HELP_STRING([--with-libubox-lib-path], [location of the libubox library])], [LIBUBOX_LDFLAGS="-L$withval"]) +AC_SUBST([LIBUBOX_LDFLAGS]) + +LIBUBOX_LIBS='-lubox' +AC_SUBST([LIBUBOX_LIBS]) + +AC_ARG_WITH([libubus-include-path], + [AS_HELP_STRING([--with-libubus-include-path], + [location of the libubus library headers])], + [LIBUBUS_CFLAGS="-I$withval"]) +AC_SUBST([LIBUBUS_CFLAGS]) + +AC_ARG_WITH([libubus-lib-path], + [AS_HELP_STRING([--with-libubus-lib-path], [location of the libubus library])], [LIBUBOX_LDFLAGS="-L$withval"]) +AC_SUBST([LIBUBUS_LDFLAGS]) + +LIBUBUS_LIBS='-lubus' +AC_SUBST([LIBUBUS_LIBS]) + +LBLOBMSG_LIBS='-lblobmsg_json' +AC_SUBST([LBLOBMSG_LIBS]) + +LIBDLOPEN_LIBS='-ldl' +AC_SUBST([LIBDLOPEN_LIBS]) + +LIBCURL_LIBS='-lcurl' +AC_SUBST([LIBCURL_LIBS]) + +# checks for header files +AC_CHECK_HEADERS([stdlib.h string.h]) + +# checks for typedefs, structures, and compiler characteristics +AC_TYPE_UINT8_T + +AC_CONFIG_FILES([Makefile]) + +AC_OUTPUT diff --git a/libbbf_ubus/libbbf_ubus.c b/libbbf_ubus/libbbf_ubus.c new file mode 100644 index 00000000..2a029306 --- /dev/null +++ b/libbbf_ubus/libbbf_ubus.c @@ -0,0 +1,946 @@ +/* + * Copyright (C) 2021 Iopsys Software Solutions AB + * + * Author: Suvendhu Hansa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include "dmentry.h" +#include "dmbbfcommon.h" +#include "libbbf_ubus.h" + +#define PATH_MAX 4096 + +struct obj_node { + struct ubus_object *ob; + struct ubus_object_type *ob_type; + char *obj_name; + DMOBJ *tUsrObj; + struct obj_node *next; +}; + +enum { + LIBBBF_UBUS_GET_PATH, + LIBBBF_UBUS_GET_PROTO, + __LIBBBF_UBUS_GET_MAX +}; + +enum { + LIBBBF_UBUS_SET_PATH, + LIBBBF_UBUS_SET_VALUE, + LIBBBF_UBUS_SET_PROTO, + __LIBBBF_UBUS_SET_MAX +}; + +enum { + LIBBBF_UBUS_OPERATE_PATH, + LIBBBF_UBUS_OPERATE_INPUT, + __LIBBBF_UBUS_OPERATE_MAX +}; + +enum { + LIBBBF_UBUS_SUPPORTED_PATH, + LIBBBF_UBUS_SUPPORTED_PROTO, + LIBBBF_UBUS_SUPPORTED_NXT_LEVEL, + LIBBBF_UBUS_SUPPORTED_SCHEMA_TYPE, + __LIBBBF_UBUS_SUPPORTED_MAX +}; + +enum { + LIBBBF_UBUS_ADD_DEL_PATH, + LIBBBF_UBUS_ADD_DEL_PROTO, + __LIBBBF_UBUS_ADD_DEL_MAX +}; + +static bool g_dynamicdm_transaction_start = false; +static struct obj_node *g_dynamicdm_head = NULL; + +static int libbbf_ubus_supported_dm(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg); + +static int libbbf_ubus_get_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg); + +static int libbbf_ubus_set_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg); + +static int libbbf_ubus_operate(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg); + +static int libbbf_ubus_add_del_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg); + +static int libbbf_ubus_transaction_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg); + +static const struct blobmsg_policy libbbf_ubus_supported_dm_policy[] = { + [LIBBBF_UBUS_SUPPORTED_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [LIBBBF_UBUS_SUPPORTED_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, + [LIBBBF_UBUS_SUPPORTED_NXT_LEVEL] = { .name = "next-level", .type = BLOBMSG_TYPE_INT8}, + [LIBBBF_UBUS_SUPPORTED_SCHEMA_TYPE] = { .name = "schema_type", .type = BLOBMSG_TYPE_INT32}, +}; + +static const struct blobmsg_policy libbbf_ubus_get_policy[] = { + [LIBBBF_UBUS_GET_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [LIBBBF_UBUS_GET_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING } +}; + +static const struct blobmsg_policy libbbf_ubus_set_policy[] = { + [LIBBBF_UBUS_SET_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [LIBBBF_UBUS_SET_VALUE] = { .name = "value", .type = BLOBMSG_TYPE_STRING }, + [LIBBBF_UBUS_SET_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING } +}; + +static const struct blobmsg_policy libbbf_ubus_operate_policy[] = { + [LIBBBF_UBUS_OPERATE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [LIBBBF_UBUS_OPERATE_INPUT] = { .name = "input", .type = BLOBMSG_TYPE_TABLE } +}; + +static const struct blobmsg_policy libbbf_ubus_add_del_policy[] = { + [LIBBBF_UBUS_ADD_DEL_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [LIBBBF_UBUS_ADD_DEL_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING } +}; + +static struct ubus_method libbbf_ubus_methods[] = { + UBUS_METHOD("get_supported_dm", libbbf_ubus_supported_dm, libbbf_ubus_supported_dm_policy), + UBUS_METHOD("get", libbbf_ubus_get_handler, libbbf_ubus_get_policy), + UBUS_METHOD("set", libbbf_ubus_set_handler, libbbf_ubus_set_policy), + UBUS_METHOD("operate", libbbf_ubus_operate, libbbf_ubus_operate_policy), + UBUS_METHOD("add_object", libbbf_ubus_add_del_handler, libbbf_ubus_add_del_policy), + UBUS_METHOD("del_object", libbbf_ubus_add_del_handler, libbbf_ubus_add_del_policy), + UBUS_METHOD_NOARG("transaction_start", libbbf_ubus_transaction_handler), + UBUS_METHOD_NOARG("transaction_abort", libbbf_ubus_transaction_handler), + UBUS_METHOD_NOARG("transaction_commit", libbbf_ubus_transaction_handler), +}; + +static int get_protocol(const char *val) +{ + int type; + + if (strcmp("cwmp", val) == 0) + type = BBFDM_CWMP; + else if (strcmp("usp", val) == 0) + type = BBFDM_USP; + else + type = BBFDM_BOTH; + + return type; +} + +static int get_bbf_proto_type(struct blob_attr *proto) +{ + int type; + + if (proto) { + const char *val = blobmsg_get_string(proto); + type = get_protocol(val); + } else { + type = BBFDM_BOTH; + } + + return type; +} + +static void bb_add_string(struct blob_buf *bb, const char *name, const char *value) +{ + if (value) + blobmsg_add_string(bb, name, value); + else + blobmsg_add_string(bb, name, ""); +} + +static struct obj_node* find_obj_node(const char *ubus_name) +{ + struct obj_node *temp = g_dynamicdm_head; + if (temp == NULL) + return NULL; + + while (strcmp(ubus_name, temp->obj_name) != 0) { + if (temp->next == NULL) { + return NULL; + } else { + temp = temp->next; + } + } + + return temp; +} + +static DMOBJ* get_entry_object(const char *name) +{ + if (!name) + return NULL; + + struct obj_node *ob_node = find_obj_node(name); + if (!ob_node) + return NULL; + + return ob_node->tUsrObj; +} + +static void fill_operate_schema(struct blob_buf *bb, struct dm_parameter *param) +{ + blobmsg_add_string(bb, "parameter",param->name); + blobmsg_add_string(bb,"type",param->type); + blobmsg_add_string(bb,"cmd_type",param->additional_data); + + if(param->data) { + const char **in, **out = NULL; + operation_args *args = NULL; + void *array = NULL; + int i; + + args = (operation_args *) param->data; + in = args->in; + if (in) { + array = blobmsg_open_array(bb, "in"); + for (i = 0; in[i] != NULL; i++) + blobmsg_add_string(bb, NULL, in[i]); + + blobmsg_close_array(bb, array); + } + + out = args->out; + if (out) { + array = blobmsg_open_array(bb, "out"); + for (i = 0; out[i] != NULL; i++) + blobmsg_add_string(bb, NULL, out[i]); + + blobmsg_close_array(bb, array); + } + } +} + +static void fill_event_schema(struct blob_buf *bb, struct dm_parameter *param) +{ + blobmsg_add_string(bb, "parameter",param->name); + blobmsg_add_string(bb,"type",param->type); + + if(param->data) { + event_args *ev = NULL; + + ev = (event_args *)param->data; + + if (ev->param) { + const char **in = NULL; + void *key = NULL; + int i; + + in = ev->param; + key = blobmsg_open_array(bb, "in"); + for (i = 0; in[i] != NULL; i++) + blobmsg_add_string(bb, NULL, in[i]); + blobmsg_close_array(bb, key); + } + } +} + +static void fill_param_schema(struct blob_buf *bb, struct dm_parameter *param) +{ + blobmsg_add_string(bb, "parameter", param->name); + blobmsg_add_string(bb, "writable", param->data ? param->data : "0"); + blobmsg_add_string(bb, "type", param->type); + + if (param->additional_data) { + const char **uniq_keys = NULL; + void *key = NULL; + int i; + + uniq_keys = (const char **)param->additional_data; + key = blobmsg_open_array(bb, "unique_keys"); + for (i = 0; uniq_keys[i] != NULL; i++) + blobmsg_add_string(bb, NULL, uniq_keys[i]); + + blobmsg_close_array(bb, key); + } +} + +static int handle_add_del_req(struct ubus_context *ctx, const char *ubus_name, struct ubus_request_data *req, + char *path, const char *method, int proto) +{ + int fault = 0; + struct dmctx bbf_ctx; + struct blob_buf bb; + char *pkey = "true"; + + DMOBJ *tEntryObj = get_entry_object(ubus_name); + if (!tEntryObj) { + printf("Failed to get DM entry obj\n\r"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + if (!g_dynamicdm_transaction_start) { + printf("Transaction not started\n\r"); + blobmsg_add_u32(&bb, "fault", usp_fault_map(USP_FAULT_INTERNAL_ERROR)); + ubus_send_reply(ctx, req, bb.head); + blob_buf_free(&bb); + return 0; + } + + memset(&bbf_ctx, 0, sizeof(struct dmctx)); + set_bbfdatamodel_type(proto); + dm_ctx_init_entry(&bbf_ctx, tEntryObj, 0); + + if (strcmp(method, "add_object") == 0) { + fault = dm_entry_param_method(&bbf_ctx, CMD_ADD_OBJECT, path, pkey, NULL); + } else { + fault = dm_entry_param_method(&bbf_ctx, CMD_DEL_OBJECT, path, pkey, NULL); + } + + void *array = blobmsg_open_array(&bb, "parameters"); + void *table = blobmsg_open_table(&bb, NULL); + + bb_add_string(&bb, "parameter", path); + if (fault) { + blobmsg_add_u32(&bb, "fault", fault); + blobmsg_add_u8(&bb, "status", 0); + } else { + if (strcmp(method, "add_object") == 0) { + if (bbf_ctx.addobj_instance) { + blobmsg_add_u8(&bb, "status", 1); + bb_add_string(&bb, "instance", bbf_ctx.addobj_instance); + } else { + blobmsg_add_u8(&bb, "status", 0); + } + } else { + blobmsg_add_u8(&bb, "status", 1); + } + } + + blobmsg_close_table(&bb, table); + blobmsg_close_array(&bb, array); + + dm_ctx_clean(&bbf_ctx); + ubus_send_reply(ctx, req, bb.head); + blob_buf_free(&bb); + + return 0; +} + +static void libbbf_ubus_obj_node_free(struct obj_node *obj) +{ + if (!obj) + return; + + if (obj->ob_type) + FREE(obj->ob_type); + + if (obj->ob) + FREE(obj->ob); + + if (obj->obj_name) + FREE(obj->obj_name); + + FREE(obj); +} + +static int libbbf_ubus_supported_dm(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg) +{ + struct blob_attr *tb[__LIBBBF_UBUS_SUPPORTED_MAX]; + char path[PATH_MAX] = {0}; + bool nxt_lvl = false; + uint32_t schema_type = 0; + struct blob_buf bb; + int fault = 0, proto; + struct dmctx bbf_ctx; + + if (blobmsg_parse(libbbf_ubus_supported_dm_policy, __LIBBBF_UBUS_SUPPORTED_MAX, tb, blob_data(msg), blob_len(msg)) == 0) { + if (tb[LIBBBF_UBUS_SUPPORTED_PATH]) + snprintf(path, PATH_MAX, "%s", (char *)blobmsg_data(tb[LIBBBF_UBUS_SUPPORTED_PATH])); + + if (tb[LIBBBF_UBUS_SUPPORTED_NXT_LEVEL]) + nxt_lvl = blobmsg_get_bool(tb[LIBBBF_UBUS_SUPPORTED_NXT_LEVEL]); + + if (tb[LIBBBF_UBUS_SUPPORTED_SCHEMA_TYPE]) + schema_type = blobmsg_get_u32(tb[LIBBBF_UBUS_SUPPORTED_SCHEMA_TYPE]); + } + + DMOBJ *tEntryObj = get_entry_object(obj->name); + if (!tEntryObj) { + printf("Failed to get DM entry obj\n\r"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + memset(&bbf_ctx, 0, sizeof(struct dmctx)); + proto = get_bbf_proto_type(tb[LIBBBF_UBUS_SUPPORTED_PROTO]); + set_bbfdatamodel_type(proto); + + dm_ctx_init_entry(&bbf_ctx, tEntryObj, 0); + + fault = dm_get_supported_dm(&bbf_ctx, path, nxt_lvl, schema_type); + if(fault) { + blobmsg_add_u32(&bb, "fault", fault); + } else { + struct dm_parameter *param = NULL; + void *array = NULL, *table = NULL; + + array = blobmsg_open_array(&bb,"parameters"); + list_for_each_entry(param, &bbf_ctx.list_parameter, list) { + int cmd = get_dm_type(param->type); + + table = blobmsg_open_table(&bb, NULL); + if (cmd == DMT_COMMAND) { + fill_operate_schema(&bb, param); + } else if (cmd == DMT_EVENT) { + fill_event_schema(&bb, param); + } else { + fill_param_schema(&bb, param); + } + + blobmsg_close_table(&bb, table); + } + blobmsg_close_array(&bb, array); + } + + ubus_send_reply(ctx, req, bb.head); + + blob_buf_free(&bb); + dm_ctx_clean(&bbf_ctx); + + return fault; +} + +static void init_dm_path(DMOBJ *tEntryObj) +{ + struct dmctx bbf_ctx; + memset(&bbf_ctx, 0, sizeof(struct dmctx)); + + set_bbfdatamodel_type(BBFDM_BOTH); + + dm_ctx_init_entry(&bbf_ctx, tEntryObj, 0); + + dm_entry_param_method(&bbf_ctx, CMD_GET_VALUE, "", NULL, NULL); + + dm_ctx_clean(&bbf_ctx); +} + +static int libbbf_ubus_get_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg) +{ + struct blob_attr *tb[__LIBBBF_UBUS_GET_MAX] = {NULL}; + char path[PATH_MAX] = {0}; + struct dmctx bbf_ctx; + struct blob_buf bb; + int fault = 0, proto; + + if (blobmsg_parse(libbbf_ubus_get_policy, __LIBBBF_UBUS_GET_MAX, tb, blob_data(msg), blob_len(msg))) { + printf("Failed to parse blob\n"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + if (!(tb[LIBBBF_UBUS_GET_PATH])) + return UBUS_STATUS_INVALID_ARGUMENT; + + DMOBJ *tEntryObj = get_entry_object(obj->name); + if (!tEntryObj) { + printf("Failed to get DM entry obj\n\r"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + snprintf(path, PATH_MAX, "%s", (char *)blobmsg_data(tb[LIBBBF_UBUS_GET_PATH])); + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + memset(&bbf_ctx, 0, sizeof(struct dmctx)); + proto = get_bbf_proto_type(tb[LIBBBF_UBUS_GET_PROTO]); + set_bbfdatamodel_type(proto); + dm_ctx_init_entry(&bbf_ctx, tEntryObj, 0); + + fault = dm_entry_param_method(&bbf_ctx, CMD_GET_VALUE, path, NULL, NULL); + + if (!fault) { + struct dm_parameter *n = NULL; + + void *array = blobmsg_open_array(&bb, "parameters"); + list_for_each_entry(n, &bbf_ctx.list_parameter, list) { + void *table = blobmsg_open_table(&bb, NULL); + bb_add_string(&bb, "parameter", n->name); + bb_add_string(&bb, "value", n->data); + bb_add_string(&bb, "type", n->type); + blobmsg_close_table(&bb, table); + } + blobmsg_close_array(&bb, array); + } else { + blobmsg_add_u32(&bb, "fault", fault); + } + + ubus_send_reply(ctx, req, bb.head); + + blob_buf_free(&bb); + dm_ctx_clean(&bbf_ctx); + + return fault; +} + +static int libbbf_ubus_operate(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg) +{ + struct blob_attr *tb[__LIBBBF_UBUS_OPERATE_MAX] = {NULL}; + char path[PATH_MAX] = {0}; + char *input = NULL; + struct blob_buf bb; + struct dmctx bbf_ctx; + int fault = 0, len; + + if (blobmsg_parse(libbbf_ubus_operate_policy, __LIBBBF_UBUS_OPERATE_MAX, tb, blob_data(msg), blob_len(msg))) { + printf("Failed to parse blob\n\r"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + if (!(tb[LIBBBF_UBUS_OPERATE_PATH])) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (tb[LIBBBF_UBUS_OPERATE_INPUT]) + input = blobmsg_format_json(tb[LIBBBF_UBUS_OPERATE_INPUT], true); + + DMOBJ *tEntryObj = get_entry_object(obj->name); + if (!tEntryObj) { + printf("Failed to get DM entry obj\n\r"); + if (input) + free(input); + + return UBUS_STATUS_UNKNOWN_ERROR; + } + + snprintf(path, PATH_MAX, "%s", (char *)blobmsg_data(tb[LIBBBF_UBUS_OPERATE_PATH])); + + len = strlen(path); + if (path[len - 1] == '.') { + printf("path can't end with (.)\n\r"); + if (input) + free(input); + + return UBUS_STATUS_UNKNOWN_ERROR; + } + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + memset(&bbf_ctx, 0, sizeof(struct dmctx)); + set_bbfdatamodel_type(BBFDM_USP); + dm_ctx_init_entry(&bbf_ctx, tEntryObj, 0); + + fault = dm_entry_param_method(&bbf_ctx, CMD_USP_OPERATE, path, input, NULL); + + switch (fault) { + case CMD_NOT_FOUND: + fault = USP_FAULT_INVALID_PATH; + break; + case CMD_INVALID_ARGUMENTS: + fault = USP_FAULT_INVALID_ARGUMENT; + break; + case CMD_FAIL: + fault = USP_FAULT_COMMAND_FAILURE; + break; + case CMD_SUCCESS: + fault = 0; + break; + default: + printf("Case(%d) not found\n\r", fault); + fault = USP_FAULT_INVALID_PATH; + break; + } + + void *array = blobmsg_open_array(&bb, "Results"); + void *table = blobmsg_open_table(&bb, NULL); + blobmsg_add_string(&bb, "path", path); + + if (fault == 0) { + struct dm_parameter *n = NULL; + + void *array_in = blobmsg_open_array(&bb, "parameters"); + list_for_each_entry(n, &bbf_ctx.list_parameter, list) { + void *table_in = blobmsg_open_table(&bb, NULL); + bb_add_string(&bb, "parameter", n->name); + bb_add_string(&bb, "value", n->data); + bb_add_string(&bb, "type", n->type); + blobmsg_close_table(&bb, table_in); + } + blobmsg_close_array(&bb, array_in); + } else { + fault = usp_fault_map(fault); + blobmsg_add_u32(&bb, "fault", fault); + } + + blobmsg_close_table(&bb, table); + blobmsg_close_array(&bb, array); + + dm_ctx_clean(&bbf_ctx); + ubus_send_reply(ctx, req, bb.head); + + blob_buf_free(&bb); + + if (input) + free(input); + + return 0; +} + +static int libbbf_ubus_add_del_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__LIBBBF_UBUS_ADD_DEL_MAX] = {NULL}; + char path[PATH_MAX] = {0}; + int plen, proto; + + if (blobmsg_parse(libbbf_ubus_add_del_policy, __LIBBBF_UBUS_ADD_DEL_MAX, tb, blob_data(msg), blob_len(msg))) { + printf("Failed to parse blob"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + if (!tb[LIBBBF_UBUS_ADD_DEL_PATH]) + return UBUS_STATUS_INVALID_ARGUMENT; + + snprintf(path, PATH_MAX, "%s", (char *)blobmsg_data(tb[LIBBBF_UBUS_ADD_DEL_PATH])); + + plen = strlen(path); + if (path[plen - 1] != '.') { + if (plen > PATH_MAX - 2) { + printf("path too long(%d) can't append (.)\n\r", plen); + return UBUS_STATUS_UNKNOWN_ERROR; + } + strcat(path, "."); + } + + proto = get_bbf_proto_type(tb[LIBBBF_UBUS_ADD_DEL_PROTO]); + return handle_add_del_req(ctx, obj->name, req, path, method, proto); +} + +static int libbbf_ubus_set_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_buf bb; + struct blob_attr *tb[__LIBBBF_UBUS_SET_MAX] = {NULL}; + char path[PATH_MAX] = {'\0'}, value[PATH_MAX] = {'\0'}; + int fault = 0, proto; + void *array = NULL, *table = NULL; + struct dmctx bbf_ctx; + bool fault_occured = false; + + if (blobmsg_parse(libbbf_ubus_set_policy, __LIBBBF_UBUS_SET_MAX, tb, blob_data(msg), blob_len(msg))) { + printf("Failed to parse blob"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + if (!tb[LIBBBF_UBUS_SET_PATH]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (!tb[LIBBBF_UBUS_SET_VALUE]) + return UBUS_STATUS_INVALID_ARGUMENT; + + DMOBJ *tEntryObj = get_entry_object(obj->name); + if (!tEntryObj) { + printf("Failed to get DM entry obj\n\r"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + snprintf(path, PATH_MAX, "%s", (char *)blobmsg_data(tb[LIBBBF_UBUS_SET_PATH])); + snprintf(value, PATH_MAX, "%s", (char *)blobmsg_data(tb[LIBBBF_UBUS_SET_VALUE])); + + int plen = strlen(path); + if (path[plen - 1] == '.') { + printf("path can't end with (.)\n\r"); + return UBUS_STATUS_INVALID_ARGUMENT; + } + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + if (!g_dynamicdm_transaction_start) { + printf("Transaction not started\n\r"); + blobmsg_add_u32(&bb, "fault", usp_fault_map(USP_FAULT_INTERNAL_ERROR)); + ubus_send_reply(ctx, req, bb.head); + blob_buf_free(&bb); + return 0; + } + + proto = get_bbf_proto_type(tb[LIBBBF_UBUS_SET_PROTO]); + set_bbfdatamodel_type(proto); + memset(&bbf_ctx, 0, sizeof(struct dmctx)); + dm_ctx_init_entry(&bbf_ctx, tEntryObj, 0); + + fault = dm_entry_param_method(&bbf_ctx, CMD_SET_VALUE, path, value, NULL); + if (fault) { + if (fault_occured == false) { + fault_occured = true; + array = blobmsg_open_array(&bb, "parameters"); + } + } + + while (bbf_ctx.list_fault_param.next != &bbf_ctx.list_fault_param) { + struct param_fault *p = list_entry(bbf_ctx.list_fault_param.next, struct param_fault, list); + table = blobmsg_open_table(&bb, NULL); + bb_add_string(&bb, "path", p->name); + blobmsg_add_u8(&bb, "status", false); + blobmsg_add_u32(&bb, "fault", (uint32_t)p->fault); + blobmsg_close_table(&bb, table); + del_list_fault_param(p); + } + + //Apply the parameter + fault = dm_entry_apply(&bbf_ctx, CMD_SET_VALUE, NULL); + if (fault == 0 && fault_occured == false) { + blobmsg_add_u8(&bb, "status", true); + if (get_bbfdatamodel_type() == BBFDM_CWMP) + blobmsg_add_u64(&bb, "flag", bbf_ctx.end_session_flag); + } else { + if (!array) + array = blobmsg_open_array(&bb, "parameters"); + + while (bbf_ctx.list_fault_param.next != &bbf_ctx.list_fault_param) { + struct param_fault *p = list_entry(bbf_ctx.list_fault_param.next, struct param_fault, list); + table = blobmsg_open_table(&bb, NULL); + bb_add_string(&bb, "path", p->name); + blobmsg_add_u8(&bb, "status", false); + blobmsg_add_u32(&bb, "fault", (uint32_t)p->fault); + blobmsg_close_table(&bb, table); + del_list_fault_param(p); + } + } + + if (array) + blobmsg_close_array(&bb, array); + + ubus_send_reply(ctx, req, bb.head); + + // free + blob_buf_free(&bb); + dm_ctx_clean(&bbf_ctx); + + return 0; +} + +static int libbbf_ubus_transaction_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg) +{ + struct dmctx bbf_ctx; + struct blob_buf bb; + + DMOBJ *tEntryObj = get_entry_object(obj->name); + if (!tEntryObj) { + printf("Failed to get DM entry obj\n\r"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + memset(&bbf_ctx, 0, sizeof(struct dmctx)); + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + if (strcmp(method, "transaction_start") == 0) { + if (!g_dynamicdm_transaction_start) { + g_dynamicdm_transaction_start = true; + blobmsg_add_u8(&bb, "status", true); + } else { + printf("Transaction already in process\n"); + blobmsg_add_u8(&bb, "status", false); + } + } else if(strcmp(method, "transaction_abort") == 0) { + if (g_dynamicdm_transaction_start) { + g_dynamicdm_transaction_start = false; + dm_ctx_init_entry(&bbf_ctx, tEntryObj, 0); + dm_entry_revert_changes(); + dm_ctx_clean(&bbf_ctx); + blobmsg_add_u8(&bb, "status", true); + } else { + printf("Transaction still not started\n\r"); + blobmsg_add_u8(&bb, "status", false); + } + } else if (strcmp(method, "transaction_commit") == 0) { + if (g_dynamicdm_transaction_start) { + g_dynamicdm_transaction_start = false; + dm_ctx_init_entry(&bbf_ctx, tEntryObj, 0); + dm_entry_manage_services(&bb, true); + dm_entry_restart_services(); + dm_ctx_clean(&bbf_ctx); + blobmsg_add_u8(&bb, "status", true); + } else { + printf("Transaction still not started\n\r"); + blobmsg_add_u8(&bb, "status", false); + } + } else { + printf("Unsupported method %s\n\r", method); + } + + ubus_send_reply(ctx, req, bb.head); + blob_buf_free(&bb); + + return 0; +} + +int dynamicdm_init(struct ubus_context *ctx, char *ubus_name, DMOBJ *entry) +{ + if (!ctx || !ubus_name || ubus_name[0] == '\0' || !entry) + return -1; + + struct obj_node *new = (struct obj_node *)malloc(sizeof(struct obj_node)); + if (!new) + return -1; + + memset(new, 0, sizeof(struct obj_node)); + new->ob = (struct ubus_object *) calloc(1, sizeof(struct ubus_object)); + if (!new->ob) { + printf("Out of memory!!\n\r"); + libbbf_ubus_obj_node_free(new); + return -1; + } + + new->ob_type = (struct ubus_object_type *) calloc(1, sizeof(struct ubus_object_type)); + if (!new->ob_type) { + printf("Out of memory!!\n\r"); + libbbf_ubus_obj_node_free(new); + return -1; + } + + new->obj_name = strdup(ubus_name); + + new->ob_type->name = new->obj_name; + new->ob_type->id = 0; + new->ob_type->methods = libbbf_ubus_methods; + new->ob_type->n_methods = ARRAY_SIZE(libbbf_ubus_methods); + + new->ob->name = new->obj_name; + new->ob->type = new->ob_type; + new->ob->methods = libbbf_ubus_methods; + new->ob->n_methods = ARRAY_SIZE(libbbf_ubus_methods); + + if (ubus_add_object(ctx, new->ob)) { + printf("Failed to add object.\n\r"); + libbbf_ubus_obj_node_free(new); + return -1; + } + + new->tUsrObj = entry; + + new->next = g_dynamicdm_head; + g_dynamicdm_head = new; + + init_dm_path(entry); + + return 0; +} + +int dynamicdm_init_plugin_object(struct ubus_context *ctx, char *ubus_name, DM_MAP_OBJ *entry) +{ + int i; + DMOBJ *tEntryObj = NULL; + + if (!entry) + return -1; + + for (i = 0; entry[i].path != NULL; i++) { + tEntryObj = (DMOBJ*)realloc(tEntryObj, sizeof(DMOBJ) * (i+1)); + if (!tEntryObj) { + printf("No Memory exists\n\r"); + return -1; + } + + memset(&tEntryObj[i], 0, sizeof(DMOBJ)); + + tEntryObj[i].obj = entry[i].path; + tEntryObj[i].permission = &DMREAD; + tEntryObj[i].nextobj = entry[i].root_obj; + tEntryObj[i].leaf = entry[i].root_leaf; + tEntryObj[i].bbfdm_type = BBFDM_BOTH; + } + + /* Make the last empty entry */ + tEntryObj = (DMOBJ*)realloc(tEntryObj, sizeof(DMOBJ) * (i+1)); + if (!tEntryObj) { + printf("No Memory exists\n\r"); + return -1; + } + + memset(&tEntryObj[i], 0, sizeof(DMOBJ)); + + if (0 != dynamicdm_init(ctx, ubus_name, tEntryObj)) { + FREE(tEntryObj); + return -1; + } + + return 0; +} + +void dynamicdm_free(struct ubus_context *ctx, const char *ubus_name) +{ + struct obj_node *curr = g_dynamicdm_head, *prev = NULL; + + if (!ctx|| !ubus_name || ubus_name[0] == '\0') + return; + + if (curr == NULL) + return; + + while (strcmp(ubus_name, curr->obj_name) != 0) { + if (curr->next == NULL) { + return; + } else { + prev = curr; + curr = curr->next; + } + } + + if (curr == g_dynamicdm_head) { + g_dynamicdm_head = g_dynamicdm_head->next; + } else { + prev->next = curr->next; + } + + if (curr->tUsrObj) + dm_cleanup_dynamic_entry(curr->tUsrObj); + + ubus_remove_object(ctx, curr->ob); + + libbbf_ubus_obj_node_free(curr); +} + +void dynamicdm_free_plugin_object(struct ubus_context *ctx, const char *ubus_name) +{ + if (!ctx || !ubus_name || ubus_name[0] == '\0') + return; + + struct obj_node *ob_node = find_obj_node(ubus_name); + if (!ob_node) + return; + + dm_cleanup_dynamic_entry(ob_node->tUsrObj); + FREE(ob_node->tUsrObj); + + dynamicdm_free(ctx, ubus_name); +} diff --git a/schemas/dmtest.json b/schemas/dmtest.json new file mode 100644 index 00000000..e362abf3 --- /dev/null +++ b/schemas/dmtest.json @@ -0,0 +1,621 @@ +{ + "definitions": { + "path_t": { + "description": "Complete object element path as per TR181", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": [ + "Device.", + "Device.DeviceInfo.Manufacturer", + "Device.WiFi.SSID.1.", + "Device.WiFi." + ] + }, + "schema_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": [ + "Device.Bridging.Bridge.{i}.", + "Device.DeviceInfo.Manufacturer", + "Device.WiFi.SSID.{i}.SSID" + ] + }, + "boolean_t": { + "type": "string", + "enum": [ + "0", + "1" + ] + }, + "operate_path_t": { + "description": "Datamodel object schema path", + "type": "string", + "minLength": 6, + "maxLength": 1024, + "examples": [ + "Device.DHCPv4.Client.{i}.Renew()", + "Device.FactoryReset()" + ] + }, + "operate_type_t": { + "type": "string", + "enum": [ + "async", + "sync" + ] + }, + "instance_t": { + "description": "Multi object instances", + "type": "string", + "minLength": 6, + "maxLength": 256 + }, + "proto_t": { + "type": "string", + "default": "both", + "enum": [ + "usp", + "cwmp", + "both" + ] + }, + "type_t": { + "type": "string", + "enum": [ + "xsd:string", + "xsd:unsignedInt", + "xsd:int", + "xsd:unsignedLong", + "xsd:long", + "xsd:boolean", + "xsd:dateTime", + "xsd:hexBinary", + "xsd:object", + "xsd:command", + "xsd:event" + ] + }, + "fault_t": { + "type": "integer", + "minimum": 7000, + "maximum": 9050 + } + }, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://dev.iopsys.eu/iopsys/uspd/-/blob/devel/docs/api/dmtest.json", + "type": "object", + "title": "dmtest", + "object": "user defined dynamic DM exposed on ubus", + "additionalProperties": false, + "properties": { + "get_supported_dm": { + "title": "Get list of supported datamodel parameters", + "description": "Schema will have all the nodes/objects supported by libbbf", + "type": "object", + "required": [ + "output" + ], + "properties": { + "input": { + "type": "object", + "properties": { + "path": { + "$ref": "#/definitions/path_t" + }, + "proto": { + "$ref": "#/definitions/proto_t" + }, + "next-level": { + "type": "boolean", + "description": "gets only next level objects if true" + }, + "schema_type": { + "type": "integer", + "minimum": 0, + "maximum": 3, + "description": "0-All, 1-Parameter only 2- Event only 3- operate only" + } + } + }, + "output": { + "oneof": [ + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "parameter": { + "$ref": "#/definitions/schema_path_t" + }, + "writable": { + "$ref": "#/definitions/boolean_t" + }, + "type": { + "$ref": "#/definitions/type_t" + }, + "cmd_type": { + "$ref": "#/definitions/operate_type_t" + }, + "in": { + "type": "array", + "uniqueItems": true, + "items": [ + { + "type": "string" + } + ] + }, + "out": { + "type": "array", + "uniqueItems": true, + "items": [ + { + "type": "string" + } + ] + } + }, + "required": [ + "parameter", + "type" + ] + } + ] + } + } + } + ] + } + } + }, + "get": { + "title": "Get handler", + "description": "Query the datamodel object", + "type": "object", + "required": [ + "input", + "output" + ], + "properties": { + "input": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "$ref": "#/definitions/path_t" + }, + "proto": { + "$ref": "#/definitions/proto_t" + } + } + }, + "output": { + "oneof": [ + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "parameter", + "value", + "type" + ], + "properties": { + "parameter": { + "$ref": "#/definitions/path_t" + }, + "value": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/type_t" + } + } + } + ] + } + } + } + ] + } + } + }, + "add_object": { + "title": "Add a new object instance", + "description": "Add a new object in multi instance object", + "type": "object", + "required": [ + "input", + "output" + ], + "properties": { + "input": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "$ref": "#/definitions/path_t" + }, + "proto": { + "$ref": "#/definitions/proto_t" + } + } + }, + "output": { + "oneof": [ + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "required": [ + "parameters" + ], + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "parameter", + "status" + ], + "properties": { + "parameter": { + "$ref": "#/definitions/path_t" + }, + "status": { + "type": "boolean" + }, + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be determined by fault code" + }, + "instance": { + "type": "string" + } + } + } + ] + } + } + } + ] + } + } + }, + "del_object": { + "title": "Delete object instance", + "description": "Delete a object instance from multi instance object", + "type": "object", + "required": [ + "input", + "output" + ], + "properties": { + "input": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "$ref": "#/definitions/path_t" + }, + "proto": { + "$ref": "#/definitions/proto_t" + } + } + }, + "output": { + "oneof": [ + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "required": [ + "parameters" + ], + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "parameter", + "status" + ], + "properties": { + "parameter": { + "$ref": "#/definitions/path_t" + }, + "status": { + "type": "boolean" + }, + "fault": { + "$ref": "#/definitions/fault_t" + } + } + } + ] + } + } + } + ] + } + } + }, + "set": { + "title": "Set handler", + "description": "Set values of datamodel object element", + "type": "object", + "required": [ + "input", + "output" + ], + "properties": { + "input": { + "type": "object", + "required": [ + "path", + "value" + ], + "properties": { + "path": { + "$ref": "#/definitions/path_t" + }, + "proto": { + "$ref": "#/definitions/proto_t" + }, + "value": { + "description": "value of the object element provided in path, path should contains valid writable object element", + "type": "string", + "examples": [ + "{\"path\":\"Device.WiFi.SSID.1.SSID\", \"value\":\"test_ssid\"}", + "{\"path\":\"Device.WiFi.SSID.2.Enable\", \"value\":\"true\"}", + "{\"path\":\"Device.WiFi.SSID.1.Enable\", \"value\":\"0\"}" + ] + } + } + }, + "output": { + "oneof": [ + { + "type": "object", + "properties": { + "status": { + "const": "1" + } + } + }, + { + "fault": { + "$ref": "#/definitions/fault_t", + "Description": "Any discrepancy in input will result in fault. The type of fault can be identified by fault code" + } + }, + { + "type": "object", + "required": [ + "parameters" + ], + "properties": { + "parameters": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "parameter" + ], + "properties": { + "parameter": { + "$ref": "#/definitions/path_t" + }, + "status": { + "type": "boolean" + }, + "fault": { + "$ref": "#/definitions/fault_t" + } + } + } + ] + } + } + } + ] + } + } + }, + "operate": { + "title": "Operate handler", + "description": "Operate on object element provided in path", + "type": "object", + "required": [ + "input", + "output" + ], + "properties": { + "input": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "$ref": "#/definitions/operate_path_t" + }, + "input": { + "description": "Input arguments for the operate command as defined in TR-181-2.13", + "examples": [ + "{\"path\":\"Device.IP.Diagnostics.IPPing\\(\\)\", \"input\":{\"Host\":\"iopsys.eu\"}}" + ], + "type": "object", + "properties": {} + } + } + }, + "output": { + "type": "object", + "required": [ + "Results" + ], + "properties": { + "Results": { + "type": "array", + "items": [ + { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "$ref": "#/definitions/path_t" + }, + "parameters": { + "description": "Output will have status for sync commands and for async commands parameters as defined in TR-181-2.13", + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "parameter": { + "type": "string" + }, + "value": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/type_t" + }, + "fault": { + "$ref": "#/definitions/fault_t" + } + } + } + ], + "examples": [ + "{\n\t\"status\": true}", + "{\n\t\"AverageResponseTime\": \"0\",\n\t\"AverageResponseTimeDetailed\": \"130\",\n\t\"FailureCount\": \"0\",\n\t\"MaximumResponseTime\": \"0\",\n\t\"MaximumResponseTimeDetailed\": \"140\",\n\t\"MinimumResponseTime\": \"0\",\n\t\"MinimumResponseTimeDetailed\": \"120\",\n\t\"SuccessCount\": \"3\"}" + ] + } + } + } + ] + } + } + } + } + }, + "transaction_start": { + "title": "Start a transaction before set/add/del operations", + "type": "object", + "properties": { + "input": { + "type": "object", + "properties": {} + }, + "output": { + "type": "object", + "properties": { + "status": { + "type": "boolean" + } + }, + "required": [ + "status" + ] + } + }, + "required": [ + "output" + ] + }, + "transaction_abort": { + "title": "Aborts an on-going transaction", + "type": "object", + "properties": { + "input": { + "type": "object", + "properties": {} + }, + "output": { + "type": "object", + "properties": { + "status": { + "type": "boolean" + } + }, + "required": [ + "status" + ] + } + }, + "required": [ + "output" + ] + }, + "transaction_commit": { + "title": "Commits an on-going transaction", + "type": "object", + "properties": { + "input": { + "type": "object", + "properties": {} + }, + "output": { + "type": "object", + "properties": { + "status": { + "type": "boolean" + } + }, + "required": [ + "status" + ] + } + }, + "required": [ + "output" + ] + } + } +} diff --git a/test/api/json/dmtest.validation.json b/test/api/json/dmtest.validation.json new file mode 100644 index 00000000..7c8b856d --- /dev/null +++ b/test/api/json/dmtest.validation.json @@ -0,0 +1,104 @@ +{ + "object": "dmtest", + "methods": [ + { + "method": "get", + "args": { + "path": "Device.", + "proto": "usp" + }, + "rc": 0 + }, + { + "method": "transaction_start", + "args": {}, + "rc": 0 + }, + { + "method": "set", + "args": { + "path": "Device..X_IOPSYS_EU_Syslog.ConsoleLogLevel", + "value": "1" + }, + "rc": 0 + }, + { + "method": "transaction_abort", + "args": {}, + "rc": 0 + }, + { + "method": "add_object", + "args": { + "path": "Device.ManagementServer..InformParameter.", + "proto": "cwmp" + }, + "rc": 0 + }, + { + "method": "get_supported_dm", + "args": { + "path":"Device.", + "next-level":false, + "schema_type":1 + }, + "rc": 0 + }, + { + "method": "get_supported_dm", + "args": { + "path":"Device." + }, + "rc": 0 + }, + { + "method": "get_supported_dm", + "args": { + "path":"Device.", + "next-level":false, + "schema_type":2 + }, + "rc": 0 + }, + { + "method": "get_supported_dm", + "args": { + "path":"Device.", + "next-level":false, + "schema_type":3 + }, + "rc": 0 + }, + { + "method": "get_supported_dm", + "args": { + "path":"Device.", + "schema_type":1 + }, + "rc": 0 + }, + { + "method": "get_supported_dm", + "args": {}, + "rc": 0 + }, + { + "method": "transaction_start", + "args": {}, + "rc": 0 + }, + { + "method": "transaction_commit", + "args": {}, + "rc": 0 + }, + { + "method": "del_object", + "args": { + "path": "Device.ManagementServer..InformParameter.2", + "proto": "cwmp" + }, + "rc": 0 + } + ] +} diff --git a/test/dynamicdm_ubus_test/Makefile b/test/dynamicdm_ubus_test/Makefile new file mode 100644 index 00000000..68aedece --- /dev/null +++ b/test/dynamicdm_ubus_test/Makefile @@ -0,0 +1,16 @@ +BIN = bbf_ubus + +BIN_OBJ = bbf_ubus.o +BIN_CFLAGS = $(CFLAGS) -Wall -Werror -fPIC -I /usr/local/include/ +BIN_LDFLAGS = $(LDFLAGS) -lbbf_ubus -lubus -lubox -lbbf_test + +%.o: %.c + $(CC) $(BIN_CFLAGS) $(FPIC) -c -o $@ $< + +all: $(BIN) + +$(BIN): $(BIN_OBJ) + $(CC) -o $@ $^ $(BIN_LDFLAGS) + +clean: + rm -fv *.o $(BIN) diff --git a/test/dynamicdm_ubus_test/bbf_ubus.c b/test/dynamicdm_ubus_test/bbf_ubus.c new file mode 100644 index 00000000..35762d98 --- /dev/null +++ b/test/dynamicdm_ubus_test/bbf_ubus.c @@ -0,0 +1,34 @@ +#include + +#include +#include +#include + +extern DM_MAP_OBJ tDynamicObj[]; + +int main(int argc, char *argv[]) +{ + struct ubus_context *ctx = ubus_connect(NULL); + + if (!ctx) { + printf("Failed to connect to ubus\n\r"); + return -1; + } + + printf("Sending entry obj: (%s)\n\r", tDynamicObj[0].path); + + ubus_add_uloop(ctx); + + if (-1 == dynamicdm_init_plugin_object(ctx, "dmtest", tDynamicObj)) { + printf("Failed to create ubus object\n\r"); + return -1; + } + + uloop_run(); + + dynamicdm_free_plugin_object(ctx, "dmtest"); + ubus_free(ctx); + uloop_done(); + + return 0; +}