github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/cloud/pkg/devicecontroller/controller/downstream.go (about) 1 /* 2 Copyright 2019 The KubeEdge Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package controller 18 19 import ( 20 "encoding/json" 21 "reflect" 22 "strconv" 23 "time" 24 25 "github.com/satori/go.uuid" 26 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/watch" 29 "k8s.io/client-go/kubernetes" 30 "k8s.io/client-go/rest" 31 "k8s.io/klog" 32 33 beehiveContext "github.com/kubeedge/beehive/pkg/core/context" 34 "github.com/kubeedge/beehive/pkg/core/model" 35 "github.com/kubeedge/kubeedge/cloud/pkg/apis/devices/v1alpha1" 36 "github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/constants" 37 "github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/manager" 38 "github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/messagelayer" 39 "github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/types" 40 "github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/utils" 41 ) 42 43 // Constants for protocol, datatype, configmap, deviceProfile 44 const ( 45 OPCUA = "opcua" 46 ModbusRTU = "modbus-rtu" 47 ModbusTCP = "modbus-tcp" 48 Modbus = "modbus" 49 Bluetooth = "bluetooth" 50 51 DataTypeInt = "int" 52 DataTypeString = "string" 53 54 ConfigMapKind = "ConfigMap" 55 ConfigMapVersion = "v1" 56 57 DeviceProfileConfigPrefix = "device-profile-config-" 58 59 DeviceProfileJSON = "deviceProfile.json" 60 ) 61 62 // DownstreamController watch kubernetes api server and send change to edge 63 type DownstreamController struct { 64 kubeClient *kubernetes.Clientset 65 messageLayer messagelayer.MessageLayer 66 67 deviceManager *manager.DeviceManager 68 deviceModelManager *manager.DeviceModelManager 69 configMapManager *manager.ConfigMapManager 70 71 crdClient *rest.RESTClient 72 } 73 74 // syncDeviceModel is used to get events from informer 75 func (dc *DownstreamController) syncDeviceModel() { 76 for { 77 select { 78 case <-beehiveContext.Done(): 79 klog.Info("stop syncDeviceModel") 80 return 81 case e := <-dc.deviceModelManager.Events(): 82 deviceModel, ok := e.Object.(*v1alpha1.DeviceModel) 83 if !ok { 84 klog.Warningf("object type: %T unsupported", deviceModel) 85 continue 86 } 87 switch e.Type { 88 case watch.Added: 89 dc.deviceModelAdded(deviceModel) 90 case watch.Deleted: 91 dc.deviceModelDeleted(deviceModel) 92 case watch.Modified: 93 dc.deviceModelUpdated(deviceModel) 94 default: 95 klog.Warningf("deviceModel event type: %s unsupported", e.Type) 96 } 97 } 98 } 99 } 100 101 // deviceModelAdded is function to process addition of new deviceModel in apiserver 102 func (dc *DownstreamController) deviceModelAdded(deviceModel *v1alpha1.DeviceModel) { 103 // nothing to do when deviceModel added, only add in map 104 dc.deviceModelManager.DeviceModel.Store(deviceModel.Name, deviceModel) 105 } 106 107 // isDeviceModelUpdated is function to check if deviceModel is actually updated 108 func isDeviceModelUpdated(oldTwin *v1alpha1.DeviceModel, newTwin *v1alpha1.DeviceModel) bool { 109 // does not care fields 110 oldTwin.ObjectMeta.ResourceVersion = newTwin.ObjectMeta.ResourceVersion 111 oldTwin.ObjectMeta.Generation = newTwin.ObjectMeta.Generation 112 113 // return true if ObjectMeta or Spec or Status changed, else false 114 return !reflect.DeepEqual(oldTwin.ObjectMeta, newTwin.ObjectMeta) || !reflect.DeepEqual(oldTwin.Spec, newTwin.Spec) 115 } 116 117 // deviceModelUpdated is function to process updated deviceModel 118 func (dc *DownstreamController) deviceModelUpdated(deviceModel *v1alpha1.DeviceModel) { 119 value, ok := dc.deviceModelManager.DeviceModel.Load(deviceModel.Name) 120 dc.deviceModelManager.DeviceModel.Store(deviceModel.Name, deviceModel) 121 if ok { 122 cachedDeviceModel := value.(*v1alpha1.DeviceModel) 123 if isDeviceModelUpdated(cachedDeviceModel, deviceModel) { 124 dc.updateAllConfigMaps(deviceModel) 125 } 126 } else { 127 dc.deviceModelAdded(deviceModel) 128 } 129 } 130 131 // updateAllConfigMaps is function to update configMaps which refer to an updated deviceModel 132 func (dc *DownstreamController) updateAllConfigMaps(deviceModel *v1alpha1.DeviceModel) { 133 //TODO: add logic to update all config maps, How to manage if a property is deleted but a device is referring that property. Need to come up with a design. 134 } 135 136 // deviceModelDeleted is function to process deleted deviceModel 137 func (dc *DownstreamController) deviceModelDeleted(deviceModel *v1alpha1.DeviceModel) { 138 // TODO: Need to use finalizer like method to delete all devices referring to this model. Need to come up with a design. 139 dc.deviceModelManager.DeviceModel.Delete(deviceModel.Name) 140 } 141 142 // syncDevice is used to get device events from informer 143 func (dc *DownstreamController) syncDevice() { 144 for { 145 select { 146 case <-beehiveContext.Done(): 147 klog.Info("Stop syncDevice") 148 return 149 case e := <-dc.deviceManager.Events(): 150 device, ok := e.Object.(*v1alpha1.Device) 151 if !ok { 152 klog.Warningf("Object type: %T unsupported", device) 153 continue 154 } 155 switch e.Type { 156 case watch.Added: 157 dc.deviceAdded(device) 158 case watch.Deleted: 159 dc.deviceDeleted(device) 160 case watch.Modified: 161 dc.deviceUpdated(device) 162 default: 163 klog.Warningf("Device event type: %s unsupported", e.Type) 164 } 165 } 166 } 167 } 168 169 // addToConfigMap adds device in the configmap 170 func (dc *DownstreamController) addToConfigMap(device *v1alpha1.Device) { 171 configMap, ok := dc.configMapManager.ConfigMap.Load(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0]) 172 if !ok { 173 nodeConfigMap := &v1.ConfigMap{} 174 nodeConfigMap.Kind = ConfigMapKind 175 nodeConfigMap.APIVersion = ConfigMapVersion 176 nodeConfigMap.Name = DeviceProfileConfigPrefix + device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0] 177 nodeConfigMap.Namespace = device.Namespace 178 nodeConfigMap.Data = make(map[string]string) 179 // TODO: how to handle 2 device of multiple namespaces bind to same node ? 180 dc.addDeviceProfile(device, nodeConfigMap) 181 // store new config map 182 dc.configMapManager.ConfigMap.Store(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0], nodeConfigMap) 183 184 if _, err := dc.kubeClient.CoreV1().ConfigMaps(device.Namespace).Get(nodeConfigMap.Name, metav1.GetOptions{}); err != nil { 185 if _, err := dc.kubeClient.CoreV1().ConfigMaps(device.Namespace).Create(nodeConfigMap); err != nil { 186 klog.Errorf("Failed to create config map %v in namespace %v, error %v", nodeConfigMap, device.Namespace, err) 187 return 188 } 189 } 190 if _, err := dc.kubeClient.CoreV1().ConfigMaps(device.Namespace).Update(nodeConfigMap); err != nil { 191 klog.Errorf("Failed to update config map %v in namespace %v, error %v", nodeConfigMap, device.Namespace, err) 192 return 193 } 194 return 195 } 196 nodeConfigMap, ok := configMap.(*v1.ConfigMap) 197 if !ok { 198 klog.Error("Failed to assert to configmap") 199 return 200 } 201 dc.addDeviceProfile(device, nodeConfigMap) 202 // store new config map 203 dc.configMapManager.ConfigMap.Store(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0], nodeConfigMap) 204 if _, err := dc.kubeClient.CoreV1().ConfigMaps(device.Namespace).Update(nodeConfigMap); err != nil { 205 klog.Errorf("Failed to update config map %v in namespace %v", nodeConfigMap, device.Namespace) 206 return 207 } 208 } 209 210 // addDeviceProfile is function to add deviceProfile in configMap 211 func (dc *DownstreamController) addDeviceProfile(device *v1alpha1.Device, configMap *v1.ConfigMap) { 212 deviceProfile := &types.DeviceProfile{} 213 dp, ok := configMap.Data[DeviceProfileJSON] 214 if !ok { 215 // create deviceProfileStruct 216 deviceProfile.DeviceInstances = make([]*types.DeviceInstance, 0) 217 deviceProfile.DeviceModels = make([]*types.DeviceModel, 0) 218 deviceProfile.PropertyVisitors = make([]*types.PropertyVisitor, 0) 219 deviceProfile.Protocols = make([]*types.Protocol, 0) 220 } else { 221 err := json.Unmarshal([]byte(dp), deviceProfile) 222 if err != nil { 223 klog.Errorf("Failed to Unmarshal deviceprofile: %v", deviceProfile) 224 return 225 } 226 } 227 228 addDeviceInstanceAndProtocol(device, deviceProfile) 229 dm, ok := dc.deviceModelManager.DeviceModel.Load(device.Spec.DeviceModelRef.Name) 230 if !ok { 231 klog.Errorf("Failed to get device model %v", device.Spec.DeviceModelRef.Name) 232 return 233 } 234 deviceModel := dm.(*v1alpha1.DeviceModel) 235 // if model already exists no need to add model and visitors 236 checkModelExists := false 237 for _, dm := range deviceProfile.DeviceModels { 238 if dm.Name == deviceModel.Name { 239 checkModelExists = true 240 break 241 } 242 } 243 if checkModelExists != true { 244 addDeviceModelAndVisitors(deviceModel, deviceProfile) 245 } 246 bytes, err := json.Marshal(deviceProfile) 247 if err != nil { 248 klog.Errorf("Failed to marshal deviceprofile: %v", deviceProfile) 249 return 250 } 251 configMap.Data[DeviceProfileJSON] = string(bytes) 252 } 253 254 // addDeviceModelAndVisitors adds deviceModels and deviceVisitors in configMap 255 func addDeviceModelAndVisitors(deviceModel *v1alpha1.DeviceModel, deviceProfile *types.DeviceProfile) { 256 model := &types.DeviceModel{} 257 model.Name = deviceModel.Name 258 model.Properties = make([]*types.Property, 0) 259 for _, ppt := range deviceModel.Spec.Properties { 260 property := &types.Property{} 261 property.Name = ppt.Name 262 property.Description = ppt.Description 263 if ppt.Type.Int != nil { 264 property.AccessMode = string(ppt.Type.Int.AccessMode) 265 property.DataType = DataTypeInt 266 property.DefaultValue = ppt.Type.Int.DefaultValue 267 property.Maximum = ppt.Type.Int.Maximum 268 property.Minimum = ppt.Type.Int.Minimum 269 property.Unit = ppt.Type.Int.Unit 270 } else if ppt.Type.String != nil { 271 property.AccessMode = string(ppt.Type.String.AccessMode) 272 property.DataType = DataTypeString 273 property.DefaultValue = ppt.Type.String.DefaultValue 274 } 275 model.Properties = append(model.Properties, property) 276 } 277 deviceProfile.DeviceModels = append(deviceProfile.DeviceModels, model) 278 for _, pptv := range deviceModel.Spec.PropertyVisitors { 279 propertyVisitor := &types.PropertyVisitor{} 280 propertyVisitor.Name = pptv.PropertyName 281 propertyVisitor.PropertyName = pptv.PropertyName 282 propertyVisitor.ModelName = deviceModel.Name 283 if pptv.Modbus != nil { 284 propertyVisitor.Protocol = Modbus 285 propertyVisitor.VisitorConfig = pptv.Modbus 286 } else if pptv.OpcUA != nil { 287 propertyVisitor.Protocol = OPCUA 288 propertyVisitor.VisitorConfig = pptv.OpcUA 289 } else if pptv.Bluetooth != nil { 290 propertyVisitor.Protocol = Bluetooth 291 propertyVisitor.VisitorConfig = pptv.Bluetooth 292 } 293 deviceProfile.PropertyVisitors = append(deviceProfile.PropertyVisitors, propertyVisitor) 294 } 295 } 296 297 // addDeviceInstanceAndProtocol adds deviceInstance and protocol in configMap 298 func addDeviceInstanceAndProtocol(device *v1alpha1.Device, deviceProfile *types.DeviceProfile) { 299 deviceInstance := &types.DeviceInstance{} 300 deviceProtocol := &types.Protocol{} 301 deviceInstance.ID = device.Name 302 deviceInstance.Name = device.Name 303 deviceInstance.Model = device.Spec.DeviceModelRef.Name 304 var protocol string 305 if device.Spec.Protocol.OpcUA != nil { 306 protocol = OPCUA + "-" + device.Name 307 deviceInstance.Protocol = protocol 308 deviceProtocol.Name = protocol 309 deviceProtocol.Protocol = OPCUA 310 deviceProtocol.ProtocolConfig = device.Spec.Protocol.OpcUA 311 } else if device.Spec.Protocol.Modbus != nil && device.Spec.Protocol.Modbus.RTU != nil { 312 protocol = ModbusRTU + "-" + device.Name 313 deviceInstance.Protocol = protocol 314 deviceProtocol.Name = protocol 315 deviceProtocol.Protocol = ModbusRTU 316 deviceProtocol.ProtocolConfig = device.Spec.Protocol.Modbus.RTU 317 } else if device.Spec.Protocol.Modbus != nil && device.Spec.Protocol.Modbus.TCP != nil { 318 protocol = ModbusTCP + "-" + device.Name 319 deviceInstance.Protocol = protocol 320 deviceProtocol.Name = protocol 321 deviceProtocol.Protocol = ModbusTCP 322 deviceProtocol.ProtocolConfig = device.Spec.Protocol.Modbus.TCP 323 } else if device.Spec.Protocol.Bluetooth != nil { 324 protocol = Bluetooth + "-" + device.Name 325 deviceInstance.Protocol = protocol 326 deviceProtocol.Name = protocol 327 deviceProtocol.Protocol = Bluetooth 328 deviceProtocol.ProtocolConfig = device.Spec.Protocol.Bluetooth 329 } else { 330 klog.Warning("Device doesnt support valid protocol") 331 } 332 deviceProfile.DeviceInstances = append(deviceProfile.DeviceInstances, deviceInstance) 333 deviceProfile.Protocols = append(deviceProfile.Protocols, deviceProtocol) 334 } 335 336 // deviceAdded creates a device, adds in deviceManagers map, send a message to edge node if node selector is present. 337 func (dc *DownstreamController) deviceAdded(device *v1alpha1.Device) { 338 dc.deviceManager.Device.Store(device.Name, device) 339 if len(device.Spec.NodeSelector.NodeSelectorTerms) != 0 && len(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions) != 0 && len(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values) != 0 { 340 dc.addToConfigMap(device) 341 edgeDevice := createDevice(device) 342 msg := model.NewMessage("") 343 344 resource, err := messagelayer.BuildResource(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0], "membership", "") 345 if err != nil { 346 klog.Warningf("Built message resource failed with error: %s", err) 347 return 348 } 349 msg.BuildRouter(constants.DeviceControllerModuleName, constants.GroupTwin, resource, model.UpdateOperation) 350 351 content := types.MembershipUpdate{AddDevices: []types.Device{ 352 edgeDevice, 353 }} 354 content.EventID = uuid.NewV4().String() 355 content.Timestamp = time.Now().UnixNano() / 1e6 356 msg.Content = content 357 358 err = dc.messageLayer.Send(*msg) 359 if err != nil { 360 klog.Errorf("Failed to send device addition message %v due to error %v", msg, err) 361 } 362 } 363 } 364 365 // createDevice creates a device from CRD 366 func createDevice(device *v1alpha1.Device) types.Device { 367 edgeDevice := types.Device{ 368 // ID and name can be used as ID as we are using CRD and name(key in ETCD) will always be unique 369 ID: device.Name, 370 Name: device.Name, 371 } 372 373 description, ok := device.Labels["description"] 374 if ok { 375 edgeDevice.Description = description 376 } 377 378 // TODO: optional is Always false, currently not present in CRD definition, need to add or remove from deviceTwin @ Edge 379 opt := false 380 optional := &opt 381 twin := make(map[string]*types.MsgTwin) 382 for i, dtwin := range device.Status.Twins { 383 expected := &types.TwinValue{} 384 expected.Value = &device.Status.Twins[i].Desired.Value 385 metadataType, ok := device.Status.Twins[i].Desired.Metadata["type"] 386 if !ok { 387 metadataType = "string" 388 } 389 timestamp := time.Now().UnixNano() / 1e6 390 391 metadata := &types.ValueMetadata{Timestamp: timestamp} 392 expected.Metadata = metadata 393 394 // TODO: how to manage versioning ?? 395 cloudVersion, err := strconv.ParseInt(device.ResourceVersion, 10, 64) 396 if err != nil { 397 klog.Warningf("Failed to parse cloud version due to error %v", err) 398 } 399 twinVersion := &types.TwinVersion{CloudVersion: cloudVersion, EdgeVersion: 0} 400 msgTwin := &types.MsgTwin{ 401 Expected: expected, 402 Optional: optional, 403 Metadata: &types.TypeMetadata{Type: metadataType}, 404 ExpectedVersion: twinVersion, 405 } 406 twin[dtwin.PropertyName] = msgTwin 407 } 408 edgeDevice.Twin = twin 409 return edgeDevice 410 } 411 412 // isDeviceUpdated checks if device is actually updated 413 func isDeviceUpdated(oldTwin *v1alpha1.Device, newTwin *v1alpha1.Device) bool { 414 // does not care fields 415 oldTwin.ObjectMeta.ResourceVersion = newTwin.ObjectMeta.ResourceVersion 416 oldTwin.ObjectMeta.Generation = newTwin.ObjectMeta.Generation 417 418 // return true if ObjectMeta or Spec or Status changed, else false 419 return !reflect.DeepEqual(oldTwin.ObjectMeta, newTwin.ObjectMeta) || !reflect.DeepEqual(oldTwin.Spec, newTwin.Spec) || !reflect.DeepEqual(oldTwin.Status, newTwin.Status) 420 } 421 422 // isNodeSelectorUpdated checks if nodeSelector is updated 423 func isNodeSelectorUpdated(oldTwin *v1.NodeSelector, newTwin *v1.NodeSelector) bool { 424 return !reflect.DeepEqual(oldTwin.NodeSelectorTerms, newTwin.NodeSelectorTerms) 425 } 426 427 // isProtocolConfigUpdated checks if protocol is updated 428 func isProtocolConfigUpdated(oldTwin *v1alpha1.ProtocolConfig, newTwin *v1alpha1.ProtocolConfig) bool { 429 return !reflect.DeepEqual(oldTwin, newTwin) 430 } 431 432 // updateProtocolInConfigMap updates the protocol in the deviceProfile in configmap 433 func (dc *DownstreamController) updateProtocolInConfigMap(device *v1alpha1.Device) { 434 if len(device.Spec.NodeSelector.NodeSelectorTerms) != 0 && len(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions) != 0 && len(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values) != 0 { 435 configMap, ok := dc.configMapManager.ConfigMap.Load(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0]) 436 if !ok { 437 klog.Error("Failed to load configmap") 438 return 439 } 440 441 nodeConfigMap, ok := configMap.(*v1.ConfigMap) 442 if !ok { 443 klog.Error("Failed to assert to configmap") 444 return 445 } 446 dp, ok := nodeConfigMap.Data[DeviceProfileJSON] 447 if !ok || dp == "{}" { 448 // This case should never be hit as we delete empty configmaps 449 klog.Error("Failed to get deviceProfile from configmap data or deviceProfile is empty") 450 return 451 } 452 453 deviceProfile := &types.DeviceProfile{} 454 if err := json.Unmarshal([]byte(dp), deviceProfile); err != nil { 455 klog.Errorf("Failed to unmarshal due to error: %v", err) 456 return 457 } 458 var oldProtocol string 459 for _, devInst := range deviceProfile.DeviceInstances { 460 if device.Name == devInst.Name { 461 oldProtocol = devInst.Protocol 462 break 463 } 464 } 465 466 // delete the old protocol 467 for i, ptcl := range deviceProfile.Protocols { 468 if ptcl.Name == oldProtocol { 469 deviceProfile.Protocols = append(deviceProfile.Protocols[:i], deviceProfile.Protocols[i+1:]...) 470 break 471 } 472 } 473 474 // add new protocol 475 deviceProtocol := &types.Protocol{} 476 if device.Spec.Protocol.OpcUA != nil { 477 deviceProtocol = buildDeviceProtocol(OPCUA, device.Name, device.Spec.Protocol.OpcUA) 478 } else if device.Spec.Protocol.Modbus != nil && device.Spec.Protocol.Modbus.RTU != nil { 479 deviceProtocol = buildDeviceProtocol(ModbusRTU, device.Name, device.Spec.Protocol.Modbus.RTU) 480 } else if device.Spec.Protocol.Modbus != nil && device.Spec.Protocol.Modbus.TCP != nil { 481 deviceProtocol = buildDeviceProtocol(ModbusTCP, device.Name, device.Spec.Protocol.Modbus.TCP) 482 } else if device.Spec.Protocol.Bluetooth != nil { 483 deviceProtocol = buildDeviceProtocol(Bluetooth, device.Name, device.Spec.Protocol.Bluetooth) 484 } else { 485 klog.Warning("Unsupported device protocol") 486 } 487 488 // update the protocol in deviceInstance 489 for _, devInst := range deviceProfile.DeviceInstances { 490 if device.Name == devInst.Name { 491 devInst.Protocol = deviceProtocol.Name 492 break 493 } 494 } 495 deviceProfile.Protocols = append(deviceProfile.Protocols, deviceProtocol) 496 497 bytes, err := json.Marshal(deviceProfile) 498 if err != nil { 499 klog.Errorf("Failed to marshal deviceprofile: %v", deviceProfile) 500 return 501 } 502 nodeConfigMap.Data[DeviceProfileJSON] = string(bytes) 503 // store new config map 504 dc.configMapManager.ConfigMap.Store(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0], nodeConfigMap) 505 if _, err := dc.kubeClient.CoreV1().ConfigMaps(device.Namespace).Update(nodeConfigMap); err != nil { 506 klog.Errorf("Failed to update config map %v in namespace %v", nodeConfigMap, device.Namespace) 507 return 508 } 509 } 510 } 511 512 func buildDeviceProtocol(protocol, deviceName string, ProtocolConfig interface{}) *types.Protocol { 513 var deviceProtocol types.Protocol 514 deviceProtocol.Name = protocol + "-" + deviceName 515 deviceProtocol.Protocol = protocol 516 deviceProtocol.ProtocolConfig = ProtocolConfig 517 return &deviceProtocol 518 } 519 520 // deviceUpdated updates the map, check if device is actually updated. 521 // If nodeSelector is updated, call add device for newNode, deleteDevice for old Node. 522 // If twin is updated, send twin update message to edge 523 func (dc *DownstreamController) deviceUpdated(device *v1alpha1.Device) { 524 value, ok := dc.deviceManager.Device.Load(device.Name) 525 dc.deviceManager.Device.Store(device.Name, device) 526 if ok { 527 cachedDevice := value.(*v1alpha1.Device) 528 if isDeviceUpdated(cachedDevice, device) { 529 // if node selector updated delete from old node and create in new node 530 if isNodeSelectorUpdated(cachedDevice.Spec.NodeSelector, device.Spec.NodeSelector) { 531 dc.deviceAdded(device) 532 deletedDevice := &v1alpha1.Device{ObjectMeta: cachedDevice.ObjectMeta, 533 Spec: cachedDevice.Spec, 534 Status: cachedDevice.Status, 535 TypeMeta: device.TypeMeta, 536 } 537 dc.deviceDeleted(deletedDevice) 538 539 } else if isProtocolConfigUpdated(&cachedDevice.Spec.Protocol, &device.Spec.Protocol) { 540 dc.updateProtocolInConfigMap(device) 541 } else { 542 // TODO: add an else if condition to check if DeviceModelReference has changed, if yes whether deviceModelReference exists 543 twin := make(map[string]*types.MsgTwin) 544 addUpdatedTwins(device.Status.Twins, twin, device.ResourceVersion) 545 addDeletedTwins(cachedDevice.Status.Twins, device.Status.Twins, twin, device.ResourceVersion) 546 msg := model.NewMessage("") 547 548 resource, err := messagelayer.BuildResource(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0], "device/"+device.Name+"/twin/cloud_updated", "") 549 if err != nil { 550 klog.Warningf("Built message resource failed with error: %s", err) 551 return 552 } 553 msg.BuildRouter(constants.DeviceControllerModuleName, constants.GroupTwin, resource, model.UpdateOperation) 554 content := types.DeviceTwinUpdate{Twin: twin} 555 content.EventID = uuid.NewV4().String() 556 content.Timestamp = time.Now().UnixNano() / 1e6 557 msg.Content = content 558 559 err = dc.messageLayer.Send(*msg) 560 if err != nil { 561 klog.Errorf("Failed to send deviceTwin message %v due to error %v", msg, err) 562 } 563 } 564 } 565 } else { 566 // If device not present in device map means it is not modified and added. 567 dc.deviceAdded(device) 568 } 569 } 570 571 // addDeletedTwins add deleted twins in the message 572 func addDeletedTwins(oldTwin []v1alpha1.Twin, newTwin []v1alpha1.Twin, twin map[string]*types.MsgTwin, version string) { 573 opt := false 574 optional := &opt 575 for i, dtwin := range oldTwin { 576 if !ifTwinPresent(dtwin, newTwin) { 577 expected := &types.TwinValue{} 578 expected.Value = &oldTwin[i].Desired.Value 579 timestamp := time.Now().UnixNano() / 1e6 580 581 metadata := &types.ValueMetadata{Timestamp: timestamp} 582 expected.Metadata = metadata 583 584 // TODO: how to manage versioning ?? 585 cloudVersion, err := strconv.ParseInt(version, 10, 64) 586 if err != nil { 587 klog.Warningf("Failed to parse cloud version due to error %v", err) 588 } 589 twinVersion := &types.TwinVersion{CloudVersion: cloudVersion, EdgeVersion: 0} 590 msgTwin := &types.MsgTwin{ 591 Expected: expected, 592 Optional: optional, 593 Metadata: &types.TypeMetadata{Type: "deleted"}, 594 ExpectedVersion: twinVersion, 595 } 596 twin[dtwin.PropertyName] = msgTwin 597 } 598 } 599 } 600 601 // ifTwinPresent checks if twin is present in the array of twins 602 func ifTwinPresent(twin v1alpha1.Twin, newTwins []v1alpha1.Twin) bool { 603 for _, dtwin := range newTwins { 604 if twin.PropertyName == dtwin.PropertyName { 605 return true 606 } 607 } 608 return false 609 } 610 611 // addUpdatedTwins is function of add updated twins to send to edge 612 func addUpdatedTwins(newTwin []v1alpha1.Twin, twin map[string]*types.MsgTwin, version string) { 613 opt := false 614 optional := &opt 615 for i, dtwin := range newTwin { 616 expected := &types.TwinValue{} 617 expected.Value = &newTwin[i].Desired.Value 618 metadataType, ok := newTwin[i].Desired.Metadata["type"] 619 if !ok { 620 metadataType = "string" 621 } 622 timestamp := time.Now().UnixNano() / 1e6 623 624 metadata := &types.ValueMetadata{Timestamp: timestamp} 625 expected.Metadata = metadata 626 627 // TODO: how to manage versioning ?? 628 cloudVersion, err := strconv.ParseInt(version, 10, 64) 629 if err != nil { 630 klog.Warningf("Failed to parse cloud version due to error %v", err) 631 } 632 twinVersion := &types.TwinVersion{CloudVersion: cloudVersion, EdgeVersion: 0} 633 msgTwin := &types.MsgTwin{ 634 Expected: expected, 635 Optional: optional, 636 Metadata: &types.TypeMetadata{Type: metadataType}, 637 ExpectedVersion: twinVersion, 638 } 639 twin[dtwin.PropertyName] = msgTwin 640 } 641 } 642 643 // deleteFromConfigMap deletes a device from configMap 644 func (dc *DownstreamController) deleteFromConfigMap(device *v1alpha1.Device) { 645 if len(device.Spec.NodeSelector.NodeSelectorTerms) != 0 && len(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions) != 0 && len(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values) != 0 { 646 configMap, ok := dc.configMapManager.ConfigMap.Load(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0]) 647 if !ok { 648 return 649 } 650 nodeConfigMap, ok := configMap.(*v1.ConfigMap) 651 if !ok { 652 klog.Error("Failed to assert to configmap") 653 return 654 } 655 656 dc.deleteFromDeviceProfile(device, nodeConfigMap) 657 658 // no device is bound to the configMap, then remove the configMap directly. 659 if nodeConfigMap.Data[DeviceProfileJSON] == "{}" { 660 deleteOptions := &metav1.DeleteOptions{} 661 dc.kubeClient.CoreV1().ConfigMaps(device.Namespace).Delete(nodeConfigMap.Name, deleteOptions) 662 // remove from cache 663 dc.configMapManager.ConfigMap.Delete(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0]) 664 return 665 } 666 667 // store new config map 668 dc.configMapManager.ConfigMap.Store(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0], nodeConfigMap) 669 if _, err := dc.kubeClient.CoreV1().ConfigMaps(device.Namespace).Update(nodeConfigMap); err != nil { 670 klog.Errorf("Failed to update config map %v in namespace %v", nodeConfigMap, device.Namespace) 671 return 672 } 673 } 674 } 675 676 // deleteFromDeviceProfile deletes a device from deviceProfile 677 func (dc *DownstreamController) deleteFromDeviceProfile(device *v1alpha1.Device, configMap *v1.ConfigMap) { 678 dp, ok := configMap.Data[DeviceProfileJSON] 679 if !ok { 680 klog.Error("Device profile does not exist in the configmap") 681 return 682 } 683 684 deviceProfile := &types.DeviceProfile{} 685 err := json.Unmarshal([]byte(dp), deviceProfile) 686 if err != nil { 687 klog.Errorf("Failed to Unmarshal deviceprofile: %v", deviceProfile) 688 return 689 } 690 deleteDeviceInstanceAndProtocol(device, deviceProfile) 691 692 dm, ok := dc.deviceModelManager.DeviceModel.Load(device.Spec.DeviceModelRef.Name) 693 if !ok { 694 klog.Errorf("Failed to get device model %v", device.Spec.DeviceModelRef.Name) 695 return 696 } 697 deviceModel := dm.(*v1alpha1.DeviceModel) 698 // if model referenced by other devices, no need to delete the model 699 checkModelReferenced := false 700 for _, dvc := range deviceProfile.DeviceInstances { 701 if dvc.Model == deviceModel.Name { 702 checkModelReferenced = true 703 break 704 } 705 } 706 if checkModelReferenced != true { 707 deleteDeviceModelAndVisitors(deviceModel, deviceProfile) 708 } 709 bytes, err := json.Marshal(deviceProfile) 710 if err != nil { 711 klog.Errorf("Failed to marshal deviceprofile: %v", deviceProfile) 712 return 713 } 714 configMap.Data[DeviceProfileJSON] = string(bytes) 715 } 716 717 // deleteDeviceInstanceAndProtocol deletes deviceInstance and protocol from deviceProfile 718 func deleteDeviceInstanceAndProtocol(device *v1alpha1.Device, deviceProfile *types.DeviceProfile) { 719 var protocol string 720 for i, devInst := range deviceProfile.DeviceInstances { 721 if device.Name == devInst.Name { 722 protocol = devInst.Protocol 723 deviceProfile.DeviceInstances[i] = deviceProfile.DeviceInstances[len(deviceProfile.DeviceInstances)-1] 724 deviceProfile.DeviceInstances[len(deviceProfile.DeviceInstances)-1] = nil 725 deviceProfile.DeviceInstances = deviceProfile.DeviceInstances[:len(deviceProfile.DeviceInstances)-1] 726 break 727 } 728 } 729 730 for i, ptcl := range deviceProfile.Protocols { 731 if ptcl.Name == protocol { 732 deviceProfile.Protocols[i] = deviceProfile.Protocols[len(deviceProfile.Protocols)-1] 733 deviceProfile.Protocols[len(deviceProfile.Protocols)-1] = nil 734 deviceProfile.Protocols = deviceProfile.Protocols[:len(deviceProfile.Protocols)-1] 735 return 736 } 737 } 738 } 739 740 // deleteDeviceModelAndVisitors deletes deviceModel and visitor from deviceProfile 741 func deleteDeviceModelAndVisitors(deviceModel *v1alpha1.DeviceModel, deviceProfile *types.DeviceProfile) { 742 for i, dm := range deviceProfile.DeviceModels { 743 if dm.Name == deviceModel.Name { 744 deviceProfile.DeviceModels[i] = deviceProfile.DeviceModels[len(deviceProfile.DeviceModels)-1] 745 deviceProfile.DeviceModels[len(deviceProfile.DeviceModels)-1] = nil 746 deviceProfile.DeviceModels = deviceProfile.DeviceModels[:len(deviceProfile.DeviceModels)-1] 747 break 748 } 749 } 750 751 allVisitorsNotDeleted := true 752 for allVisitorsNotDeleted { 753 allVisitorsNotDeleted = false 754 for i, vst := range deviceProfile.PropertyVisitors { 755 if vst.ModelName == deviceModel.Name { 756 deviceProfile.PropertyVisitors[i] = deviceProfile.PropertyVisitors[len(deviceProfile.PropertyVisitors)-1] 757 deviceProfile.PropertyVisitors[len(deviceProfile.PropertyVisitors)-1] = nil 758 deviceProfile.PropertyVisitors = deviceProfile.PropertyVisitors[:len(deviceProfile.PropertyVisitors)-1] 759 allVisitorsNotDeleted = true 760 break 761 } 762 } 763 } 764 } 765 766 // deviceDeleted send a deleted message to the edgeNode and deletes the device from the deviceManager.Device map 767 func (dc *DownstreamController) deviceDeleted(device *v1alpha1.Device) { 768 dc.deviceManager.Device.Delete(device.Name) 769 dc.deleteFromConfigMap(device) 770 edgeDevice := createDevice(device) 771 msg := model.NewMessage("") 772 773 if len(device.Spec.NodeSelector.NodeSelectorTerms) != 0 && len(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions) != 0 && len(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values) != 0 { 774 resource, err := messagelayer.BuildResource(device.Spec.NodeSelector.NodeSelectorTerms[0].MatchExpressions[0].Values[0], "membership", "") 775 msg.BuildRouter(constants.DeviceControllerModuleName, constants.GroupTwin, resource, model.UpdateOperation) 776 777 content := types.MembershipUpdate{RemoveDevices: []types.Device{ 778 edgeDevice, 779 }} 780 content.EventID = uuid.NewV4().String() 781 content.Timestamp = time.Now().UnixNano() / 1e6 782 msg.Content = content 783 if err != nil { 784 klog.Warningf("Built message resource failed with error: %s", err) 785 return 786 } 787 err = dc.messageLayer.Send(*msg) 788 if err != nil { 789 klog.Errorf("Failed to send device addition message %v due to error %v", msg, err) 790 } 791 } 792 } 793 794 // Start DownstreamController 795 func (dc *DownstreamController) Start() error { 796 klog.Info("Start downstream devicecontroller") 797 798 go dc.syncDeviceModel() 799 800 // Wait for adding all device model 801 // TODO need to think about sync 802 time.Sleep(1 * time.Second) 803 go dc.syncDevice() 804 805 return nil 806 } 807 808 // NewDownstreamController create a DownstreamController from config 809 func NewDownstreamController() (*DownstreamController, error) { 810 cli, err := utils.KubeClient() 811 if err != nil { 812 klog.Warningf("Create kube client failed with error: %s", err) 813 return nil, err 814 } 815 816 config, err := utils.KubeConfig() 817 if err != nil { 818 klog.Warningf("Get kubeConfig error: %v", err) 819 return nil, err 820 } 821 822 crdcli, err := utils.NewCRDClient(config) 823 if err != nil { 824 klog.Warningf("Failed to create crd client: %s", err) 825 return nil, err 826 } 827 deviceManager, err := manager.NewDeviceManager(crdcli, v1.NamespaceAll) 828 if err != nil { 829 klog.Warningf("Create device manager failed with error: %s", err) 830 return nil, err 831 } 832 833 deviceModelManager, err := manager.NewDeviceModelManager(crdcli, v1.NamespaceAll) 834 if err != nil { 835 klog.Warningf("Create device manager failed with error: %s", err) 836 return nil, err 837 } 838 839 dc := &DownstreamController{ 840 kubeClient: cli, 841 deviceManager: deviceManager, 842 deviceModelManager: deviceModelManager, 843 messageLayer: messagelayer.NewContextMessageLayer(), 844 configMapManager: manager.NewConfigMapManager(), 845 } 846 return dc, nil 847 }