Add support to restart EAPOL and DHCP for a specific UNI

Change-Id: Idacb94e6a9e91221f2f008ec9b5d63210d74b7ce
diff --git a/api/bbsim/bbsim.pb.go b/api/bbsim/bbsim.pb.go
index 12cf16e..132c8b4 100644
--- a/api/bbsim/bbsim.pb.go
+++ b/api/bbsim/bbsim.pb.go
@@ -1804,7 +1804,7 @@
 func init() { proto.RegisterFile("api/bbsim/bbsim.proto", fileDescriptor_ef7750073d18011b) }
 
 var fileDescriptor_ef7750073d18011b = []byte{
-	// 2090 bytes of a gzipped FileDescriptorProto
+	// 2092 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0xcb, 0x6e, 0xdb, 0xd8,
 	0xf9, 0xb7, 0xee, 0xd2, 0x27, 0x4b, 0xa2, 0x4f, 0x62, 0x0f, 0xe1, 0x78, 0x26, 0x02, 0x27, 0xf3,
 	0x87, 0x13, 0xfc, 0xc7, 0x99, 0x24, 0xed, 0x34, 0x59, 0xd2, 0x92, 0xac, 0x70, 0x2c, 0x93, 0xc2,
@@ -1926,16 +1926,16 @@
 	0x0f, 0xfe, 0x9e, 0xc2, 0x72, 0x25, 0xd9, 0xb6, 0x73, 0x32, 0xc9, 0xd1, 0x79, 0x07, 0x3b, 0x29,
 	0x47, 0xbe, 0xe6, 0x8c, 0x35, 0x35, 0x76, 0x97, 0xdc, 0xa8, 0xf2, 0xdd, 0xc5, 0x73, 0xf8, 0xe0,
 	0x10, 0x5f, 0x41, 0x9b, 0xeb, 0x3c, 0x38, 0xc2, 0xb7, 0x20, 0x24, 0x6e, 0xbe, 0x2a, 0xc0, 0x5f,
-	0xc1, 0x36, 0x1f, 0x9a, 0xf0, 0xb1, 0xf0, 0xb0, 0x10, 0xdf, 0x40, 0x93, 0x6b, 0xb1, 0x37, 0xc4,
-	0xc3, 0x94, 0x8e, 0x61, 0x57, 0x67, 0xfd, 0x65, 0xe7, 0xa8, 0xe2, 0x98, 0xd6, 0xdc, 0xa0, 0x7b,
-	0x18, 0xda, 0x4b, 0xd4, 0xd3, 0x47, 0xec, 0x67, 0x6c, 0xd0, 0x55, 0x7f, 0x8f, 0x8d, 0xec, 0x31,
-	0x9d, 0x67, 0xe3, 0x47, 0xa8, 0x0f, 0x49, 0x10, 0xee, 0xdc, 0x39, 0x91, 0x47, 0xc5, 0x66, 0x02,
-	0xac, 0xb6, 0x9d, 0xde, 0xb5, 0xe1, 0x5c, 0x11, 0x7a, 0x08, 0x85, 0x2f, 0x0f, 0xc4, 0x45, 0x52,
-	0xc7, 0x52, 0x9e, 0xa3, 0x53, 0xf8, 0x26, 0x1c, 0xe8, 0xcd, 0xd7, 0x48, 0x8e, 0xdf, 0x27, 0x09,
-	0xb4, 0x21, 0x1f, 0x2f, 0x3d, 0xb6, 0x34, 0xf2, 0x97, 0x5e, 0xb8, 0x2a, 0x8e, 0x7f, 0xf8, 0xed,
-	0xf7, 0x57, 0x56, 0x70, 0xbd, 0xba, 0x38, 0x9a, 0xbb, 0x0b, 0xf6, 0x07, 0xf0, 0xdc, 0xf5, 0x4c,
-	0xfe, 0x8f, 0x71, 0xfc, 0xdf, 0xf1, 0x45, 0x95, 0xfd, 0xe1, 0xfb, 0xe6, 0xbf, 0x01, 0x00, 0x00,
-	0xff, 0xff, 0x64, 0x15, 0xb3, 0xe8, 0x4f, 0x16, 0x00, 0x00,
+	0xc1, 0x36, 0x1f, 0x9a, 0xf0, 0xb1, 0xf0, 0x99, 0x56, 0xa5, 0xb4, 0xde, 0x40, 0x93, 0x6b, 0xb1,
+	0x37, 0xc4, 0xc3, 0x94, 0x8e, 0x61, 0x57, 0x67, 0xfd, 0x65, 0xe7, 0xa8, 0xe2, 0x98, 0xd6, 0xdc,
+	0xa0, 0x7b, 0x18, 0xda, 0x4b, 0xca, 0x92, 0x3e, 0x62, 0x3f, 0x63, 0x83, 0xae, 0xfa, 0x7b, 0x6c,
+	0x64, 0x8f, 0xe9, 0x3c, 0x1b, 0x3f, 0x42, 0x7d, 0x48, 0x82, 0x70, 0xe7, 0xce, 0xe9, 0x48, 0x54,
+	0x6c, 0x26, 0xc0, 0x6a, 0xdb, 0xe9, 0x5d, 0x1b, 0xce, 0x15, 0xa1, 0x87, 0x50, 0xf8, 0xf2, 0x40,
+	0x5c, 0x24, 0x75, 0x2c, 0xe5, 0x39, 0x3a, 0x85, 0x6f, 0xc2, 0x81, 0xde, 0x7c, 0x8d, 0xe4, 0xf8,
+	0x7d, 0x92, 0x40, 0x1b, 0xf2, 0xf1, 0xd2, 0x63, 0x4b, 0x23, 0x7f, 0xe9, 0x85, 0xab, 0xe2, 0xf8,
+	0x87, 0xdf, 0x7e, 0x7f, 0x65, 0x05, 0xd7, 0xab, 0x8b, 0xa3, 0xb9, 0xbb, 0x60, 0x7f, 0x00, 0xcf,
+	0x5d, 0xcf, 0xe4, 0xff, 0x18, 0xc7, 0xff, 0x1d, 0x5f, 0x54, 0xd9, 0x1f, 0xbe, 0x6f, 0xfe, 0x1b,
+	0x00, 0x00, 0xff, 0xff, 0x2d, 0xf0, 0xfb, 0x77, 0x4f, 0x16, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -1990,9 +1990,9 @@
 	// Poweron all ONUs under a PON by pon-port-ID
 	PoweronONUsOnPON(ctx context.Context, in *PONRequest, opts ...grpc.CallOption) (*Response, error)
 	// Restart EAPOL for ONU
-	RestartEapol(ctx context.Context, in *ONURequest, opts ...grpc.CallOption) (*Response, error)
+	RestartEapol(ctx context.Context, in *UNIRequest, opts ...grpc.CallOption) (*Response, error)
 	// Restart DHCP for ONU
-	RestartDhcp(ctx context.Context, in *ONURequest, opts ...grpc.CallOption) (*Response, error)
+	RestartDhcp(ctx context.Context, in *UNIRequest, opts ...grpc.CallOption) (*Response, error)
 	// Send ONU alarm indication
 	SetOnuAlarmIndication(ctx context.Context, in *ONUAlarmRequest, opts ...grpc.CallOption) (*Response, error)
 	// Send OLT alarm indication for Interface type NNI or PON
@@ -2195,7 +2195,7 @@
 	return out, nil
 }
 
-func (c *bBSimClient) RestartEapol(ctx context.Context, in *ONURequest, opts ...grpc.CallOption) (*Response, error) {
+func (c *bBSimClient) RestartEapol(ctx context.Context, in *UNIRequest, opts ...grpc.CallOption) (*Response, error) {
 	out := new(Response)
 	err := c.cc.Invoke(ctx, "/bbsim.BBSim/RestartEapol", in, out, opts...)
 	if err != nil {
@@ -2204,7 +2204,7 @@
 	return out, nil
 }
 
-func (c *bBSimClient) RestartDhcp(ctx context.Context, in *ONURequest, opts ...grpc.CallOption) (*Response, error) {
+func (c *bBSimClient) RestartDhcp(ctx context.Context, in *UNIRequest, opts ...grpc.CallOption) (*Response, error) {
 	out := new(Response)
 	err := c.cc.Invoke(ctx, "/bbsim.BBSim/RestartDhcp", in, out, opts...)
 	if err != nil {
@@ -2309,9 +2309,9 @@
 	// Poweron all ONUs under a PON by pon-port-ID
 	PoweronONUsOnPON(context.Context, *PONRequest) (*Response, error)
 	// Restart EAPOL for ONU
-	RestartEapol(context.Context, *ONURequest) (*Response, error)
+	RestartEapol(context.Context, *UNIRequest) (*Response, error)
 	// Restart DHCP for ONU
-	RestartDhcp(context.Context, *ONURequest) (*Response, error)
+	RestartDhcp(context.Context, *UNIRequest) (*Response, error)
 	// Send ONU alarm indication
 	SetOnuAlarmIndication(context.Context, *ONUAlarmRequest) (*Response, error)
 	// Send OLT alarm indication for Interface type NNI or PON
@@ -2390,10 +2390,10 @@
 func (*UnimplementedBBSimServer) PoweronONUsOnPON(ctx context.Context, req *PONRequest) (*Response, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method PoweronONUsOnPON not implemented")
 }
-func (*UnimplementedBBSimServer) RestartEapol(ctx context.Context, req *ONURequest) (*Response, error) {
+func (*UnimplementedBBSimServer) RestartEapol(ctx context.Context, req *UNIRequest) (*Response, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method RestartEapol not implemented")
 }
-func (*UnimplementedBBSimServer) RestartDhcp(ctx context.Context, req *ONURequest) (*Response, error) {
+func (*UnimplementedBBSimServer) RestartDhcp(ctx context.Context, req *UNIRequest) (*Response, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method RestartDhcp not implemented")
 }
 func (*UnimplementedBBSimServer) SetOnuAlarmIndication(ctx context.Context, req *ONUAlarmRequest) (*Response, error) {
@@ -2780,7 +2780,7 @@
 }
 
 func _BBSim_RestartEapol_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(ONURequest)
+	in := new(UNIRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
@@ -2792,13 +2792,13 @@
 		FullMethod: "/bbsim.BBSim/RestartEapol",
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(BBSimServer).RestartEapol(ctx, req.(*ONURequest))
+		return srv.(BBSimServer).RestartEapol(ctx, req.(*UNIRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
 func _BBSim_RestartDhcp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(ONURequest)
+	in := new(UNIRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
@@ -2810,7 +2810,7 @@
 		FullMethod: "/bbsim.BBSim/RestartDhcp",
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(BBSimServer).RestartDhcp(ctx, req.(*ONURequest))
+		return srv.(BBSimServer).RestartDhcp(ctx, req.(*UNIRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
diff --git a/api/bbsim/bbsim.pb.gw.go b/api/bbsim/bbsim.pb.gw.go
index 1826d3b..1637fd2 100644
--- a/api/bbsim/bbsim.pb.gw.go
+++ b/api/bbsim/bbsim.pb.gw.go
@@ -427,8 +427,12 @@
 
 }
 
+var (
+	filter_BBSim_RestartEapol_0 = &utilities.DoubleArray{Encoding: map[string]int{"OnuSerialNumber": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
+)
+
 func request_BBSim_RestartEapol_0(ctx context.Context, marshaler runtime.Marshaler, client BBSimClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
-	var protoReq ONURequest
+	var protoReq UNIRequest
 	var metadata runtime.ServerMetadata
 
 	var (
@@ -438,15 +442,22 @@
 		_   = err
 	)
 
-	val, ok = pathParams["SerialNumber"]
+	val, ok = pathParams["OnuSerialNumber"]
 	if !ok {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "SerialNumber")
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "OnuSerialNumber")
 	}
 
-	protoReq.SerialNumber, err = runtime.String(val)
+	protoReq.OnuSerialNumber, err = runtime.String(val)
 
 	if err != nil {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "SerialNumber", err)
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "OnuSerialNumber", err)
+	}
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_BBSim_RestartEapol_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
 	}
 
 	msg, err := client.RestartEapol(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
@@ -455,7 +466,7 @@
 }
 
 func local_request_BBSim_RestartEapol_0(ctx context.Context, marshaler runtime.Marshaler, server BBSimServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
-	var protoReq ONURequest
+	var protoReq UNIRequest
 	var metadata runtime.ServerMetadata
 
 	var (
@@ -465,15 +476,19 @@
 		_   = err
 	)
 
-	val, ok = pathParams["SerialNumber"]
+	val, ok = pathParams["OnuSerialNumber"]
 	if !ok {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "SerialNumber")
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "OnuSerialNumber")
 	}
 
-	protoReq.SerialNumber, err = runtime.String(val)
+	protoReq.OnuSerialNumber, err = runtime.String(val)
 
 	if err != nil {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "SerialNumber", err)
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "OnuSerialNumber", err)
+	}
+
+	if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_BBSim_RestartEapol_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
 	}
 
 	msg, err := server.RestartEapol(ctx, &protoReq)
@@ -481,8 +496,12 @@
 
 }
 
+var (
+	filter_BBSim_RestartDhcp_0 = &utilities.DoubleArray{Encoding: map[string]int{"OnuSerialNumber": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
+)
+
 func request_BBSim_RestartDhcp_0(ctx context.Context, marshaler runtime.Marshaler, client BBSimClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
-	var protoReq ONURequest
+	var protoReq UNIRequest
 	var metadata runtime.ServerMetadata
 
 	var (
@@ -492,15 +511,22 @@
 		_   = err
 	)
 
-	val, ok = pathParams["SerialNumber"]
+	val, ok = pathParams["OnuSerialNumber"]
 	if !ok {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "SerialNumber")
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "OnuSerialNumber")
 	}
 
-	protoReq.SerialNumber, err = runtime.String(val)
+	protoReq.OnuSerialNumber, err = runtime.String(val)
 
 	if err != nil {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "SerialNumber", err)
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "OnuSerialNumber", err)
+	}
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_BBSim_RestartDhcp_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
 	}
 
 	msg, err := client.RestartDhcp(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
@@ -509,7 +535,7 @@
 }
 
 func local_request_BBSim_RestartDhcp_0(ctx context.Context, marshaler runtime.Marshaler, server BBSimServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
-	var protoReq ONURequest
+	var protoReq UNIRequest
 	var metadata runtime.ServerMetadata
 
 	var (
@@ -519,15 +545,19 @@
 		_   = err
 	)
 
-	val, ok = pathParams["SerialNumber"]
+	val, ok = pathParams["OnuSerialNumber"]
 	if !ok {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "SerialNumber")
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "OnuSerialNumber")
 	}
 
-	protoReq.SerialNumber, err = runtime.String(val)
+	protoReq.OnuSerialNumber, err = runtime.String(val)
 
 	if err != nil {
-		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "SerialNumber", err)
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "OnuSerialNumber", err)
+	}
+
+	if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_BBSim_RestartDhcp_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
 	}
 
 	msg, err := server.RestartDhcp(ctx, &protoReq)
@@ -1880,9 +1910,9 @@
 
 	pattern_BBSim_PoweronONUsOnPON_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"v1", "olt", "port", "PonPortId", "onus"}, "", runtime.AssumeColonVerbOpt(true)))
 
-	pattern_BBSim_RestartEapol_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"v1", "olt", "onus", "SerialNumber", "eapol"}, "", runtime.AssumeColonVerbOpt(true)))
+	pattern_BBSim_RestartEapol_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"v1", "olt", "onus", "OnuSerialNumber", "eapol"}, "", runtime.AssumeColonVerbOpt(true)))
 
-	pattern_BBSim_RestartDhcp_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"v1", "olt", "onus", "SerialNumber", "dhcp"}, "", runtime.AssumeColonVerbOpt(true)))
+	pattern_BBSim_RestartDhcp_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"v1", "olt", "onus", "OnuSerialNumber", "dhcp"}, "", runtime.AssumeColonVerbOpt(true)))
 
 	pattern_BBSim_SetOnuAlarmIndication_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6}, []string{"v1", "olt", "onus", "SerialNumber", "alarms", "AlarmType", "Status"}, "", runtime.AssumeColonVerbOpt(true)))
 
diff --git a/api/bbsim/bbsim.proto b/api/bbsim/bbsim.proto
index 64bd659..c84cd77 100644
--- a/api/bbsim/bbsim.proto
+++ b/api/bbsim/bbsim.proto
@@ -316,10 +316,10 @@
     }
 
     // Restart EAPOL for ONU
-    rpc RestartEapol (ONURequest) returns (Response) {
+    rpc RestartEapol (UNIRequest) returns (Response) {
     }
     // Restart DHCP for ONU
-    rpc RestartDhcp (ONURequest) returns (Response) {
+    rpc RestartDhcp (UNIRequest) returns (Response) {
     }
     // Send ONU alarm indication
     rpc SetOnuAlarmIndication (ONUAlarmRequest) returns (Response) {
diff --git a/api/bbsim/bbsim.yaml b/api/bbsim/bbsim.yaml
index aacc160..972c21f 100644
--- a/api/bbsim/bbsim.yaml
+++ b/api/bbsim/bbsim.yaml
@@ -53,9 +53,9 @@
     get: "/v1/olt/onus/{SerialNumber}/trafficschedulers"
 
   - selector: bbsim.BBSim.RestartEapol
-    post: "/v1/olt/onus/{SerialNumber}/eapol"
+    post: "/v1/olt/onus/{OnuSerialNumber}/eapol"
   - selector: bbsim.BBSim.RestartDhcp
-    post: "/v1/olt/onus/{SerialNumber}/dhcp"
+    post: "/v1/olt/onus/{OnuSerialNumber}/dhcp"
   - selector: bbsim.BBSim.ChangeIgmpState
     post: "/v1/olt/onus/{OnuReq.SerialNumber}/igmp/{SubActionVal}/{GroupAddress}"
 
diff --git a/internal/bbsim/api/onus_handler.go b/internal/bbsim/api/onus_handler.go
index 4ea60e7..91a107d 100644
--- a/internal/bbsim/api/onus_handler.go
+++ b/internal/bbsim/api/onus_handler.go
@@ -21,6 +21,7 @@
 	"fmt"
 	"github.com/opencord/bbsim/internal/bbsim/types"
 	"github.com/opencord/voltha-protos/v4/go/openolt"
+	"strconv"
 
 	"github.com/opencord/bbsim/api/bbsim"
 	"github.com/opencord/bbsim/internal/bbsim/devices"
@@ -341,19 +342,26 @@
 	return res, nil
 }
 
-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
+func (s BBSimServer) RestartEapol(ctx context.Context, req *bbsim.UNIRequest) (*bbsim.Response, error) {
+	// NOTE this API will change the EAPOL state for all UNIs on the requested ONU if no UNI is specified
+	// Otherwise, it will change the EAPOL state for only the specified UNI on the ONU
 
 	res := &bbsim.Response{}
 
-	logger.WithFields(log.Fields{
-		"OnuSn": req.SerialNumber,
-	}).Infof("Received request to restart authentication ONU")
+	if req.UniID == "" {
+		logger.WithFields(log.Fields{
+			"OnuSn": req.OnuSerialNumber,
+		}).Infof("Received request to restart authentication for all UNIs on the ONU")
+	} else {
+		logger.WithFields(log.Fields{
+			"OnuSn": req.OnuSerialNumber,
+			"UniId": req.UniID,
+		}).Infof("Received request to restart authentication on UNI")
+	}
 
 	olt := devices.GetOLT()
 
-	onu, err := olt.FindOnuBySn(req.SerialNumber)
+	onu, err := olt.FindOnuBySn(req.OnuSerialNumber)
 
 	if err != nil {
 		res.StatusCode = int32(codes.NotFound)
@@ -365,8 +373,13 @@
 	startedOn := []string{}
 	success := true
 
+	uniIDint, err := strconv.Atoi(req.UniID)
 	for _, u := range onu.UniPorts {
 		uni := u.(*devices.UniPort)
+		//if a specific uni is specified, only restart it
+		if err == nil && req.UniID != "" && uni.ID != uint32(uniIDint) {
+			continue
+		}
 		if !uni.OperState.Is(devices.UniStateUp) {
 			// if the UNI is disabled, ignore it
 			continue
@@ -410,35 +423,65 @@
 			res.Message = "No service requires EAPOL"
 		}
 
-		logger.WithFields(log.Fields{
-			"OnuSn":   req.SerialNumber,
-			"Message": res.Message,
-		}).Info("Processed EAPOL restart request for ONU")
+		if req.UniID == "" {
+			logger.WithFields(log.Fields{
+				"OnuSn":   req.OnuSerialNumber,
+				"Message": res.Message,
+			}).Info("Processed EAPOL restart request for all UNIs on the ONU")
+		} else {
+			logger.WithFields(log.Fields{
+				"OnuSn":   req.OnuSerialNumber,
+				"Message": res.Message,
+				"UniId":   req.UniID,
+			}).Info("Processed EAPOL restart request on UNI")
+		}
+
 	} else {
 		res.StatusCode = int32(codes.FailedPrecondition)
 		res.Message = fmt.Sprintf("%v", errors)
 		logger.WithFields(log.Fields{
-			"OnuSn":   req.SerialNumber,
+			"OnuSn":   req.OnuSerialNumber,
 			"Message": res.Message,
-		}).Error("Error while processing DHCP restart request for ONU")
+		}).Error("Error while processing EAPOL restart request for ONU")
+
+		if req.UniID == "" {
+			logger.WithFields(log.Fields{
+				"OnuSn":   req.OnuSerialNumber,
+				"Message": res.Message,
+			}).Error("Error while processing EAPOL restart request for all UNIs on the ONU")
+		} else {
+			logger.WithFields(log.Fields{
+				"OnuSn":   req.OnuSerialNumber,
+				"Message": res.Message,
+				"UniId":   req.UniID,
+			}).Error("Error while processing EAPOL restart request on UNI")
+		}
+
 	}
 
 	return res, nil
 }
 
-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
+func (s BBSimServer) RestartDhcp(ctx context.Context, req *bbsim.UNIRequest) (*bbsim.Response, error) {
+	// NOTE this API will change the DHCP state for all UNIs on the requested ONU if no UNI is specified
+	// Otherwise, it will change the DHCP state for only the specified UNI on the ONU
 
 	res := &bbsim.Response{}
 
-	logger.WithFields(log.Fields{
-		"OnuSn": req.SerialNumber,
-	}).Infof("Received request to restart DHCP on ONU")
+	if req.UniID == "" {
+		logger.WithFields(log.Fields{
+			"OnuSn": req.OnuSerialNumber,
+		}).Infof("Received request to restart authentication for all UNIs on the ONU")
+	} else {
+		logger.WithFields(log.Fields{
+			"OnuSn": req.OnuSerialNumber,
+			"UniId": req.UniID,
+		}).Infof("Received request to restart authentication on UNI")
+	}
 
 	olt := devices.GetOLT()
 
-	onu, err := olt.FindOnuBySn(req.SerialNumber)
+	onu, err := olt.FindOnuBySn(req.OnuSerialNumber)
 
 	if err != nil {
 		res.StatusCode = int32(codes.NotFound)
@@ -450,8 +493,13 @@
 	startedOn := []string{}
 	success := true
 
+	uniIDint, err := strconv.Atoi(req.UniID)
 	for _, u := range onu.UniPorts {
 		uni := u.(*devices.UniPort)
+		//if a specific uni is specified, only restart it
+		if err == nil && req.UniID != "" && uni.ID != uint32(uniIDint) {
+			continue
+		}
 		if !uni.OperState.Is(devices.UniStateUp) {
 			// if the UNI is disabled, ignore it
 			continue
@@ -460,7 +508,6 @@
 			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,
@@ -486,17 +533,35 @@
 		} else {
 			res.Message = "No service requires DHCP"
 		}
-		logger.WithFields(log.Fields{
-			"OnuSn":   req.SerialNumber,
-			"Message": res.Message,
-		}).Info("Processed DHCP restart request for ONU")
+
+		if req.UniID == "" {
+			logger.WithFields(log.Fields{
+				"OnuSn":   req.OnuSerialNumber,
+				"Message": res.Message,
+			}).Info("Processed DHCP restart request for all UNIs on the ONU")
+		} else {
+			logger.WithFields(log.Fields{
+				"OnuSn":   req.OnuSerialNumber,
+				"Message": res.Message,
+				"UniId":   req.UniID,
+			}).Info("Processed DHCP restart request on UNI")
+		}
 	} else {
 		res.StatusCode = int32(codes.FailedPrecondition)
 		res.Message = fmt.Sprintf("%v", errors)
-		logger.WithFields(log.Fields{
-			"OnuSn":   req.SerialNumber,
-			"Message": res.Message,
-		}).Error("Error while processing DHCP restart request for ONU")
+
+		if req.UniID == "" {
+			logger.WithFields(log.Fields{
+				"OnuSn":   req.OnuSerialNumber,
+				"Message": res.Message,
+			}).Error("Error while processing DHCP restart request for all UNIs on the ONU")
+		} else {
+			logger.WithFields(log.Fields{
+				"OnuSn":   req.OnuSerialNumber,
+				"Message": res.Message,
+				"UniId":   req.UniID,
+			}).Error("Error while processing DHCP restart request on UNI")
+		}
 	}
 
 	return res, nil
diff --git a/internal/bbsimctl/commands/onu.go b/internal/bbsimctl/commands/onu.go
index 4ae8841..f929f39 100644
--- a/internal/bbsimctl/commands/onu.go
+++ b/internal/bbsimctl/commands/onu.go
@@ -245,8 +245,9 @@
 
 	ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
 	defer cancel()
-	req := pb.ONURequest{
-		SerialNumber: string(options.Args.OnuSn),
+	req := pb.UNIRequest{
+		OnuSerialNumber: string(options.Args.OnuSn),
+		UniID:           "",
 	}
 	res, err := client.RestartEapol(ctx, &req)
 
@@ -266,8 +267,9 @@
 
 	ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
 	defer cancel()
-	req := pb.ONURequest{
-		SerialNumber: string(options.Args.OnuSn),
+	req := pb.UNIRequest{
+		OnuSerialNumber: string(options.Args.OnuSn),
+		UniID:           "",
 	}
 	res, err := client.RestartDhcp(ctx, &req)
 
diff --git a/internal/bbsimctl/commands/uni.go b/internal/bbsimctl/commands/uni.go
index 81f7c96..b025e72 100644
--- a/internal/bbsimctl/commands/uni.go
+++ b/internal/bbsimctl/commands/uni.go
@@ -19,6 +19,7 @@
 
 import (
 	"context"
+	"fmt"
 	"os"
 	"strconv"
 	"strings"
@@ -56,10 +57,26 @@
 	} `positional-args:"yes"`
 }
 
+type UNIEapolRestart struct {
+	Args struct {
+		OnuSn OnuSnString
+		UniId UniIdInt
+	} `positional-args:"yes" required:"yes"`
+}
+
+type UNIDhcpRestart struct {
+	Args struct {
+		OnuSn OnuSnString
+		UniId UniIdInt
+	} `positional-args:"yes" required:"yes"`
+}
+
 type UNIOptions struct {
-	List     UNIList     `command:"list"`
-	Get      UNIGet      `command:"get"`
-	Services UNIServices `command:"services"`
+	List         UNIList         `command:"list"`
+	Get          UNIGet          `command:"get"`
+	Services     UNIServices     `command:"services"`
+	RestartEapol UNIEapolRestart `command:"auth_restart"`
+	RestartDchp  UNIDhcpRestart  `command:"dhcp_restart"`
 }
 
 func RegisterUNICommands(parser *flags.Parser) {
@@ -142,6 +159,52 @@
 	return nil
 }
 
+func (options *UNIEapolRestart) Execute(args []string) error {
+	client, conn := connect()
+	defer conn.Close()
+
+	ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	req := pb.UNIRequest{
+		OnuSerialNumber: string(options.Args.OnuSn),
+		UniID:           string(options.Args.UniId),
+	}
+	res, err := client.RestartEapol(ctx, &req)
+
+	if err != nil {
+		log.Fatalf("Cannot restart EAPOL for ONU %s and UNI %s: %v", options.Args.OnuSn, options.Args.UniId, err)
+		return err
+	}
+
+	fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+
+	return nil
+}
+
+func (options *UNIDhcpRestart) Execute(args []string) error {
+	client, conn := connect()
+	defer conn.Close()
+
+	ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	req := pb.UNIRequest{
+		OnuSerialNumber: string(options.Args.OnuSn),
+		UniID:           string(options.Args.UniId),
+	}
+	res, err := client.RestartDhcp(ctx, &req)
+
+	if err != nil {
+		log.Fatalf("Cannot restart DHCP for ONU %s and UNI %s: %v", options.Args.OnuSn, options.Args.UniId, err)
+		return err
+	}
+
+	fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+
+	return nil
+}
+
 func (uniId *UniIdInt) Complete(match string) []flags.Completion {
 	client, conn := connect()
 	defer conn.Close()