[VOL-1036] Initial implementation of device lifecycle management

Change-Id: I5aa58fdcbcd852f6f5eef35d48f25f76e20c0418
diff --git a/rw_core/core/adapter_request_handler.go b/rw_core/core/adapter_request_handler.go
index bfc4ee4..7ae9f1a 100644
--- a/rw_core/core/adapter_request_handler.go
+++ b/rw_core/core/adapter_request_handler.go
@@ -25,6 +25,7 @@
 	"github.com/opencord/voltha-go/protos/voltha"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
+	"reflect"
 )
 
 type AdapterRequestHandlerProxy struct {
@@ -89,6 +90,24 @@
 	}
 }
 
+// updatePartialDeviceData updates a subset of a device that an Adapter can update.
+// TODO:  May need a specific proto to handle only a subset of a device that can be changed by an adapter
+func (rhp *AdapterRequestHandlerProxy) mergeDeviceInfoFromAdapter(device *voltha.Device) (*voltha.Device, error) {
+	//		First retrieve the most up to date device info
+	var currentDevice *voltha.Device
+	var err error
+	if currentDevice, err = rhp.deviceMgr.getDevice(device.Id); err != nil {
+		return nil, err
+	}
+	cloned := reflect.ValueOf(currentDevice).Elem().Interface().(voltha.Device)
+	cloned.Root = device.Root
+	cloned.Vendor = device.Vendor
+	cloned.Model = device.Model
+	cloned.SerialNumber = device.SerialNumber
+	cloned.MacAddress = device.MacAddress
+	return &cloned, nil
+}
+
 func (rhp *AdapterRequestHandlerProxy) DeviceUpdate(args []*ca.Argument) (*empty.Empty, error) {
 	if len(args) != 1 {
 		log.Warn("invalid-number-of-args", log.Fields{"args": args})
@@ -100,15 +119,21 @@
 		log.Warnw("cannot-unmarshal-device", log.Fields{"error": err})
 		return nil, err
 	}
-	log.Debugw("DeviceUpdate", log.Fields{"device": device})
+	log.Debugw("DeviceUpdate", log.Fields{"deviceId": device.Id})
 
 	if rhp.TestMode { // Execute only for test cases
 		return new(empty.Empty), nil
 	}
-	if err := rhp.deviceMgr.updateDevice(device); err != nil {
-		log.Debugw("DeviceUpdate-error", log.Fields{"device": device, "error": err})
+
+	//Merge the adapter device info (only the ones an adapter can change) with the latest device data
+	if updatedDevice, err := rhp.mergeDeviceInfoFromAdapter(device); err != nil {
 		return nil, status.Errorf(codes.Internal, "%s", err.Error())
+	} else {
+		// An adapter request needs an Ack without having to wait for the update to be
+		// completed.  We therefore run the update in its own routine.
+		go rhp.deviceMgr.updateDevice(updatedDevice)
 	}
+
 	return new(empty.Empty), nil
 }
 
@@ -137,28 +162,30 @@
 		err := errors.New("invalid-number-of-args")
 		return nil, err
 	}
-	pID := &voltha.ID{}
-	if err := ptypes.UnmarshalAny(args[0].Value, pID); err != nil {
-		log.Warnw("cannot-unmarshal-ID", log.Fields{"error": err})
-		return nil, err
-	}
-	// Porttype is an enum sent as an integer proto
+	deviceId := &voltha.ID{}
 	pt := &ca.IntType{}
-	if err := ptypes.UnmarshalAny(args[1].Value, pt); err != nil {
-		log.Warnw("cannot-unmarshal-porttype", log.Fields{"error": err})
-		return nil, err
+	for _, arg := range args {
+		switch arg.Key {
+		case "device_id":
+			if err := ptypes.UnmarshalAny(arg.Value, deviceId); err != nil {
+				log.Warnw("cannot-unmarshal-device-id", log.Fields{"error": err})
+				return nil, err
+			}
+		case "port_type":
+			if err := ptypes.UnmarshalAny(arg.Value, pt); err != nil {
+				log.Warnw("cannot-unmarshal-porttype", log.Fields{"error": err})
+				return nil, err
+			}
+		}
 	}
-
-	log.Debugw("GetPorts", log.Fields{"deviceID": pID.Id, "portype": pt.Val})
-
+	log.Debugw("GetPorts", log.Fields{"deviceID": deviceId.Id, "portype": pt.Val})
 	if rhp.TestMode { // Execute only for test cases
 		aPort := &voltha.Port{Label: "test_port"}
 		allPorts := &voltha.Ports{}
 		allPorts.Items = append(allPorts.Items, aPort)
 		return allPorts, nil
 	}
-	return nil, nil
-
+	return rhp.deviceMgr.getPorts(nil, deviceId.Id, voltha.Port_PortType(pt.Val))
 }
 
 func (rhp *AdapterRequestHandlerProxy) GetChildDevices(args []*ca.Argument) (*voltha.Device, error) {
@@ -254,29 +281,69 @@
 				log.Warnw("cannot-unmarshal-operStatus", log.Fields{"error": err})
 				return nil, err
 			}
-			if operStatus.Val == -1 {
-				operStatus = nil
-			}
+			//if operStatus.Val == -1 {
+			//	operStatus = nil
+			//}
 		case "connect_status":
 			if err := ptypes.UnmarshalAny(arg.Value, connStatus); err != nil {
 				log.Warnw("cannot-unmarshal-connStatus", log.Fields{"error": err})
 				return nil, err
 			}
-			if connStatus.Val == -1 {
-				connStatus = nil
-			}
+			//if connStatus.Val == -1 {
+			//	connStatus = nil
+			//}
 		}
 	}
-
 	log.Debugw("DeviceStateUpdate", log.Fields{"deviceId": deviceId.Id, "oper-status": operStatus, "conn-status": connStatus})
-
 	if rhp.TestMode { // Execute only for test cases
 		return nil, nil
 	}
-	if err := rhp.deviceMgr.updateDeviceState(deviceId.Id, operStatus, connStatus); err != nil {
-		log.Debugw("DeviceUpdate-error", log.Fields{"deviceId": deviceId.Id, "error": err})
-		return nil, status.Errorf(codes.Internal, "%s", err.Error())
+
+	// When the enum is not set (i.e. -1), Go still convert to the Enum type with the value being -1
+	go rhp.deviceMgr.updateDeviceStatus(deviceId.Id, voltha.OperStatus_OperStatus(operStatus.Val), voltha.ConnectStatus_ConnectStatus(connStatus.Val))
+	return new(empty.Empty), nil
+}
+
+func (rhp *AdapterRequestHandlerProxy) PortStateUpdate(args []*ca.Argument) (*empty.Empty, error) {
+	if len(args) < 2 {
+		log.Warn("invalid-number-of-args", log.Fields{"args": args})
+		err := errors.New("invalid-number-of-args")
+		return nil, err
 	}
+	deviceId := &voltha.ID{}
+	portType := &ca.IntType{}
+	portNo := &ca.IntType{}
+	operStatus := &ca.IntType{}
+	for _, arg := range args {
+		switch arg.Key {
+		case "device_id":
+			if err := ptypes.UnmarshalAny(arg.Value, deviceId); err != nil {
+				log.Warnw("cannot-unmarshal-device-id", log.Fields{"error": err})
+				return nil, err
+			}
+		case "oper_status":
+			if err := ptypes.UnmarshalAny(arg.Value, operStatus); err != nil {
+				log.Warnw("cannot-unmarshal-operStatus", log.Fields{"error": err})
+				return nil, err
+			}
+		case "port_type":
+			if err := ptypes.UnmarshalAny(arg.Value, portType); err != nil {
+				log.Warnw("cannot-unmarshal-porttype", log.Fields{"error": err})
+				return nil, err
+			}
+		case "port_no":
+			if err := ptypes.UnmarshalAny(arg.Value, portNo); err != nil {
+				log.Warnw("cannot-unmarshal-portno", log.Fields{"error": err})
+				return nil, err
+			}
+
+		}
+	}
+	log.Debugw("PortStateUpdate", log.Fields{"deviceId": deviceId.Id, "operStatus": operStatus, "portType": portType, "portNo": portNo})
+	if rhp.TestMode { // Execute only for test cases
+		return nil, nil
+	}
+	go rhp.deviceMgr.updatePortState(deviceId.Id, voltha.Port_PortType(portType.Val), uint32(portNo.Val), voltha.OperStatus_OperStatus(operStatus.Val))
 	return new(empty.Empty), nil
 }
 
@@ -309,10 +376,14 @@
 		return nil, nil
 	}
 
-	if err := rhp.deviceMgr.addPort(deviceId.Id, port); err != nil {
-		log.Debugw("addport-error", log.Fields{"deviceId": deviceId.Id, "error": err})
-		return nil, status.Errorf(codes.Internal, "%s", err.Error())
-	}
+	// Run port creation in its own go routine
+	go rhp.deviceMgr.addPort(deviceId.Id, port)
+
+	//if err := rhp.deviceMgr.addPort(deviceId.Id, port); err != nil {
+	//	log.Debugw("addport-error", log.Fields{"deviceId": deviceId.Id, "error": err})
+	//	return nil, status.Errorf(codes.Internal, "%s", err.Error())
+	//}
+	// Return an Ack
 	return new(empty.Empty), nil
 }
 
@@ -346,10 +417,14 @@
 		return nil, nil
 	}
 
-	if err := rhp.deviceMgr.updatePmConfigs(pmConfigs.Id, pmConfigs); err != nil {
-		log.Debugw("update-pmconfigs-error", log.Fields{"deviceId": pmConfigs.Id, "error": err})
-		return nil, status.Errorf(codes.Internal, "%s", err.Error())
-	}
+	// Run PM config update in its own go routine
+	go rhp.deviceMgr.updatePmConfigs(pmConfigs.Id, pmConfigs)
+
+	//if err := rhp.deviceMgr.updatePmConfigs(pmConfigs.Id, pmConfigs); err != nil {
+	//	log.Debugw("update-pmconfigs-error", log.Fields{"deviceId": pmConfigs.Id, "error": err})
+	//	return nil, status.Errorf(codes.Internal, "%s", err.Error())
+	//}
+	// Return an Ack
 	return new(empty.Empty), nil
 
 }