diff --git a/examples/main.go b/examples/main.go index 043d9cd..06d007c 100644 --- a/examples/main.go +++ b/examples/main.go @@ -18,7 +18,6 @@ package main import ( "fmt" - "os" "github.com/ebay/go-ovn/goovn" @@ -34,6 +33,7 @@ const ( var ovndbapi goovn.OVNDBApi + func init() { var ovs_rundir = os.Getenv("OVS_RUNDIR") if ovs_rundir == "" { @@ -44,6 +44,7 @@ func init() { } func main() { + ocmd := ovndbapi.LSWAdd("ls1") ovndbapi.Execute(ocmd) ocmd = ovndbapi.LSPAdd("ls1", "test") diff --git a/goovn/apis.go b/goovn/apis.go index 0be2a06..67108e7 100644 --- a/goovn/apis.go +++ b/goovn/apis.go @@ -57,6 +57,13 @@ type OVNDBApi interface { ASAdd(name string, addrs []string, external_ids map[string]string) *OvnCommand // Delete addressset ASDel(name string) *OvnCommand + // Add LB + LBAdd(name string, vipPort string, protocol string, addrs []string) *OvnCommand + // Delete LB with given name + LBDel(name string) *OvnCommand + // Update existing LB + LBUpdate(name string, vipPort string, protocol string, addrs []string) *OvnCommand + // Set options in lswtich LSSetOpt(lsp string, options map[string]string) *OvnCommand // Exec command, support mul-commands in one transaction. @@ -71,6 +78,8 @@ type OVNDBApi interface { GetAddressSets() []*AddressSet GetASByName(name string) *AddressSet + // Get LB with given name + GetLB(name string) []*LoadBalancer SetCallBack(callback OVNSignal) } @@ -109,6 +118,15 @@ type LogicalSwitch struct { ExternalID map[interface{}]interface{} } +type LoadBalancer struct { + UUID string + Name string + vips map[interface{}]interface{} + protocol string + ExternalID map[interface{}]interface{} +} + + type LogcalPort struct { UUID string Name string diff --git a/goovn/ovndb.go b/goovn/ovndb.go index fc73cc4..80c4152 100644 --- a/goovn/ovndb.go +++ b/goovn/ovndb.go @@ -41,6 +41,7 @@ const ( LSWITCH string = "Logical_Switch" LPORT string = "Logical_Switch_Port" ACLS string = "ACL" + LB string = "Load_Balancer" Address_Set string = "Address_Set" ) @@ -88,7 +89,6 @@ func GetInstance(socketfile string, protocol string, server string, port int, ca } else { err = errors.New(fmt.Sprintf("The protocol [%s] is not supported", protocol)) } - if err != nil { panic(fmt.Sprint("Library goovn initilizing failed", err)) os.Exit(1) diff --git a/goovn/ovnnb.go b/goovn/ovnnb.go index c51a9e9..89e9302 100644 --- a/goovn/ovnnb.go +++ b/goovn/ovnnb.go @@ -64,7 +64,7 @@ func newNBBySocket(socketfile string, callback OVNSignal) (*OVNDB, error) { func newNBByServer(server string, port int, callback OVNSignal) (*OVNDB, error) { odb, err := newNBClient("", TCP, server, port) - if err != nil { + if err == nil { return &OVNDB{newNBImp(odb, callback)}, nil } else { return nil, err @@ -91,6 +91,20 @@ func (odb *OVNDB) LSPDel(lsp string) *OvnCommand { return odb.imp.lspDelImp(lsp) } + + +func (odb *OVNDB) LBAdd(name string, vipPort string, protocol string, addrs []string) *OvnCommand { + return odb.imp.lbAddImpl(name, vipPort, protocol, addrs) +} + +func (odb *OVNDB) LBUpdate(name string, vipPort string, protocol string, addrs []string) *OvnCommand { + return odb.imp.lbUpdateImpl(name, vipPort, protocol, addrs) +} + +func (odb *OVNDB) LBDel(name string) *OvnCommand { + return odb.imp.lbDelImp(name) +} + func (odb *OVNDB) LSPSetAddress(lsp string, addresses ...string) *OvnCommand { return odb.imp.lspSetAddressImp(lsp, addresses...) } @@ -139,6 +153,7 @@ func (odb *OVNDB) GetACLsBySwitch(lsw string) []*ACL { return odb.imp.GetACLsBySwitch(lsw) } + func (odb *OVNDB) GetAddressSets() []*AddressSet { return odb.imp.GetAddressSets() } @@ -147,6 +162,11 @@ func (odb *OVNDB) GetASByName(name string) *AddressSet { return odb.imp.GetASByName(name) } +func (odb *OVNDB) GetLB(name string) []*LoadBalancer { + return odb.imp.GetLB(name) +} + + func (odb *OVNDB) SetCallBack(callback OVNSignal) { odb.imp.callback = callback } diff --git a/goovn/ovnnbimp.go b/goovn/ovnnbimp.go index 9a2f9f1..b196929 100644 --- a/goovn/ovnnbimp.go +++ b/goovn/ovnnbimp.go @@ -62,6 +62,96 @@ func (odbi *ovnDBImp) lswListImp() *OvnCommand { return &OvnCommand{operations, odbi, make([][]map[string]interface{}, len(operations))} } + +func (odbi *ovnDBImp) lbUpdateImpl(name string, vipPort string, protocol string, addrs []string) *OvnCommand { + + //row to update + lb := make(OVNRow) + + // prepare vips map + vipMap := make(map[string]string) + members := strings.Join(addrs, ",") + vipMap[vipPort] = members + + oMap, err := libovsdb.NewOvsMap(vipMap) + if err != nil { + glog.Fatalf("Update LB:%v vip map is not correct: %v", name, err) + return nil + } + lb["vips"] = oMap + lb["protocol"] = protocol + + condition := libovsdb.NewCondition("name", "==", name) + + insertOp := libovsdb.Operation{ + Op: update, + Table: LB, + Row: lb, + Where: []interface{}{condition}, + } + operations := []libovsdb.Operation{insertOp} + return &OvnCommand{operations, odbi, make([][]map[string]interface{}, len(operations))} +} + + +func (odbi *ovnDBImp) lbAddImpl(name string, vipPort string, protocol string, addrs []string) *OvnCommand { + namedUUID := "lb_add" + strconv.Itoa(rand.Int()) + //row to insert + lb := make(OVNRow) + lb["name"] = name + + if odbi.getRowUUID(LB, lb) != "" { + glog.V(OVNLOGLEVEL).Info("The load balancer existed, and will get nil command") + return nil + } + // prepare vips map + vipMap := make(map[string]string) + members := strings.Join(addrs, ",") + vipMap[vipPort] = members + + oMap, err := libovsdb.NewOvsMap(vipMap) + if err != nil { + glog.Fatalf("Add LB: vip map is not correct") + return nil + } + lb["vips"] = oMap + lb["protocol"] = protocol + + insertOp := libovsdb.Operation{ + Op: insert, + Table: LB, + Row: lb, + UUIDName: namedUUID, + } + + mutateUUID := []libovsdb.UUID{{namedUUID}} + mutateSet, _ := libovsdb.NewOvsSet(mutateUUID) + mutation := libovsdb.NewMutation("load_balancer", insert, mutateSet) + // TODO: Add filter for LS name + condition := libovsdb.NewCondition("name", "!=", "") + + mutateOp := libovsdb.Operation{ + Op: mutate, + Table: LSWITCH, + Mutations: []interface{}{mutation}, + Where: []interface{}{condition}, + } + operations := []libovsdb.Operation{insertOp, mutateOp} + return &OvnCommand{operations, odbi, make([][]map[string]interface{}, len(operations))} +} + +func (odbi *ovnDBImp) lbDelImp(name string) *OvnCommand { + condition := libovsdb.NewCondition("name", "==", name) + delOp := libovsdb.Operation{ + Op: del, + Table: LB, + Where: []interface{}{condition}, + } + operations := []libovsdb.Operation{delOp} + return &OvnCommand{operations, odbi, make([][]map[string]interface{}, len(operations))} +} + + func (odbi *ovnDBImp) lswAddImp(lsw string) *OvnCommand { namedUUID := "lsw_add" + strconv.Itoa(rand.Int()) //row to insert @@ -703,6 +793,31 @@ func (odbi *ovnDBImp) GetLogicPortsBySwitch(lsw string) []*LogcalPort { return lplist } +func (odbi *ovnDBImp) GetLB(name string) []*LoadBalancer { + var lbList []*LoadBalancer + odbi.cachemutex.Lock() + defer odbi.cachemutex.Unlock() + + for uuid, drows := range odbi.cache[LB] { + if lbName, ok := drows.Fields["name"].(string); ok && lbName == name { + lb := odbi.RowToLB(uuid) + lbList = append(lbList, lb) + } + } + return lbList +} + +func (odbi *ovnDBImp) RowToLB(uuid string) *LoadBalancer { + return &LoadBalancer{ + UUID: uuid, + protocol: odbi.cache[LB][uuid].Fields["protocol"].(string), + Name: odbi.cache[LB][uuid].Fields["name"].(string), + vips: odbi.cache[LB][uuid].Fields["vips"].(libovsdb.OvsMap).GoMap, + ExternalID: odbi.cache[LB][uuid].Fields["external_ids"].(libovsdb.OvsMap).GoMap, + } +} + + func (odbi *ovnDBImp) RowToACL(uuid string) *ACL { acl := &ACL{ UUID: uuid, diff --git a/goovn/ovnnbimp_test.go b/goovn/ovnnbimp_test.go index 0208afd..6430dce 100644 --- a/goovn/ovnnbimp_test.go +++ b/goovn/ovnnbimp_test.go @@ -214,3 +214,43 @@ func TestAddressSet(t *testing.T) { ovndbapi.Execute(c...) assert.Equal(t, false, findAS("AS2"), "test AS remove") } + + +func TestLoadBalancer(t *testing.T){ + t.Logf("Adding LB to OVN") + ocmd := ovndbapi.LBAdd("lb1", "192.168.0.19:80", "tcp", []string{"10.0.0.11:80","10.0.0.12:80"}) + err := ovndbapi.Execute(ocmd) + if err != nil { + t.Fatalf("Adding LB OVN failed with err %v", err) + } + t.Logf("Adding LB to OVN Done") + + t.Logf("Updating LB to OVN") + ocmd = ovndbapi.LBUpdate("lb1", "192.168.0.10:80", "tcp", []string{"10.10.10.127:8080","10.10.10.120:8080"}) + err = ovndbapi.Execute(ocmd) + if err != nil { + t.Fatalf("Updating LB OVN failed with err %v", err) + } + t.Logf("Updating LB to OVN done") + + t.Logf("Gettting LB by name") + lb := ovndbapi.GetLB("lb1") + if len(lb) != 1 { + t.Fatalf("err getting lbs, total:%v", len(lb)) + } + t.Logf("Lb found:%+v", lb[0]) + + t.Logf("Deleting LB") + ocmd = ovndbapi.LBDel("lb1") + err = ovndbapi.Execute(ocmd) + if err != nil { + t.Fatalf("err executing command:%v", err) + } + + // Verify deletion + lb = ovndbapi.GetLB("lb1") + if len(lb) != 0 { + t.Fatalf("error: lb deletion not done, total:%v", len(lb)) + } + t.Logf("LB deletion done") +} \ No newline at end of file