SEBA-173

Change-Id: I3480e254784d4af68316d482990d664c50544cff
diff --git a/api/abstract_olt_api.proto b/api/abstract_olt_api.proto
index 12c8941..72dad9b 100644
--- a/api/abstract_olt_api.proto
+++ b/api/abstract_olt_api.proto
@@ -50,13 +50,7 @@
    string DeviceID =1;
    string ChassisDeviceID =2;
 }
-message ActivateSlotMessage{
-   string CLLI =1;
-   int32 SlotNumber = 2;
-}
-message ActivateSlotReturn{
-   bool Success = 1;
-}
+
 message AddOntMessage{
    string CLLI=1;
    int32 SlotNumber=2;
@@ -68,6 +62,16 @@
    bool Success=1;
 }
 
+message DeleteOntMessage{
+   string CLLI=1;
+   int32 SlotNumber=2;
+   int32 PortNumber=3;
+   int32 OntNumber=4;
+   string SerialNumber=5;
+}
+message DeleteOntReturn{
+   bool Success=1;
+}
 service AbstractOLT{
    rpc CreateChassis(AddChassisMessage) returns (AddChassisReturn) {
       option(google.api.http) = {
@@ -75,24 +79,23 @@
 	 body:"*"
       };
    }
-
    rpc CreateOLTChassis(AddOLTChassisMessage) returns (AddOLTChassisReturn) {
       option(google.api.http) = {
          post: "/v1/CreateOLTChassis"
 	 body:"*"
       };
    }
-   rpc EnableSlot(ActivateSlotMessage) returns (ActivateSlotReturn){
-      option(google.api.http) = {
-         post: "/v1/EnableSlot"
-	 body:"*"
-      };
-   }
    rpc ProvisionOnt(AddOntMessage) returns (AddOntReturn) {
       option(google.api.http) = {
          post:"/v1/ProvsionOnt"
 	 body:"*"
       };
    }
+   rpc DeleteOnt(DeleteOntMessage) returns (DeleteOntReturn){
+      option(google.api.http)={
+        post:"/v1/DeleteOnt"
+	body:"*"
+      };
+   }
 }
 
diff --git a/api/handler.go b/api/handler.go
index 4aeae13..6beba76 100644
--- a/api/handler.go
+++ b/api/handler.go
@@ -17,7 +17,6 @@
 package api
 
 import (
-	"errors"
 	"fmt"
 	"log"
 	"net"
@@ -111,23 +110,30 @@
 }
 
 /*
-EnableSlot - activates an OLT Chassis
-*/
-func (s *Server) EnableSlot(ctx context.Context, in *ActivateSlotMessage) (*ActivateSlotReturn, error) {
-	return nil, errors.New("garbage error")
-}
-
-/*
 ProvisionOnt provisions an ONT on a specific Chassis/LineCard/Port
 */
 func (s *Server) ProvisionOnt(ctx context.Context, in *AddOntMessage) (*AddOntReturn, error) {
 	absChassisMap := models.GetAbstractChassisMap()
 	clli := in.GetCLLI()
 	chassis := (*absChassisMap)[clli]
-	err := chassis.ActivateONT(int(in.GetSlotNumber()), int(in.GetPortNumber()), int(in.GetPortNumber()), in.GetSerialNumber())
+	err := chassis.ActivateONT(int(in.GetSlotNumber()), int(in.GetPortNumber()), int(in.GetOntNumber()), in.GetSerialNumber())
 
 	if err != nil {
 		return nil, err
 	}
 	return &AddOntReturn{Success: true}, nil
 }
+
+/*
+DeleteOnt - deletes a previously provision ont
+*/
+func (s *Server) DeleteOnt(ctx context.Context, in *DeleteOntMessage) (*DeleteOntReturn, error) {
+	absChassisMap := models.GetAbstractChassisMap()
+	clli := in.GetCLLI()
+	chassis := (*absChassisMap)[clli]
+	err := chassis.DeleteONT(int(in.GetSlotNumber()), int(in.GetPortNumber()), int(in.GetOntNumber()), in.GetSerialNumber())
+	if err != nil {
+		return nil, err
+	}
+	return &DeleteOntReturn{Success: true}, nil
+}
diff --git a/api/handler_test.go b/api/handler_test.go
index c319e6d..59aad10 100644
--- a/api/handler_test.go
+++ b/api/handler_test.go
@@ -43,7 +43,7 @@
 	clli = ret.DeviceID
 }
 func TestHandler_CreateOLTChassis(t *testing.T) {
-	fmt.Println("in handlerTest_CreateChassis")
+	fmt.Println("in handlerTest_CreateOltChassis")
 	message := &api.AddOLTChassisMessage{CLLI: clli, SlotIP: "12.2.2.0", SlotPort: 9191,
 		Hostname: slotHostname, Type: api.AddOLTChassisMessage_edgecore}
 	ret, err := server.CreateOLTChassis(ctx, message)
@@ -52,43 +52,31 @@
 	}
 	fmt.Printf("CreateOLTChassis success %v\n", ret)
 }
-func TestHandler_EnableSlot(t *testing.T) {
-	ctx = context.Background()
-	server = api.Server{}
-	fmt.Println("in handler_test_EnableSlot")
-	// slot number 1 should be provisioned above
-	message := &api.ActivateSlotMessage{CLLI: clli, SlotNumber: 1}
-	ret, err := server.EnableSlot(ctx, message)
-	if err != nil {
-		t.Fatalf("EnableSlot failed with %v\n", err)
-	}
-	fmt.Printf("EnableSlot succeeded with %v\n", ret)
-	// Slot 2 isn't provisioned and should fail
-	message = &api.ActivateSlotMessage{CLLI: clli, SlotNumber: 2}
-	ret, err = server.EnableSlot(ctx, message)
-	if err != nil {
-		switch err.(type) {
-		case *physical.UnprovisionedSlotError:
-			fmt.Printf("EnableSlot failed as it should with %v\n", err)
-		default:
-			t.Fatalf("EnableSlot failed with %v\n", err)
-		}
-		t.Fatalf("EnableSlot should have failed but didn't")
-	}
-
-}
 func TestHandler_ProvisionOnt(t *testing.T) {
 	ctx = context.Background()
 	server = api.Server{}
-	fmt.Println("in handlerTest_CreateChassis")
+	fmt.Println("in handlerTest_ProvisonOnt")
 	// this one should succeed
 	message := &api.AddOntMessage{CLLI: clli, SlotNumber: 1, PortNumber: 3, OntNumber: 2, SerialNumber: "2033029402"}
 	ret, err := server.ProvisionOnt(ctx, message)
 	if err != nil {
 		t.Fatalf("ProvisionOnt failed %v\n", err)
 	}
+	// do it again on same ont/port should fail with AllReadyActiveError
+	ret, err = server.ProvisionOnt(ctx, message)
+	if err != nil {
+		switch err.(type) {
+		case *physical.AllReadyActiveError:
+			fmt.Printf("ProvisionOnt failed as it should with %v\n", err)
+		default:
+			t.Fatalf("ProvsionOnt test failed with %v\n", err)
+		}
+
+	} else {
+		t.Fatalf("ProvsionOnt should have failed with AllReadyActiveError but didn't")
+	}
+
 	// this one should fail
-	fmt.Println("here")
 	//SlotNumber 1 hasn't been provisioned
 	message = &api.AddOntMessage{CLLI: clli, SlotNumber: 2, PortNumber: 3, OntNumber: 2, SerialNumber: "2033029402"}
 	ret, err = server.ProvisionOnt(ctx, message)
@@ -104,3 +92,43 @@
 	}
 	fmt.Printf("ProvisionOnt success %v\n", ret)
 }
+func TestHandler_DeleteOnt(t *testing.T) {
+	ctx = context.Background()
+	server = api.Server{}
+	fmt.Println("in handlerTest_DeleteOnt")
+	// this one should succeed
+	//De-Activate ONT 3 on PONPort 0 Slot 0 on my cilli but not active
+	message := &api.DeleteOntMessage{CLLI: clli, SlotNumber: 1, PortNumber: 3, OntNumber: 2, SerialNumber: "2033029402"}
+	ret, err := server.DeleteOnt(ctx, message)
+	if err != nil {
+		t.Fatalf("DeleteOnt failed %v\n", err)
+	}
+	// This should fail with AllReadyDeactivatedError
+	ret, err = server.DeleteOnt(ctx, message)
+	if err != nil {
+		switch err.(type) {
+		case *physical.AllReadyDeactivatedError:
+			fmt.Printf("DeleteOnt failed as expected with %v\n", err)
+		default:
+			t.Fatalf("DeleteOnt failed with %v\n", err)
+		}
+	} else {
+		t.Fatal("DeleteOnt should have failed with AllReadyDeactivatedError")
+	}
+
+	// this one should fail
+	//SlotNumber 1 hasn't been provisioned
+	message = &api.DeleteOntMessage{CLLI: clli, SlotNumber: 2, PortNumber: 3, OntNumber: 2, SerialNumber: "2033029402"}
+	ret, err = server.DeleteOnt(ctx, message)
+	if err != nil {
+		switch err.(type) {
+		case *abstract.UnprovisonedPortError:
+			fmt.Printf("DeleteOnt failed as it should with %v\n", err)
+		default:
+			t.Fatalf("DeleteOnt test failed with %v\n", err)
+		}
+	} else {
+		t.Fatalf("DeleteOnt should have failed but didn't")
+	}
+	fmt.Printf("DeletenOnt success %v\n", ret)
+}
diff --git a/models/abstract/chassis.go b/models/abstract/chassis.go
index 65bd228..687ecae 100644
--- a/models/abstract/chassis.go
+++ b/models/abstract/chassis.go
@@ -62,3 +62,7 @@
 	err := chassis.Slots[slotNumber-1].Ports[portNumber-1].provisionOnt(ontNumber, serialNumber)
 	return err
 }
+func (chassis *Chassis) DeleteONT(slotNumber int, portNumber int, ontNumber int, serialNumber string) error {
+	err := chassis.Slots[slotNumber-1].Ports[portNumber-1].deleteOnt(ontNumber, serialNumber)
+	return err
+}
diff --git a/models/abstract/port.go b/models/abstract/port.go
index 5dfa195..2c2e07c 100644
--- a/models/abstract/port.go
+++ b/models/abstract/port.go
@@ -33,12 +33,18 @@
 	Parent   *Slot `json:"-"`
 }
 
+/*
+UnprovisonedPortError - thrown when an attempt is made to address a physical port that hasn't been mapped to an abstract port
+*/
 type UnprovisonedPortError struct {
 	oltNum  int
 	clli    string
 	portNum int
 }
 
+/*
+Error - the interface method that must be implemented on error
+*/
 func (e *UnprovisonedPortError) Error() string {
 	return fmt.Sprintf("Port %d for olt %d on AbstractChasis  %s is not provisioned", e.portNum, e.oltNum, e.clli)
 }
@@ -49,9 +55,20 @@
 		err := UnprovisonedPortError{oltNum: slot.Number, clli: chassis.CLLI, portNum: port.Number}
 		return &err
 	}
-
 	phyPort := port.PhysPort
 	ont := port.Onts[ontNumber-1]
-	phyPort.ActivateOnt(ontNumber, ont.Svlan, ont.Cvlan, serialNumber)
-	return nil
+	err := phyPort.ActivateOnt(ontNumber, ont.Svlan, ont.Cvlan, serialNumber)
+	return err
+}
+func (port *Port) deleteOnt(ontNumber int, serialNumber string) error {
+	if port.PhysPort == nil {
+		slot := port.Parent
+		chassis := slot.Parent
+		err := UnprovisonedPortError{oltNum: slot.Number, clli: chassis.CLLI, portNum: port.Number}
+		return &err
+	}
+	phyPort := port.PhysPort
+	ont := port.Onts[ontNumber-1]
+	err := phyPort.DeleteOnt(ontNumber, ont.Svlan, ont.Cvlan, serialNumber)
+	return err
 }
diff --git a/models/physical/chassis.go b/models/physical/chassis.go
index 8f60838..705de80 100644
--- a/models/physical/chassis.go
+++ b/models/physical/chassis.go
@@ -51,6 +51,10 @@
 	//TODO - api call to provison s/c vlans and ont serial number etc
 	fmt.Printf("chassis.provisionONT(%s,SVlan:%d,CVlan:%d)\n", ont.SerialNumber, ont.Svlan, ont.Cvlan)
 }
+func (chassis *Chassis) deleteONT(ont Ont) {
+	//TODO - api call to provison s/c vlans and ont serial number etc
+	fmt.Printf("chassis.deleteONT(%s,SVlan:%d,CVlan:%d)\n", ont.SerialNumber, ont.Svlan, ont.Cvlan)
+}
 func (chassis *Chassis) ActivateSlot(slotNumber int) error {
 	// AT&T backend systems start at 1 and not 0 :P
 	if chassis.Linecards[slotNumber-1] == nil {
diff --git a/models/physical/ont.go b/models/physical/ont.go
index 4a9ca89..3e5e659 100644
--- a/models/physical/ont.go
+++ b/models/physical/ont.go
@@ -25,4 +25,5 @@
 	Cvlan        int
 	SerialNumber string
 	Parent       *PONPort `json:"-"`
+	Active       bool
 }
diff --git a/models/physical/ponport.go b/models/physical/ponport.go
index e17a07e..15b7328 100644
--- a/models/physical/ponport.go
+++ b/models/physical/ponport.go
@@ -16,6 +16,8 @@
 
 package physical
 
+import "fmt"
+
 /*
 PONPort represents a single PON port on the OLT chassis
 */
@@ -26,9 +28,74 @@
 	Parent   *Edgecore `json:"-"`
 }
 
-func (port *PONPort) ActivateOnt(number int, sVlan int, cVlan int, serialNumber string) {
+/*
+AllReadyActiveError - thrown when an attempt to activate a ONT which is already activated
+*/
+type AllReadyActiveError struct {
+	slotNum    int
+	clli       string
+	ponportNum int
+	ontNumber  int
+}
+
+/*
+Error - the interface method that must be implemented on error
+*/
+func (e *AllReadyActiveError) Error() string {
+	return fmt.Sprintf("Attempt to Activate ONT %d on PONPort %d Slot %d on %s but already active", e.ontNumber, e.ponportNum, e.slotNum, e.clli)
+}
+
+/*
+AllReadyDeactivatedError - thrown when an attempt to activate a ONT which is already activated
+*/
+type AllReadyDeactivatedError struct {
+	slotNum    int
+	clli       string
+	ponportNum int
+	ontNumber  int
+}
+
+/*
+Error - the interface method that must be implemented on error
+*/
+func (e *AllReadyDeactivatedError) Error() string {
+	return fmt.Sprintf("Attempt to De-Activate ONT %d on PONPort %d Slot %d on %s but not active", e.ontNumber, e.ponportNum, e.slotNum, e.clli)
+}
+
+/*
+ActivateOnt - passes ont information to chassis to make call to NEM to activate (whitelist) ont
+*/
+func (port *PONPort) ActivateOnt(number int, sVlan int, cVlan int, serialNumber string) error {
+	slot := port.Parent
+	chassis := slot.Parent
+	fmt.Printf("Calling ActivateOnt and port state is %t\n", port.Onts[number-1].Active)
+
+	if port.Onts[number-1].Active {
+		e := AllReadyActiveError{ontNumber: number, slotNum: slot.Number, ponportNum: port.Number, clli: chassis.CLLI}
+		return &e
+	}
 	ont := Ont{Number: number, Svlan: sVlan, Cvlan: cVlan, SerialNumber: serialNumber, Parent: port}
 	port.Onts[number-1] = ont
 	port.Parent.Parent.provisionONT(ont)
+	port.Onts[number-1].Active = true
+	return nil
 
 }
+
+/*
+DeleteOnt - passes ont information to chassis to make call to NEM to de-activate (de-whitelist) ont
+*/
+func (port *PONPort) DeleteOnt(number int, sVlan int, cVlan int, serialNumber string) error {
+	slot := port.Parent
+	chassis := slot.Parent
+	fmt.Printf("Calling ActivateOnt and port state is %t\n", port.Onts[number-1].Active)
+	if port.Onts[number-1].Active != true {
+		e := AllReadyDeactivatedError{ontNumber: number, slotNum: slot.Number, ponportNum: port.Number, clli: chassis.CLLI}
+		return &e
+	}
+	ont := Ont{Number: number, Svlan: sVlan, Cvlan: cVlan, SerialNumber: serialNumber, Parent: port}
+	chassis.deleteONT(ont)
+	port.Onts[number-1].Active = false
+
+	return nil
+}