Merge pull request #1688 from hxy7yx/main-2

add http api ft
This commit is contained in:
fengzeroz
2023-11-23 09:57:03 +08:00
committed by GitHub
8 changed files with 287 additions and 117 deletions
-9
View File
@@ -107,9 +107,6 @@ static struct neu_http_handler cors_handler[] = {
{
.url = "/api/v2/password",
},
{
.url = "/api/v2/logs",
},
{
.url = "/api/v2/log/level",
},
@@ -366,12 +363,6 @@ static struct neu_http_handler rest_handlers[] = {
.url = "/api/v2/node/state",
.value.handler = handle_get_node_state,
},
{
.method = NEU_HTTP_METHOD_GET,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/logs",
.value.handler = handle_logs_files,
},
{
.method = NEU_HTTP_METHOD_PUT,
.type = NEU_HTTP_HANDLER_FUNCTION,
-104
View File
@@ -42,70 +42,6 @@
#include "log_handle.h"
#include "utils/utarray.h"
void handle_logs_files(nng_aio *aio)
{
void * data = NULL;
size_t len = 0;
int rv = 0;
NEU_VALIDATE_JWT(aio);
/* check whether the neuron directory exists */
rv = access("../neuron", F_OK);
if (0 != rv) {
nlog_error("The neuron directory does not exists");
NEU_JSON_RESPONSE_ERROR(NEU_ERR_FILE_NOT_EXIST, {
neu_http_response(aio, error_code.error, result_error);
});
return;
}
rv = system("rm -rf /tmp/neuron_debug.tar.gz");
nlog_warn("remove old neuron_debug.tar.gz, rv: %d", rv);
/* tar the neuron directory */
rv = system("tar -zcvf /tmp/neuron_debug.tar.gz "
"--exclude='persistence/neuron.lic' ../neuron");
if (rv == -1) {
nlog_error("failed to create neuron_debug.tar.gz, rv: %d", rv);
NEU_JSON_RESPONSE_ERROR(NEU_ERR_COMMAND_EXECUTION_FAILED, {
neu_http_response(aio, error_code.error, result_error);
});
return;
} else {
if (WIFEXITED(rv)) {
if (WEXITSTATUS(rv) < 0) {
nlog_error("failed to create neuron_debug.tar.gz, rv: %d",
WEXITSTATUS(rv));
NEU_JSON_RESPONSE_ERROR(NEU_ERR_COMMAND_EXECUTION_FAILED, {
neu_http_response(aio, error_code.error, result_error);
});
return;
}
} else {
nlog_error("failed to create neuron_debug.tar.gz, rv: %d",
WEXITSTATUS(rv));
NEU_JSON_RESPONSE_ERROR(NEU_ERR_COMMAND_EXECUTION_FAILED, {
neu_http_response(aio, error_code.error, result_error);
});
return;
}
}
rv = read_file("/tmp/neuron_debug.tar.gz", &data, &len);
if (0 != rv) {
NEU_JSON_RESPONSE_ERROR(
rv, { neu_http_response(aio, error_code.error, result_error); });
return;
}
/* handle http response */
rv = neu_http_response_file(aio, data, len,
"attachment; filename=neuron_debug.tar.gz");
free(data);
nlog_notice("download neuron_debug.tar.gz, ret: %d", rv);
}
void handle_log_level(nng_aio *aio)
{
neu_plugin_t *plugin = neu_rest_get_plugin();
@@ -156,44 +92,4 @@ void handle_log_level(nng_aio *aio)
}
}
})
}
int read_file(const char *file_name, void **datap, size_t *lenp)
{
int rv = 0;
FILE * f;
struct stat st;
size_t len;
void * data;
if (stat(file_name, &st) != 0) {
return NEU_ERR_FILE_NOT_EXIST;
}
if ((f = fopen(file_name, "rb")) == NULL) {
nlog_error("open fail: %s", file_name);
return NEU_ERR_FILE_OPEN_FAILURE;
}
len = st.st_size;
if (len > 0) {
if ((data = malloc(len)) == NULL) {
rv = NEU_ERR_EINTERNAL;
goto done;
}
if (fread(data, 1, len, f) != len) {
nlog_error("file read failued, errno = %d", errno);
rv = NEU_ERR_FILE_READ_FAILURE;
free(data);
goto done;
}
} else {
data = NULL;
}
*datap = data;
*lenp = len;
done:
fclose(f);
return (rv);
}
-3
View File
@@ -23,9 +23,6 @@
#include <nng/nng.h>
// void handle_get_log(nng_aio *aio);
void handle_logs_files(nng_aio *aio);
void handle_log_level(nng_aio *aio);
int read_file(const char *file_name, void **datap, size_t *lenp);
#endif
+14
View File
@@ -896,6 +896,20 @@ class TestModbus:
node=param[0], group='group', tag=hold_string_l1[0]['name'])
assert 'b' == api.read_tag(
node=param[0], group='group', tag=hold_string_l2[0]['name'])
@description(given="created modbus node ,groups and tags", when="write/read gtags", then="write/read success")
def test_write_read_gtags(self, param):
modbus_write_gtags = {"node": param[0], "groups": [{"group": "group", "tags" : [{"tag": "hold_uint16", "value": 1}, {"tag": "hold_string", "value": 'hello'},
{"tag": "coil_bit_1", "value": 1}, {"tag": "hold_double_decimal", "value": 513.11}, {"tag": "hold_bytes", "value": [0x1, 0x2, 0x3, 0x4]}]},
{"group": "group1", "tags" : [{"tag": "hold_int16", "value": 1}]}]}
response = api.write_gtags(json=modbus_write_gtags)
assert 200 == response.status_code
assert error.NEU_ERR_SUCCESS == response.json()['error']
time.sleep(1.0)
assert 1 == api.read_tag(
node=param[0], group='group', tag=hold_uint16[0]['name'])
assert 1 == api.read_tag(
node=param[0], group='group1', tag=hold_int16[0]['name'])
@description(given="created modbus device_error_test node/tag", when="read tag", then="read failed")
def test_read_modbus_device_err(self, param):
+3
View File
@@ -0,0 +1,3 @@
import sys
sys.path.append("tests/ft")
+229
View File
@@ -0,0 +1,229 @@
import neuron.api as api
import neuron.error as error
import neuron.config as config
from neuron.common import *
hold_int16 = [{"name": "hold_int16", "address": "1!400001",
"attribute": config.NEU_TAG_ATTRIBUTE_RW, "type": config.NEU_TYPE_INT16}]
global_config = {
"nodes": [
{
"plugin": "MQTT",
"name": "mqtt"
},
{
"plugin": "Modbus TCP",
"name": "modbus"
}
],
"groups": [
{
"driver": "modbus",
"group": "group",
"tag_count": 1,
"interval": 100
}
],
"tags": [
{
"tags": [
{
"type": 3,
"name": "hold_int16",
"attribute": 3,
"precision": 0,
"decimal": 0.0,
"address": "1!400001",
"description": ""
},
{
"type": 3,
"name": "hold_int16_s",
"attribute": 11,
"precision": 0,
"decimal": 0.0,
"address": "1!400002",
"description": "",
"value": 1
}
],
"driver": "modbus",
"group": "group"
}
],
"subscriptions": [
{
"groups": [
{
"driver": "modbus",
"group": "group"
}
],
"app": "mqtt"
}
],
"settings": [
{
"node": "mqtt",
"params": {
"client-id": "neuron_aBcDeF",
"qos": 0,
"format": 0,
"write-req-topic": "/neuron/mqtt/write/req",
"write-resp-topic": "/neuron/mqtt/write/resp",
"offline-cache": False,
"cache-sync-interval": 100,
"host": "broker.emqx.io",
"port": 1883,
"username": "",
"password": "",
"ssl": False
}
},
{
"node": "modbus",
"params": {
"connection_mode": 0,
"transport_mode": 0,
"interval": 0,
"host": "127.0.0.1",
"port": 502,
"timeout": 3000,
"max_retries": 2
}
}
]
}
class TestHttp:
@description(given="running neuron", when="add driver/app node/group/tag, app subscribes driver group, and get global config", then="success")
def test_get_global_config(self):
response = api.put_global_config(json=global_config)
assert 200 == response.status_code
assert error.NEU_ERR_SUCCESS == response.json()['error']
response = api.get_global_config()
assert 200 == response.status_code
config_data = response.json()
assert 'nodes' in config_data
assert len(config_data['nodes']) == 2
assert config_data['nodes'][0]['plugin'] == 'MQTT'
assert config_data['nodes'][1]['plugin'] == 'Modbus TCP'
assert 'groups' in config_data
assert len(config_data['groups']) == 1
assert config_data['groups'][0]['driver'] == 'modbus'
assert 'tags' in config_data
assert len(config_data['tags']) == 1
assert config_data['tags'][0]['driver'] == 'modbus'
assert len(config_data['tags'][0]['tags']) == 2
assert 'subscriptions' in config_data
assert len(config_data['subscriptions']) == 1
assert config_data['subscriptions'][0]['app'] == 'mqtt'
assert 'settings' in config_data
assert len(config_data['settings']) == 2
assert config_data['settings'][0]['node'] == 'mqtt'
response = api.del_node_check(node='modbus')
response = api.get_global_config()
assert 200 == response.status_code
config_data = response.json()
assert 'nodes' in config_data
assert len(config_data['nodes']) == 1
assert config_data['nodes'][0]['plugin'] == 'MQTT'
assert 'groups' in config_data
assert len(config_data['groups']) == 0
assert 'tags' in config_data
assert len(config_data['tags']) == 0
assert 'subscriptions' in config_data
assert len(config_data['subscriptions']) == 0
assert 'settings' in config_data
assert len(config_data['settings']) == 1
assert config_data['settings'][0]['node'] == 'mqtt'
@description(given="running neuron", when="get mqtt shcema", then="success")
def test_get_plugin_schema(self):
response = api.get_plugin_schema(plugin='mqtt')
assert 200 == response.status_code
response = api.get_plugin_schema(plugin='modbus-tcp')
assert 200 == response.status_code
response = api.get_plugin_schema(plugin='invalid')
assert 404 == response.status_code
assert response.json()['status'] == 'error'
@description(given="running neuron", when="get plugin", then="success")
def test_get_plugin(self):
response = api.get_plugin()
assert 200 == response.status_code
@description(given="running neuron", when="get version", then="success")
def test_get_version(self):
response = api.get_version()
assert 200 == response.status_code
@description(given="running neuron", when="test ndriver", then="failed because it is not supported yet")
def test_ndriver(self):
api.add_node_check(node='modbus', plugin=config.PLUGIN_MODBUS_TCP)
api.add_group_check(node='modbus', group='group')
api.add_tags_check(node='modbus', group='group', tags=hold_int16)
response = api.subscribe_group(app='mqtt', driver='modbus', group='group')
assert 200 == response.status_code
assert error.NEU_ERR_SUCCESS == response.json()['error']
response = api.post_ndriver_map(ndriver='mqtt', driver='modbus', group='group')
assert 400 == response.status_code
response = api.get_ndriver_map(ndriver='mqtt')
assert 400 == response.status_code
ndriver_tag_param = {"ndriver": "mqtt", "driver": "modbus", "group": "group", "tags": [
{"name": "hold_int16", "params": {"address": "1!400002"}}]}
response = api.put_ndriver_tag_param(json=ndriver_tag_param)
assert 200 == response.status_code
ndriver_tag_info = {"ndriver": "mqtt", "driver": "modbus", "group": "group", "tags": [
{"name": "hold_int16", "address": "1!400002"}]}
response = api.put_ndriver_tag_info(json=ndriver_tag_info)
assert 200 == response.status_code
response = api.get_ndriver_tag(ndriver='mqtt', driver='modbus', group='group')
assert 200 == response.status_code
response = api.delete_ndriver_map(ndriver='mqtt', driver='modbus', group='group')
assert 200 == response.status_code
@description(given="running neuron", when="test jwt error", then="failed")
def test_jwt_err(self):
response = api.change_password(new_password='123456', jwt='invalid')
assert 401 == response.status_code
response = api.get_hwtoken(jwt='invalid')
assert 404 == response.status_code
response = api.get_license(jwt='invalid')
assert 404 == response.status_code
response = api.upload_license(license='abaaba', jwt='invalid')
assert 404 == response.status_code
response = api.upload_virtual_license(license='abaaba', jwt='invalid')
assert 404 == response.status_code
response = api.delete_virtual_license(jwt='invalid')
assert 404 == response.status_code
+5 -1
View File
@@ -121,4 +121,8 @@ class TestLog:
response = api.get_nodes_state('modbus-tcp-1')
assert 200 == response.status_code
assert "notice" == response.json()['neuron_core']
assert "info" == response.json()['log_level']
assert "info" == response.json()['log_level']
response = api.get_nodes_state(node='')
assert 200 == response.status_code
assert "notice" == response.json()['neuron_core']
+36
View File
@@ -194,6 +194,9 @@ def write_tag(node, group, tag, value):
def write_tags(node, group, tag_values):
return requests.post(url=config.BASE_URL + "/api/v2/write/tags", headers={"Authorization": config.default_jwt}, json={"node": node, "group": group, "tags": tag_values})
def write_gtags(json):
return requests.post(url=config.BASE_URL + "/api/v2/write/gtags", headers={"Authorization": config.default_jwt}, json=json)
def add_plugin(library_name, so_file, schema_file):
return requests.post(url=config.BASE_URL + "/api/v2/plugin", json={"library": library_name, "so_file": so_file, "schema_file": schema_file}, headers={"Authorization": config.default_jwt})
@@ -293,6 +296,39 @@ def unsubscribe_group(app, driver, group):
def get_metrics(category="global", node=""):
return requests.get(url=config.BASE_URL + "/api/v2/metrics?category=" + category + "&node=" + node, headers={"Authorization": config.default_jwt})
def get_global_config():
return requests.get(url=config.BASE_URL + "/api/v2/global/config", headers={"Authorization": config.default_jwt})
def put_global_config(json):
return requests.put(url=config.BASE_URL + "/api/v2/global/config", headers={"Authorization": config.default_jwt}, json=json)
def get_plugin_schema(plugin):
return requests.get(url=config.BASE_URL + "/api/v2/schema?schema_name=" + plugin, headers={"Authorization": config.default_jwt})
def get_plugin():
return requests.get(url=config.BASE_URL + "/api/v2/plugin", headers={"Authorization": config.default_jwt})
def get_version():
return requests.get(url=config.BASE_URL + "/api/v2/version", headers={"Authorization": config.default_jwt})
def post_ndriver_map(ndriver, driver, group):
return requests.post(url=config.BASE_URL + "/api/v2/ndriver/map", headers={"Authorization": config.default_jwt}, json={"ndriver": ndriver, "driver": driver, "group":group})
def delete_ndriver_map(ndriver, driver, group):
return requests.delete(url=config.BASE_URL + "/api/v2/ndriver/map", headers={"Authorization": config.default_jwt}, json={"ndriver": ndriver, "driver": driver, "group":group})
def get_ndriver_map(ndriver):
return requests.get(url=config.BASE_URL + "/api/v2/ndriver/map?ndriver=" + ndriver, headers={"Authorization": config.default_jwt})
def put_ndriver_tag_param(json):
return requests.put(url=config.BASE_URL + "/api/v2/ndriver/tag/param", headers={"Authorization": config.default_jwt}, json=json)
def put_ndriver_tag_info(json):
return requests.put(url=config.BASE_URL + "/api/v2/ndriver/tag/info", headers={"Authorization": config.default_jwt}, json=json)
def get_ndriver_tag(ndriver, driver, group):
return requests.get(url=config.BASE_URL + "/api/v2/ndriver/tag?ndriver=" + ndriver + "&driver=" + driver + "&group=" + group, headers={"Authorization": config.default_jwt})
# plugin setting