github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/docs/proposals/device-crd.md (about) 1 --- 2 title: Device CRD Design 3 authors: 4 - "@rohitsardesai83" 5 - "@kevin-wangzefeng" 6 approvers: 7 - "@qizha" 8 - "@CindyXing" 9 - "@Baoqiang-Zhang" 10 - "@m1093782566" 11 creation-date: 2019-02-27 12 last-updated: 2019-04-01 13 status: implementable 14 --- 15 16 # Device Management using CRDs 17 18 * [Device Management using CRDs](#device-management-using-crds) 19 * [Motivation](#motivation) 20 * [Goals](#goals) 21 * [Non\-goals](#non-goals) 22 * [Proposal](#proposal) 23 * [Use Cases](#use-cases) 24 * [Design Details](#design-details) 25 * [CRD API Group and Version](#crd-api-group-and-version) 26 * [Device model CRD](#device-model-crd) 27 * [Device model type definition](#device-model-type-definition) 28 * [Device model sample](#device-model-sample) 29 * [Device instance CRD](#device-instance-crd) 30 * [Device instance type definition](#device-instance-type-definition) 31 * [Device instance sample](#device-instance-sample) 32 * [Validation](#validation) 33 * [Synchronizing Device Twin Updates](#synchronizing-device-twin-updates) 34 * [Syncing Desired Device Twin Property Update From Cloud To Edge](#syncing-desired-device-twin-property-update-from-cloud-to-edge) 35 * [Syncing Reported Device Twin Property Update From Edge To Cloud](#syncing-reported-device-twin-property-update-from-edge-to-cloud) 36 * [Device Controller Design](#device-controller-design) 37 * [Downstream Controller](#downstream-controller) 38 * [Upstream Controller](#upstream-controller) 39 * [Offline Scenarios](#offline-scenarios) 40 * [Scalability](#scalability) 41 * [Device Lifecycle Management](#device-lifecycle-management) 42 * [Device Actions](#device-actions) 43 * [Device Events](#device-events) 44 * [Security](#security) 45 * [Open questions](#open-questions) 46 47 ## Motivation 48 49 Device management is a key feature required for IoT use-cases in edge computing. 50 This proposal addresses how can we manage devices from the cloud and synchronize 51 the device updates between edge nodes and cloud. 52 53 ### Goals 54 55 Device management must: 56 * provide APIs for managing devices from the cloud. 57 * synchronize the device updates between cloud and edge nodes. 58 59 ### Non-goals 60 61 * To design **secure** device provisioning. 62 * To address OTA device firmware upgrades. 63 * To address how device auto-discovery can happen. 64 * To address device migration scenarios. 65 66 ## Proposal 67 We propose using Kubernetes [Custom Resource Definitions (CRDs)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) to describe device metadata/status and a controller to synchronize these device updates between edge and cloud. 68 <img src="../images/device-crd/device-crd-model.png"> 69 70 ### Use Cases 71 72 * Describe device properties. 73 * Users can describe device properties and access mechanisms to interact with / control the device. 74 * Perform CRUD operations on devices from cloud. 75 * Users can create, update and delete device metadata from the cloud via the CRD APIs exposed by the Kubernetes API server. 76 * Users can control the desired state of a device via the CRD APIs. 77 * Report device properties values. 78 * Mapper applications running on the edge can report the current values of the device properties. 79 80 ## Design Details 81 82 ### CRD API Group and Version 83 The `DeviceModel` and `Device` CRD's will be namespace-scoped. 84 The tables below summarize the group, kind and API version details for the CRDs. 85 86 * DeviceModel 87 88 | Field | Description | 89 |-----------------------|-------------------------| 90 |Group | devices.kubeedge.io | 91 |APIVersion | v1alpha1 | 92 |Kind | DeviceModel | 93 94 * DeviceInstance 95 96 | Field | Description | 97 |-----------------------|-------------------------| 98 |Group | devices.kubeedge.io | 99 |APIVersion | v1alpha1 | 100 |Kind | Device | 101 102 ### Device model CRD 103 <img src="../images/device-crd/device-model-crd.png"> 104 105 A `device model` describes the device properties exposed by the device and property visitors to access these properties. A device model is like a reusable template using which many devices can be created and managed. 106 107 ### Device Model Type Definition 108 ```go 109 // DeviceModelSpec defines the model / template for a device.It is a blueprint which describes the device 110 // capabilities and access mechanism via property visitors. 111 type DeviceModelSpec struct { 112 // Required: List of device properties. 113 Properties []DeviceProperty `json:"properties,omitempty"` 114 // Required: List of property visitors which describe how to access the device properties. 115 // PropertyVisitors must unique by propertyVisitor.propertyName. 116 PropertyVisitors []DevicePropertyVisitor `json:"propertyVisitors,omitempty"` 117 } 118 119 // DeviceProperty describes an individual device property / attribute like temperature / humidity etc. 120 type DeviceProperty struct { 121 // Required: The device property name. 122 Name string `json:"name,omitempty"` 123 // The device property description. 124 // +optional 125 Description string `json:"description,omitempty"` 126 // Required: PropertyType represents the type and data validation of the property. 127 Type PropertyType `json:"type,omitempty"` 128 } 129 130 // Represents the type and data validation of a property. 131 // Only one of its members may be specified. 132 type PropertyType struct { 133 // +optional 134 Int PropertyTypeInt64 `json:"int,omitempty"` 135 // +optional 136 String PropertyTypeString `json:"string,omitempty"` 137 } 138 139 type PropertyTypeInt64 struct { 140 // Required: Access mode of property, ReadWrite or ReadOnly. 141 AccessMode PropertyAccessMode `json:"accessMode,omitempty"` 142 // +optional 143 DefaultValue int64 `json:"defaultValue,omitempty"` 144 // +optional 145 Minimum int64 `json:"minimum,omitempty"` 146 // +optional 147 Maximum int64 `json:"maximum,omitempty"` 148 // The unit of the property 149 // +optional 150 Unit string `json:"unit,omitempty"` 151 } 152 153 type PropertyTypeString struct { 154 // Required: Access mode of property, ReadWrite or ReadOnly. 155 AccessMode PropertyAccessMode `json:"accessMode,omitempty"` 156 // +optional 157 DefaultValue string `json:"defaultValue,omitempty"` 158 } 159 160 // The access mode for a device property. 161 type PropertyAccessMode string 162 163 // Access mode constants for a device property. 164 const ( 165 ReadWrite PropertyAccessMode = "ReadWrite" 166 ReadOnly PropertyAccessMode = "ReadOnly" 167 ) 168 169 // DevicePropertyVisitor describes the specifics of accessing a particular device 170 // property. Visitors are intended to be consumed by device mappers which connect to devices 171 // and collect data / perform actions on the device. 172 type DevicePropertyVisitor struct { 173 // Required: The device property name to be accessed. This should refer to one of the 174 // device properties defined in the device model. 175 PropertyName string `json:"propertyName,omitempty"` 176 // Required: Protocol relevant config details about the how to access the device property. 177 VisitorConfig `json:",inline"` 178 } 179 180 // At least one of its members must be specified. 181 type VisitorConfig struct { 182 // Opcua represents a set of additional visitor config fields of opc-ua protocol. 183 // +optional 184 OpcUA VisitorConfigOPCUA `json:"opcua,omitempty"` 185 // Modbus represents a set of additional visitor config fields of modbus protocol. 186 // +optional 187 Modbus VisitorConfigModbus `json:"modbus,omitempty"` 188 // Bluetooth represents a set of additional visitor config fields of bluetooth protocol. 189 // +optional 190 Bluetooth VisitorConfigBluetooth `json:"bluetooth,omitempty"` 191 } 192 193 // Common visitor configurations for bluetooth protocol 194 type VisitorConfigBluetooth struct { 195 // Required: Unique ID of the corresponding operation 196 CharacteristicUUID string `json:"characteristicUUID,omitempty"` 197 // Responsible for converting the data coming from the platform into a form that is understood by the bluetooth device 198 // For example: "ON":[1], "OFF":[0] 199 //+optional 200 DataWriteToBluetooth map[string][]byte `json:"dataWrite,omitempty"` 201 // Responsible for converting the data being read from the bluetooth device into a form that is understandable by the platform 202 //+optional 203 BluetoothDataConverter BluetoothReadConverter `json:"dataConverter,omitempty"` 204 } 205 206 // Specifies the operations that may need to be performed to convert the data 207 type BluetoothReadConverter struct { 208 // Required: Specifies the start index of the incoming byte stream to be considered to convert the data. 209 // For example: start-index:2, end-index:3 concatenates the value present at second and third index of the incoming byte stream. If we want to reverse the order we can give it as start-index:3, end-index:2 210 StartIndex int `json:"startIndex,omitempty"` 211 // Required: Specifies the end index of incoming byte stream to be considered to convert the data 212 // the value specified should be inclusive for example if 3 is specified it includes the third index 213 EndIndex int `json:"endIndex,omitempty"` 214 // Refers to the number of bits to shift left, if left-shift operation is necessary for conversion 215 // +optional 216 ShiftLeft uint `json:"shiftLeft,omitempty"` 217 // Refers to the number of bits to shift right, if right-shift operation is necessary for conversion 218 // +optional 219 ShiftRight uint `json:"shiftRight,omitempty"` 220 // Specifies in what order the operations(which are required to be performed to convert incoming data into understandable form) are performed 221 //+optional 222 OrderOfOperations []BluetoothOperations `json:"orderOfOperations,omitempty"` 223 } 224 225 // Specify the operation that should be performed to convert incoming data into understandable form 226 type BluetoothOperations struct { 227 // Required: Specifies the operation to be performed to convert incoming data 228 BluetoothOperationType BluetoothArithmaticOperationType `json:"operationType,omitempty"` 229 // Required: Specifies with what value the operation is to be performed 230 BluetoothOperationValue float64 `json:"operationValue,omitempty"` 231 } 232 233 // Operations supported by Bluetooth protocol to convert the value being read from the device into an understandable form 234 type BluetoothArithmeticOperationType string 235 236 // Bluetooth Protocol Operation type 237 const ( 238 BluetoothAdd BluetoothArithmeticOperationType = "Add" 239 BluetoothSubtract BluetoothArithmeticOperationType = "Subtract" 240 BluetoothMultiply BluetoothArithmeticOperationType = "Multiply" 241 BluetoothDivide BluetoothArithmeticOperationType = "Divide" 242 ) 243 244 // Common visitor configurations for opc-ua protocol 245 type VisitorConfigOPCUA struct { 246 // Required: The ID of opc-ua node, e.g. "ns=1,i=1005" 247 NodeID string `json:"nodeID,omitempty"` 248 // The name of opc-ua node 249 BrowseName string `json:"browseName,omitempty"` 250 } 251 252 // Common visitor configurations for modbus protocol 253 type VisitorConfigModbus struct { 254 // Required: Type of register 255 Register ModbusRegisterType `json:"register,omitempty"` 256 // Required: Offset indicates the starting register number to read/write data. 257 Offset int64 `json:"offset,omitempty"` 258 // Required: Limit number of registers to read/write. 259 Limit int64 `json:"limit,omitempty"` 260 // The scale to convert raw property data into final units. 261 // Defaults to 1.0 262 // +optional 263 Scale float64 `json:"scale,omitempty"` 264 // Indicates whether the high and low byte swapped. 265 // Defaults to false. 266 // +optional 267 IsSwap bool `json:"isSwap,omitempty"` 268 // Indicates whether the high and low register swapped. 269 // Defaults to false. 270 // +optional 271 IsRegisterSwap bool `json:"isRegisterSwap,omitempty"` 272 } 273 274 // The Modbus register type to read a device property. 275 type ModbusRegisterType string 276 277 // Modbus protocol register types 278 const ( 279 ModbusRegisterTypeCoilRegister ModbusRegisterType = "CoilRegister" 280 ModbusRegisterTypeDiscreteInputRegister ModbusRegisterType = "DiscreteInputRegister" 281 ModbusRegisterTypeInputRegister ModbusRegisterType = "InputRegister" 282 ModbusRegisterTypeHoldingRegister ModbusRegisterType = "HoldingRegister" 283 ) 284 285 // +genclient 286 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 287 288 // DeviceModel is the Schema for the device model API 289 // +k8s:openapi-gen=true 290 type DeviceModel struct { 291 metav1.TypeMeta `json:",inline"` 292 metav1.ObjectMeta `json:"metadata,omitempty"` 293 294 Spec DeviceModelSpec `json:"spec,omitempty"` 295 } 296 297 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 298 299 // DeviceModelList contains a list of DeviceModel 300 type DeviceModelList struct { 301 metav1.TypeMeta `json:",inline"` 302 metav1.ListMeta `json:"metadata,omitempty"` 303 Items []DeviceModel `json:"items"` 304 } 305 ``` 306 307 ### Device model sample 308 ```yaml 309 apiVersion: devices.kubeedge.io/v1alpha1 310 kind: DeviceModel 311 metadata: 312 labels: 313 description: 'TI Simplelink SensorTag Device Model' 314 manufacturer: 'Texas Instruments' 315 model: CC2650 316 name: sensor-tag-model 317 spec: 318 properties: 319 - name: temperature 320 description: temperature in degree celsius 321 type: 322 int: 323 accessMode: ReadOnly 324 maximum: 100 325 unit: Degree Celsius 326 - name: temperature-enable 327 description: enable data collection of temperature sensor 328 type: 329 string: 330 accessMode: ReadWrite 331 defaultValue: OFF 332 - name: pressure 333 description: barometric pressure sensor in hectopascal 334 type: 335 int: 336 accessMode: ReadOnly 337 unit: hectopascal 338 - name: pressure-enable 339 description: enable data collection of barometric pressure sensor 340 type: 341 string: 342 accessMode: ReadWrite 343 defaultValue: OFF 344 propertyVisitors: 345 - propertyName: temperature 346 modbus: 347 register: CoilRegister 348 offset: 2 349 limit: 1 350 scale: 1.0 351 isSwap: true 352 isRegisterSwap: true 353 - propertyName: temperature-enable 354 modbus: 355 register: DiscreteInputRegister 356 offset: 3 357 limit: 1 358 scale: 1.0 359 isSwap: true 360 isRegisterSwap: true 361 - propertyName: pressure-enable 362 bluetooth: 363 characteristicUUID: f000aa4204514000b000000000000000 364 dataWrite: 365 ON: [1] 366 OFF: [0] 367 - propertyName: pressure 368 bluetooth: 369 characteristicUUID: f000aa4104514000b000000000000000 370 dataConverter: 371 startIndex: 3 372 endIndex: 5 373 orderOfOperations: 374 - operationType: Divide 375 operationValue: 100 376 ``` 377 Shown above is an example device model for a temperature sensor with Modbus protocol. It has two properties: 378 - `temperature`: the temperature readings from the sensor. The `type` field indicates that the temperature property is of type `int`, it is read-only and the maximum value it can take is 100. 379 - `temperature-enable`: this property defines whether data collection is enabled from the sensor. It is a writable property. Data collection is disabled if it is set to `OFF`. To turn on data collection, it needs to be set to `ON`. 380 381 Property visitors provide details like how to access device properties. In the above example, there are two visitors defined which describe how to read/write the device properties using `modbus` protocol. Detailed information on the Modbus registers to access is provided along with the offset, limit and other settings. 382 383 ### Device instance CRD 384 <img src="../images/device-crd/device-crd.png"> 385 386 A `device` instance represents an actual device object. It is like an instantiation of the `device model` and references properties defined in the model. The device spec is static while the device status contains dynamically changing data like the desired state of a device property and the state reported by the device. 387 388 ### Device instance type definition 389 ```go 390 // DeviceSpec represents the static information of a single device instance. 391 type DeviceSpec struct { 392 // Required: DeviceModelRef is reference to the device model used as a template 393 // to create the device instance. 394 DeviceModelRef *core.LocalObjectReference `json:"deviceModelRef,omitempty"` 395 // Required: The protocol configuration used to connect to the device. 396 Protocol ProtocolConfig `json:"protocol,omitempty"` 397 // NodeSelector indicates the binding preferences between devices and nodes. 398 // Refer to k8s.io/kubernetes/pkg/apis/core NodeSelector for more details 399 // +optional 400 NodeSelector *core.NodeSelector `json:"nodeSelector,omitempty"` 401 } 402 403 // Only one of its members may be specified. 404 type ProtocolConfig struct { 405 // Protocol configuration for opc-ua 406 // +optional 407 OpcUA *ProtocolConfigOpcUA `json:"opcua,omitempty"` 408 // Protocol configuration for modbus 409 // +optional 410 Modbus *ProtocolConfigModbus `json:"modbus,omitempty"` 411 } 412 413 type ProtocolConfigOpcUA struct { 414 // Required: The URL for opc server endpoint. 415 Url string `json:"url,omitempty"` 416 // Username for access opc server. 417 // +optional 418 UserName string `json:"userName,omitempty"` 419 // Password for access opc server. 420 // +optional 421 Password string `json:"password,omitempty"` 422 // Defaults to "none". 423 // +optional 424 SecurityPolicy string `json:"securityPolicy,omitempty"` 425 // Defaults to "none". 426 // +optional 427 SecurityMode string `json:"securityMode,omitempty"` 428 // Certificate for access opc server. 429 // +optional 430 Certificate string `json:"certificate,omitempty"` 431 // PrivateKey for access opc server. 432 // +optional 433 PrivateKey string `json:"privateKey,omitempty"` 434 // Timeout seconds for the opc server connection.??? 435 // +optional 436 Timeout int64 `json:"timeout,omitempty"` 437 } 438 439 // Only one of its members may be specified. 440 type ProtocolConfigModbus struct { 441 // +optional 442 RTU *ProtocolConfigModbusRTU `json:"rtu,omitempty"` 443 // +optional 444 TCP *ProtocolConfigModbusTCP `json:"tcp,omitempty"` 445 } 446 447 type ProtocolConfigModbusTCP struct { 448 // Required. 449 IP string `json:"ip,omitempty"` 450 // Required. 451 Port int64 `json:"port,omitempty"` 452 // Required. 453 SlaveID string `json:"slaveID,omitempty"` 454 } 455 456 type ProtocolConfigModbusRTU struct { 457 458 // Required. 459 SerialPort string `json:"serialPort,omitempty"` 460 // Required. BaudRate 115200|57600|38400|19200|9600|4800|2400|1800|1200|600|300|200|150|134|110|75|50 461 BaudRate int64 `json:"baudRate,omitempty"` 462 // Required. Valid values are 8, 7, 6, 5. 463 DataBits int64 `json:"dataBits,omitempty"` 464 // Required. Valid options are "none", "even", "odd". Defaults to "none". 465 Parity string `json:"parity,omitempty"` 466 // Required. Bit that stops 1|2 467 StopBits int64 `json:"stopBits,omitempty"` 468 // Required. 0-255 469 SlaveID int64 `json:"slaveID,omitempty"` 470 } 471 472 // DeviceStatus contains the desired/reported values of device twin properties. 473 type DeviceStatus struct { 474 // A list of device twins containing desired/reported values of twin properties. 475 // A passive device won't have twin properties and this list could be empty. 476 // +optional 477 Twins []Twin `json:"twins,omitempty"` 478 } 479 480 // A Twin provides a logical representation of control properties (writable properties in the 481 // device model). The properties can have a desired (expected) state and a reported(actual) state. 482 // The cloud configures the `desired` state of a device property and this configuration update is pushed 483 // to the edge node. The mapper sends a command to the device to change this property value as per the desired state. 484 // The mapper sends the state reported by the device to the cloud. Offline device interaction in the edge is // possible via twin properties for control/command operations. 485 type Twin struct { 486 // Required: The property name for which the desired/reported values are specified. 487 // This property should be present in the device model. 488 PropertyName string `json:"propertyName,omitempty"` 489 // Required: the desired property value 490 Desired TwinProperty `json:"desired,omitempty"` 491 // Required: the reported property value. 492 Reported TwinProperty `json:"reported,omitempty"` 493 } 494 495 // TwinProperty represents the device property for which a desired/reported state can be defined. 496 type TwinProperty struct { 497 // Required: The value for this property. 498 Value string `json:"value,omitempty"` 499 // Additional metadata like timestamp when the value was reported etc. 500 // +optional 501 Metadata map[string]string `json:"metadata,omitempty"` 502 } 503 504 // +genclient 505 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 506 507 // Device is the Schema for the devices API 508 // +k8s:openapi-gen=true 509 type Device struct { 510 metav1.TypeMeta `json:",inline"` 511 metav1.ObjectMeta `json:"metadata,omitempty"` 512 513 Spec DeviceSpec `json:"spec,omitempty"` 514 Status DeviceStatus `json:"status,omitempty"` 515 } 516 517 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 518 519 // DeviceList contains a list of Device 520 type DeviceList struct { 521 metav1.TypeMeta `json:",inline"` 522 metav1.ListMeta `json:"metadata,omitempty"` 523 Items []Device `json:"items"` 524 } 525 ``` 526 527 ### Device instance sample 528 ```yaml 529 apiVersion: devices.kubeedge.io/v1alpha1 530 kind: Device 531 metadata: 532 name: sensor-tag01 533 labels: 534 description: 'TI Simplelink SensorTag 2.0 with Bluetooth 4.0' 535 manufacturer: 'Texas Instruments' 536 model: CC2650 537 spec: 538 deviceModelRef: 539 name: sensor-tag-model 540 protocol: 541 modbus: 542 rtu: 543 serialPort: '1' 544 baudRate: 115200 545 dataBits: 8 546 parity: even 547 stopBits: 1 548 slaveID: 1 549 nodeSelector: 550 nodeSelectorTerms: 551 - matchExpressions: 552 - key: '' 553 operator: In 554 values: 555 - node1 556 status: 557 twins: 558 - propertyName: temperature-enable 559 reported: 560 metadata: 561 timestamp: '1550049403598' 562 type: string 563 value: OFF 564 desired: 565 metadata: 566 timestamp: '1550049403598' 567 type: string 568 value: OFF 569 ``` 570 571 ### Validation 572 [Open API v3 Schema based validation](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#validation) can be used to guard against bad requests. 573 Invalid values for fields ( example string value for a boolean field etc) can be validated using this. 574 In some cases , we also need custom validations (e.g create a device instance which refers to a non -existent device model ) . 575 [Validation admission web hooks](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#validatingadmissionwebhook) can be used to implement such custom validation rules. 576 577 Here is a list of validations we need to support : 578 579 #### Device Model Validations 580 - Don't allow model creation if any `Required` fields are missing ( like property name , type, access mode etc.) 581 - Property type can be only string or int64. Other types are not supported. 582 - The Property access mode for a property can be one of `ReadWrite` or `ReadOnly`. Other access modes are not supported. 583 - Property visitors are currently supported for bluetooth, modbus and opcua protocols only. Other protocol visitors are not supported. 584 - The BluetoothArithmeticOperationType for Bluetooth read converter can have values one of `[Add, Subtract, Multiply, Divide]` 585 - The ModbusRegisterType for Modbus visitor config can have values one of `[CoilRegister, DiscreteInputRegister, InputRegister, HoldingRegister]`. Other register types are not supported. 586 - Don't allow model deletion if there is at least one device instance referring to it. 587 - Don't allow deletion of a device property from the model if there is a corresponding device twin property in a device instance which refers to this model. 588 - Don't allow deletion of a device property visitor if the corresponding device property exists. 589 590 #### Device Instance Validations 591 - Don't allow device instance creation if any `Required` fields are missing ( like devicemodel ref , twin property value, twin property name etc.) 592 - Don't allow device instance creation if it refers to a device model reference which doesn't exist. 593 - Don't allow device instance creation if a desired twin property's name cannot be matched to a device property in the device model it refers to. 594 - Protocol configs are optional , but if provided , they can be one of `[opcua, modbus and bluetooth]`. Other protocol configs are not supported. 595 596 ## Synchronizing Device Twin Updates 597 598 The below illustrations describe the flow of events that would occur when device twin desired/reported property values are updated from the cloud/edge. 599 600 ### Syncing Desired Device Twin Property Update From Cloud To Edge 601 <img src="../images/device-crd/device-updates-cloud-edge.png"> 602 The device controller watches device updates in the cloud and relays them to the edge node. These updates are stored locally by the device twin. The mapper gets these updates via the broker and operates on the device based on the updates. 603 604 ### Syncing Reported Device Twin Property Update From Edge To Cloud 605 <img src="../images/device-crd/device-updates-edge-cloud.png"> 606 The mapper watches devices for updates and reports them to the event bus via the broker. The event bus sends the reported state of the device to the device twin which stores it locally and then syncs the updates to the cloud. The device controller watches for device updates from the edge ( via the cloudhub ) and updates the reported state in the cloud. 607 608 ## Device Controller Design 609 The device controller starts two separate goroutines called `upstream` controller and `downstream` controller. These are not separate controllers as such but named here for clarity. 610 The job of the downstream controller is to synchronize the device updates from the cloud to the edge node. The job of the upstream controller is the reverse. 611 612 ### Downstream Controller 613 <img src="../images/device-crd/device-downstream-controller.png"> 614 615 The downstream controller watches for device updates against the K8S API server. 616 Updates are categorized below along with the possible actions that the downstream controller can take: 617 618 | Update Type | Action | 619 |-------------------------------|---------------------------------------------- | 620 |New Device Model Created | NA | 621 |New Device Created | The controller creates a new config map to store the device properties and visitors defined in the device model associated with the device. This config map is stored in etcd. The existing config map sync mechanism in the edge controller is used to sync the config map to the egde. The mapper application running in a container can get the updated config map and use the property and visitor metadata to access the device. The device controller additionally reports the device twin metadata updates to the edge node.| 622 |Device Node Membership Updated | The device controller sends a membership update event to the edge node.| 623 |Device Twin Desired State Updated | The device controller sends a twin update event to the edge node.| 624 |Device Model Updated | TODO: What happens to existing devices using this model which are generating telemetry data as per the old model ? Do we update the config map associated with the devices which are using this device model ?| 625 |Device Deleted | The controller sends the device twin delete event to delete all device twins associated with the device. It also deletes config maps associated with the device and this delete event is synced to the edge. The mapper application effectively stops operating on the device.| 626 |Device Model Deleted | The controller needs to run [`finalizers`](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers) to ensure that all device instances using the device model are deleted first, and only then should the model deletion proceed.| 627 628 The idea behind using config map to store device properties and visitors is that these metadata are only required by the mapper applications running on the edge node in order to connect to the device and collect data. 629 Mappers if run as containers can load these properties as config maps . Any additions , deletions or updates to properties , visitors etc in the cloud are watched upon by the downstream controller and config maps are updated in etcd. The existing edge controller already has the mechanism to watch on config map updates and push them to the edge node. A mapper application can get these updates and then adjust the data collection process. 630 A separate design proposal can be prepared to illustrate the details of how mappers can leverage these config maps. 631 632 A sample config map for the device model described earlier is shown below 633 634 ### Device Config Map sample 635 ```yaml 636 apiVersion: v1 637 kind: ConfigMap 638 metadata: 639 name: device-profile-config-01 // needs to be generated by device controller. 640 namespace: foo 641 data: 642 deviceProfile.json: |- 643 { 644 "deviceInstances": [ 645 { 646 "id": "1", 647 "name": "device1", 648 "protocol": "modbus-rtu-01", // needs to be generated by device controller. 649 "model": "SensorTagModel" 650 } 651 ], 652 "deviceModels": [ 653 { 654 "name": "SensorTagModel", 655 "description": "TI Simplelink SensorTag Device Attributes Model", 656 "properties": [ 657 { 658 "name": "temperature", 659 "datatype": "int", 660 "accessMode": "r", 661 "unit": "Degree Celsius", 662 "maximum": "100", 663 }, 664 { 665 "name": "temperature-enable", 666 "datatype": "string", 667 "accessMode": "rw", 668 "defaultValue": "OFF", 669 } 670 ] 671 } 672 ], 673 "protocols": [ 674 { 675 "name": "modbus-rtu-01", 676 "protocol": "modbus-rtu", 677 "protocolConfig": { 678 "serialPort": "1", 679 "baudRate": "115200", 680 "dataBits": "8", 681 "parity": "even", 682 "stopBits": "1", 683 "slaveID": "1" 684 } 685 } 686 ], 687 "propertyVisitors": [ 688 { 689 "name": "temperature", 690 "propertyName": "temperature", 691 "modelName": "SensorTagModel", 692 "protocol": "modbus-rtu", 693 "visitorConfig": { 694 "register": "CoilRegister", 695 "offset": "2", 696 "limit": "1", 697 "scale": "1.0", 698 "isSwap": "true", 699 "isRegisterSwap": "true" 700 } 701 }, 702 { 703 "name": "temperatureEnable", 704 "propertyName": "temperature-enable", 705 "modelName": "SensorTagModel", 706 "protocol": "modbus-rtu", 707 "visitorConfig": { 708 "register": "DiscreteInputRegister", 709 "offset": "3", 710 "limit": "1", 711 "scale": "1.0", 712 "isSwap": "true", 713 "isRegisterSwap": "true" 714 } 715 } 716 ] 717 } 718 ``` 719 720 If the mapper wants to discover what properties a device supports, it can get the model information from the device instance. 721 Also , it can get the protocol information to connect to the device from the device instace. Once it has access to the device model , 722 it can get the properties supported by the device. In order to access the property , the mapper needs to get the corresponding visitor information. 723 This can be retrieved from the propertyVisitors list. Finally , using the visitorConfig, the mapper can read/write the data associated with the property. 724 725 ### Upstream Controller 726 <img src="../images/device-crd/device-upstream-controller.png"> 727 The upstream controller watches for updates from the edge node and applies these updates against the API server in the cloud. Updates are categorized below along with the possible actions that the upstream controller can take: 728 729 | Update Type | Action | 730 |------------------------------- |---------------------------------------------- | 731 |Device Twin Reported State Updated | The controller patches the reported state of the device twin property in the cloud. | 732 733 ## Offline scenarios 734 In case where there is intermittent / no connectivity between the edge node and the cloud , we need to have mechanisms to retry until the updates are correctly propagated. A retry mechanism with a configurable retry timeout and number of retry attempts can be implemented. 735 736 ## Scalability 737 The following factors need to be evaluated in order to analyze issues with scale : 738 - What device data to sync between cloud and edge; how frequent; do we store historical data ? 739 - The downstream controller needs to sync device twin updates, node membership updates, and store the device model properties and visitors as config maps if being consumed by a device. The edge controller needs to sync config maps to the edge. The upstream controller needs to sync device twin updates and device state to cloud. 740 - The detailed design of device controller watching against cloud and edge and the tree structure of device data stored at cloud and edge. 741 - A detailed design for the device controller is provided in earlier section. The device model and the device instance would be stored in etcd in the cloud. The device twin updates are stored at the edge. The device property and visitors , protocol config are stored in config-maps and consumed by mappers. 742 - How are we going to use the device data at cloud ? This can help evaluate item 1 743 - This is desribed in the device controller design. 744 - Currently, we have only one config map per node which stores all the device instances, device models, protocols and visitors for all the devices connected to the edge node. Mappers running on an edge node managing different devices now need to access one global configmap in order to extract information about the device properties and visitors. What should be the best way to partition a monolithic config map into smaller config maps ? Should the partitioning be based on the protocol type or based on device model ? 745 746 ## Device Lifecycle Management 747 IoT device lifecycle management comprises of several steps listed below 748 - Device onboarding / provisioning 749 - The device needs to be registered (via authorization or admission control mechanism). This is currently not in scope of this design. 750 - Device configuration 751 - The device needs to be reconfigured many a times during it's lifecycle. No new capabilities are added. 752 The device CRD has device twins which contain desired values for control properties. By changing the desired value of a control property , we can re-configure the device behaviour. 753 - Device Updates 754 - Firmware updates or some bug fixes need to be applied to the device. This can be a scheduled or ad-hoc update. 755 The current design doesn't support applying such updates. We can support additional actions in the future to perform such tasks. 756 - Device monitoring 757 - Device status needs to be monitored to support proper management actions. Currently we rely on the mapper to report the current device state in the Status of the device CRD. Additional health checks or probes can be further explored to enhance monitoring and troubleshooting capabilities of the platform. 758 - Device deprovisioning 759 - The device needs to be de-registered from the platform if no longer needed to be managed. This is currently not in scope of this design. 760 - Device retirement 761 - If a device is damaged , it needs to be retired. This is currently not in scope of this design. 762 763 ### Device Actions 764 - Currently the device model doesn't support [WOT style actions](https://iot.mozilla.org/wot/#actions-resource). The only way to perform some action on the device is by changing the desired state of the twin property in the status field. We need to see how WOT actions can be incorporated in this model and who will consume those actions ? 765 Will it be the responsibility of the mapper to expose HTTP APIs for the actions ? Can we generate server / client code to perform / consume these actions ? Can we handle firmware updates with such actions ? 766 767 ### Device Events 768 - Currently the device model doesn't support [WOT style events](https://iot.mozilla.org/wot/#events-resource). Should telemetry data like sensor temperature readings etc emitted by devices be reported as events ? WOT events are some abnormal events reported by the device like overheated, or reboot etc. 769 770 ## Security 771 Secure device provisioning is the first step in the device lifecycle management. This is not in scope of the current design proposal. Various provisioning techniques could be explored like Trusted Platform Modules(TPM), X.509 certificates etc. Depending on the device type (directly connect to the edge or LTE/wifi or something else), in case of LTE/wifi, further security guard would be needed. 772 773 ## Open questions 774 - How do we resolve conflicts if the same data is updated in the edge and cloud ?