SEBA-258 - Added MONGODB backup
Change-Id: I2542e7dfc894dfa5a1269c6be99cfa8036b9911c
diff --git a/api/handler.go b/api/handler.go
index ce5c7a1..bd9d2f0 100644
--- a/api/handler.go
+++ b/api/handler.go
@@ -25,11 +25,15 @@
"net/http"
"os"
"strings"
+ "sync"
"gerrit.opencord.org/abstract-olt/internal/pkg/settings"
"gerrit.opencord.org/abstract-olt/models"
"gerrit.opencord.org/abstract-olt/models/abstract"
"gerrit.opencord.org/abstract-olt/models/physical"
+ "github.com/mongodb/mongo-go-driver/bson"
+ "github.com/mongodb/mongo-go-driver/mongo"
+ "github.com/mongodb/mongo-go-driver/mongo/updateopt"
context "golang.org/x/net/context"
)
@@ -39,10 +43,30 @@
type Server struct {
}
+var syncChan chan bool
+var isDirty bool
+
+var runOnce sync.Once
+
+func getSyncChannel() chan bool {
+ runOnce.Do(func() {
+ syncChan = make(chan bool, 1)
+ syncChan <- true
+ })
+
+ return syncChan
+}
+func done(myChan chan bool, done bool) {
+ myChan <- done
+}
+
/*
Echo - Tester function which just returns same string sent to it
*/
func (s *Server) Echo(ctx context.Context, in *EchoMessage) (*EchoReplyMessage, error) {
+ myChan := getSyncChannel()
+ <-myChan
+ defer done(myChan, true)
fmt.Println("HELLO WTF")
ping := in.GetPing()
pong := EchoReplyMessage{Pong: ping}
@@ -53,6 +77,10 @@
CreateChassis - allocates a new Chassis struct and stores it in chassisMap
*/
func (s *Server) CreateChassis(ctx context.Context, in *AddChassisMessage) (*AddChassisReturn, error) {
+ myChan := getSyncChannel()
+ fmt.Println("after getSyncChannel")
+ <-myChan
+ defer done(myChan, true)
chassisMap := models.GetChassisMap()
clli := in.GetCLLI()
@@ -92,6 +120,7 @@
log.Printf("new chassis %s\n", formatted)
}
(*chassisMap)[clli] = chassisHolder
+ isDirty = true
return &AddChassisReturn{DeviceID: clli}, nil
}
@@ -99,6 +128,9 @@
ChangeXOSUserPassword - allows update of xos credentials
*/
func (s *Server) ChangeXOSUserPassword(ctx context.Context, in *ChangeXOSUserPasswordMessage) (*ChangeXOSUserPasswordReturn, error) {
+ myChan := getSyncChannel()
+ <-myChan
+ defer done(myChan, true)
clli := in.GetCLLI()
xosUser := in.GetXOSUser()
xosPassword := in.GetXOSPassword()
@@ -120,6 +152,7 @@
chassisHolder.PhysicalChassis.XOSUser = xosUser
chassisHolder.PhysicalChassis.XOSPassword = xosPassword
+ isDirty = true
return &ChangeXOSUserPasswordReturn{Success: true}, nil
}
@@ -128,6 +161,9 @@
CreateOLTChassis adds an OLT chassis/line card to the Physical chassis
*/
func (s *Server) CreateOLTChassis(ctx context.Context, in *AddOLTChassisMessage) (*AddOLTChassisReturn, error) {
+ myChan := getSyncChannel()
+ <-myChan
+ defer done(myChan, true)
fmt.Printf(" CreateOLTChassis %v \n", *in)
chassisMap := models.GetChassisMap()
clli := in.GetCLLI()
@@ -154,6 +190,7 @@
absPort.PhysPort = &ports[i]
//AssignTraits(&ports[i], absPort)
}
+ isDirty = true
return &AddOLTChassisReturn{DeviceID: in.GetHostname(), ChassisDeviceID: clli}, nil
}
@@ -162,6 +199,9 @@
ProvisionOnt provisions an ONT on a specific Chassis/LineCard/Port
*/
func (s *Server) ProvisionOnt(ctx context.Context, in *AddOntMessage) (*AddOntReturn, error) {
+ myChan := getSyncChannel()
+ <-myChan
+ defer done(myChan, true)
chassisMap := models.GetChassisMap()
clli := in.GetCLLI()
chassisHolder := (*chassisMap)[clli]
@@ -174,6 +214,7 @@
if err != nil {
return nil, err
}
+ isDirty = true
return &AddOntReturn{Success: true}, nil
}
@@ -181,6 +222,9 @@
DeleteOnt - deletes a previously provision ont
*/
func (s *Server) DeleteOnt(ctx context.Context, in *DeleteOntMessage) (*DeleteOntReturn, error) {
+ myChan := getSyncChannel()
+ <-myChan
+ defer done(myChan, true)
chassisMap := models.GetChassisMap()
clli := in.GetCLLI()
chassisHolder := (*chassisMap)[clli]
@@ -188,19 +232,70 @@
if err != nil {
return nil, err
}
+ isDirty = true
return &DeleteOntReturn{Success: true}, nil
}
func (s *Server) Output(ctx context.Context, in *OutputMessage) (*OutputReturn, error) {
- chassisMap := models.GetChassisMap()
- for clli, chassisHolder := range *chassisMap {
+ return DoOutput()
- json, _ := (chassisHolder).Serialize()
- backupFile := fmt.Sprintf("backup/%s", clli)
- f, _ := os.Create(backupFile)
- defer f.Close()
- _, _ = f.WriteString(string(json))
- f.Sync()
+}
+func DoOutput() (*OutputReturn, error) {
+ if isDirty {
+ myChan := getSyncChannel()
+ <-myChan
+ defer done(myChan, true)
+ chassisMap := models.GetChassisMap()
+ if settings.GetMongo() {
+ client, err := mongo.NewClient(settings.GetMongodb())
+ client.Connect(context.Background())
+ if err != nil {
+ log.Printf("client connect to mongo db @%s failed with %v\n", settings.GetMongodb(), err)
+ }
+ defer client.Disconnect(context.Background())
+ for clli, chassisHolder := range *chassisMap {
+ json, _ := (chassisHolder).Serialize()
+ collection := client.Database("AbstractOLT").Collection("backup")
+ doc := bson.NewDocument(bson.EC.String("_id", clli))
+ filter := bson.NewDocument(bson.EC.String("_id", clli))
+ doc.Append(bson.EC.Binary("body", json))
+
+ updateDoc := bson.NewDocument(bson.EC.SubDocument("$set", doc))
+ //update or insert if not existent
+ res, err := collection.UpdateOne(context.Background(), filter, updateDoc, updateopt.Upsert(true))
+ if err != nil {
+ log.Printf("collection.UpdateOne failed with %v\n", err)
+ } else {
+ id := res.UpsertedID
+ if settings.GetDebug() {
+ log.Printf("Update Succeeded with id %v\n", id)
+ }
+ }
+ }
+ } else {
+ for clli, chassisHolder := range *chassisMap {
+
+ json, _ := (chassisHolder).Serialize()
+ if settings.GetMongo() {
+
+ } else {
+ //TODO parameterize dump location
+ backupFile := fmt.Sprintf("backup/%s", clli)
+ f, _ := os.Create(backupFile)
+
+ defer f.Close()
+
+ _, _ = f.WriteString(string(json))
+ f.Sync()
+ }
+ }
+ }
+ isDirty = false
+ } else {
+ if settings.GetDebug() {
+ log.Print("Not dirty not dumping config")
+ }
+
}
return &OutputReturn{Success: true}, nil
diff --git a/cmd/AbstractOLT/AbstractOLT.go b/cmd/AbstractOLT/AbstractOLT.go
index 869c2c0..3e50828 100644
--- a/cmd/AbstractOLT/AbstractOLT.go
+++ b/cmd/AbstractOLT/AbstractOLT.go
@@ -25,11 +25,14 @@
"net/http"
"os"
"strings"
+ "time"
"gerrit.opencord.org/abstract-olt/api"
"gerrit.opencord.org/abstract-olt/internal/pkg/settings"
"gerrit.opencord.org/abstract-olt/models"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "github.com/mongodb/mongo-go-driver/bson"
+ "github.com/mongodb/mongo-go-driver/mongo"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
@@ -186,6 +189,9 @@
help := flag.Bool("help", false, "Show usage")
dummy := flag.Bool("dummy", false, "Run in dummy mode where YAML is not sent to XOS")
+ useMongo := flag.Bool("useMongo", false, "use mongo db for backup/restore")
+ mongodb := flag.String("mongodb", "mongodb://foundry:foundry@localhost:27017", "connect string for mongodb backup/restore")
+
flag.Parse()
settings.SetDummy(*dummy)
@@ -196,12 +202,17 @@
-a [default false] : Run in Authentication mode currently very basic
-listenAddress IP_ADDRESS [default localhost] -grpc_port [default 7777] PORT1 -rest_port [default 7778] PORT2: Listen for grpc on IP_ADDRESS:PORT1 and rest on IP_ADDRESS:PORT2
-log_file [default $WORKING_DIR/AbstractOLT.log] LOG_FILE
+ -mongo [default false] use mongodb for backup restore
+ -mongodb [default mongodb://foundry:foundry@localhost:27017] connect string for mongodb - Required if mongo == true
-h(elp) print this usage
+
`
fmt.Println(usage)
return
}
settings.SetDebug(*debugPtr)
+ settings.SetMongo(*useMongo)
+ settings.SetMongodb(*mongodb)
fmt.Println("Startup Params: debug:", *debugPtr, " Authentication:", *useAuthentication, " SSL:", *useSsl, "Cert Directory", *certDirectory,
"ListenAddress:", *listenAddress, " grpc port:", *grpcPort, " rest port:", *restPort, "Logging to ", *logFile)
@@ -244,29 +255,70 @@
}()
// infinite loop
- files, err := ioutil.ReadDir("backup")
- if err != nil {
- log.Fatal(err)
- }
- for _, file := range files {
- fmt.Println(file.Name())
- chassisHolder := models.ChassisHolder{}
- if file.Name() != "BackupPlaceHolder" {
- fileName := fmt.Sprintf("backup/%s", file.Name())
- json, _ := ioutil.ReadFile(fileName)
- err := chassisHolder.Deserialize([]byte(json))
+ if *useMongo {
+ client, err := mongo.NewClient(*mongodb)
+ client.Connect(context.Background())
+ defer client.Disconnect(context.Background())
+ if err != nil {
+ log.Fatalf("unable to connect to mongodb with %v\n", err)
+ }
+ collection := client.Database("AbstractOLT").Collection("backup")
+ cur, err := collection.Find(context.Background(), nil)
+ if err != nil {
+ log.Fatalf("Unable to connect to collection with %v\n", err)
+ }
+ defer cur.Close(context.Background())
+ for cur.Next(context.Background()) {
+ elem := bson.NewDocument()
+ err := cur.Decode(elem)
if err != nil {
- fmt.Printf("Deserialize threw an error %v\n", err)
+ log.Fatal(err)
}
- chassisMap := models.GetChassisMap()
- (*chassisMap)[file.Name()] = &chassisHolder
- } else {
- fmt.Println("Ignoring BackupPlaceHolder")
+ clli := elem.LookupElement("_id").Value()
+ body := elem.LookupElement("body").Value()
+ _, bodyBin := (*body).Binary()
+
+ chassisHolder := models.ChassisHolder{}
+ err = chassisHolder.Deserialize(bodyBin)
+ if err != nil {
+ log.Printf("Deserialize threw an error for clli %s %v\n", (*clli).StringValue(), err)
+ } else {
+ chassisMap := models.GetChassisMap()
+ (*chassisMap)[(*clli).StringValue()] = &chassisHolder
+
+ }
+ }
+ } else {
+ files, err := ioutil.ReadDir("backup")
+ if err != nil {
+ log.Fatal(err)
+ }
+ for _, file := range files {
+ chassisHolder := models.ChassisHolder{}
+ if file.Name() != "BackupPlaceHolder" {
+ fileName := fmt.Sprintf("backup/%s", file.Name())
+ json, _ := ioutil.ReadFile(fileName)
+ err := chassisHolder.Deserialize([]byte(json))
+ if err != nil {
+ fmt.Printf("Deserialize threw an error %v\n", err)
+ }
+ chassisMap := models.GetChassisMap()
+ (*chassisMap)[file.Name()] = &chassisHolder
+ } else {
+ fmt.Println("Ignoring BackupPlaceHolder")
+ }
}
}
log.Printf("Entering infinite loop")
- select {}
+ var ticker = time.NewTicker(60 * time.Second)
+ for {
+ select {
+ case <-ticker.C:
+ api.DoOutput()
+ }
+ }
+
//TODO publish periodic stats etc
fmt.Println("AbstractOLT")
}
diff --git a/internal/pkg/settings/Settings.go b/internal/pkg/settings/Settings.go
index af61a28..b4eb128 100644
--- a/internal/pkg/settings/Settings.go
+++ b/internal/pkg/settings/Settings.go
@@ -18,6 +18,8 @@
var debug = false
var dummy = false
+var mongo = false
+var mongodb = ""
/*
SetDebug - sets debug setting
@@ -46,3 +48,15 @@
func GetDummy() bool {
return dummy
}
+func SetMongo(useMongo bool) {
+ mongo = useMongo
+}
+func GetMongo() bool {
+ return mongo
+}
+func SetMongodb(connectString string) {
+ mongodb = connectString
+}
+func GetMongodb() string {
+ return mongodb
+}
diff --git a/models/physical/olt.go b/models/physical/olt.go
index 721e731..c800ad5 100644
--- a/models/physical/olt.go
+++ b/models/physical/olt.go
@@ -45,7 +45,7 @@
Number int
Ports []PONPort
Active bool
- Parent *Chassis `json:"-"`
+ Parent *Chassis `json:"-" bson:"-"`
DataSwitchPort int
}
diff --git a/models/physical/ont.go b/models/physical/ont.go
index 79c21ee..21fbbae 100644
--- a/models/physical/ont.go
+++ b/models/physical/ont.go
@@ -20,12 +20,12 @@
Ont represents a single ont/onu connect to a splitter on a Port
*/
type Ont struct {
- Number int
- Svlan int
- Cvlan int
- SerialNumber string
- Parent *PONPort `json:"-"`
- Active bool
- NasPortID string
- CircuitID string
+ Number int `json:",omitempty"`
+ Svlan int `,json:",omitempty"`
+ Cvlan int `,json:",omitempty"`
+ SerialNumber string `,json:",omitempty"`
+ Parent *PONPort `json:"-" bson:"-"`
+ Active bool `json:",omitempty"`
+ NasPortID string `json:",omitempty"`
+ CircuitID string `json:",omitempty"`
}
diff --git a/models/physical/ponport.go b/models/physical/ponport.go
index 683489e..9c626c3 100644
--- a/models/physical/ponport.go
+++ b/models/physical/ponport.go
@@ -27,7 +27,7 @@
Number int
DeviceID string
Onts [64]Ont
- Parent *SimpleOLT `json:"-"`
+ Parent *SimpleOLT `json:"-" bson:"-"`
}
/*
diff --git a/models/serialize.go b/models/serialize.go
index a0c2a01..fbcf460 100644
--- a/models/serialize.go
+++ b/models/serialize.go
@@ -18,24 +18,32 @@
import (
"encoding/json"
+ "log"
+
+ "gerrit.opencord.org/abstract-olt/internal/pkg/settings"
+ "gerrit.opencord.org/abstract-olt/models/abstract"
+ "gerrit.opencord.org/abstract-olt/models/physical"
)
func (chassisHolder ChassisHolder) Serialize() ([]byte, error) {
- return json.Marshal(chassisHolder)
+ return json.Marshal(chassisHolder.PhysicalChassis)
}
func (chassisHolder *ChassisHolder) Deserialize(jsonData []byte) error {
- err := json.Unmarshal(jsonData, chassisHolder)
+ physicalChassis := physical.Chassis{}
+ err := json.Unmarshal(jsonData, &physicalChassis)
if err != nil {
return err
}
- physicalChassis := &chassisHolder.PhysicalChassis
- abstractChassis := &chassisHolder.AbstractChassis
+ abstractChassis := abstract.GenerateChassis(physicalChassis.CLLI, 1, 1)
+ chassisHolder.AbstractChassis = abstractChassis
+ chassisHolder.PhysicalChassis = physicalChassis
+
//first handle abstract parent pointers
for i := 0; i < len(abstractChassis.Slots); i++ {
slot := &abstractChassis.Slots[i]
- slot.Parent = abstractChassis
+ slot.Parent = &abstractChassis
for j := 0; j < len(slot.Ports); j++ {
port := &slot.Ports[j]
port.Parent = slot
@@ -47,7 +55,7 @@
//second handle physical parent pointers
for i := 0; i < len(physicalChassis.Linecards); i++ {
slot := physicalChassis.Linecards[i]
- slot.Parent = physicalChassis
+ slot.Parent = &physicalChassis
for j := 0; j < len(slot.Ports); j++ {
port := &slot.Ports[j]
port.Parent = &slot
@@ -65,5 +73,8 @@
absPort.PhysPort = &slot.Ports[j]
}
}
+ if settings.GetDebug() {
+ log.Printf("created chassis %v\n", abstractChassis)
+ }
return nil
}