[VOL-2176] Adding -no-auth and -no-dhcp option in BBSim CLI

Change-Id: I3d0f7adb26c1df0d42f67b9b8fbc782754491c31
diff --git a/VERSION b/VERSION
index ccf3e96..bbdeab6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.0.5-dev
+0.0.5
diff --git a/cmd/bbr/bbr.go b/cmd/bbr/bbr.go
index a98decd..4f4c590 100644
--- a/cmd/bbr/bbr.go
+++ b/cmd/bbr/bbr.go
@@ -70,7 +70,19 @@
 	apiDoneChannel := make(chan bool)
 
 	// create the OLT device
-	olt := devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon, options.STag, options.CTagInit, &oltDoneChannel, &apiDoneChannel, true)
+	olt := devices.CreateOLT(
+		options.OltID,
+		options.NumNniPerOlt,
+		options.NumPonPerOlt,
+		options.NumOnuPerPon,
+		options.STag,
+		options.CTagInit,
+		&oltDoneChannel,
+		&apiDoneChannel,
+		true, // this parameter is not important in the BBR Case
+		true, // this parameter is not important in the BBR Case
+		true,
+	)
 	oltMock := bbrdevices.OltMock{
 		Olt:           olt,
 		TargetOnus:    options.NumPonPerOlt * options.NumOnuPerPon,
diff --git a/cmd/bbsim/bbsim.go b/cmd/bbsim/bbsim.go
index d8cfcf0..e7eef11 100644
--- a/cmd/bbsim/bbsim.go
+++ b/cmd/bbsim/bbsim.go
@@ -151,6 +151,8 @@
 		"NumPonPerOlt": options.NumPonPerOlt,
 		"NumOnuPerPon": options.NumOnuPerPon,
 		"TotalOnus":    options.NumPonPerOlt * options.NumOnuPerPon,
+		"Auth":         options.Auth,
+		"Dhcp":         options.Dhcp,
 	}).Info("BroadBand Simulator is on")
 
 	// control channels, they are only closed when the goroutine needs to be terminated
@@ -171,7 +173,19 @@
 	wg := sync.WaitGroup{}
 	wg.Add(5)
 
-	olt := devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon, options.STag, options.CTagInit, &oltDoneChannel, &apiDoneChannel, false)
+	olt := devices.CreateOLT(
+		options.OltID,
+		options.NumNniPerOlt,
+		options.NumPonPerOlt,
+		options.NumOnuPerPon,
+		options.STag,
+		options.CTagInit,
+		&oltDoneChannel,
+		&apiDoneChannel,
+		options.Auth,
+		options.Dhcp,
+		false,
+	)
 	go devices.StartOlt(olt, &wg)
 	log.Debugf("Created OLT with id: %d", options.OltID)
 	go startApiServer(apiDoneChannel, &wg)
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 9acc4d5..585de35 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -41,12 +41,24 @@
 
     helm install -n bbsim cord/bbsim
 
+If you need to specify a custom image for BBSim you can:
+
+.. code:: bash
+
+    helm install -n bbsim cord/bbsim --set images.bbsim.repository=bbsim --set images.bbsim.tag=candidate --set images.bbsim.pullPolicy=Never
+
 The BBSim installation can be customized to emulate multiple ONUs and multiple PON Ports:
 
 .. code:: bash
 
     helm install -n bbsim cord/bbsim --set onu=8 --set pon=2
 
+BBSim can also be configured to automatically start Authentication or DHCP:
+
+.. code:: bash
+
+   helm install -n bbsim cord/bbsim --set auth=true --set dhcp=true
+
 Once BBSim is installed you can verify that it's running with:
 
 .. code:: bash
@@ -67,3 +79,35 @@
 .. code:: bash
 
     voltctl device enable $(voltctl device list --filter Type~openolt -q)
+
+BBSim startup options
+---------------------
+
+``BBSim`` supports a series of options that can be set at startup, you can see the list via ``./bbsim --help``
+
+.. code:: bash
+
+   $ ./bbsim --help
+   Usage of ./bbsim:
+     -auth
+           Set this flag if you want authentication to start automatically
+     -c_tag int
+           C-Tag starting value, each ONU will get a sequential one (targeting 1024 ONUs per BBSim instance the range is big enough) (default 900)
+     -cpuprofile string
+           write cpu profile to file
+     -dhcp
+           Set this flag if you want DHCP to start automatically
+     -logCaller
+           Whether to print the caller filename or not
+     -logLevel string
+           Set the log level (trace, debug, info, warn, error) (default "debug")
+     -nni int
+           Number of NNI ports per OLT device to be emulated (default 1)
+     -olt_id int
+           Number of OLT devices to be emulated
+     -onu int
+           Number of ONU devices per PON port to be emulated (default 1)
+     -pon int
+           Number of PON ports per OLT device to be emulated (default 1)
+     -s_tag int
+           S-Tag value (default 900)
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index 2a31eb2..c374d4a 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -65,7 +65,7 @@
 	return &olt
 }
 
-func CreateOLT(oltId int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, oltDoneChannel *chan bool, apiDoneChannel *chan bool, isMock bool) *OltDevice {
+func CreateOLT(oltId int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, oltDoneChannel *chan bool, apiDoneChannel *chan bool, auth bool, dhcp bool, isMock bool) *OltDevice {
 	oltLogger.WithFields(log.Fields{
 		"ID":           oltId,
 		"NumNni":       nni,
@@ -133,7 +133,7 @@
 
 		// create ONU devices
 		for j := 0; j < onuPerPon; j++ {
-			o := CreateONU(olt, p, uint32(j+1), sTag, availableCTag)
+			o := CreateONU(olt, p, uint32(j+1), sTag, availableCTag, auth, dhcp)
 			p.Onus = append(p.Onus, o)
 			availableCTag = availableCTag + 1
 		}
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index cc467b8..564c21e 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -44,6 +44,8 @@
 	PonPort   PonPort
 	STag      int
 	CTag      int
+	Auth      bool // automatically start EAPOL if set to true
+	Dhcp      bool // automatically start DHCP if set to true
 	// PortNo comes with flows and it's used when sending packetIndications,
 	// There is one PortNo per UNI Port, for now we're only storing the first one
 	// FIXME add support for multiple UNIs
@@ -72,7 +74,7 @@
 	return common.OnuSnToString(o.SerialNumber)
 }
 
-func CreateONU(olt OltDevice, pon PonPort, id uint32, sTag int, cTag int) *Onu {
+func CreateONU(olt OltDevice, pon PonPort, id uint32, sTag int, cTag int, auth bool, dhcp bool) *Onu {
 
 	o := Onu{
 		ID:               id,
@@ -80,6 +82,8 @@
 		PonPort:          pon,
 		STag:             sTag,
 		CTag:             cTag,
+		Auth:             auth,
+		Dhcp:             dhcp,
 		HwAddress:        net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(pon.ID), byte(id)},
 		PortNo:           0,
 		Channel:          make(chan Message, 2048),
@@ -511,9 +515,19 @@
 				log.Warnf("Can't go to eapol_flow_received: %v", err)
 			}
 		} else if o.InternalState.Is("gem_port_added") {
-			if err := o.InternalState.Event("start_auth"); err != nil {
-				log.Warnf("Can't go to auth_started: %v", err)
+
+			if o.Auth == true {
+				if err := o.InternalState.Event("start_auth"); err != nil {
+					log.Warnf("Can't go to auth_started: %v", err)
+				}
+			} else {
+				onuLogger.WithFields(log.Fields{
+					"IntfId":       o.PonPortID,
+					"OnuId":        o.ID,
+					"SerialNumber": o.Sn(),
+				}).Warn("Not starting authentication as Auth bit is not set in CLI parameters")
 			}
+
 		}
 	} else if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeIPv4) &&
 		msg.Flow.Classifier.SrcPort == uint32(68) &&
@@ -521,9 +535,18 @@
 
 		// keep track that we reveived the DHCP Flows so that we can transition the state to dhcp_started
 		o.DhcpFlowReceived = true
-		// NOTE we are receiving mulitple DHCP flows but we shouldn't call the transition multiple times
-		if err := o.InternalState.Event("start_dhcp"); err != nil {
-			log.Errorf("Can't go to dhcp_started: %v", err)
+
+		if o.Dhcp == true {
+			// NOTE we are receiving mulitple DHCP flows but we shouldn't call the transition multiple times
+			if err := o.InternalState.Event("start_dhcp"); err != nil {
+				log.Errorf("Can't go to dhcp_started: %v", err)
+			}
+		} else {
+			onuLogger.WithFields(log.Fields{
+				"IntfId":       o.PonPortID,
+				"OnuId":        o.ID,
+				"SerialNumber": o.Sn(),
+			}).Warn("Not starting DHCP as Dhcp bit is not set in CLI parameters")
 		}
 	}
 }
diff --git a/internal/bbsim/devices/onu_flow_test.go b/internal/bbsim/devices/onu_flow_test.go
index 4194aa5..fad206c 100644
--- a/internal/bbsim/devices/onu_flow_test.go
+++ b/internal/bbsim/devices/onu_flow_test.go
@@ -25,7 +25,7 @@
 )
 
 func Test_Onu_SendEapolFlow(t *testing.T) {
-	onu := createMockOnu(1, 1, 900, 900)
+	onu := createMockOnu(1, 1, 900, 900, false, false)
 
 	client := &mockClient{
 		FlowAddSpy: FlowAddSpy{
@@ -49,7 +49,7 @@
 // it transition to auth_started state
 func Test_HandleFlowUpdateEapolFromGem(t *testing.T) {
 
-	onu := createMockOnu(1, 1, 900, 900)
+	onu := createMockOnu(1, 1, 900, 900, true, false)
 
 	onu.InternalState = fsm.NewFSM(
 		"gem_port_added",
@@ -90,7 +90,7 @@
 // no action is taken
 func Test_HandleFlowUpdateEapolFromGemIgnore(t *testing.T) {
 
-	onu := createMockOnu(1, 1, 900, 900)
+	onu := createMockOnu(1, 1, 900, 900, false, false)
 
 	onu.InternalState = fsm.NewFSM(
 		"gem_port_added",
@@ -131,7 +131,7 @@
 // it transition to auth_started state
 func Test_HandleFlowUpdateEapolFromEnabled(t *testing.T) {
 
-	onu := createMockOnu(1, 1, 900, 900)
+	onu := createMockOnu(1, 1, 900, 900, false, false)
 
 	onu.InternalState = fsm.NewFSM(
 		"enabled",
@@ -172,7 +172,7 @@
 // no action is taken
 func Test_HandleFlowUpdateEapolFromEnabledIgnore(t *testing.T) {
 
-	onu := createMockOnu(1, 1, 900, 900)
+	onu := createMockOnu(1, 1, 900, 900, false, false)
 
 	onu.InternalState = fsm.NewFSM(
 		"enabled",
@@ -209,4 +209,122 @@
 	assert.Equal(t, onu.InternalState.Current(), "enabled")
 }
 
-// TODO add tests for DHCP flow
+// validates that when an ONU receives an EAPOL flow for UNI 0
+// but the noAuth bit is set no action is taken
+func Test_HandleFlowUpdateEapolNoAuth(t *testing.T) {
+	onu := createMockOnu(1, 1, 900, 900, false, false)
+
+	onu.InternalState = fsm.NewFSM(
+		"gem_port_added",
+		fsm.Events{
+			{Name: "start_auth", Src: []string{"eapol_flow_received", "gem_port_added"}, Dst: "auth_started"},
+		},
+		fsm.Callbacks{},
+	)
+
+	flow := openolt.Flow{
+		AccessIntfId:  int32(onu.PonPortID),
+		OnuId:         int32(onu.ID),
+		UniId:         int32(0),
+		FlowId:        uint32(onu.ID),
+		FlowType:      "downstream",
+		AllocId:       int32(0),
+		NetworkIntfId: int32(0),
+		Classifier: &openolt.Classifier{
+			EthType: uint32(layers.EthernetTypeEAPOL),
+			OVid:    4091,
+		},
+		Action:   &openolt.Action{},
+		Priority: int32(100),
+		PortNo:   uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
+	}
+
+	msg := OnuFlowUpdateMessage{
+		PonPortID: 1,
+		OnuID:     1,
+		Flow:      &flow,
+	}
+
+	onu.handleFlowUpdate(msg)
+	assert.Equal(t, onu.InternalState.Current(), "gem_port_added")
+}
+
+func Test_HandleFlowUpdateDhcp(t *testing.T) {
+	onu := createMockOnu(1, 1, 900, 900, false, true)
+
+	onu.InternalState = fsm.NewFSM(
+		"eap_response_success_received",
+		fsm.Events{
+			{Name: "start_dhcp", Src: []string{"eap_response_success_received"}, Dst: "dhcp_started"},
+		},
+		fsm.Callbacks{},
+	)
+
+	flow := openolt.Flow{
+		AccessIntfId:  int32(onu.PonPortID),
+		OnuId:         int32(onu.ID),
+		UniId:         int32(0),
+		FlowId:        uint32(onu.ID),
+		FlowType:      "downstream",
+		AllocId:       int32(0),
+		NetworkIntfId: int32(0),
+		Classifier: &openolt.Classifier{
+			EthType: uint32(layers.EthernetTypeIPv4),
+			SrcPort: uint32(68),
+			DstPort: uint32(67),
+		},
+		Action:   &openolt.Action{},
+		Priority: int32(100),
+		PortNo:   uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
+	}
+
+	msg := OnuFlowUpdateMessage{
+		PonPortID: 1,
+		OnuID:     1,
+		Flow:      &flow,
+	}
+
+	onu.handleFlowUpdate(msg)
+	assert.Equal(t, onu.InternalState.Current(), "dhcp_started")
+	assert.Equal(t, onu.DhcpFlowReceived, true)
+}
+
+func Test_HandleFlowUpdateDhcpNoDhcp(t *testing.T) {
+	onu := createMockOnu(1, 1, 900, 900, false, false)
+
+	onu.InternalState = fsm.NewFSM(
+		"eap_response_success_received",
+		fsm.Events{
+			{Name: "start_dhcp", Src: []string{"eap_response_success_received"}, Dst: "dhcp_started"},
+		},
+		fsm.Callbacks{},
+	)
+
+	flow := openolt.Flow{
+		AccessIntfId:  int32(onu.PonPortID),
+		OnuId:         int32(onu.ID),
+		UniId:         int32(0),
+		FlowId:        uint32(onu.ID),
+		FlowType:      "downstream",
+		AllocId:       int32(0),
+		NetworkIntfId: int32(0),
+		Classifier: &openolt.Classifier{
+			EthType: uint32(layers.EthernetTypeIPv4),
+			SrcPort: uint32(68),
+			DstPort: uint32(67),
+		},
+		Action:   &openolt.Action{},
+		Priority: int32(100),
+		PortNo:   uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
+	}
+
+	msg := OnuFlowUpdateMessage{
+		PonPortID: 1,
+		OnuID:     1,
+		Flow:      &flow,
+	}
+
+	onu.handleFlowUpdate(msg)
+	assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
+	assert.Equal(t, onu.DhcpFlowReceived, true)
+}
diff --git a/internal/bbsim/devices/onu_state_machine_test.go b/internal/bbsim/devices/onu_state_machine_test.go
index 239869b..c8ad84c 100644
--- a/internal/bbsim/devices/onu_state_machine_test.go
+++ b/internal/bbsim/devices/onu_state_machine_test.go
@@ -22,13 +22,7 @@
 )
 
 func Test_Onu_StateMachine_enable(t *testing.T) {
-	olt := OltDevice{
-		ID: 0,
-	}
-	pon := PonPort{
-		ID: 1,
-	}
-	onu := CreateONU(olt, pon, 1, 900, 900)
+	onu := createTestOnu()
 
 	assert.Equal(t, onu.InternalState.Current(), "created")
 	onu.InternalState.Event("discover")
@@ -38,13 +32,7 @@
 }
 
 func Test_Onu_StateMachine_eapol_start_eap_flow(t *testing.T) {
-	olt := OltDevice{
-		ID: 0,
-	}
-	pon := PonPort{
-		ID: 1,
-	}
-	onu := CreateONU(olt, pon, 1, 900, 900)
+	onu := createTestOnu()
 
 	onu.InternalState.SetState("enabled")
 
@@ -62,13 +50,7 @@
 }
 
 func Test_Onu_StateMachine_eapol_start_gem_port(t *testing.T) {
-	olt := OltDevice{
-		ID: 0,
-	}
-	pon := PonPort{
-		ID: 1,
-	}
-	onu := CreateONU(olt, pon, 1, 900, 900)
+	onu := createTestOnu()
 
 	onu.InternalState.SetState("enabled")
 
@@ -86,13 +68,7 @@
 }
 
 func Test_Onu_StateMachine_eapol_states(t *testing.T) {
-	olt := OltDevice{
-		ID: 0,
-	}
-	pon := PonPort{
-		ID: 1,
-	}
-	onu := CreateONU(olt, pon, 1, 900, 900)
+	onu := createTestOnu()
 
 	onu.InternalState.SetState("auth_started")
 
@@ -108,13 +84,7 @@
 }
 
 func Test_Onu_StateMachine_dhcp_start(t *testing.T) {
-	olt := OltDevice{
-		ID: 0,
-	}
-	pon := PonPort{
-		ID: 1,
-	}
-	onu := CreateONU(olt, pon, 1, 900, 900)
+	onu := createTestOnu()
 	onu.DhcpFlowReceived = true
 
 	onu.InternalState.SetState("eap_response_success_received")
@@ -126,13 +96,7 @@
 }
 
 func Test_Onu_StateMachine_dhcp_start_error(t *testing.T) {
-	olt := OltDevice{
-		ID: 0,
-	}
-	pon := PonPort{
-		ID: 1,
-	}
-	onu := CreateONU(olt, pon, 1, 900, 900)
+	onu := createTestOnu()
 
 	onu.InternalState.SetState("eap_response_success_received")
 	assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
@@ -144,13 +108,7 @@
 }
 
 func Test_Onu_StateMachine_dhcp_states(t *testing.T) {
-	olt := OltDevice{
-		ID: 0,
-	}
-	pon := PonPort{
-		ID: 1,
-	}
-	onu := CreateONU(olt, pon, 1, 900, 900)
+	onu := createTestOnu()
 
 	onu.DhcpFlowReceived = false
 
diff --git a/internal/bbsim/devices/onu_test_helpers.go b/internal/bbsim/devices/onu_test_helpers.go
index 1044913..a2e7ddd 100644
--- a/internal/bbsim/devices/onu_test_helpers.go
+++ b/internal/bbsim/devices/onu_test_helpers.go
@@ -104,7 +104,8 @@
 	return nil, errors.New("unimplemented-in-mock-client")
 }
 
-func createMockOnu(id uint32, ponPortId uint32, sTag int, cTag int) Onu {
+// this method creates a fake ONU used in the tests
+func createMockOnu(id uint32, ponPortId uint32, sTag int, cTag int, auth bool, dhcp bool) Onu {
 	o := Onu{
 		ID:        id,
 		PonPortID: ponPortId,
@@ -112,7 +113,21 @@
 		CTag:      cTag,
 		HwAddress: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(ponPortId), byte(id)},
 		PortNo:    0,
+		Auth:      auth,
+		Dhcp:      dhcp,
 	}
 	o.SerialNumber = o.NewSN(0, ponPortId, o.ID)
 	return o
 }
+
+// this method creates a real ONU to be used in the tests
+func createTestOnu() *Onu {
+	olt := OltDevice{
+		ID: 0,
+	}
+	pon := PonPort{
+		ID: 1,
+	}
+	onu := CreateONU(olt, pon, 1, 900, 900, false, false)
+	return onu
+}
diff --git a/internal/common/options.go b/internal/common/options.go
index 41c3522..eee91cc 100644
--- a/internal/common/options.go
+++ b/internal/common/options.go
@@ -25,6 +25,8 @@
 	NumOnuPerPon int
 	STag         int
 	CTagInit     int
+	Auth         bool
+	Dhcp         bool
 	ProfileCpu   *string
 	LogLevel     string
 	LogCaller    bool
@@ -45,6 +47,9 @@
 	pon := flag.Int("pon", 1, "Number of PON ports per OLT device to be emulated")
 	onu := flag.Int("onu", 1, "Number of ONU devices per PON port to be emulated")
 
+	auth := flag.Bool("auth", false, "Set this flag if you want authentication to start automatically")
+	dhcp := flag.Bool("dhcp", false, "Set this flag if you want DHCP to start automatically")
+
 	s_tag := flag.Int("s_tag", 900, "S-Tag value")
 	c_tag_init := flag.Int("c_tag", 900, "C-Tag starting value, each ONU will get a sequential one (targeting 1024 ONUs per BBSim instance the range is big enough)")
 
@@ -66,6 +71,8 @@
 	o.ProfileCpu = profileCpu
 	o.LogLevel = *logLevel
 	o.LogCaller = *logCaller
+	o.Auth = *auth
+	o.Dhcp = *dhcp
 
 	return o
 }