diff --git a/src/c/config.c b/src/c/config.c index 41303dce..02e868b4 100644 --- a/src/c/config.c +++ b/src/c/config.c @@ -86,6 +86,7 @@ iot_data_t *edgex_common_config_defaults (const char *svcname) iot_data_string_map_add (result, "Device/Labels", iot_data_alloc_typed_vector (0, IOT_DATA_STRING)); iot_data_string_map_add (result, "Device/ProfilesDir", iot_data_alloc_string ("", IOT_DATA_REF)); iot_data_string_map_add (result, "Device/DevicesDir", iot_data_alloc_string ("", IOT_DATA_REF)); + iot_data_string_map_add (result, "Device/ProvisionWatchersDir", iot_data_alloc_string ("", IOT_DATA_REF)); iot_data_string_map_add (result, "Device/EventQLength", iot_data_alloc_ui32 (0)); iot_data_string_map_add (result, "Device/AllowedFails", iot_data_alloc_i32 (0)); iot_data_string_map_add (result, "Device/DeviceDownTimeout", iot_data_alloc_ui64 (0)); @@ -510,6 +511,7 @@ static void edgex_device_populateCommonConfigFromMap (edgex_device_config *confi config->device.maxeventsize = iot_data_ui32 (iot_data_string_map_get (map, "MaxEventSize")); config->device.profilesdir = iot_data_string_map_get_string (map, "Device/ProfilesDir"); config->device.devicesdir = iot_data_string_map_get_string (map, "Device/DevicesDir"); + config->device.provisionwatchersdir = iot_data_string_map_get_string (map, "Device/ProvisionWatchersDir"); config->device.allowed_fails = iot_data_ui32 (iot_data_string_map_get (map, "Device/AllowedFails")); config->device.dev_downtime = iot_data_ui64 (iot_data_string_map_get (map, "Device/DeviceDownTimeout")); @@ -736,6 +738,7 @@ static JSON_Value *edgex_device_config_toJson (devsdk_service_t *svc) json_object_set_uint (dobj, "MaxEventSize", svc->config.device.maxeventsize); json_object_set_string (dobj, "ProfilesDir", svc->config.device.profilesdir); json_object_set_string (dobj, "DevicesDir", svc->config.device.devicesdir); + json_object_set_string (dobj, "ProvisionWatchersDir", svc->config.device.provisionwatchersdir); json_object_set_boolean (dobj, "UpdateLastConnected", svc->config.device.updatelastconnected); json_object_set_uint (dobj, "EventQLength", svc->config.device.eventqlen); diff --git a/src/c/config.h b/src/c/config.h index b5ebf43c..a68b6d1c 100644 --- a/src/c/config.h +++ b/src/c/config.h @@ -59,6 +59,7 @@ typedef struct edgex_device_deviceinfo _Atomic(uint32_t) maxeventsize; const char *profilesdir; const char *devicesdir; + const char *provisionwatchersdir; atomic_bool updatelastconnected; uint32_t eventqlen; uint32_t allowed_fails; diff --git a/src/c/examples/discovery/README.md b/src/c/examples/discovery/README.md index 9856c9ef..4b362365 100644 --- a/src/c/examples/discovery/README.md +++ b/src/c/examples/discovery/README.md @@ -43,13 +43,7 @@ curl -X POST 0:59999/api/v3/discovery ``` Initially, none of the discovered devices will be added to EdgeX, but by -using appropriate Provision Watchers they can be accepted. To upload the -supplied Provision Watchers to core-metadata: - -``` -curl -X POST -d@watcher1.json 0:59881/api/v3/provisionwatcher -curl -X POST -d@watcher2.json 0:59881/api/v3/provisionwatcher -``` +using appropriate Provision Watchers they can be accepted. The structure of an example provision watcher can be found in res/watchers. The SDK will parse all watcher objects inside of the directory defined by the `Device/ProvisionWatcherDir` configuration value and automatically upload them to core-metadata. If a provision watcher already exists and is registered in core-metadata, it is skipped. The Provision Watchers each match one of the discovered devices. They work by specifying acceptance criteria (`identifiers`) that match potentially many diff --git a/src/c/examples/discovery/res/configuration.yaml b/src/c/examples/discovery/res/configuration.yaml index c7739b59..637ff3b3 100644 --- a/src/c/examples/discovery/res/configuration.yaml +++ b/src/c/examples/discovery/res/configuration.yaml @@ -18,6 +18,7 @@ Service: Device: ProfilesDir: ./res/profiles DevicesDir: ./res/devices + ProvisionWatchersDir: ./res/watchers MessageBus: Optional: diff --git a/src/c/examples/discovery/res/watchers/watcher1.json b/src/c/examples/discovery/res/watchers/watcher1.json new file mode 100644 index 00000000..519343d6 --- /dev/null +++ b/src/c/examples/discovery/res/watchers/watcher1.json @@ -0,0 +1,25 @@ +{ + "apiVersion": "v3", + "name": "watcher1", + "identifiers": { + "MAC": "00-05-1B-A1-99-[0-9A-Fa-f][0-9A-Fa-f]" + }, + "blockingIdentifiers": { + "MAC": [ + "00-05-1B-A1-99-99" + ] + }, + "serviceName": "device-template", + "adminState": "UNLOCKED", + "discoveredDevice": { + "profileName": "TemplateSensor", + "serviceName": "device-template", + "adminState": "UNLOCKED", + "properties": { + "DeviceNameTemplate": { + "valueReplace": true, + "template": "device-name-{{MAC}}" + } + } + } +} \ No newline at end of file diff --git a/src/c/examples/discovery/res/watchers/watcher2.json b/src/c/examples/discovery/res/watchers/watcher2.json new file mode 100644 index 00000000..af3c99f4 --- /dev/null +++ b/src/c/examples/discovery/res/watchers/watcher2.json @@ -0,0 +1,26 @@ +{ + "apiVersion": "v3", + "name": "watcher2", + "identifiers": { + "HTTP": "10\\.0\\.0\\.[0-9]*" + }, + "blockingIdentifiers": { + "HTTP": [ + "10.0.0.1", + "10.0.0.255" + ] + }, + "profileName": "TemplateSensor", + "adminstate": "UNLOCKED", + "discoveredDevice": { + "profileName": "TemplateSensor", + "serviceName": "device-template", + "adminState": "UNLOCKED", + "properties": { + "DeviceNameTemplate": { + "valueReplace": true, + "template": "device-name-{{HTTP}}" + } + } + } +} \ No newline at end of file diff --git a/src/c/examples/discovery/watcher1.json b/src/c/examples/discovery/watcher1.json deleted file mode 100644 index 356f396b..00000000 --- a/src/c/examples/discovery/watcher1.json +++ /dev/null @@ -1 +0,0 @@ -[{"apiVersion":"v3","provisionWatcher":{"apiVersion":"v3","name":"watcher1","identifiers":{"MAC":"00-05-1B-A1-99-[0-9A-Fa-f][0-9A-Fa-f]"},"blockingIdentifiers":{"MAC":["00-05-1B-A1-99-99"]},"serviceName":"device-template","adminState":"UNLOCKED","discoveredDevice":{"profileName":"TemplateSensor","serviceName":"device-template","adminState":"UNLOCKED","properties":{"DeviceNameTemplate":{"valueReplace":true,"template":"device-name-{{MAC}}"}}}}}] diff --git a/src/c/examples/discovery/watcher2.json b/src/c/examples/discovery/watcher2.json deleted file mode 100644 index 75c190c6..00000000 --- a/src/c/examples/discovery/watcher2.json +++ /dev/null @@ -1 +0,0 @@ -[{"apiVersion":"v3","provisionWatcher":{"apiVersion":"v3","name":"watcher2","identifiers":{"HTTP":"10\\.0\\.0\\.[0-9]*"},"blockingIdentifiers":{"HTTP":["10.0.0.1","10.0.0.255"]},"profileName":"TemplateSensor","serviceName":"device-template","adminstate":"UNLOCKED","discoveredDevice":{"profileName":"TemplateSensor","serviceName":"device-template","adminState":"UNLOCKED","properties":{"DeviceNameTemplate":{"valueReplace":true,"template":"device-name-{{HTTP}}"}}}}}] diff --git a/src/c/metadata.c b/src/c/metadata.c index e90b764d..7d3ec729 100644 --- a/src/c/metadata.c +++ b/src/c/metadata.c @@ -747,3 +747,57 @@ edgex_watcher *edgex_metadata_client_get_watchers *err = EDGEX_OK; return result; } + +void edgex_metadata_client_add_watcher_jobj(iot_logger_t *lc, edgex_service_endpoints *endpoints, edgex_secret_provider_t *secretprovider, const char *servicename, JSON_Object *jobj, devsdk_error *err) +{ + + if (!json_object_get_string(jobj, "serviceName")) { + json_object_set_string (jobj, "serviceName", servicename); + } + + if (!json_object_get_string(jobj, "adminState")) { + json_object_set_string (jobj, "adminState", "UNLOCKED"); + } + + if (!json_object_get_string(jobj, "apiVersion")) { + json_object_set_string (jobj, "apiVersion", EDGEX_API_VERSION); + } + + JSON_Value *reqval = edgex_wrap_request("ProvisionWatcher", json_object_get_wrapping_value (jobj)); + char *json = json_serialize_to_string(reqval); + edgex_ctx ctx; + *err = EDGEX_OK; + char url[URL_BUF_SIZE]; + + memset (&ctx, 0, sizeof (edgex_ctx)); + + snprintf + ( + url, + URL_BUF_SIZE - 1, + "http://%s:%u/api/" EDGEX_API_VERSION "/provisionwatcher", + endpoints->metadata.host, + endpoints->metadata.port + ); + + iot_data_t *jwt_data = edgex_secrets_request_jwt (secretprovider); + ctx.jwt_token = iot_data_string (jwt_data); + + edgex_http_post (lc, &ctx, url, json, edgex_http_write_cb, err); + + iot_data_free(jwt_data); + ctx.jwt_token = NULL; + + if (err->code == 0) + { + iot_log_info(lc, "Provision watcher %s created", json_object_get_string(jobj, "name")); + } + else + { + iot_log_error(lc, "edgex_metadata_client_add_watcher_jobj: %s: %s", err->reason, ctx.buff); + } + + json_value_free (reqval); + free(ctx.buff); + json_free_serialized_string (json); +} \ No newline at end of file diff --git a/src/c/metadata.h b/src/c/metadata.h index c9d881ef..24d550b2 100644 --- a/src/c/metadata.h +++ b/src/c/metadata.h @@ -151,4 +151,14 @@ edgex_watcher *edgex_metadata_client_get_watchers devsdk_error * err ); +void edgex_metadata_client_add_watcher_jobj +( + iot_logger_t *lc, + edgex_service_endpoints *endpoints, + edgex_secret_provider_t *secretprovider, + const char *servicename, + JSON_Object *obj, + devsdk_error *err +); + #endif diff --git a/src/c/service.c b/src/c/service.c index aceac6c5..23b6515e 100644 --- a/src/c/service.c +++ b/src/c/service.c @@ -761,6 +761,17 @@ static void startConfigured (devsdk_service_t *svc, const devsdk_timeout *deadli edgex_watcher_free (w); } + /* Load Provision Watchers from files and register in metadata */ + if (svc->config.device.provisionwatchersdir && strlen (svc->config.device.provisionwatchersdir)) + { + edgex_device_watchers_upload (svc, err); + if (err->code) + { + iot_log_error (svc->logger, "Failed to upload provision watchers from directory"); + return; + } + } + /* Start scheduled events */ iot_scheduler_start (svc->scheduler); diff --git a/src/c/watchers.c b/src/c/watchers.c index 4fc984d0..ebec2f66 100644 --- a/src/c/watchers.c +++ b/src/c/watchers.c @@ -8,7 +8,10 @@ #include "watchers.h" #include "edgex-rest.h" - +#include "service.h" +#include "errorlist.h" +#include "metadata.h" +#include "filesys.h" #include typedef struct edgex_watchlist_t @@ -179,6 +182,24 @@ static bool matchpw (const edgex_watcher *pw, const iot_data_t *ids) return true; } +static bool edgex_watcher_exists (const edgex_watchlist_t *wl, const char *name) +{ + bool exists = false; + pthread_rwlock_rdlock ((pthread_rwlock_t *)&wl->lock); + + for (const edgex_watcher *w = wl->list; w; w = w->next) + { + if (strcmp (w->name, name) == 0) + { + exists = true; + break; + } + } + + pthread_rwlock_unlock ((pthread_rwlock_t *)&wl->lock); + return exists; +} + edgex_watcher *edgex_watchlist_match (const edgex_watchlist_t *wl, const iot_data_t *ids) { pthread_rwlock_rdlock ((pthread_rwlock_t *)&wl->lock); @@ -199,3 +220,55 @@ edgex_watcher *edgex_watchlist_match (const edgex_watchlist_t *wl, const iot_dat pthread_rwlock_unlock ((pthread_rwlock_t *)&wl->lock); return result; } + +static void edgex_add_watcher_json (devsdk_service_t *svc, const char *fname, devsdk_error *err) +{ + + JSON_Value *jval = json_parse_file (fname); + if (jval) + { + JSON_Object *jobj = json_value_get_object (jval); + const char *name = json_object_get_string (jobj, "name"); + if (name) + { + iot_log_debug(svc->logger, "Checking existence of ProvisionWatcher %s", name); + if (edgex_watcher_exists (svc->watchlist, name)) + { + iot_log_info(svc->logger, "ProvisionWatcher %s already exists: skipped", name); + } + else + { + JSON_Value *copy = json_value_deep_copy(jval); + JSON_Object *watchobj = json_value_get_object(copy); + edgex_metadata_client_add_watcher_jobj(svc->logger, &svc->config.endpoints, svc->secretstore, svc->name, watchobj, err); + } + } + else + { + iot_log_warn (svc->logger, "Provision watcher upload: Missing provisionwatcher name in %s", fname); + } + + json_value_free (jval); + } + else + { + iot_log_error (svc->logger, "File %s does not parse as JSON", fname); + *err = EDGEX_CONF_PARSE_ERROR; + } +} + +void edgex_device_watchers_upload (devsdk_service_t *svc, devsdk_error *err) +{ + *err = EDGEX_OK; + + iot_log_info (svc->logger, "Processing Provision Watchers from %s", svc->config.device.provisionwatchersdir); + + devsdk_strings *filenames = devsdk_scandir (svc->logger, svc->config.device.provisionwatchersdir, "json"); + + for (devsdk_strings *f = filenames; f; f = f->next) + { + edgex_add_watcher_json (svc, f->str, err); + } + + devsdk_strings_free (filenames); +} \ No newline at end of file diff --git a/src/c/watchers.h b/src/c/watchers.h index f36fab4c..713c99f9 100644 --- a/src/c/watchers.h +++ b/src/c/watchers.h @@ -31,5 +31,6 @@ extern bool edgex_watchlist_remove_watcher (edgex_watchlist_t *list, const char extern void edgex_watchlist_update_watcher (edgex_watchlist_t *list, const edgex_watcher *updated); extern edgex_watcher *edgex_watchlist_match (const edgex_watchlist_t *list, const iot_data_t *ids); +extern void edgex_device_watchers_upload (devsdk_service_t *svc, devsdk_error *err); #endif