diff --git a/crates/common/tedge_config/src/tedge_toml/tedge_config.rs b/crates/common/tedge_config/src/tedge_toml/tedge_config.rs index bd84361549f..9607454b4ca 100644 --- a/crates/common/tedge_config/src/tedge_toml/tedge_config.rs +++ b/crates/common/tedge_config/src/tedge_toml/tedge_config.rs @@ -699,6 +699,10 @@ define_tedge_config! { /// Enable device_profile feature #[tedge_config(example = "true", default(value = true))] device_profile: bool, + + /// Enable device restart feature + #[tedge_config(example = "true", default(value = true))] + device_restart: bool, }, mapper: { diff --git a/crates/common/tedge_config/src/tedge_toml/tedge_config/mapper_config/compat.rs b/crates/common/tedge_config/src/tedge_toml/tedge_config/mapper_config/compat.rs index e7e2ef14f13..b3004cb2ad6 100644 --- a/crates/common/tedge_config/src/tedge_toml/tedge_config/mapper_config/compat.rs +++ b/crates/common/tedge_config/src/tedge_toml/tedge_config/mapper_config/compat.rs @@ -128,6 +128,7 @@ impl FromCloudConfig for C8yMapperSpecificConfig { config_update: c8y.enable.config_update, firmware_update: c8y.enable.firmware_update, device_profile: c8y.enable.device_profile, + device_restart: c8y.enable.device_restart, }, mqtt_service: MqttServiceConfig { enabled: c8y.mqtt_service.enabled, diff --git a/crates/common/tedge_config/src/tedge_toml/tedge_config/mapper_config/mod.rs b/crates/common/tedge_config/src/tedge_toml/tedge_config/mapper_config/mod.rs index c88054bea58..36a82863253 100644 --- a/crates/common/tedge_config/src/tedge_toml/tedge_config/mapper_config/mod.rs +++ b/crates/common/tedge_config/src/tedge_toml/tedge_config/mapper_config/mod.rs @@ -384,6 +384,9 @@ pub struct EnableConfig { /// Enable device_profile feature pub device_profile: bool, + + /// Enable device restart feature + pub device_restart: bool, } /// Bridge include configuration diff --git a/crates/extensions/c8y_mapper_ext/src/config.rs b/crates/extensions/c8y_mapper_ext/src/config.rs index 9d974116d69..d058deccca1 100644 --- a/crates/extensions/c8y_mapper_ext/src/config.rs +++ b/crates/extensions/c8y_mapper_ext/src/config.rs @@ -201,6 +201,7 @@ impl C8yMapperConfig { config_update: c8y_config.cloud_specific.enable.config_update, firmware_update: c8y_config.cloud_specific.enable.firmware_update, device_profile: c8y_config.cloud_specific.enable.device_profile, + device_restart: c8y_config.cloud_specific.enable.device_restart, }; let bridge_config = BridgeConfig { c8y_prefix: c8y_config.bridge.topic_prefix.clone(), diff --git a/crates/extensions/c8y_mapper_ext/src/converter.rs b/crates/extensions/c8y_mapper_ext/src/converter.rs index 44b57d7bc97..269bc1b7180 100644 --- a/crates/extensions/c8y_mapper_ext/src/converter.rs +++ b/crates/extensions/c8y_mapper_ext/src/converter.rs @@ -657,7 +657,12 @@ impl CumulocityConverter { let msgs = match C8yDeviceControlOperation::from_json_object(extras)? { C8yDeviceControlOperation::Restart(_) => { - self.forward_restart_request(device_xid, cmd_id)? + if self.config.capabilities.device_restart { + self.forward_restart_request(device_xid, cmd_id)? + } else { + warn!("Received a c8y_Restart operation, however, device_restart feature is disabled"); + vec![] + } } C8yDeviceControlOperation::SoftwareUpdate(request) => { self.forward_software_request(device_xid, cmd_id, request) @@ -1540,6 +1545,11 @@ impl CumulocityConverter { &mut self, target: &EntityTopicId, ) -> Result, ConversionError> { + if !self.config.capabilities.device_restart { + warn!("Received restart metadata, however, device restart feature is disabled"); + return Ok(vec![]); + } + match self.register_operation(target, "c8y_Restart").await { Err(_) => { error!("Fail to register `restart` operation for unknown device: {target}"); diff --git a/crates/extensions/c8y_mapper_ext/src/lib.rs b/crates/extensions/c8y_mapper_ext/src/lib.rs index 7358260f00d..045c1cab9cc 100644 --- a/crates/extensions/c8y_mapper_ext/src/lib.rs +++ b/crates/extensions/c8y_mapper_ext/src/lib.rs @@ -26,6 +26,7 @@ pub struct Capabilities { pub config_update: bool, pub firmware_update: bool, pub device_profile: bool, + pub device_restart: bool, } #[cfg(test)] @@ -37,6 +38,7 @@ impl Default for Capabilities { config_update: true, firmware_update: true, device_profile: true, + device_restart: true, } } } diff --git a/crates/extensions/c8y_mapper_ext/src/operations/handler.rs b/crates/extensions/c8y_mapper_ext/src/operations/handler.rs index a4cd1b40e72..7a66f0641da 100644 --- a/crates/extensions/c8y_mapper_ext/src/operations/handler.rs +++ b/crates/extensions/c8y_mapper_ext/src/operations/handler.rs @@ -251,6 +251,12 @@ impl OperationHandler { (AnyEntity, CommandMetadata(OperationType::DeviceProfile)), ]); } + if capabilities.device_restart { + topics.extend([ + (AnyEntity, Command(OperationType::Restart)), + (AnyEntity, CommandMetadata(OperationType::Restart)), + ]); + } topics } diff --git a/tests/RobotFramework/requirements/requirements.txt b/tests/RobotFramework/requirements/requirements.txt index f121f84d7bc..50e6c5e029a 100644 --- a/tests/RobotFramework/requirements/requirements.txt +++ b/tests/RobotFramework/requirements/requirements.txt @@ -1,7 +1,7 @@ paho-mqtt~=1.6.1 python-dotenv~=1.0.0 robotframework~=7.0.0 -robotframework-c8y @ git+https://github.com/thin-edge/robotframework-c8y.git@0.49.0 +robotframework-c8y @ git+https://github.com/thin-edge/robotframework-c8y.git@0.51.0 robotframework-aws @ git+https://github.com/thin-edge/robotframework-aws.git@0.0.11 robotframework-debuglibrary~=2.5.0 robotframework-jsonlibrary~=0.5 diff --git a/tests/RobotFramework/tests/cumulocity/restart/restart_device.robot b/tests/RobotFramework/tests/cumulocity/restart/restart_device.robot index c3234422902..026d3acd4be 100644 --- a/tests/RobotFramework/tests/cumulocity/restart/restart_device.robot +++ b/tests/RobotFramework/tests/cumulocity/restart/restart_device.robot @@ -32,6 +32,23 @@ tedge-mapper-c8y does not react to local restart operations transitions topic=te/device/main///cmd/restart/local-2222 payload={"status":"failed"} expected_status=failed c8y_fragment=c8y_Restart topic=te/device/main///cmd/restart/local-3333 payload={"status":"successful"} expected_status=successful c8y_fragment=c8y_Restart +Supports disabling the Cumulocity c8y_Restart Command + Execute Command tedge config set c8y.enable.device_restart false + Execute Command rm -f /etc/tedge/operations/c8y/c8y_Restart + + Restart Service tedge-mapper-c8y + Service Health Status Should Be Up tedge-mapper-c8y + File Should Not Exist /etc/tedge/operations/c8y/c8y_Restart + Should Not Contain Supported Operations c8y_Restart + ${operation}= Cumulocity.Restart Device + Operation Should Be PENDING ${operation} timeout=30 + + # Cleanup operation for cleaner logs (as pending operations pollute the test report output) + # Note: It does not need to be run under a TearDown hook as this code will only run + # if the operation is not processed, otherwise it would of been processed and thus not need cleaning up + Execute Command tedge mqtt pub c8y/s/us '505,${operation.to_json()["id"]},Cancelled operation' + Operation Should Be FAILED ${operation} + *** Keywords *** Set Service User