[VOL-4111] Moving Services under the UNI struct
Controlling the UNI state via OMCI Set Messages
Upgraded APIs to reflect the new format

Change-Id: I3a6c166205fad4a381e562ab3b873d03b633303e
diff --git a/internal/bbsim/api/onus_handler.go b/internal/bbsim/api/onus_handler.go
index 9fc73e7..0a67105 100644
--- a/internal/bbsim/api/onus_handler.go
+++ b/internal/bbsim/api/onus_handler.go
@@ -42,8 +42,6 @@
 				OperState:                     o.OperState.Current(),
 				InternalState:                 o.InternalState.Current(),
 				PonPortID:                     int32(o.PonPortID),
-				PortNo:                        int32(o.PortNo),
-				Services:                      convertBBsimServicesToProtoServices(o.Services),
 				ImageSoftwareReceivedSections: int32(o.ImageSoftwareReceivedSections),
 				ImageSoftwareExpectedSections: int32(o.ImageSoftwareExpectedSections),
 				ActiveImageEntityId:           int32(o.ActiveImageEntityId),
@@ -71,8 +69,6 @@
 		OperState:     onu.OperState.Current(),
 		InternalState: onu.InternalState.Current(),
 		PonPortID:     int32(onu.PonPortID),
-		PortNo:        int32(onu.PortNo),
-		Services:      convertBBsimServicesToProtoServices(onu.Services),
 		Unis:          convertBBsimUniPortsToProtoUniPorts(onu.UniPorts),
 	}
 	return &res, nil
@@ -232,7 +228,8 @@
 
 func (s BBSimServer) ChangeIgmpState(ctx context.Context, req *bbsim.IgmpRequest) (*bbsim.Response, error) {
 
-	// TODO check that the ONU is enabled and the services are initialized before changing the state
+	// NOTE this API will change the IGMP state for all UNIs on the requested ONU
+	// TODO a new API needs to be created to individually manage the UNIs
 
 	res := &bbsim.Response{}
 
@@ -269,28 +266,48 @@
 		startedOn := []string{}
 		success := true
 
-		for _, s := range onu.Services {
-			service := s.(*devices.Service)
-			if service.NeedsIgmp {
-
-				logger.WithFields(log.Fields{
-					"OnuId":   onu.ID,
-					"IntfId":  onu.PonPortID,
-					"OnuSn":   onu.Sn(),
-					"Service": service.Name,
-				}).Debugf("Sending %s event on Service %s", event, service.Name)
-
-				if err := service.IGMPState.Event(event, types.IgmpMessage{GroupAddress: req.GroupAddress}); err != nil {
+		for _, u := range onu.UniPorts {
+			uni := u.(*devices.UniPort)
+			if !uni.OperState.Is(devices.UniStateUp) {
+				// if the UNI is disabled, ignore it
+				continue
+			}
+			for _, s := range uni.Services {
+				service := s.(*devices.Service)
+				serviceKey := fmt.Sprintf("uni[%d]%s", uni.ID, service.Name)
+				if service.NeedsIgmp {
+					if !service.InternalState.Is(devices.ServiceStateInitialized) {
+						logger.WithFields(log.Fields{
+							"OnuId":   onu.ID,
+							"UniId":   uni.ID,
+							"IntfId":  onu.PonPortID,
+							"OnuSn":   onu.Sn(),
+							"Service": service.Name,
+						}).Warn("service-not-initialized-skipping-event")
+						continue
+					}
 					logger.WithFields(log.Fields{
 						"OnuId":   onu.ID,
+						"UniId":   uni.ID,
 						"IntfId":  onu.PonPortID,
 						"OnuSn":   onu.Sn(),
 						"Service": service.Name,
-					}).Errorf("IGMP request failed: %s", err.Error())
-					errors = append(errors, fmt.Sprintf("%s: %s", service.Name, err.Error()))
-					success = false
+						"Uni":     uni.ID,
+					}).Debugf("Sending %s event on Service %s", event, service.Name)
+
+					if err := service.IGMPState.Event(event, types.IgmpMessage{GroupAddress: req.GroupAddress}); err != nil {
+						logger.WithFields(log.Fields{
+							"OnuId":   onu.ID,
+							"UniId":   uni.ID,
+							"IntfId":  onu.PonPortID,
+							"OnuSn":   onu.Sn(),
+							"Service": service.Name,
+						}).Errorf("IGMP request failed: %s", err.Error())
+						errors = append(errors, fmt.Sprintf("%s: %s", serviceKey, err.Error()))
+						success = false
+					}
+					startedOn = append(startedOn, serviceKey)
 				}
-				startedOn = append(startedOn, service.Name)
 			}
 		}
 
@@ -325,6 +342,9 @@
 }
 
 func (s BBSimServer) RestartEapol(ctx context.Context, req *bbsim.ONURequest) (*bbsim.Response, error) {
+	// NOTE this API will change the EAPOL state for all UNIs on the requested ONU
+	// TODO a new API needs to be created to individually manage the UNIs
+
 	res := &bbsim.Response{}
 
 	logger.WithFields(log.Fields{
@@ -345,20 +365,39 @@
 	startedOn := []string{}
 	success := true
 
-	for _, s := range onu.Services {
-		service := s.(*devices.Service)
-		if service.NeedsEapol {
-			if err := service.EapolState.Event("start_auth"); err != nil {
-				logger.WithFields(log.Fields{
-					"OnuId":   onu.ID,
-					"IntfId":  onu.PonPortID,
-					"OnuSn":   onu.Sn(),
-					"Service": service.Name,
-				}).Errorf("Cannot restart authenticaton for Service: %s", err.Error())
-				errors = append(errors, fmt.Sprintf("%s: %s", service.Name, err.Error()))
-				success = false
+	for _, u := range onu.UniPorts {
+		uni := u.(*devices.UniPort)
+		if !uni.OperState.Is(devices.UniStateUp) {
+			// if the UNI is disabled, ignore it
+			continue
+		}
+		for _, s := range uni.Services {
+			service := s.(*devices.Service)
+			serviceKey := fmt.Sprintf("uni[%d]%s", uni.ID, service.Name)
+			if service.NeedsEapol {
+				if !service.InternalState.Is(devices.ServiceStateInitialized) {
+					logger.WithFields(log.Fields{
+						"OnuId":   onu.ID,
+						"UniId":   uni.ID,
+						"IntfId":  onu.PonPortID,
+						"OnuSn":   onu.Sn(),
+						"Service": service.Name,
+					}).Warn("service-not-initialized-skipping-event")
+					continue
+				}
+				if err := service.EapolState.Event("start_auth"); err != nil {
+					logger.WithFields(log.Fields{
+						"OnuId":   onu.ID,
+						"IntfId":  onu.PonPortID,
+						"OnuSn":   onu.Sn(),
+						"UniId":   uni.ID,
+						"Service": service.Name,
+					}).Errorf("Cannot restart authenticaton for Service: %s", err.Error())
+					errors = append(errors, fmt.Sprintf("%s: %s", serviceKey, err.Error()))
+					success = false
+				}
+				startedOn = append(startedOn, serviceKey)
 			}
-			startedOn = append(startedOn, service.Name)
 		}
 	}
 
@@ -388,6 +427,9 @@
 }
 
 func (s BBSimServer) RestartDhcp(ctx context.Context, req *bbsim.ONURequest) (*bbsim.Response, error) {
+	// NOTE this API will change the DHCP state for all UNIs on the requested ONU
+	// TODO a new API needs to be created to individually manage the UNIs
+
 	res := &bbsim.Response{}
 
 	logger.WithFields(log.Fields{
@@ -408,21 +450,30 @@
 	startedOn := []string{}
 	success := true
 
-	for _, s := range onu.Services {
-		service := s.(*devices.Service)
-		if service.NeedsDhcp {
+	for _, u := range onu.UniPorts {
+		uni := u.(*devices.UniPort)
+		if !uni.OperState.Is(devices.UniStateUp) {
+			// if the UNI is disabled, ignore it
+			continue
+		}
+		for _, s := range uni.Services {
+			service := s.(*devices.Service)
+			serviceKey := fmt.Sprintf("uni[%d]%s", uni.ID, service.Name)
+			if service.NeedsDhcp {
 
-			if err := service.DHCPState.Event("start_dhcp"); err != nil {
-				logger.WithFields(log.Fields{
-					"OnuId":   onu.ID,
-					"IntfId":  onu.PonPortID,
-					"OnuSn":   onu.Sn(),
-					"Service": service.Name,
-				}).Errorf("Cannot restart DHCP for Service: %s", err.Error())
-				errors = append(errors, fmt.Sprintf("%s: %s", service.Name, err.Error()))
-				success = false
+				if err := service.DHCPState.Event("start_dhcp"); err != nil {
+					logger.WithFields(log.Fields{
+						"OnuId":   onu.ID,
+						"IntfId":  onu.PonPortID,
+						"OnuSn":   onu.Sn(),
+						"UniId":   uni.ID,
+						"Service": service.Name,
+					}).Errorf("Cannot restart DHCP for Service: %s", err.Error())
+					errors = append(errors, fmt.Sprintf("%s: %s", serviceKey, err.Error()))
+					success = false
+				}
+				startedOn = append(startedOn, serviceKey)
 			}
-			startedOn = append(startedOn, service.Name)
 		}
 	}