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 ?