From f25159b37627c342e544a68d42845357e3ab96f9 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Thu, 13 Nov 2014 19:09:43 -0800 Subject: [PATCH 1/5] Adding Unmarshalling pre-req support in RowUpdate and Notation Signed-off-by: Madhu Venugopal --- example/play_with_ovs.go | 4 ++-- notation.go | 36 +++++++++++++++++++++++++++++++++--- row.go | 21 +++++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 row.go diff --git a/example/play_with_ovs.go b/example/play_with_ovs.go index 100725ac..c967d382 100644 --- a/example/play_with_ovs.go +++ b/example/play_with_ovs.go @@ -24,8 +24,8 @@ func play(ovs *libovsdb.OvsdbClient) { rows := tableUpdate.Rows for uuid, row := range rows { newRow := row.New - if newRow != nil { - name := newRow["name"].(string) + if _, ok := newRow.Fields["name"]; ok { + name := newRow.Fields["name"].(string) if name == "stop" { fmt.Println("Bridge stop detected : ", uuid) quit <- true diff --git a/notation.go b/notation.go index 6ecbfc79..bc3d8d27 100644 --- a/notation.go +++ b/notation.go @@ -1,5 +1,7 @@ package libovsdb +import "encoding/json" + // Operation represents an operation according to RFC7047 section 5.2 type Operation struct { Op string `json:"op"` @@ -55,9 +57,9 @@ type TableUpdate struct { } type RowUpdate struct { - Uuid UUID `json:"-,omitempty"` - New map[string]interface{} `json:"new,omitempty"` - Old map[string]interface{} `json:"old,omitempty"` + Uuid UUID `json:"-,omitempty"` + New Row `json:"new,omitempty"` + Old Row `json:"old,omitempty"` } // OvsdbError is an OVS Error Condition @@ -89,4 +91,32 @@ type OperationResult struct { Rows []map[string]interface{} `json:"rows,omitempty"` } +func ovsSliceToGoNotation(val interface{}) (interface{}, error) { + switch val.(type) { + case []interface{}: + sl := val.([]interface{}) + bsliced, err := json.Marshal(sl) + if err != nil { + return nil, err + } + + switch sl[0] { + case "uuid": + var uuid UUID + err = json.Unmarshal(bsliced, &uuid) + return uuid, err + case "set": + var oSet OvsSet + err = json.Unmarshal(bsliced, &oSet) + return oSet, err + case "map": + var oMap OvsMap + err = json.Unmarshal(bsliced, &oMap) + return oMap, err + } + return val, nil + } + return val, nil +} + // TODO : add Condition, Function, Mutation and Mutator notations diff --git a/row.go b/row.go new file mode 100644 index 00000000..f1782157 --- /dev/null +++ b/row.go @@ -0,0 +1,21 @@ +package libovsdb + +import "encoding/json" + +type Row struct { + Fields map[string]interface{} +} + +func (r *Row) UnmarshalJSON(b []byte) (err error) { + r.Fields = make(map[string]interface{}) + var raw map[string]interface{} + err = json.Unmarshal(b, &raw) + for key, val := range raw { + val, err = ovsSliceToGoNotation(val) + if err != nil { + return err + } + r.Fields[key] = val + } + return err +} From 00fed77490a9f0291c83a60973050d03ff6811ca Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Thu, 13 Nov 2014 19:10:45 -0800 Subject: [PATCH 2/5] Map Unmarshalling support Signed-off-by: Madhu Venugopal --- map.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/map.go b/map.go index 306f9297..736f4d2d 100644 --- a/map.go +++ b/map.go @@ -31,6 +31,19 @@ func (o OvsMap) MarshalJSON() ([]byte, error) { return json.Marshal(ovsMap) } +func (o *OvsMap) UnmarshalJSON(b []byte) (err error) { + var oMap []interface{} + o.GoMap = make(map[interface{}]interface{}) + if err := json.Unmarshal(b, &oMap); err == nil && len(oMap) > 1 { + innerSlice := oMap[1].([]interface{}) + for _, val := range innerSlice { + f := val.([]interface{}) + o.GoMap[f[0]] = f[1] + } + } + return err +} + // notation requires special marshaling func NewOvsMap(goMap interface{}) (*OvsMap, error) { v := reflect.ValueOf(goMap) From c6d4a1cb340eb61a8fad3c8145ee8a9e1c58c1a3 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Thu, 13 Nov 2014 19:11:01 -0800 Subject: [PATCH 3/5] Set Unmarshalling support Signed-off-by: Madhu Venugopal --- set.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/set.go b/set.go index 3a8f1b28..46aaeeac 100644 --- a/set.go +++ b/set.go @@ -38,3 +38,17 @@ func (o OvsSet) MarshalJSON() ([]byte, error) { oSet = append(oSet, o.GoSet) return json.Marshal(oSet) } + +func (o *OvsSet) UnmarshalJSON(b []byte) (err error) { + var oSet []interface{} + if err = json.Unmarshal(b, &oSet); err == nil && len(oSet) > 1 { + innerSet := oSet[1].([]interface{}) + for _, val := range innerSet { + goVal, err := ovsSliceToGoNotation(val) + if err == nil { + o.GoSet = append(o.GoSet, goVal) + } + } + } + return err +} From ad0487fac6fc67ba6afd57c1380d49f3432f90c8 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Fri, 14 Nov 2014 04:41:13 -0800 Subject: [PATCH 4/5] Updated example app with Caching Signed-off-by: Madhu Venugopal --- .gitignore | 1 + example/play_with_ovs.go | 30 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0104d19a..6585b841 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.o *.a *.so +*.swp # Folders _obj diff --git a/example/play_with_ovs.go b/example/play_with_ovs.go index c967d382..8745a95b 100644 --- a/example/play_with_ovs.go +++ b/example/play_with_ovs.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "reflect" "github.com/socketplane/libovsdb" ) @@ -12,17 +13,16 @@ import ( var quit chan bool var update chan *libovsdb.TableUpdates +var cache map[string]map[string]libovsdb.Row func play(ovs *libovsdb.OvsdbClient) { - ovs.MonitorAll("Open_vSwitch", "") for { select { case currUpdate := <-update: for table, tableUpdate := range currUpdate.Updates { fmt.Println("Received Table update on : ", table) if table == "Bridge" { - rows := tableUpdate.Rows - for uuid, row := range rows { + for uuid, row := range tableUpdate.Rows { newRow := row.New if _, ok := newRow.Fields["name"]; ok { name := newRow.Fields["name"].(string) @@ -39,15 +39,33 @@ func play(ovs *libovsdb.OvsdbClient) { } +func populateCache(updates libovsdb.TableUpdates) { + for table, tableUpdate := range updates.Updates { + if _, ok := cache[table]; !ok { + cache[table] = make(map[string]libovsdb.Row) + + } + for uuid, row := range tableUpdate.Rows { + empty := libovsdb.Row{} + if !reflect.DeepEqual(row.New, empty) { + cache[table][uuid] = row.New + } else { + delete(cache[table], uuid) + } + } + } +} + func main() { quit = make(chan bool) update = make(chan *libovsdb.TableUpdates) + cache = make(map[string]map[string]libovsdb.Row) // By default libovsdb connects to 127.0.0.0:6400. ovs, err := libovsdb.Connect("", 0) // If you prefer to connect to OVS in a specific location : - //ovs, err := libovsdb.Connect("192.168.56.101", 6640) + // ovs, err := libovsdb.Connect("192.168.56.101", 6640) if err != nil { fmt.Println("Unable to Connect ", err) @@ -56,6 +74,9 @@ func main() { var notifier Notifier ovs.Register(notifier) + initial, _ := ovs.MonitorAll("Open_vSwitch", "") + populateCache(*initial) + fmt.Println(`Silly game of stopping this app when a Bridge with name "stop" is monitored`) go play(ovs) <-quit @@ -65,6 +86,7 @@ type Notifier struct { } func (n Notifier) Update(context interface{}, tableUpdates libovsdb.TableUpdates) { + populateCache(tableUpdates) update <- &tableUpdates } func (n Notifier) Locked([]interface{}) { From 9d99294753a91e2f3ed8f602e09e778403671755 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Fri, 14 Nov 2014 05:25:53 -0800 Subject: [PATCH 5/5] Insert - Monitor & processing example Signed-off-by: Madhu Venugopal --- example/play_with_ovs.go | 69 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/example/play_with_ovs.go b/example/play_with_ovs.go index 8745a95b..731dda1f 100644 --- a/example/play_with_ovs.go +++ b/example/play_with_ovs.go @@ -16,11 +16,11 @@ var update chan *libovsdb.TableUpdates var cache map[string]map[string]libovsdb.Row func play(ovs *libovsdb.OvsdbClient) { + go processInput(ovs) for { select { case currUpdate := <-update: for table, tableUpdate := range currUpdate.Updates { - fmt.Println("Received Table update on : ", table) if table == "Bridge" { for uuid, row := range tableUpdate.Rows { newRow := row.New @@ -39,6 +39,71 @@ func play(ovs *libovsdb.OvsdbClient) { } +func createBridge(ovs *libovsdb.OvsdbClient, bridgeName string) { + namedUuid := "gopher" + // bridge row to insert + bridge := make(map[string]interface{}) + bridge["name"] = bridgeName + + // simple insert operation + insertOp := libovsdb.Operation{ + Op: "insert", + Table: "Bridge", + Row: bridge, + UUIDName: namedUuid, + } + + // Inserting a Bridge row in Bridge table requires mutating the open_vswitch table. + mutateUuid := []libovsdb.UUID{libovsdb.UUID{namedUuid}} + mutateSet, _ := libovsdb.NewOvsSet(mutateUuid) + mutation := libovsdb.NewMutation("bridges", "insert", mutateSet) + condition := libovsdb.NewCondition("_uuid", "==", libovsdb.UUID{getRootUuid()}) + + // simple mutate operation + mutateOp := libovsdb.Operation{ + Op: "mutate", + Table: "Open_vSwitch", + Mutations: []interface{}{mutation}, + Where: []interface{}{condition}, + } + + operations := []libovsdb.Operation{insertOp, mutateOp} + reply, _ := ovs.Transact("Open_vSwitch", operations...) + + if len(reply) < len(operations) { + fmt.Println("Number of Replies should be atleast equal to number of Operations") + } + ok := true + for i, o := range reply { + if o.Error != "" && i < len(operations) { + fmt.Println("Transaction Failed due to an error :", o.Error, " details:", o.Details, " in ", operations[i]) + ok = false + } else if o.Error != "" { + fmt.Println("Transaction Failed due to an error :", o.Error) + ok = false + } + } + if ok { + fmt.Println("Bridge Addition Successful : ", reply[0].UUID.GoUuid) + } +} + +func processInput(ovs *libovsdb.OvsdbClient) { + for { + fmt.Printf("\n Enter a Bridge Name : ") + var bridgeName string + fmt.Scanf("%s", &bridgeName) + createBridge(ovs, bridgeName) + } +} + +func getRootUuid() string { + for uuid, _ := range cache["Open_vSwitch"] { + return uuid + } + return "" +} + func populateCache(updates libovsdb.TableUpdates) { for table, tableUpdate := range updates.Updates { if _, ok := cache[table]; !ok { @@ -77,7 +142,7 @@ func main() { initial, _ := ovs.MonitorAll("Open_vSwitch", "") populateCache(*initial) - fmt.Println(`Silly game of stopping this app when a Bridge with name "stop" is monitored`) + fmt.Println(`Silly game of stopping this app when a Bridge with name "stop" is monitored !`) go play(ovs) <-quit }