Dep update to current voltha-go and voltha-protos

voltha-go update includes techprofile and
resource manager common library updates.

Also flow decomposer utilities were refactored
out of the flow_decomposer.go file into its own
utils file.  This required a cooresponding code update.

Change-Id: I785f7fdfcbc6e790bf9220d31d95afcbbf373658
See:  https://gerrit.opencord.org/#/c/13904/
diff --git a/vendor/github.com/gyuho/goraph/.travis.yml b/vendor/github.com/gyuho/goraph/.travis.yml
deleted file mode 100644
index 4228e24..0000000
--- a/vendor/github.com/gyuho/goraph/.travis.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-language: go
-
-sudo: false
-
-go:
-- 1.6
-- tip
-
-script:
-- ./test
-
diff --git a/vendor/github.com/gyuho/goraph/LICENSE b/vendor/github.com/gyuho/goraph/LICENSE
deleted file mode 100644
index f7303ba..0000000
--- a/vendor/github.com/gyuho/goraph/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2015 Gyu-Ho Lee
-Copyright (c) 2016 Google Inc
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/gyuho/goraph/README.md b/vendor/github.com/gyuho/goraph/README.md
deleted file mode 100644
index 0b5b590..0000000
--- a/vendor/github.com/gyuho/goraph/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
-## goraph [![Build Status](https://img.shields.io/travis/gyuho/goraph.svg?style=flat-square)](https://travis-ci.org/gyuho/goraph) [![Godoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://godoc.org/github.com/gyuho/goraph)
-
-Package goraph implements graph data structure and algorithms.
-
-```
-go get -v gopkg.in/gyuho/goraph.v2;
-```
-
-<br>
-I have tutorials and visualizations of graph, tree algorithms:
-
-- [**_Binary search tree_**](https://github.com/gyuho/learn/tree/master/doc/binary_search_tree)
-- [**_Go: heap, priority queue_**](https://github.com/gyuho/learn/tree/master/doc/go_heap_priority_queue)
-- [**_Go: red black tree_**](https://github.com/gyuho/learn/tree/master/doc/go_red_black_tree)
-- [**_Go: b-tree_**](https://github.com/gyuho/learn/tree/master/doc/go_b_tree)
-- [**_Go: graph, interface_**](https://github.com/gyuho/learn/tree/master/doc/go_graph_interface)
-- [**_Go: graph, traversal_**](https://github.com/gyuho/learn/tree/master/doc/go_graph_traversal)
-- [**_Go: graph, shortest path_**](https://github.com/gyuho/learn/tree/master/doc/go_graph_shortest_path)
-- [**_Go: graph, topological sort_**](https://github.com/gyuho/learn/tree/master/doc/go_graph_topological_sort)
-- [**_Go: graph, minimum spanning tree_**](https://github.com/gyuho/learn/tree/master/doc/go_graph_minimum_spanning_tree)
-- [**_Go: graph, strongly connected components_**](https://github.com/gyuho/learn/tree/master/doc/go_graph_strongly_connected_components)
-
-<br>
-For fast query and retrieval, please check out  <a href="http://google-opensource.blogspot.co.uk/2014/06/cayley-graphs-in-go.html" target="_blank">Cayley</a>.
-
-
-<br>
-<a href="http://www.youtube.com/watch?v=ImMnYq2zP4Y" target="_blank"><img src="http://img.youtube.com/vi/ImMnYq2zP4Y/0.jpg"></a>
-
-- <a href="https://www.youtube.com/channel/UCWzSgIp_DYRQnEsJuH32Fww" target="_blank">Please visit my YouTube Channel</a>
-- <a href="https://www.youtube.com/watch?v=NdfIfxTsVDo&list=PLT6aABhFfinvsSn1H195JLuHaXNS6UVhf" target="_blank">`Tree`, `Graph` Theory Algorithms (Playlist)</a>
-- <a href="https://www.youtube.com/watch?v=ImMnYq2zP4Y&list=PLT6aABhFfinvsSn1H195JLuHaXNS6UVhf&index=4" target="_blank">`Graph` : BFS, DFS</a>
diff --git a/vendor/github.com/gyuho/goraph/disjoint_set.go b/vendor/github.com/gyuho/goraph/disjoint_set.go
deleted file mode 100644
index 3a8085f..0000000
--- a/vendor/github.com/gyuho/goraph/disjoint_set.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package goraph
-
-import "sync"
-
-// DisjointSet implements disjoint set.
-// (https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
-type DisjointSet struct {
-	represent string
-	members   map[string]struct{}
-}
-
-// Forests is a set of DisjointSet.
-type Forests struct {
-	mu   sync.Mutex // guards the following
-	data map[*DisjointSet]struct{}
-}
-
-// NewForests creates a new Forests.
-func NewForests() *Forests {
-	set := &Forests{}
-	set.data = make(map[*DisjointSet]struct{})
-	return set
-}
-
-// MakeDisjointSet creates a DisjointSet.
-func MakeDisjointSet(forests *Forests, name string) {
-	newDS := &DisjointSet{}
-	newDS.represent = name
-	members := make(map[string]struct{})
-	members[name] = struct{}{}
-	newDS.members = members
-	forests.mu.Lock()
-	defer forests.mu.Unlock()
-	forests.data[newDS] = struct{}{}
-}
-
-// FindSet returns the DisjointSet with the represent name.
-func FindSet(forests *Forests, name string) *DisjointSet {
-	forests.mu.Lock()
-	defer forests.mu.Unlock()
-	for data := range forests.data {
-		if data.represent == name {
-			return data
-		}
-		for k := range data.members {
-			if k == name {
-				return data
-			}
-		}
-	}
-	return nil
-}
-
-// Union unions two DisjointSet, with ds1's represent.
-func Union(forests *Forests, ds1, ds2 *DisjointSet) {
-	newDS := &DisjointSet{}
-	newDS.represent = ds1.represent
-	newDS.members = ds1.members
-	for k := range ds2.members {
-		newDS.members[k] = struct{}{}
-	}
-	forests.mu.Lock()
-	defer forests.mu.Unlock()
-	forests.data[newDS] = struct{}{}
-	delete(forests.data, ds1)
-	delete(forests.data, ds2)
-}
diff --git a/vendor/github.com/gyuho/goraph/doc.go b/vendor/github.com/gyuho/goraph/doc.go
deleted file mode 100644
index 191d299..0000000
--- a/vendor/github.com/gyuho/goraph/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package goraph implements graph data structure and algorithms.
-package goraph // import "github.com/gyuho/goraph"
diff --git a/vendor/github.com/gyuho/goraph/graph.go b/vendor/github.com/gyuho/goraph/graph.go
deleted file mode 100644
index 87f87c5..0000000
--- a/vendor/github.com/gyuho/goraph/graph.go
+++ /dev/null
@@ -1,503 +0,0 @@
-package goraph
-
-import (
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"io"
-	"sync"
-)
-
-// ID is unique identifier.
-type ID interface {
-	// String returns the string ID.
-	String() string
-}
-
-type StringID string
-
-func (s StringID) String() string {
-	return string(s)
-}
-
-// Node is vertex. The ID must be unique within the graph.
-type Node interface {
-	// ID returns the ID.
-	ID() ID
-	String() string
-}
-
-type node struct {
-	id string
-}
-
-var nodeCnt uint64
-
-func NewNode(id string) Node {
-	return &node{
-		id: id,
-	}
-}
-
-func (n *node) ID() ID {
-	return StringID(n.id)
-}
-
-func (n *node) String() string {
-	return n.id
-}
-
-// Edge connects between two Nodes.
-type Edge interface {
-	Source() Node
-	Target() Node
-	Weight() float64
-	String() string
-}
-
-// edge is an Edge from Source to Target.
-type edge struct {
-	src Node
-	tgt Node
-	wgt float64
-}
-
-func NewEdge(src, tgt Node, wgt float64) Edge {
-	return &edge{
-		src: src,
-		tgt: tgt,
-		wgt: wgt,
-	}
-}
-
-func (e *edge) Source() Node {
-	return e.src
-}
-
-func (e *edge) Target() Node {
-	return e.tgt
-}
-
-func (e *edge) Weight() float64 {
-	return e.wgt
-}
-
-func (e *edge) String() string {
-	return fmt.Sprintf("%s -- %.3f -→ %s\n", e.src, e.wgt, e.tgt)
-}
-
-type EdgeSlice []Edge
-
-func (e EdgeSlice) Len() int           { return len(e) }
-func (e EdgeSlice) Less(i, j int) bool { return e[i].Weight() < e[j].Weight() }
-func (e EdgeSlice) Swap(i, j int)      { e[i], e[j] = e[j], e[i] }
-
-// Graph describes the methods of graph operations.
-// It assumes that the identifier of a Node is unique.
-// And weight values is float64.
-type Graph interface {
-	// Init initializes a Graph.
-	Init()
-
-	// GetNodeCount returns the total number of nodes.
-	GetNodeCount() int
-
-	// GetNode finds the Node. It returns nil if the Node
-	// does not exist in the graph.
-	GetNode(id ID) Node
-
-	// GetNodes returns a map from node ID to
-	// empty struct value. Graph does not allow duplicate
-	// node ID or name.
-	GetNodes() map[ID]Node
-
-	// AddNode adds a node to a graph, and returns false
-	// if the node already existed in the graph.
-	AddNode(nd Node) bool
-
-	// DeleteNode deletes a node from a graph.
-	// It returns true if it got deleted.
-	// And false if it didn't get deleted.
-	DeleteNode(id ID) bool
-
-	// AddEdge adds an edge from nd1 to nd2 with the weight.
-	// It returns error if a node does not exist.
-	AddEdge(id1, id2 ID, weight float64) error
-
-	// ReplaceEdge replaces an edge from id1 to id2 with the weight.
-	ReplaceEdge(id1, id2 ID, weight float64) error
-
-	// DeleteEdge deletes an edge from id1 to id2.
-	DeleteEdge(id1, id2 ID) error
-
-	// GetWeight returns the weight from id1 to id2.
-	GetWeight(id1, id2 ID) (float64, error)
-
-	// GetSources returns the map of parent Nodes.
-	// (Nodes that come towards the argument vertex.)
-	GetSources(id ID) (map[ID]Node, error)
-
-	// GetTargets returns the map of child Nodes.
-	// (Nodes that go out of the argument vertex.)
-	GetTargets(id ID) (map[ID]Node, error)
-
-	// String describes the Graph.
-	String() string
-}
-
-// graph is an internal default graph type that
-// implements all methods in Graph interface.
-type graph struct {
-	mu sync.RWMutex // guards the following
-
-	// idToNodes stores all nodes.
-	idToNodes map[ID]Node
-
-	// nodeToSources maps a Node identifer to sources(parents) with edge weights.
-	nodeToSources map[ID]map[ID]float64
-
-	// nodeToTargets maps a Node identifer to targets(children) with edge weights.
-	nodeToTargets map[ID]map[ID]float64
-}
-
-// newGraph returns a new graph.
-func newGraph() *graph {
-	return &graph{
-		idToNodes:     make(map[ID]Node),
-		nodeToSources: make(map[ID]map[ID]float64),
-		nodeToTargets: make(map[ID]map[ID]float64),
-		//
-		// without this
-		// panic: assignment to entry in nil map
-	}
-}
-
-// NewGraph returns a new graph.
-func NewGraph() Graph {
-	return newGraph()
-}
-
-func (g *graph) Init() {
-	// (X) g = newGraph()
-	// this only updates the pointer
-	//
-	//
-	// (X) *g = *newGraph()
-	// assignment copies lock value
-
-	g.idToNodes = make(map[ID]Node)
-	g.nodeToSources = make(map[ID]map[ID]float64)
-	g.nodeToTargets = make(map[ID]map[ID]float64)
-}
-
-func (g *graph) GetNodeCount() int {
-	g.mu.RLock()
-	defer g.mu.RUnlock()
-
-	return len(g.idToNodes)
-}
-
-func (g *graph) GetNode(id ID) Node {
-	g.mu.RLock()
-	defer g.mu.RUnlock()
-
-	return g.idToNodes[id]
-}
-
-func (g *graph) GetNodes() map[ID]Node {
-	g.mu.RLock()
-	defer g.mu.RUnlock()
-
-	return g.idToNodes
-}
-
-func (g *graph) unsafeExistID(id ID) bool {
-	_, ok := g.idToNodes[id]
-	return ok
-}
-
-func (g *graph) AddNode(nd Node) bool {
-	g.mu.Lock()
-	defer g.mu.Unlock()
-
-	if g.unsafeExistID(nd.ID()) {
-		return false
-	}
-
-	id := nd.ID()
-	g.idToNodes[id] = nd
-	return true
-}
-
-func (g *graph) DeleteNode(id ID) bool {
-	g.mu.Lock()
-	defer g.mu.Unlock()
-
-	if !g.unsafeExistID(id) {
-		return false
-	}
-
-	delete(g.idToNodes, id)
-
-	delete(g.nodeToTargets, id)
-	for _, smap := range g.nodeToTargets {
-		delete(smap, id)
-	}
-
-	delete(g.nodeToSources, id)
-	for _, smap := range g.nodeToSources {
-		delete(smap, id)
-	}
-
-	return true
-}
-
-func (g *graph) AddEdge(id1, id2 ID, weight float64) error {
-	g.mu.Lock()
-	defer g.mu.Unlock()
-
-	if !g.unsafeExistID(id1) {
-		return fmt.Errorf("%s does not exist in the graph.", id1)
-	}
-	if !g.unsafeExistID(id2) {
-		return fmt.Errorf("%s does not exist in the graph.", id2)
-	}
-
-	if _, ok := g.nodeToTargets[id1]; ok {
-		if v, ok2 := g.nodeToTargets[id1][id2]; ok2 {
-			g.nodeToTargets[id1][id2] = v + weight
-		} else {
-			g.nodeToTargets[id1][id2] = weight
-		}
-	} else {
-		tmap := make(map[ID]float64)
-		tmap[id2] = weight
-		g.nodeToTargets[id1] = tmap
-	}
-	if _, ok := g.nodeToSources[id2]; ok {
-		if v, ok2 := g.nodeToSources[id2][id1]; ok2 {
-			g.nodeToSources[id2][id1] = v + weight
-		} else {
-			g.nodeToSources[id2][id1] = weight
-		}
-	} else {
-		tmap := make(map[ID]float64)
-		tmap[id1] = weight
-		g.nodeToSources[id2] = tmap
-	}
-
-	return nil
-}
-
-func (g *graph) ReplaceEdge(id1, id2 ID, weight float64) error {
-	g.mu.Lock()
-	defer g.mu.Unlock()
-
-	if !g.unsafeExistID(id1) {
-		return fmt.Errorf("%s does not exist in the graph.", id1)
-	}
-	if !g.unsafeExistID(id2) {
-		return fmt.Errorf("%s does not exist in the graph.", id2)
-	}
-
-	if _, ok := g.nodeToTargets[id1]; ok {
-		g.nodeToTargets[id1][id2] = weight
-	} else {
-		tmap := make(map[ID]float64)
-		tmap[id2] = weight
-		g.nodeToTargets[id1] = tmap
-	}
-	if _, ok := g.nodeToSources[id2]; ok {
-		g.nodeToSources[id2][id1] = weight
-	} else {
-		tmap := make(map[ID]float64)
-		tmap[id1] = weight
-		g.nodeToSources[id2] = tmap
-	}
-	return nil
-}
-
-func (g *graph) DeleteEdge(id1, id2 ID) error {
-	g.mu.Lock()
-	defer g.mu.Unlock()
-
-	if !g.unsafeExistID(id1) {
-		return fmt.Errorf("%s does not exist in the graph.", id1)
-	}
-	if !g.unsafeExistID(id2) {
-		return fmt.Errorf("%s does not exist in the graph.", id2)
-	}
-
-	if _, ok := g.nodeToTargets[id1]; ok {
-		if _, ok := g.nodeToTargets[id1][id2]; ok {
-			delete(g.nodeToTargets[id1], id2)
-		}
-	}
-	if _, ok := g.nodeToSources[id2]; ok {
-		if _, ok := g.nodeToSources[id2][id1]; ok {
-			delete(g.nodeToSources[id2], id1)
-		}
-	}
-	return nil
-}
-
-func (g *graph) GetWeight(id1, id2 ID) (float64, error) {
-	g.mu.RLock()
-	defer g.mu.RUnlock()
-
-	if !g.unsafeExistID(id1) {
-		return 0, fmt.Errorf("%s does not exist in the graph.", id1)
-	}
-	if !g.unsafeExistID(id2) {
-		return 0, fmt.Errorf("%s does not exist in the graph.", id2)
-	}
-
-	if _, ok := g.nodeToTargets[id1]; ok {
-		if v, ok := g.nodeToTargets[id1][id2]; ok {
-			return v, nil
-		}
-	}
-	return 0.0, fmt.Errorf("there is no edge from %s to %s", id1, id2)
-}
-
-func (g *graph) GetSources(id ID) (map[ID]Node, error) {
-	g.mu.RLock()
-	defer g.mu.RUnlock()
-
-	if !g.unsafeExistID(id) {
-		return nil, fmt.Errorf("%s does not exist in the graph.", id)
-	}
-
-	rs := make(map[ID]Node)
-	if _, ok := g.nodeToSources[id]; ok {
-		for n := range g.nodeToSources[id] {
-			rs[n] = g.idToNodes[n]
-		}
-	}
-	return rs, nil
-}
-
-func (g *graph) GetTargets(id ID) (map[ID]Node, error) {
-	g.mu.RLock()
-	defer g.mu.RUnlock()
-
-	if !g.unsafeExistID(id) {
-		return nil, fmt.Errorf("%s does not exist in the graph.", id)
-	}
-
-	rs := make(map[ID]Node)
-	if _, ok := g.nodeToTargets[id]; ok {
-		for n := range g.nodeToTargets[id] {
-			rs[n] = g.idToNodes[n]
-		}
-	}
-	return rs, nil
-}
-
-func (g *graph) String() string {
-	g.mu.RLock()
-	defer g.mu.RUnlock()
-
-	buf := new(bytes.Buffer)
-	for id1, nd1 := range g.idToNodes {
-		nmap, _ := g.GetTargets(id1)
-		for id2, nd2 := range nmap {
-			weight, _ := g.GetWeight(id1, id2)
-			fmt.Fprintf(buf, "%s -- %.3f -→ %s\n", nd1, weight, nd2)
-		}
-	}
-	return buf.String()
-}
-
-// NewGraphFromJSON returns a new Graph from a JSON file.
-// Here's the sample JSON data:
-//
-//	{
-//	    "graph_00": {
-//	        "S": {
-//	            "A": 100,
-//	            "B": 14,
-//	            "C": 200
-//	        },
-//	        "A": {
-//	            "S": 15,
-//	            "B": 5,
-//	            "D": 20,
-//	            "T": 44
-//	        },
-//	        "B": {
-//	            "S": 14,
-//	            "A": 5,
-//	            "D": 30,
-//	            "E": 18
-//	        },
-//	        "C": {
-//	            "S": 9,
-//	            "E": 24
-//	        },
-//	        "D": {
-//	            "A": 20,
-//	            "B": 30,
-//	            "E": 2,
-//	            "F": 11,
-//	            "T": 16
-//	        },
-//	        "E": {
-//	            "B": 18,
-//	            "C": 24,
-//	            "D": 2,
-//	            "F": 6,
-//	            "T": 19
-//	        },
-//	        "F": {
-//	            "D": 11,
-//	            "E": 6,
-//	            "T": 6
-//	        },
-//	        "T": {
-//	            "A": 44,
-//	            "D": 16,
-//	            "F": 6,
-//	            "E": 19
-//	        }
-//	    },
-//	}
-//
-func NewGraphFromJSON(rd io.Reader, graphID string) (Graph, error) {
-	js := make(map[string]map[string]map[string]float64)
-	dec := json.NewDecoder(rd)
-	for {
-		if err := dec.Decode(&js); err == io.EOF {
-			break
-		} else if err != nil {
-			return nil, err
-		}
-	}
-	if _, ok := js[graphID]; !ok {
-		return nil, fmt.Errorf("%s does not exist", graphID)
-	}
-	gmap := js[graphID]
-
-	g := newGraph()
-	for id1, mm := range gmap {
-		nd1 := g.GetNode(StringID(id1))
-		if nd1 == nil {
-			nd1 = NewNode(id1)
-			if ok := g.AddNode(nd1); !ok {
-				return nil, fmt.Errorf("%s already exists", nd1)
-			}
-		}
-		for id2, weight := range mm {
-			nd2 := g.GetNode(StringID(id2))
-			if nd2 == nil {
-				nd2 = NewNode(id2)
-				if ok := g.AddNode(nd2); !ok {
-					return nil, fmt.Errorf("%s already exists", nd2)
-				}
-			}
-			g.ReplaceEdge(nd1.ID(), nd2.ID(), weight)
-		}
-	}
-
-	return g, nil
-}
diff --git a/vendor/github.com/gyuho/goraph/minimum_spanning_tree.go b/vendor/github.com/gyuho/goraph/minimum_spanning_tree.go
deleted file mode 100644
index a86e279..0000000
--- a/vendor/github.com/gyuho/goraph/minimum_spanning_tree.go
+++ /dev/null
@@ -1,287 +0,0 @@
-package goraph
-
-import (
-	"container/heap"
-	"math"
-	"sort"
-)
-
-// Kruskal finds the minimum spanning tree with disjoint-set data structure.
-// (http://en.wikipedia.org/wiki/Kruskal%27s_algorithm)
-//
-//	 0. Kruskal(G)
-//	 1.
-//	 2. 	A = ∅
-//	 3.
-//	 4. 	for each vertex v in G:
-//	 5. 		MakeDisjointSet(v)
-//	 6.
-//	 7. 	edges = get all edges
-//	 8. 	sort edges in ascending order of weight
-//	 9.
-//	10. 	for each edge (u, v) in edges:
-//	11. 		if FindSet(u) ≠ FindSet(v):
-//	12. 			A = A ∪ {(u, v)}
-//	13. 			Union(u, v)
-//	14.
-//	15. 	return A
-//
-func Kruskal(g Graph) (map[Edge]struct{}, error) {
-
-	// A = ∅
-	A := make(map[Edge]struct{})
-
-	// disjointSet maps a member Node to a represent.
-	// (https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
-	forests := NewForests()
-
-	// for each vertex v in G:
-	for _, nd := range g.GetNodes() {
-		// MakeDisjointSet(v)
-		MakeDisjointSet(forests, nd.String())
-	}
-
-	// edges = get all edges
-	edges := []Edge{}
-	foundEdge := make(map[string]struct{})
-	for id1, nd1 := range g.GetNodes() {
-		tm, err := g.GetTargets(id1)
-		if err != nil {
-			return nil, err
-		}
-		for id2, nd2 := range tm {
-			weight, err := g.GetWeight(id1, id2)
-			if err != nil {
-				return nil, err
-			}
-			edge := NewEdge(nd1, nd2, weight)
-			if _, ok := foundEdge[edge.String()]; !ok {
-				edges = append(edges, edge)
-				foundEdge[edge.String()] = struct{}{}
-			}
-		}
-
-		sm, err := g.GetSources(id1)
-		if err != nil {
-			return nil, err
-		}
-		for id3, nd3 := range sm {
-			weight, err := g.GetWeight(id3, id1)
-			if err != nil {
-				return nil, err
-			}
-			edge := NewEdge(nd3, nd1, weight)
-			if _, ok := foundEdge[edge.String()]; !ok {
-				edges = append(edges, edge)
-				foundEdge[edge.String()] = struct{}{}
-			}
-		}
-	}
-
-	// sort edges in ascending order of weight
-	sort.Sort(EdgeSlice(edges))
-
-	// for each edge (u, v) in edges:
-	for _, edge := range edges {
-		// if FindSet(u) ≠ FindSet(v):
-		if FindSet(forests, edge.Source().String()).represent != FindSet(forests, edge.Target().String()).represent {
-
-			// A = A ∪ {(u, v)}
-			A[edge] = struct{}{}
-
-			// Union(u, v)
-			// overwrite v's represent with u's represent
-			Union(forests, FindSet(forests, edge.Source().String()), FindSet(forests, edge.Target().String()))
-		}
-	}
-
-	return A, nil
-}
-
-// Prim finds the minimum spanning tree with min-heap (priority queue).
-// (http://en.wikipedia.org/wiki/Prim%27s_algorithm)
-//
-//	 0. Prim(G, source)
-//	 1.
-//	 2. 	let Q be a priority queue
-//	 3. 	distance[source] = 0
-//	 4.
-//	 5. 	for each vertex v in G:
-//	 6.
-//	 7. 		if v ≠ source:
-//	 8. 			distance[v] = ∞
-//	 9. 			prev[v] = undefined
-//	10.
-//	11. 		Q.add_with_priority(v, distance[v])
-//	12.
-//	13.
-//	14. 	while Q is not empty:
-//	15.
-//	16. 		u = Q.extract_min()
-//	17.
-//	18. 		for each adjacent vertex v of u:
-//	19.
-//	21. 			if v ∈ Q and distance[v] > weight(u, v):
-//	22. 				distance[v] = weight(u, v)
-//	23. 				prev[v] = u
-//	24. 				Q.decrease_priority(v, weight(u, v))
-//	25.
-//	26.
-//	27. 	return tree from prev
-//
-func Prim(g Graph, src ID) (map[Edge]struct{}, error) {
-
-	// let Q be a priority queue
-	minHeap := &nodeDistanceHeap{}
-
-	// distance[source] = 0
-	distance := make(map[ID]float64)
-	distance[src] = 0.0
-
-	// for each vertex v in G:
-	for id := range g.GetNodes() {
-
-		// if v ≠ src:
-		if id != src {
-			// distance[v] = ∞
-			distance[id] = math.MaxFloat64
-
-			// prev[v] = undefined
-			// prev[v] = ""
-		}
-
-		// Q.add_with_priority(v, distance[v])
-		nds := nodeDistance{}
-		nds.id = id
-		nds.distance = distance[id]
-
-		heap.Push(minHeap, nds)
-	}
-
-	heap.Init(minHeap)
-	prev := make(map[ID]ID)
-
-	// while Q is not empty:
-	for minHeap.Len() != 0 {
-
-		// u = Q.extract_min()
-		u := heap.Pop(minHeap).(nodeDistance)
-		uID := u.id
-
-		// for each adjacent vertex v of u:
-		tm, err := g.GetTargets(uID)
-		if err != nil {
-			return nil, err
-		}
-		for vID := range tm {
-
-			isExist := false
-			for _, one := range *minHeap {
-				if vID == one.id {
-					isExist = true
-					break
-				}
-			}
-
-			// weight(u, v)
-			weight, err := g.GetWeight(uID, vID)
-			if err != nil {
-				return nil, err
-			}
-
-			// if v ∈ Q and distance[v] > weight(u, v):
-			if isExist && distance[vID] > weight {
-
-				// distance[v] = weight(u, v)
-				distance[vID] = weight
-
-				// prev[v] = u
-				prev[vID] = uID
-
-				// Q.decrease_priority(v, weight(u, v))
-				minHeap.updateDistance(vID, weight)
-				heap.Init(minHeap)
-			}
-		}
-
-		sm, err := g.GetSources(uID)
-		if err != nil {
-			return nil, err
-		}
-		vID := uID
-		for uID := range sm {
-
-			isExist := false
-			for _, one := range *minHeap {
-				if vID == one.id {
-					isExist = true
-					break
-				}
-			}
-
-			// weight(u, v)
-			weight, err := g.GetWeight(uID, vID)
-			if err != nil {
-				return nil, err
-			}
-
-			// if v ∈ Q and distance[v] > weight(u, v):
-			if isExist && distance[vID] > weight {
-
-				// distance[v] = weight(u, v)
-				distance[vID] = weight
-
-				// prev[v] = u
-				prev[vID] = uID
-
-				// Q.decrease_priority(v, weight(u, v))
-				minHeap.updateDistance(vID, weight)
-				heap.Init(minHeap)
-			}
-		}
-	}
-
-	tree := make(map[Edge]struct{})
-	for k, v := range prev {
-		weight, err := g.GetWeight(v, k)
-		if err != nil {
-			return nil, err
-		}
-		tree[NewEdge(g.GetNode(v), g.GetNode(k), weight)] = struct{}{}
-	}
-	return tree, nil
-}
-
-type nodeDistance struct {
-	id       ID
-	distance float64
-}
-
-// container.Heap's Interface needs sort.Interface, Push, Pop to be implemented
-
-// nodeDistanceHeap is a min-heap of nodeDistances.
-type nodeDistanceHeap []nodeDistance
-
-func (h nodeDistanceHeap) Len() int           { return len(h) }
-func (h nodeDistanceHeap) Less(i, j int) bool { return h[i].distance < h[j].distance } // Min-Heap
-func (h nodeDistanceHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
-
-func (h *nodeDistanceHeap) Push(x interface{}) {
-	*h = append(*h, x.(nodeDistance))
-}
-
-func (h *nodeDistanceHeap) Pop() interface{} {
-	heapSize := len(*h)
-	lastNode := (*h)[heapSize-1]
-	*h = (*h)[0 : heapSize-1]
-	return lastNode
-}
-
-func (h *nodeDistanceHeap) updateDistance(id ID, val float64) {
-	for i := 0; i < len(*h); i++ {
-		if (*h)[i].id == id {
-			(*h)[i].distance = val
-			break
-		}
-	}
-}
diff --git a/vendor/github.com/gyuho/goraph/shortest_path.go b/vendor/github.com/gyuho/goraph/shortest_path.go
deleted file mode 100644
index e6f405c..0000000
--- a/vendor/github.com/gyuho/goraph/shortest_path.go
+++ /dev/null
@@ -1,348 +0,0 @@
-package goraph
-
-import (
-	"container/heap"
-	"fmt"
-	"math"
-)
-
-// Dijkstra returns the shortest path using Dijkstra
-// algorithm with a min-priority queue. This algorithm
-// does not work with negative weight edges.
-// (https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)
-//
-//	 0. Dijkstra(G, source, target)
-//	 1.
-//	 2. 	let Q be a priority queue
-//	 3. 	distance[source] = 0
-//	 4.
-//	 5. 	for each vertex v in G:
-//	 6.
-//	 7. 		if v ≠ source:
-//	 8. 			distance[v] = ∞
-//	 9. 			prev[v] = undefined
-//	10.
-//	11. 		Q.add_with_priority(v, distance[v])
-//	12.
-//	13. 	while Q is not empty:
-//	14.
-//	15. 		u = Q.extract_min()
-//	16. 		if u == target:
-//	17. 			break
-//	18.
-//	19. 		for each child vertex v of u:
-//	20.
-//	21. 			alt = distance[u] + weight(u, v)
-//	22. 			if distance[v] > alt:
-//	23. 				distance[v] = alt
-//	24. 				prev[v] = u
-//	25. 				Q.decrease_priority(v, alt)
-//	26.
-//	27. 		reheapify(Q)
-//	28.
-//	29.
-//	30. 	path = []
-//	31. 	u = target
-//	32. 	while prev[u] is defined:
-//	33. 		path.push_front(u)
-//	34. 		u = prev[u]
-//	35.
-//	36. 	return path, prev
-//
-func Dijkstra(g Graph, source, target ID) ([]ID, map[ID]float64, error) {
-	// let Q be a priority queue
-	minHeap := &nodeDistanceHeap{}
-
-	// distance[source] = 0
-	distance := make(map[ID]float64)
-	distance[source] = 0.0
-
-	// for each vertex v in G:
-	for id := range g.GetNodes() {
-		// if v ≠ source:
-		if id != source {
-			// distance[v] = ∞
-			distance[id] = math.MaxFloat64
-
-			// prev[v] = undefined
-			// prev[v] = ""
-		}
-
-		// Q.add_with_priority(v, distance[v])
-		nds := nodeDistance{}
-		nds.id = id
-		nds.distance = distance[id]
-
-		heap.Push(minHeap, nds)
-	}
-
-	heap.Init(minHeap)
-	prev := make(map[ID]ID)
-
-	// while Q is not empty:
-	for minHeap.Len() != 0 {
-
-		// u = Q.extract_min()
-		u := heap.Pop(minHeap).(nodeDistance)
-
-		// if u == target:
-		if u.id == target {
-			break
-		}
-
-		// for each child vertex v of u:
-		cmap, err := g.GetTargets(u.id)
-		if err != nil {
-			return nil, nil, err
-		}
-		for v := range cmap {
-
-			// alt = distance[u] + weight(u, v)
-			weight, err := g.GetWeight(u.id, v)
-			if err != nil {
-				return nil, nil, err
-			}
-			alt := distance[u.id] + weight
-
-			// if distance[v] > alt:
-			if distance[v] > alt {
-
-				// distance[v] = alt
-				distance[v] = alt
-
-				// prev[v] = u
-				prev[v] = u.id
-
-				// Q.decrease_priority(v, alt)
-				minHeap.updateDistance(v, alt)
-			}
-		}
-		heap.Init(minHeap)
-	}
-
-	// path = []
-	path := []ID{}
-
-	// u = target
-	u := target
-
-	// while prev[u] is defined:
-	for {
-		if _, ok := prev[u]; !ok {
-			break
-		}
-		// path.push_front(u)
-		temp := make([]ID, len(path)+1)
-		temp[0] = u
-		copy(temp[1:], path)
-		path = temp
-
-		// u = prev[u]
-		u = prev[u]
-	}
-
-	// add the source
-	temp := make([]ID, len(path)+1)
-	temp[0] = source
-	copy(temp[1:], path)
-	path = temp
-
-	return path, distance, nil
-}
-
-// BellmanFord returns the shortest path using Bellman-Ford algorithm
-// This algorithm works with negative weight edges.
-// Time complexity is O(|V||E|).
-// (http://courses.csail.mit.edu/6.006/spring11/lectures/lec15.pdf)
-// It returns error when there is a negative-weight cycle.
-// A negatively-weighted cycle adds up to infinite negative-weight.
-//
-//	 0. BellmanFord(G, source, target)
-//	 1.
-//	 2. 	distance[source] = 0
-//	 3.
-//	 4. 	for each vertex v in G:
-//	 5.
-//	 6. 		if v ≠ source:
-//	 7. 			distance[v] = ∞
-//	 8. 			prev[v] = undefined
-//	 9.
-//	10.
-//	11. 	for 1 to |V|-1:
-//	12.
-//	13. 		for every edge (u, v):
-//	14.
-//	15. 			alt = distance[u] + weight(u, v)
-//	16. 			if distance[v] > alt:
-//	17. 				distance[v] = alt
-//	18. 				prev[v] = u
-//	19.
-//	20.
-//	21. 	for every edge (u, v):
-//	22.
-//	23. 		alt = distance[u] + weight(u, v)
-//	24. 		if distance[v] > alt:
-//	25. 			there is a negative-weight cycle
-//	26.
-//	27.
-//	28. 	path = []
-//	29. 	u = target
-//	30. 	while prev[u] is defined:
-//	31. 		path.push_front(u)
-//	32. 		u = prev[u]
-//	33.
-//	34. 	return path, prev
-//
-func BellmanFord(g Graph, source, target ID) ([]ID, map[ID]float64, error) {
-	// distance[source] = 0
-	distance := make(map[ID]float64)
-	distance[source] = 0.0
-
-	// for each vertex v in G:
-	for id := range g.GetNodes() {
-
-		// if v ≠ source:
-		if id != source {
-			// distance[v] = ∞
-			distance[id] = math.MaxFloat64
-
-			// prev[v] = undefined
-			// prev[v] = ""
-		}
-	}
-
-	prev := make(map[ID]ID)
-
-	// for 1 to |V|-1:
-	for i := 1; i <= g.GetNodeCount()-1; i++ {
-
-		// for every edge (u, v):
-		for id := range g.GetNodes() {
-
-			cmap, err := g.GetTargets(id)
-			if err != nil {
-				return nil, nil, err
-			}
-			u := id
-			for v := range cmap {
-				// edge (u, v)
-				weight, err := g.GetWeight(u, v)
-				if err != nil {
-					return nil, nil, err
-				}
-
-				// alt = distance[u] + weight(u, v)
-				alt := distance[u] + weight
-
-				// if distance[v] > alt:
-				if distance[v] > alt {
-					// distance[v] = alt
-					distance[v] = alt
-
-					// prev[v] = u
-					prev[v] = u
-				}
-			}
-
-			pmap, err := g.GetSources(id)
-			if err != nil {
-				return nil, nil, err
-			}
-			v := id
-			for u := range pmap {
-				// edge (u, v)
-				weight, err := g.GetWeight(u, v)
-				if err != nil {
-					return nil, nil, err
-				}
-
-				// alt = distance[u] + weight(u, v)
-				alt := distance[u] + weight
-
-				// if distance[v] > alt:
-				if distance[v] > alt {
-					// distance[v] = alt
-					distance[v] = alt
-
-					// prev[v] = u
-					prev[v] = u
-				}
-			}
-		}
-	}
-
-	// for every edge (u, v):
-	for id := range g.GetNodes() {
-
-		cmap, err := g.GetTargets(id)
-		if err != nil {
-			return nil, nil, err
-		}
-		u := id
-		for v := range cmap {
-			// edge (u, v)
-			weight, err := g.GetWeight(u, v)
-			if err != nil {
-				return nil, nil, err
-			}
-
-			// alt = distance[u] + weight(u, v)
-			alt := distance[u] + weight
-
-			// if distance[v] > alt:
-			if distance[v] > alt {
-				return nil, nil, fmt.Errorf("there is a negative-weight cycle: %v", g)
-			}
-		}
-
-		pmap, err := g.GetSources(id)
-		if err != nil {
-			return nil, nil, err
-		}
-		v := id
-		for u := range pmap {
-			// edge (u, v)
-			weight, err := g.GetWeight(u, v)
-			if err != nil {
-				return nil, nil, err
-			}
-
-			// alt = distance[u] + weight(u, v)
-			alt := distance[u] + weight
-
-			// if distance[v] > alt:
-			if distance[v] > alt {
-				return nil, nil, fmt.Errorf("there is a negative-weight cycle: %v", g)
-			}
-		}
-	}
-
-	// path = []
-	path := []ID{}
-
-	// u = target
-	u := target
-
-	// while prev[u] is defined:
-	for {
-		if _, ok := prev[u]; !ok {
-			break
-		}
-		// path.push_front(u)
-		temp := make([]ID, len(path)+1)
-		temp[0] = u
-		copy(temp[1:], path)
-		path = temp
-
-		// u = prev[u]
-		u = prev[u]
-	}
-
-	// add the source
-	temp := make([]ID, len(path)+1)
-	temp[0] = source
-	copy(temp[1:], path)
-	path = temp
-
-	return path, distance, nil
-}
diff --git a/vendor/github.com/gyuho/goraph/strongly_connected_components.go b/vendor/github.com/gyuho/goraph/strongly_connected_components.go
deleted file mode 100644
index b0a11a5..0000000
--- a/vendor/github.com/gyuho/goraph/strongly_connected_components.go
+++ /dev/null
@@ -1,195 +0,0 @@
-package goraph
-
-import "sync"
-
-// Tarjan finds the strongly connected components.
-// In the mathematics, a directed graph is "strongly connected"
-// if every vertex is reachable from every other node.
-// Therefore, a graph is strongly connected if there is a path
-// in each direction between each pair of node of a graph.
-// Then a pair of vertices u and v is strongly connected to each other
-// because there is a path in each direction.
-// "Strongly connected components" of an arbitrary graph
-// partition into sub-graphs that are themselves strongly connected.
-// That is, "strongly connected component" of a directed graph
-// is a sub-graph that is strongly connected.
-// Formally, "Strongly connected components" of a graph is a maximal
-// set of vertices C in G.V such that for all u, v ∈ C, there is a path
-// both from u to v, and from v to u.
-// (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)
-//
-//	 0. Tarjan(G):
-//	 1.
-//	 2. 	globalIndex = 0 // smallest unused index
-//	 3. 	let S be a stack
-//	 4. 	result = [][]
-//	 5.
-//	 6. 	for each vertex v in G:
-//	 7. 		if v.index is undefined:
-//	 8. 			tarjan(G, v, globalIndex, S, result)
-//	 9.
-//	10. 	return result
-//	11.
-//	12.
-//	13. tarjan(G, v, globalIndex, S, result):
-//	14.
-//	15. 	v.index = globalIndex
-//	16. 	v.lowLink = globalIndex
-//	17. 	globalIndex++
-//	18. 	S.push(v)
-//	19.
-//	20. 	for each child vertex w of v:
-//	21.
-//	22. 		if w.index is undefined:
-//	23. 			recursively tarjan(G, w, globalIndex, S, result)
-//	24. 			v.lowLink = min(v.lowLink, w.lowLink)
-//	25.
-//	26. 		else if w is in S:
-//	27. 			v.lowLink = min(v.lowLink, w.index)
-//	28.
-//	29. 	// if v is the root
-//	30. 	if v.lowLink == v.index:
-//	31.
-//	32. 		// start a new strongly connected component
-//	33. 		component = []
-//	34.
-//	35. 		while True:
-//	36.
-//	37. 			u = S.pop()
-//	38. 			component.push(u)
-//	39.
-//	40. 			if u == v:
-//	41. 				result.push(component)
-//	42. 				break
-//
-func Tarjan(g Graph) [][]ID {
-	d := newTarjanData()
-
-	// for each vertex v in G:
-	for v := range g.GetNodes() {
-		// if v.index is undefined:
-		if _, ok := d.index[v]; !ok {
-			// tarjan(G, v, globalIndex, S, result)
-			tarjan(g, v, d)
-		}
-	}
-	return d.result
-}
-
-type tarjanData struct {
-	mu sync.Mutex // guards the following
-
-	// globalIndex is the smallest unused index
-	globalIndex int
-
-	// index is an index of a node to record
-	// the order of being discovered.
-	index map[ID]int
-
-	// lowLink is the smallest index of any index
-	// reachable from v, including v itself.
-	lowLink map[ID]int
-
-	// S is the stack.
-	S []ID
-
-	// extra map to check if a vertex is in S.
-	smap map[ID]struct{}
-
-	result [][]ID
-}
-
-func newTarjanData() *tarjanData {
-	return &tarjanData{
-		globalIndex: 0,
-		index:       make(map[ID]int),
-		lowLink:     make(map[ID]int),
-		S:           []ID{},
-		smap:        make(map[ID]struct{}),
-		result:      [][]ID{},
-	}
-}
-
-func tarjan(
-	g Graph,
-	id ID,
-	data *tarjanData,
-) {
-	// This is not inherently parallelizable problem,
-	// but just to make sure.
-	data.mu.Lock()
-
-	// v.index = globalIndex
-	data.index[id] = data.globalIndex
-
-	// v.lowLink = globalIndex
-	data.lowLink[id] = data.globalIndex
-
-	// globalIndex++
-	data.globalIndex++
-
-	// S.push(v)
-	data.S = append(data.S, id)
-	data.smap[id] = struct{}{}
-
-	data.mu.Unlock()
-
-	// for each child vertex w of v:
-	cmap, err := g.GetTargets(id)
-	if err != nil {
-		panic(err)
-	}
-	for w := range cmap {
-
-		// if w.index is undefined:
-		if _, ok := data.index[w]; !ok {
-
-			// recursively tarjan(G, w, globalIndex, S, result)
-			tarjan(g, w, data)
-
-			// v.lowLink = min(v.lowLink, w.lowLink)
-			data.lowLink[id] = min(data.lowLink[id], data.lowLink[w])
-
-		} else if _, ok := data.smap[w]; ok {
-			// else if w is in S:
-
-			// v.lowLink = min(v.lowLink, w.index)
-			data.lowLink[id] = min(data.lowLink[id], data.index[w])
-		}
-	}
-
-	data.mu.Lock()
-	defer data.mu.Unlock()
-
-	// if v is the root
-	// if v.lowLink == v.index:
-	if data.lowLink[id] == data.index[id] {
-		// start a new strongly connected component
-		component := []ID{}
-
-		// while True:
-		for {
-
-			// u = S.pop()
-			u := data.S[len(data.S)-1]
-			data.S = data.S[:len(data.S)-1 : len(data.S)-1]
-			delete(data.smap, u)
-
-			// component.push(u)
-			component = append(component, u)
-
-			// if u == v:
-			if u == id {
-				data.result = append(data.result, component)
-				break
-			}
-		}
-	}
-}
-
-func min(a, b int) int {
-	if a < b {
-		return a
-	}
-	return b
-}
diff --git a/vendor/github.com/gyuho/goraph/test b/vendor/github.com/gyuho/goraph/test
deleted file mode 100755
index a7d8a6d..0000000
--- a/vendor/github.com/gyuho/goraph/test
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-TEST=./...;
-FMT="*.go"
-
-echo "Running tests...";
-go test -v -cover -cpu 1,2,4 $TEST;
-go test -v -cover -cpu 1,2,4 -race $TEST;
-
-echo "Checking gofmt..."
-fmtRes=$(gofmt -l -s $FMT)
-if [ -n "${fmtRes}" ]; then
-	echo -e "gofmt checking failed:\n${fmtRes}"
-	exit 255
-fi
-
-echo "Checking govet..."
-vetRes=$(go vet $TEST)
-if [ -n "${vetRes}" ]; then
-	echo -e "govet checking failed:\n${vetRes}"
-	exit 255
-fi
-
-echo "Success";
diff --git a/vendor/github.com/gyuho/goraph/topological_sort.go b/vendor/github.com/gyuho/goraph/topological_sort.go
deleted file mode 100644
index b63675a..0000000
--- a/vendor/github.com/gyuho/goraph/topological_sort.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package goraph
-
-// TopologicalSort does topological sort(ordering) with DFS.
-// It returns true if the graph is a DAG (no cycle, with a topological sort).
-// False if the graph is not a DAG (cycle, with no topological sort).
-//
-//	 0. TopologicalSort(G)
-//	 1.
-//	 2. 	L = Empty list that will contain the sorted nodes
-//	 3. 	isDAG = true
-//	 4.
-//	 5. 	for each vertex v in G:
-//	 6.
-//	 7. 		if v.color == "white":
-//	 8.
-//	 9. 			topologicalSortVisit(v, L, isDAG)
-//	10.
-//	11.
-//	12.
-//	13.
-//	14. topologicalSortVisit(v, L, isDAG)
-//	15.
-//	16. 	if v.color == "gray":
-//	17. 		isDAG = false
-//	18. 		return
-//	19.
-//	20. 	if v.color == "white":
-//	21.
-//	22. 		v.color = "gray":
-//	23.
-//	24.			for each child vertex w of v:
-//	25. 			topologicalSortVisit(w, L, isDAG)
-//	26.
-//	27. 		v.color = "black"
-//	28.			L.push_front(v)
-//
-func TopologicalSort(g Graph) ([]ID, bool) {
-
-	// L = Empty list that will contain the sorted nodes
-	L := []ID{}
-	isDAG := true
-	color := make(map[ID]string)
-	for v := range g.GetNodes() {
-		color[v] = "white"
-	}
-
-	// for each vertex v in G:
-	for v := range g.GetNodes() {
-		// if v.color == "white":
-		if color[v] == "white" {
-			// topologicalSortVisit(v, L, isDAG)
-			topologicalSortVisit(g, v, &L, &isDAG, &color)
-		}
-	}
-
-	return L, isDAG
-}
-
-func topologicalSortVisit(
-	g Graph,
-	id ID,
-	L *[]ID,
-	isDAG *bool,
-	color *map[ID]string,
-) {
-
-	// if v.color == "gray":
-	if (*color)[id] == "gray" {
-		// isDAG = false
-		*isDAG = false
-		return
-	}
-
-	// if v.color == "white":
-	if (*color)[id] == "white" {
-		// v.color = "gray":
-		(*color)[id] = "gray"
-
-		// for each child vertex w of v:
-		cmap, err := g.GetTargets(id)
-		if err != nil {
-			panic(err)
-		}
-		for w := range cmap {
-			// topologicalSortVisit(w, L, isDAG)
-			topologicalSortVisit(g, w, L, isDAG, color)
-		}
-
-		// v.color = "black"
-		(*color)[id] = "black"
-
-		// L.push_front(v)
-		temp := make([]ID, len(*L)+1)
-		temp[0] = id
-		copy(temp[1:], *L)
-		*L = temp
-	}
-}
diff --git a/vendor/github.com/gyuho/goraph/traversal.go b/vendor/github.com/gyuho/goraph/traversal.go
deleted file mode 100644
index fa45c29..0000000
--- a/vendor/github.com/gyuho/goraph/traversal.go
+++ /dev/null
@@ -1,184 +0,0 @@
-package goraph
-
-// BFS does breadth-first search, and returns the list of vertices.
-// (https://en.wikipedia.org/wiki/Breadth-first_search)
-//
-//	 0. BFS(G, v):
-//	 1.
-//	 2. 	let Q be a queue
-//	 3. 	Q.push(v)
-//	 4. 	label v as visited
-//	 5.
-//	 6. 	while Q is not empty:
-//	 7.
-//	 8. 		u = Q.dequeue()
-//	 9.
-//	10. 		for each vertex w adjacent to u:
-//	11.
-//	12. 			if w is not visited yet:
-//	13. 				Q.push(w)
-//	14. 				label w as visited
-//
-func BFS(g Graph, id ID) []ID {
-	if g.GetNode(id) == nil {
-		return nil
-	}
-
-	q := []ID{id}
-	visited := make(map[ID]bool)
-	visited[id] = true
-	rs := []ID{id}
-
-	// while Q is not empty:
-	for len(q) != 0 {
-
-		u := q[0]
-		q = q[1:len(q):len(q)]
-
-		// for each vertex w adjacent to u:
-		cmap, _ := g.GetTargets(u)
-		for _, w := range cmap {
-			// if w is not visited yet:
-			if _, ok := visited[w.ID()]; !ok {
-				q = append(q, w.ID())  // Q.push(w)
-				visited[w.ID()] = true // label w as visited
-
-				rs = append(rs, w)
-			}
-		}
-		pmap, _ := g.GetSources(u)
-		for _, w := range pmap {
-			// if w is not visited yet:
-			if _, ok := visited[w.ID()]; !ok {
-				q = append(q, w.ID())  // Q.push(w)
-				visited[w.ID()] = true // label w as visited
-
-				rs = append(rs, w.ID())
-			}
-		}
-	}
-
-	return rs
-}
-
-// DFS does depth-first search, and returns the list of vertices.
-// (https://en.wikipedia.org/wiki/Depth-first_search)
-//
-//	 0. DFS(G, v):
-//	 1.
-//	 2. 	let S be a stack
-//	 3. 	S.push(v)
-//	 4.
-//	 5. 	while S is not empty:
-//	 6.
-//	 7. 		u = S.pop()
-//	 8.
-//	 9. 		if u is not visited yet:
-//	10.
-//	11. 			label u as visited
-//	12.
-//	13. 			for each vertex w adjacent to u:
-//	14.
-//	15. 				if w is not visited yet:
-//	16. 					S.push(w)
-//
-func DFS(g Graph, id ID) []ID {
-	if g.GetNode(id) == nil {
-		return nil
-	}
-
-	s := []ID{id}
-	visited := make(map[ID]bool)
-	rs := []ID{}
-
-	// while S is not empty:
-	for len(s) != 0 {
-
-		u := s[len(s)-1]
-		s = s[:len(s)-1 : len(s)-1]
-
-		// if u is not visited yet:
-		if _, ok := visited[u]; !ok {
-			// label u as visited
-			visited[u] = true
-
-			rs = append(rs, u)
-
-			// for each vertex w adjacent to u:
-			cmap, _ := g.GetTargets(u)
-			for _, w := range cmap {
-				// if w is not visited yet:
-				if _, ok := visited[w.ID()]; !ok {
-					s = append(s, w.ID()) // S.push(w)
-				}
-			}
-			pmap, _ := g.GetSources(u)
-			for _, w := range pmap {
-				// if w is not visited yet:
-				if _, ok := visited[w.ID()]; !ok {
-					s = append(s, w.ID()) // S.push(w)
-				}
-			}
-		}
-	}
-
-	return rs
-}
-
-// DFSRecursion does depth-first search recursively.
-//
-//	 0. DFS(G, v):
-//	 1.
-//	 2. 	if v is visited:
-//	 3. 		return
-//	 4.
-//	 5. 	label v as visited
-//	 6.
-//	 7. 	for each vertex u adjacent to v:
-//	 8.
-//	 9. 		if u is not visited yet:
-//	10. 			recursive DFS(G, u)
-//
-func DFSRecursion(g Graph, id ID) []ID {
-	if g.GetNode(id) == nil {
-		return nil
-	}
-
-	visited := make(map[ID]bool)
-	rs := []ID{}
-
-	dfsRecursion(g, id, visited, &rs)
-
-	return rs
-}
-
-func dfsRecursion(g Graph, id ID, visited map[ID]bool, rs *[]ID) {
-	// base case of recursion
-	//
-	// if v is visited:
-	if _, ok := visited[id]; ok {
-		return
-	}
-
-	// label v as visited
-	visited[id] = true
-	*rs = append(*rs, id)
-
-	// for each vertex u adjacent to v:
-	cmap, _ := g.GetTargets(id)
-	for _, u := range cmap {
-		// if u is not visited yet:
-		if _, ok := visited[u.ID()]; !ok {
-			// recursive DFS(G, u)
-			dfsRecursion(g, u.ID(), visited, rs)
-		}
-	}
-	pmap, _ := g.GetSources(id)
-	for _, u := range pmap {
-		// if u is not visited yet:
-		if _, ok := visited[u.ID()]; !ok {
-			// recursive DFS(G, u)
-			dfsRecursion(g, u.ID(), visited, rs)
-		}
-	}
-}
diff --git a/vendor/github.com/opencord/voltha-go/common/ponresourcemanager/ponresourcemanager.go b/vendor/github.com/opencord/voltha-go/common/ponresourcemanager/ponresourcemanager.go
index 2873dbc..b4f9130 100755
--- a/vendor/github.com/opencord/voltha-go/common/ponresourcemanager/ponresourcemanager.go
+++ b/vendor/github.com/opencord/voltha-go/common/ponresourcemanager/ponresourcemanager.go
@@ -182,7 +182,7 @@
 		return nil, errors.New("Failed to init KV client")
 	}
 	// Initialize techprofile for this technology
-	if PONMgr.TechProfileMgr, _ = tp.NewTechProfile(&PONMgr); PONMgr.TechProfileMgr == nil {
+	if PONMgr.TechProfileMgr, _ = tp.NewTechProfile(&PONMgr, Backend, Host, Port); PONMgr.TechProfileMgr == nil {
 		log.Error("Techprofile initialization failed")
 		return nil, errors.New("Failed to init tech profile")
 	}
diff --git a/vendor/github.com/opencord/voltha-go/common/techprofile/config.go b/vendor/github.com/opencord/voltha-go/common/techprofile/config.go
index 5312dc0..9d521ed 100644
--- a/vendor/github.com/opencord/voltha-go/common/techprofile/config.go
+++ b/vendor/github.com/opencord/voltha-go/common/techprofile/config.go
@@ -30,9 +30,9 @@
 	defaultPbits                  = "0b11111111"
 
 	defaultKVStoreType    = "etcd"
-	defaultKVStoreTimeout = 5            //in seconds
-	defaultKVStoreHost    = "172.21.0.8" // TODO: Need to get IP from adapter
-	defaultKVStorePort    = 2379         // Consul = 8500; Etcd = 2379
+	defaultKVStoreTimeout = 5 //in seconds
+	defaultKVStoreHost    = "127.0.0.1"
+	defaultKVStorePort    = 2379 // Consul = 8500; Etcd = 2379
 
 	// Tech profile path prefix in kv store
 	defaultKVPathPrefix = "service/voltha/technology_profiles"
@@ -98,13 +98,13 @@
 	DefaultNumTconts     uint32
 }
 
-func NewTechProfileFlags() *TechProfileFlags {
+func NewTechProfileFlags(KVStoreType string, KVStoreHost string, KVStorePort int) *TechProfileFlags {
 	// initialize with default values
 	var techProfileFlags = TechProfileFlags{
 		KVBackend:            nil,
-		KVStoreHost:          defaultKVStoreHost,
-		KVStorePort:          defaultKVStorePort,
-		KVStoreType:          defaultKVStoreType,
+		KVStoreHost:          KVStoreHost,
+		KVStorePort:          KVStorePort,
+		KVStoreType:          KVStoreType,
 		KVStoreTimeout:       defaultKVStoreTimeout,
 		DefaultTPName:        defaultTechProfileName,
 		TPKVPathPrefix:       defaultKVPathPrefix,
diff --git a/vendor/github.com/opencord/voltha-go/common/techprofile/tech_profile.go b/vendor/github.com/opencord/voltha-go/common/techprofile/tech_profile.go
index 2879e99..e41e064 100644
--- a/vendor/github.com/opencord/voltha-go/common/techprofile/tech_profile.go
+++ b/vendor/github.com/opencord/voltha-go/common/techprofile/tech_profile.go
@@ -255,7 +255,7 @@
 
 func newKVClient(storeType string, address string, timeout int) (kvstore.Client, error) {
 
-	log.Infow("kv-store-type", log.Fields{"store": storeType})
+	log.Infow("kv-store", log.Fields{"storeType": storeType, "address": address})
 	switch storeType {
 	case "consul":
 		return kvstore.NewConsulClient(address, timeout)
@@ -265,10 +265,10 @@
 	return nil, errors.New("unsupported-kv-store")
 }
 
-func NewTechProfile(resourceMgr iPonResourceMgr) (*TechProfileMgr, error) {
+func NewTechProfile(resourceMgr iPonResourceMgr, KVStoreType string, KVStoreHost string, KVStorePort int) (*TechProfileMgr, error) {
 	var techprofileObj TechProfileMgr
 	log.Debug("Initializing techprofile Manager")
-	techprofileObj.config = NewTechProfileFlags()
+	techprofileObj.config = NewTechProfileFlags(KVStoreType, KVStoreHost, KVStorePort)
 	techprofileObj.config.KVBackend = techprofileObj.SetKVClient()
 	if techprofileObj.config.KVBackend == nil {
 		log.Error("Failed to initialize KV backend\n")
diff --git a/vendor/github.com/opencord/voltha-go/rw_core/coreIf/device_manager_if.go b/vendor/github.com/opencord/voltha-go/rw_core/coreIf/device_manager_if.go
deleted file mode 100644
index 367f442..0000000
--- a/vendor/github.com/opencord/voltha-go/rw_core/coreIf/device_manager_if.go
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
-Defines a DeviceManager Interface - Used for unit testing of the flow decomposer only at this
-time.
-*/
-package coreIf
-
-import "github.com/opencord/voltha-protos/go/voltha"
-
-// DeviceManager represents a generic device manager
-type DeviceManager interface {
-	GetDevice(string) (*voltha.Device, error)
-	IsRootDevice(string) (bool, error)
-	NotifyInvalidTransition(*voltha.Device) error
-	SetAdminStateToEnable(*voltha.Device) error
-	CreateLogicalDevice(*voltha.Device) error
-	SetupUNILogicalPorts(*voltha.Device) error
-	DisableAllChildDevices(cDevice *voltha.Device) error
-	DeleteLogicalDevice(cDevice *voltha.Device) error
-	DeleteLogicalPorts(cDevice *voltha.Device) error
-	DeleteAllChildDevices(cDevice *voltha.Device) error
-	RunPostDeviceDelete(cDevice *voltha.Device) error
-}
diff --git a/vendor/github.com/opencord/voltha-go/rw_core/coreIf/logical_device_agent_if.go b/vendor/github.com/opencord/voltha-go/rw_core/coreIf/logical_device_agent_if.go
deleted file mode 100644
index c2614b2..0000000
--- a/vendor/github.com/opencord/voltha-go/rw_core/coreIf/logical_device_agent_if.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
-  Defines a logicalDeviceAgent Interface - Used for unit testing of the flow decomposer only at this
- time.
-*/
-package coreIf
-
-import (
-	"github.com/opencord/voltha-go/rw_core/graph"
-	"github.com/opencord/voltha-go/rw_core/utils"
-	"github.com/opencord/voltha-protos/go/voltha"
-)
-
-// LogicalAgent represents a generic agent
-type LogicalDeviceAgent interface {
-	GetLogicalDevice() (*voltha.LogicalDevice, error)
-	GetDeviceGraph() *graph.DeviceGraph
-	GetAllDefaultRules() *utils.DeviceRules
-	GetWildcardInputPorts(excludePort ...uint32) []uint32
-	GetRoute(ingressPortNo uint32, egressPortNo uint32) []graph.RouteHop
-}
diff --git a/vendor/github.com/opencord/voltha-go/rw_core/flow_decomposition/flow_decomposer.go b/vendor/github.com/opencord/voltha-go/rw_core/flow_decomposition/flow_decomposer.go
deleted file mode 100644
index 41fdc4a..0000000
--- a/vendor/github.com/opencord/voltha-go/rw_core/flow_decomposition/flow_decomposer.go
+++ /dev/null
@@ -1,1314 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package flow_decomposition
-
-import (
-	"bytes"
-	"crypto/md5"
-	"fmt"
-	"github.com/gogo/protobuf/proto"
-	"github.com/opencord/voltha-go/common/log"
-	"github.com/opencord/voltha-go/rw_core/coreIf"
-	"github.com/opencord/voltha-go/rw_core/graph"
-	fu "github.com/opencord/voltha-go/rw_core/utils"
-	ofp "github.com/opencord/voltha-protos/go/openflow_13"
-	"github.com/opencord/voltha-protos/go/voltha"
-	"math/big"
-)
-
-func init() {
-	log.AddPackage(log.JSON, log.DebugLevel, nil)
-}
-
-var (
-	// Instructions shortcut
-	APPLY_ACTIONS = ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS
-
-	//OFPAT_* shortcuts
-	OUTPUT       = ofp.OfpActionType_OFPAT_OUTPUT
-	COPY_TTL_OUT = ofp.OfpActionType_OFPAT_COPY_TTL_OUT
-	COPY_TTL_IN  = ofp.OfpActionType_OFPAT_COPY_TTL_IN
-	SET_MPLS_TTL = ofp.OfpActionType_OFPAT_SET_MPLS_TTL
-	DEC_MPLS_TTL = ofp.OfpActionType_OFPAT_DEC_MPLS_TTL
-	PUSH_VLAN    = ofp.OfpActionType_OFPAT_PUSH_VLAN
-	POP_VLAN     = ofp.OfpActionType_OFPAT_POP_VLAN
-	PUSH_MPLS    = ofp.OfpActionType_OFPAT_PUSH_MPLS
-	POP_MPLS     = ofp.OfpActionType_OFPAT_POP_MPLS
-	SET_QUEUE    = ofp.OfpActionType_OFPAT_SET_QUEUE
-	GROUP        = ofp.OfpActionType_OFPAT_GROUP
-	SET_NW_TTL   = ofp.OfpActionType_OFPAT_SET_NW_TTL
-	NW_TTL       = ofp.OfpActionType_OFPAT_DEC_NW_TTL
-	SET_FIELD    = ofp.OfpActionType_OFPAT_SET_FIELD
-	PUSH_PBB     = ofp.OfpActionType_OFPAT_PUSH_PBB
-	POP_PBB      = ofp.OfpActionType_OFPAT_POP_PBB
-	EXPERIMENTER = ofp.OfpActionType_OFPAT_EXPERIMENTER
-
-	//OFPXMT_OFB_* shortcuts (incomplete)
-	IN_PORT         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IN_PORT
-	IN_PHY_PORT     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IN_PHY_PORT
-	METADATA        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_METADATA
-	ETH_DST         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_DST
-	ETH_SRC         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_SRC
-	ETH_TYPE        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_TYPE
-	VLAN_VID        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID
-	VLAN_PCP        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP
-	IP_DSCP         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_DSCP
-	IP_ECN          = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_ECN
-	IP_PROTO        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_PROTO
-	IPV4_SRC        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_SRC
-	IPV4_DST        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST
-	TCP_SRC         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TCP_SRC
-	TCP_DST         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TCP_DST
-	UDP_SRC         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC
-	UDP_DST         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_UDP_DST
-	SCTP_SRC        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_SCTP_SRC
-	SCTP_DST        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_SCTP_DST
-	ICMPV4_TYPE     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV4_TYPE
-	ICMPV4_CODE     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV4_CODE
-	ARP_OP          = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_OP
-	ARP_SPA         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_SPA
-	ARP_TPA         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_TPA
-	ARP_SHA         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_SHA
-	ARP_THA         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_THA
-	IPV6_SRC        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_SRC
-	IPV6_DST        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_DST
-	IPV6_FLABEL     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_FLABEL
-	ICMPV6_TYPE     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV6_TYPE
-	ICMPV6_CODE     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV6_CODE
-	IPV6_ND_TARGET  = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_TARGET
-	OFB_IPV6_ND_SLL = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_SLL
-	IPV6_ND_TLL     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_TLL
-	MPLS_LABEL      = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_LABEL
-	MPLS_TC         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_TC
-	MPLS_BOS        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_BOS
-	PBB_ISID        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_PBB_ISID
-	TUNNEL_ID       = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TUNNEL_ID
-	IPV6_EXTHDR     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_EXTHDR
-)
-
-//ofp_action_* shortcuts
-
-func Output(port uint32, maxLen ...ofp.OfpControllerMaxLen) *ofp.OfpAction {
-	maxLength := ofp.OfpControllerMaxLen_OFPCML_MAX
-	if len(maxLen) > 0 {
-		maxLength = maxLen[0]
-	}
-	return &ofp.OfpAction{Type: OUTPUT, Action: &ofp.OfpAction_Output{Output: &ofp.OfpActionOutput{Port: port, MaxLen: uint32(maxLength)}}}
-}
-
-func MplsTtl(ttl uint32) *ofp.OfpAction {
-	return &ofp.OfpAction{Type: SET_MPLS_TTL, Action: &ofp.OfpAction_MplsTtl{MplsTtl: &ofp.OfpActionMplsTtl{MplsTtl: ttl}}}
-}
-
-func PushVlan(ethType uint32) *ofp.OfpAction {
-	return &ofp.OfpAction{Type: PUSH_VLAN, Action: &ofp.OfpAction_Push{Push: &ofp.OfpActionPush{Ethertype: ethType}}}
-}
-
-func PopVlan() *ofp.OfpAction {
-	return &ofp.OfpAction{Type: POP_VLAN}
-}
-
-func PopMpls(ethType uint32) *ofp.OfpAction {
-	return &ofp.OfpAction{Type: POP_MPLS, Action: &ofp.OfpAction_PopMpls{PopMpls: &ofp.OfpActionPopMpls{Ethertype: ethType}}}
-}
-
-func Group(groupId uint32) *ofp.OfpAction {
-	return &ofp.OfpAction{Type: GROUP, Action: &ofp.OfpAction_Group{Group: &ofp.OfpActionGroup{GroupId: groupId}}}
-}
-
-func NwTtl(nwTtl uint32) *ofp.OfpAction {
-	return &ofp.OfpAction{Type: NW_TTL, Action: &ofp.OfpAction_NwTtl{NwTtl: &ofp.OfpActionNwTtl{NwTtl: nwTtl}}}
-}
-
-func SetField(field *ofp.OfpOxmOfbField) *ofp.OfpAction {
-	actionSetField := &ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: &ofp.OfpOxmField_OfbField{OfbField: field}}
-	return &ofp.OfpAction{Type: SET_FIELD, Action: &ofp.OfpAction_SetField{SetField: &ofp.OfpActionSetField{Field: actionSetField}}}
-}
-
-func Experimenter(experimenter uint32, data []byte) *ofp.OfpAction {
-	return &ofp.OfpAction{Type: EXPERIMENTER, Action: &ofp.OfpAction_Experimenter{Experimenter: &ofp.OfpActionExperimenter{Experimenter: experimenter, Data: data}}}
-}
-
-//ofb_field generators (incomplete set)
-
-func InPort(inPort uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IN_PORT, Value: &ofp.OfpOxmOfbField_Port{Port: inPort}}
-}
-
-func InPhyPort(inPhyPort uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IN_PHY_PORT, Value: &ofp.OfpOxmOfbField_Port{Port: inPhyPort}}
-}
-
-func Metadata_ofp(tableMetadata uint64) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: METADATA, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: tableMetadata}}
-}
-
-// should Metadata_ofp used here ?????
-func EthDst(ethDst uint64) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ETH_DST, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: ethDst}}
-}
-
-// should Metadata_ofp used here ?????
-func EthSrc(ethSrc uint64) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ETH_SRC, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: ethSrc}}
-}
-
-func EthType(ethType uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ETH_TYPE, Value: &ofp.OfpOxmOfbField_EthType{EthType: ethType}}
-}
-
-func VlanVid(vlanVid uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: VLAN_VID, Value: &ofp.OfpOxmOfbField_VlanVid{VlanVid: vlanVid}}
-}
-
-func VlanPcp(vlanPcp uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: VLAN_PCP, Value: &ofp.OfpOxmOfbField_VlanPcp{VlanPcp: vlanPcp}}
-}
-
-func IpDscp(ipDscp uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IP_DSCP, Value: &ofp.OfpOxmOfbField_IpDscp{IpDscp: ipDscp}}
-}
-
-func IpEcn(ipEcn uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IP_ECN, Value: &ofp.OfpOxmOfbField_IpEcn{IpEcn: ipEcn}}
-}
-
-func IpProto(ipProto uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IP_PROTO, Value: &ofp.OfpOxmOfbField_IpProto{IpProto: ipProto}}
-}
-
-func Ipv4Src(ipv4Src uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IPV4_SRC, Value: &ofp.OfpOxmOfbField_Ipv4Src{Ipv4Src: ipv4Src}}
-}
-
-func Ipv4Dst(ipv4Dst uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IPV4_DST, Value: &ofp.OfpOxmOfbField_Ipv4Dst{Ipv4Dst: ipv4Dst}}
-}
-
-func TcpSrc(tcpSrc uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: TCP_SRC, Value: &ofp.OfpOxmOfbField_TcpSrc{TcpSrc: tcpSrc}}
-}
-
-func TcpDst(tcpDst uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: TCP_DST, Value: &ofp.OfpOxmOfbField_TcpDst{TcpDst: tcpDst}}
-}
-
-func UdpSrc(udpSrc uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: UDP_SRC, Value: &ofp.OfpOxmOfbField_UdpSrc{UdpSrc: udpSrc}}
-}
-
-func UdpDst(udpDst uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: UDP_DST, Value: &ofp.OfpOxmOfbField_UdpDst{UdpDst: udpDst}}
-}
-
-func SctpSrc(sctpSrc uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: SCTP_SRC, Value: &ofp.OfpOxmOfbField_SctpSrc{SctpSrc: sctpSrc}}
-}
-
-func SctpDst(sctpDst uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: SCTP_DST, Value: &ofp.OfpOxmOfbField_SctpDst{SctpDst: sctpDst}}
-}
-
-func Icmpv4Type(icmpv4Type uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ICMPV4_TYPE, Value: &ofp.OfpOxmOfbField_Icmpv4Type{Icmpv4Type: icmpv4Type}}
-}
-
-func Icmpv4Code(icmpv4Code uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ICMPV4_CODE, Value: &ofp.OfpOxmOfbField_Icmpv4Code{Icmpv4Code: icmpv4Code}}
-}
-
-func ArpOp(arpOp uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ARP_OP, Value: &ofp.OfpOxmOfbField_ArpOp{ArpOp: arpOp}}
-}
-
-func ArpSpa(arpSpa uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ARP_SPA, Value: &ofp.OfpOxmOfbField_ArpSpa{ArpSpa: arpSpa}}
-}
-
-func ArpTpa(arpTpa uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ARP_TPA, Value: &ofp.OfpOxmOfbField_ArpTpa{ArpTpa: arpTpa}}
-}
-
-func ArpSha(arpSha []byte) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ARP_SHA, Value: &ofp.OfpOxmOfbField_ArpSha{ArpSha: arpSha}}
-}
-
-func ArpTha(arpTha []byte) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ARP_THA, Value: &ofp.OfpOxmOfbField_ArpTha{ArpTha: arpTha}}
-}
-
-func Ipv6Src(ipv6Src []byte) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IPV6_SRC, Value: &ofp.OfpOxmOfbField_Ipv6Src{Ipv6Src: ipv6Src}}
-}
-
-func Ipv6Dst(ipv6Dst []byte) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IPV6_DST, Value: &ofp.OfpOxmOfbField_Ipv6Dst{Ipv6Dst: ipv6Dst}}
-}
-
-func Ipv6Flabel(ipv6Flabel uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IPV6_FLABEL, Value: &ofp.OfpOxmOfbField_Ipv6Flabel{Ipv6Flabel: ipv6Flabel}}
-}
-
-func Icmpv6Type(icmpv6Type uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ICMPV6_TYPE, Value: &ofp.OfpOxmOfbField_Icmpv6Type{Icmpv6Type: icmpv6Type}}
-}
-
-func Icmpv6Code(icmpv6Code uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: ICMPV6_CODE, Value: &ofp.OfpOxmOfbField_Icmpv6Code{Icmpv6Code: icmpv6Code}}
-}
-
-func Ipv6NdTarget(ipv6NdTarget []byte) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IPV6_ND_TARGET, Value: &ofp.OfpOxmOfbField_Ipv6NdTarget{Ipv6NdTarget: ipv6NdTarget}}
-}
-
-func OfbIpv6NdSll(ofbIpv6NdSll []byte) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: OFB_IPV6_ND_SLL, Value: &ofp.OfpOxmOfbField_Ipv6NdSsl{Ipv6NdSsl: ofbIpv6NdSll}}
-}
-
-func Ipv6NdTll(ipv6NdTll []byte) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IPV6_ND_TLL, Value: &ofp.OfpOxmOfbField_Ipv6NdTll{Ipv6NdTll: ipv6NdTll}}
-}
-
-func MplsLabel(mplsLabel uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: MPLS_LABEL, Value: &ofp.OfpOxmOfbField_MplsLabel{MplsLabel: mplsLabel}}
-}
-
-func MplsTc(mplsTc uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: MPLS_TC, Value: &ofp.OfpOxmOfbField_MplsTc{MplsTc: mplsTc}}
-}
-
-func MplsBos(mplsBos uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: MPLS_BOS, Value: &ofp.OfpOxmOfbField_MplsBos{MplsBos: mplsBos}}
-}
-
-func PbbIsid(pbbIsid uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: PBB_ISID, Value: &ofp.OfpOxmOfbField_PbbIsid{PbbIsid: pbbIsid}}
-}
-
-func TunnelId(tunnelId uint64) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: TUNNEL_ID, Value: &ofp.OfpOxmOfbField_TunnelId{TunnelId: tunnelId}}
-}
-
-func Ipv6Exthdr(ipv6Exthdr uint32) *ofp.OfpOxmOfbField {
-	return &ofp.OfpOxmOfbField{Type: IPV6_EXTHDR, Value: &ofp.OfpOxmOfbField_Ipv6Exthdr{Ipv6Exthdr: ipv6Exthdr}}
-}
-
-//frequently used extractors
-
-func excludeAction(action *ofp.OfpAction, exclude ...ofp.OfpActionType) bool {
-	for _, actionToExclude := range exclude {
-		if action.Type == actionToExclude {
-			return true
-		}
-	}
-	return false
-}
-
-func GetActions(flow *ofp.OfpFlowStats, exclude ...ofp.OfpActionType) []*ofp.OfpAction {
-	if flow == nil {
-		return nil
-	}
-	for _, instruction := range flow.Instructions {
-		if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS) {
-			instActions := instruction.GetActions()
-			if instActions == nil {
-				return nil
-			}
-			if len(exclude) == 0 {
-				return instActions.Actions
-			} else {
-				filteredAction := make([]*ofp.OfpAction, 0)
-				for _, action := range instActions.Actions {
-					if !excludeAction(action, exclude...) {
-						filteredAction = append(filteredAction, action)
-					}
-				}
-				return filteredAction
-			}
-		}
-	}
-	return nil
-}
-
-func UpdateOutputPortByActionType(flow *ofp.OfpFlowStats, actionType uint32, toPort uint32) *ofp.OfpFlowStats {
-	if flow == nil {
-		return nil
-	}
-	nFlow := (proto.Clone(flow)).(*ofp.OfpFlowStats)
-	nFlow.Instructions = nil
-	nInsts := make([]*ofp.OfpInstruction, 0)
-	for _, instruction := range flow.Instructions {
-		if instruction.Type == actionType {
-			instActions := instruction.GetActions()
-			if instActions == nil {
-				return nil
-			}
-			nActions := make([]*ofp.OfpAction, 0)
-			for _, action := range instActions.Actions {
-				if action.GetOutput() != nil {
-					nActions = append(nActions, Output(toPort))
-				} else {
-					nActions = append(nActions, action)
-				}
-			}
-			instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: nActions}}
-			nInsts = append(nInsts, &ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction})
-		} else {
-			nInsts = append(nInsts, instruction)
-		}
-	}
-	nFlow.Instructions = nInsts
-	return nFlow
-}
-
-func excludeOxmOfbField(field *ofp.OfpOxmOfbField, exclude ...ofp.OxmOfbFieldTypes) bool {
-	for _, fieldToExclude := range exclude {
-		if field.Type == fieldToExclude {
-			return true
-		}
-	}
-	return false
-}
-
-func GetOfbFields(flow *ofp.OfpFlowStats, exclude ...ofp.OxmOfbFieldTypes) []*ofp.OfpOxmOfbField {
-	if flow == nil || flow.Match == nil || flow.Match.Type != ofp.OfpMatchType_OFPMT_OXM {
-		return nil
-	}
-	ofbFields := make([]*ofp.OfpOxmOfbField, 0)
-	for _, field := range flow.Match.OxmFields {
-		if field.OxmClass == ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC {
-			ofbFields = append(ofbFields, field.GetOfbField())
-		}
-	}
-	if len(exclude) == 0 {
-		return ofbFields
-	} else {
-		filteredFields := make([]*ofp.OfpOxmOfbField, 0)
-		for _, ofbField := range ofbFields {
-			if !excludeOxmOfbField(ofbField, exclude...) {
-				filteredFields = append(filteredFields, ofbField)
-			}
-		}
-		return filteredFields
-	}
-}
-
-func GetPacketOutPort(packet *ofp.OfpPacketOut) uint32 {
-	if packet == nil {
-		return 0
-	}
-	for _, action := range packet.GetActions() {
-		if action.Type == OUTPUT {
-			return action.GetOutput().Port
-		}
-	}
-	return 0
-}
-
-func GetOutPort(flow *ofp.OfpFlowStats) uint32 {
-	if flow == nil {
-		return 0
-	}
-	for _, action := range GetActions(flow) {
-		if action.Type == OUTPUT {
-			out := action.GetOutput()
-			if out == nil {
-				return 0
-			}
-			return out.GetPort()
-		}
-	}
-	return 0
-}
-
-func GetInPort(flow *ofp.OfpFlowStats) uint32 {
-	if flow == nil {
-		return 0
-	}
-	for _, field := range GetOfbFields(flow) {
-		if field.Type == IN_PORT {
-			return field.GetPort()
-		}
-	}
-	return 0
-}
-
-func GetGotoTableId(flow *ofp.OfpFlowStats) uint32 {
-	if flow == nil {
-		return 0
-	}
-	for _, instruction := range flow.Instructions {
-		if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE) {
-			gotoTable := instruction.GetGotoTable()
-			if gotoTable == nil {
-				return 0
-			}
-			return gotoTable.GetTableId()
-		}
-	}
-	return 0
-}
-
-func GetTunnelId(flow *ofp.OfpFlowStats) uint64 {
-	if flow == nil {
-		return 0
-	}
-	for _, field := range GetOfbFields(flow) {
-		if field.Type == TUNNEL_ID {
-			return field.GetTunnelId()
-		}
-	}
-	return 0
-}
-
-//GetMetaData - legacy get method (only want lower 32 bits)
-func GetMetaData(flow *ofp.OfpFlowStats) uint32 {
-	if flow == nil {
-		return 0
-	}
-	for _, field := range GetOfbFields(flow) {
-		if field.Type == METADATA {
-			return uint32(field.GetTableMetadata() & 0xffffffff)
-		}
-	}
-	return 0
-}
-
-func GetMetaData64Bit(flow *ofp.OfpFlowStats) uint64 {
-	if flow == nil {
-		return 0
-	}
-	for _, field := range GetOfbFields(flow) {
-		if field.Type == METADATA {
-			return field.GetTableMetadata()
-		}
-	}
-	return 0
-}
-
-// GetPortNumberFromMetadata retrieves the port number from the Metadata_ofp. The port number (UNI on ONU) is in the
-// lower 32-bits of Metadata_ofp and the inner_tag is in the upper 32-bits. This is set in the ONOS OltPipeline as
-// a Metadata_ofp field
-func GetPortNumberFromMetadata(flow *ofp.OfpFlowStats) uint64 {
-	md := GetMetaData64Bit(flow)
-	if md == 0 {
-		return 0
-	}
-	if md <= 0xffffffff {
-		log.Debugw("onos-upgrade-suggested", log.Fields{"Metadata_ofp": md, "message": "Legacy MetaData detected form OltPipeline"})
-		return md
-	}
-	return md & 0xffffffff
-}
-
-//GetInnerTagFromMetaData retrieves the inner tag from the Metadata_ofp. The port number (UNI on ONU) is in the
-// lower 32-bits of Metadata_ofp and the inner_tag is in the upper 32-bits. This is set in the ONOS OltPipeline as
-//// a Metadata_ofp field
-func GetInnerTagFromMetaData(flow *ofp.OfpFlowStats) uint64 {
-	md := GetMetaData64Bit(flow)
-	if md == 0 {
-		return 0
-	}
-	if md <= 0xffffffff {
-		log.Debugw("onos-upgrade-suggested", log.Fields{"Metadata_ofp": md, "message": "Legacy MetaData detected form OltPipeline"})
-		return md
-	}
-	return (md >> 32) & 0xffffffff
-}
-
-// Extract the child device port from a flow that contains the parent device peer port.  Typically the UNI port of an
-// ONU child device.  Per TST agreement this will be the lower 32 bits of tunnel id reserving upper 32 bits for later
-// use
-func GetChildPortFromTunnelId(flow *ofp.OfpFlowStats) uint32 {
-	tid := GetTunnelId(flow)
-	if tid == 0 {
-		return 0
-	}
-	// Per TST agreement we are keeping any child port id (uni port id) in the lower 32 bits
-	return uint32(tid & 0xffffffff)
-}
-
-func HasNextTable(flow *ofp.OfpFlowStats) bool {
-	if flow == nil {
-		return false
-	}
-	return GetGotoTableId(flow) != 0
-}
-
-func GetGroup(flow *ofp.OfpFlowStats) uint32 {
-	if flow == nil {
-		return 0
-	}
-	for _, action := range GetActions(flow) {
-		if action.Type == GROUP {
-			grp := action.GetGroup()
-			if grp == nil {
-				return 0
-			}
-			return grp.GetGroupId()
-		}
-	}
-	return 0
-}
-
-func HasGroup(flow *ofp.OfpFlowStats) bool {
-	return GetGroup(flow) != 0
-}
-
-// GetNextTableId returns the next table ID if the "table_id" is present in the map, otherwise return nil
-func GetNextTableId(kw fu.OfpFlowModArgs) *uint32 {
-	if val, exist := kw["table_id"]; exist {
-		ret := uint32(val)
-		return &ret
-	}
-	return nil
-}
-
-// Return unique 64-bit integer hash for flow covering the following attributes:
-// 'table_id', 'priority', 'flags', 'cookie', 'match', '_instruction_string'
-func hashFlowStats(flow *ofp.OfpFlowStats) uint64 {
-	if flow == nil { // Should never happen
-		return 0
-	}
-	// Create string with the instructions field first
-	var instructionString bytes.Buffer
-	for _, instruction := range flow.Instructions {
-		instructionString.WriteString(instruction.String())
-	}
-	var flowString = fmt.Sprintf("%d%d%d%d%s%s", flow.TableId, flow.Priority, flow.Flags, flow.Cookie, flow.Match.String(), instructionString.String())
-	h := md5.New()
-	h.Write([]byte(flowString))
-	hash := big.NewInt(0)
-	hash.SetBytes(h.Sum(nil))
-	return hash.Uint64()
-}
-
-// flowStatsEntryFromFlowModMessage maps an ofp_flow_mod message to an ofp_flow_stats message
-func FlowStatsEntryFromFlowModMessage(mod *ofp.OfpFlowMod) *ofp.OfpFlowStats {
-	flow := &ofp.OfpFlowStats{}
-	if mod == nil {
-		return flow
-	}
-	flow.TableId = mod.TableId
-	flow.Priority = mod.Priority
-	flow.IdleTimeout = mod.IdleTimeout
-	flow.HardTimeout = mod.HardTimeout
-	flow.Flags = mod.Flags
-	flow.Cookie = mod.Cookie
-	flow.Match = mod.Match
-	flow.Instructions = mod.Instructions
-	flow.Id = hashFlowStats(flow)
-	return flow
-}
-
-func GroupEntryFromGroupMod(mod *ofp.OfpGroupMod) *ofp.OfpGroupEntry {
-	group := &ofp.OfpGroupEntry{}
-	if mod == nil {
-		return group
-	}
-	group.Desc = &ofp.OfpGroupDesc{Type: mod.Type, GroupId: mod.GroupId, Buckets: mod.Buckets}
-	group.Stats = &ofp.OfpGroupStats{GroupId: mod.GroupId}
-	//TODO do we need to instantiate bucket bins?
-	return group
-}
-
-func MkOxmFields(matchFields []ofp.OfpOxmField) []*ofp.OfpOxmField {
-	oxmFields := make([]*ofp.OfpOxmField, 0)
-	for _, matchField := range matchFields {
-		oxmField := ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: matchField.Field}
-		oxmFields = append(oxmFields, &oxmField)
-	}
-	return oxmFields
-}
-
-func MkInstructionsFromActions(actions []*ofp.OfpAction) []*ofp.OfpInstruction {
-	instructions := make([]*ofp.OfpInstruction, 0)
-	instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: actions}}
-	instruction := ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction}
-	instructions = append(instructions, &instruction)
-	return instructions
-}
-
-// Convenience function to generare ofp_flow_mod message with OXM BASIC match composed from the match_fields, and
-// single APPLY_ACTIONS instruction with a list if ofp_action objects.
-func MkSimpleFlowMod(matchFields []*ofp.OfpOxmField, actions []*ofp.OfpAction, command *ofp.OfpFlowModCommand, kw fu.OfpFlowModArgs) *ofp.OfpFlowMod {
-
-	// Process actions instructions
-	instructions := make([]*ofp.OfpInstruction, 0)
-	instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: actions}}
-	instruction := ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction}
-	instructions = append(instructions, &instruction)
-
-	// Process next table
-	if tableId := GetNextTableId(kw); tableId != nil {
-		var instGotoTable ofp.OfpInstruction_GotoTable
-		instGotoTable.GotoTable = &ofp.OfpInstructionGotoTable{TableId: *tableId}
-		inst := ofp.OfpInstruction{Type: uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE), Data: &instGotoTable}
-		instructions = append(instructions, &inst)
-	}
-
-	// Process match fields
-	oxmFields := make([]*ofp.OfpOxmField, 0)
-	for _, matchField := range matchFields {
-		oxmField := ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: matchField.Field}
-		oxmFields = append(oxmFields, &oxmField)
-	}
-	var match ofp.OfpMatch
-	match.Type = ofp.OfpMatchType_OFPMT_OXM
-	match.OxmFields = oxmFields
-
-	// Create ofp_flow_message
-	msg := &ofp.OfpFlowMod{}
-	if command == nil {
-		msg.Command = ofp.OfpFlowModCommand_OFPFC_ADD
-	} else {
-		msg.Command = *command
-	}
-	msg.Instructions = instructions
-	msg.Match = &match
-
-	// Set the variadic argument values
-	msg = setVariadicModAttributes(msg, kw)
-
-	return msg
-}
-
-func MkMulticastGroupMod(groupId uint32, buckets []*ofp.OfpBucket, command *ofp.OfpGroupModCommand) *ofp.OfpGroupMod {
-	group := &ofp.OfpGroupMod{}
-	if command == nil {
-		group.Command = ofp.OfpGroupModCommand_OFPGC_ADD
-	} else {
-		group.Command = *command
-	}
-	group.Type = ofp.OfpGroupType_OFPGT_ALL
-	group.GroupId = groupId
-	group.Buckets = buckets
-	return group
-}
-
-//SetVariadicModAttributes sets only uint64 or uint32 fields of the ofp_flow_mod message
-func setVariadicModAttributes(mod *ofp.OfpFlowMod, args fu.OfpFlowModArgs) *ofp.OfpFlowMod {
-	if args == nil {
-		return mod
-	}
-	for key, val := range args {
-		switch key {
-		case "cookie":
-			mod.Cookie = val
-		case "cookie_mask":
-			mod.CookieMask = val
-		case "table_id":
-			mod.TableId = uint32(val)
-		case "idle_timeout":
-			mod.IdleTimeout = uint32(val)
-		case "hard_timeout":
-			mod.HardTimeout = uint32(val)
-		case "priority":
-			mod.Priority = uint32(val)
-		case "buffer_id":
-			mod.BufferId = uint32(val)
-		case "out_port":
-			mod.OutPort = uint32(val)
-		case "out_group":
-			mod.OutGroup = uint32(val)
-		case "flags":
-			mod.Flags = uint32(val)
-		}
-	}
-	return mod
-}
-
-func MkPacketIn(port uint32, packet []byte) *ofp.OfpPacketIn {
-	packetIn := &ofp.OfpPacketIn{
-		Reason: ofp.OfpPacketInReason_OFPR_ACTION,
-		Match: &ofp.OfpMatch{
-			Type: ofp.OfpMatchType_OFPMT_OXM,
-			OxmFields: []*ofp.OfpOxmField{
-				{
-					OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC,
-					Field: &ofp.OfpOxmField_OfbField{
-						OfbField: InPort(port)},
-				},
-			},
-		},
-		Data: packet,
-	}
-	return packetIn
-}
-
-// MkFlowStat is a helper method to build flows
-func MkFlowStat(fa *fu.FlowArgs) *ofp.OfpFlowStats {
-	//Build the matchfields
-	matchFields := make([]*ofp.OfpOxmField, 0)
-	for _, val := range fa.MatchFields {
-		matchFields = append(matchFields, &ofp.OfpOxmField{Field: &ofp.OfpOxmField_OfbField{OfbField: val}})
-	}
-	return FlowStatsEntryFromFlowModMessage(MkSimpleFlowMod(matchFields, fa.Actions, fa.Command, fa.KV))
-}
-
-func MkGroupStat(ga *fu.GroupArgs) *ofp.OfpGroupEntry {
-	return GroupEntryFromGroupMod(MkMulticastGroupMod(ga.GroupId, ga.Buckets, ga.Command))
-}
-
-type FlowDecomposer struct {
-	deviceMgr coreIf.DeviceManager
-}
-
-func NewFlowDecomposer(deviceMgr coreIf.DeviceManager) *FlowDecomposer {
-	var decomposer FlowDecomposer
-	decomposer.deviceMgr = deviceMgr
-	return &decomposer
-}
-
-//DecomposeRules decomposes per-device flows and flow-groups from the flows and groups defined on a logical device
-func (fd *FlowDecomposer) DecomposeRules(agent coreIf.LogicalDeviceAgent, flows ofp.Flows, groups ofp.FlowGroups, includeDefaultFlows bool) *fu.DeviceRules {
-	rules := agent.GetAllDefaultRules()
-	deviceRules := rules.Copy()
-	devicesToUpdate := make(map[string]string)
-
-	groupMap := make(map[uint32]*ofp.OfpGroupEntry)
-	for _, groupEntry := range groups.Items {
-		groupMap[groupEntry.Desc.GroupId] = groupEntry
-	}
-
-	var decomposedRules *fu.DeviceRules
-	for _, flow := range flows.Items {
-		decomposedRules = fd.decomposeFlow(agent, flow, groupMap)
-		for deviceId, flowAndGroups := range decomposedRules.Rules {
-			deviceRules.CreateEntryIfNotExist(deviceId)
-			deviceRules.Rules[deviceId].AddFrom(flowAndGroups)
-			devicesToUpdate[deviceId] = deviceId
-		}
-	}
-	if includeDefaultFlows {
-		return deviceRules
-	}
-	updatedDeviceRules := deviceRules.FilterRules(devicesToUpdate)
-
-	return updatedDeviceRules
-}
-
-// Handles special case of any controller-bound flow for a parent device
-func (fd *FlowDecomposer) updateOutputPortForControllerBoundFlowForParentDevide(flow *ofp.OfpFlowStats,
-	dr *fu.DeviceRules) *fu.DeviceRules {
-	EAPOL := EthType(0x888e)
-	IGMP := IpProto(2)
-	UDP := IpProto(17)
-
-	newDeviceRules := dr.Copy()
-	//	Check whether we are dealing with a parent device
-	for deviceId, fg := range dr.GetRules() {
-		if root, _ := fd.deviceMgr.IsRootDevice(deviceId); root {
-			newDeviceRules.ClearFlows(deviceId)
-			for i := 0; i < fg.Flows.Len(); i++ {
-				f := fg.GetFlow(i)
-				UpdateOutPortNo := false
-				for _, field := range GetOfbFields(f) {
-					UpdateOutPortNo = (field.String() == EAPOL.String())
-					UpdateOutPortNo = UpdateOutPortNo || (field.String() == IGMP.String())
-					UpdateOutPortNo = UpdateOutPortNo || (field.String() == UDP.String())
-					if UpdateOutPortNo {
-						break
-					}
-				}
-				if UpdateOutPortNo {
-					f = UpdateOutputPortByActionType(f, uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS),
-						uint32(ofp.OfpPortNo_OFPP_CONTROLLER))
-				}
-				// Update flow Id as a change in the instruction field will result in a new flow ID
-				f.Id = hashFlowStats(f)
-				newDeviceRules.AddFlow(deviceId, (proto.Clone(f)).(*ofp.OfpFlowStats))
-			}
-		}
-	}
-
-	return newDeviceRules
-}
-
-//processControllerBoundFlow decomposes trap flows
-func (fd *FlowDecomposer) processControllerBoundFlow(agent coreIf.LogicalDeviceAgent, route []graph.RouteHop,
-	inPortNo uint32, outPortNo uint32, flow *ofp.OfpFlowStats) *fu.DeviceRules {
-
-	log.Debugw("trap-flow", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo, "flow": flow})
-	deviceRules := fu.NewDeviceRules()
-
-	egressHop := route[1]
-
-	fg := fu.NewFlowsAndGroups()
-	if agent.GetDeviceGraph().IsRootPort(inPortNo) {
-		log.Debug("trap-nni")
-		// no decomposition required - it is already an OLT flow from NNI
-		fg.AddFlow(flow)
-	} else {
-		// Trap flow for UNI port
-		log.Debug("trap-uni")
-
-		//inPortNo is 0 for wildcard input case, do not include upstream port for 4000 flow in input
-		var inPorts []uint32
-		if inPortNo == 0 {
-			inPorts = agent.GetWildcardInputPorts(egressHop.Egress) // exclude egress_hop.egress_port.port_no
-		} else {
-			inPorts = []uint32{inPortNo}
-		}
-		for _, inputPort := range inPorts {
-			var fa *fu.FlowArgs
-			// Upstream flow
-			fa = &fu.FlowArgs{
-				KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-				MatchFields: []*ofp.OfpOxmOfbField{
-					InPort(egressHop.Ingress),
-					VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | inputPort),
-					TunnelId(uint64(inputPort)),
-				},
-				Actions: []*ofp.OfpAction{
-					PushVlan(0x8100),
-					SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000)),
-					Output(egressHop.Egress),
-				},
-			}
-			// Augment the matchfields with the ofpfields from the flow
-			fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT, VLAN_VID)...)
-			fg.AddFlow(MkFlowStat(fa))
-
-			// Downstream flow
-			fa = &fu.FlowArgs{
-				KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority)},
-				MatchFields: []*ofp.OfpOxmOfbField{
-					InPort(egressHop.Egress),
-					VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000),
-					VlanPcp(0),
-					Metadata_ofp(uint64(inputPort)),
-					TunnelId(uint64(inputPort)),
-				},
-				Actions: []*ofp.OfpAction{
-					PopVlan(),
-					Output(egressHop.Ingress),
-				},
-			}
-			fg.AddFlow(MkFlowStat(fa))
-		}
-	}
-	deviceRules.AddFlowsAndGroup(egressHop.DeviceID, fg)
-
-	return deviceRules
-}
-
-// processUpstreamNonControllerBoundFlow processes non-controller bound flow. We assume that anything that is
-// upstream needs to get Q-in-Q treatment and that this is expressed via two flow rules, the first using the
-// goto-statement. We also assume that the inner tag is applied at the ONU, while the outer tag is
-// applied at the OLT
-func (fd *FlowDecomposer) processUpstreamNonControllerBoundFlow(agent coreIf.LogicalDeviceAgent,
-	route []graph.RouteHop, inPortNo uint32, outPortNo uint32, flow *ofp.OfpFlowStats) *fu.DeviceRules {
-
-	log.Debugw("upstream-non-controller-bound-flow", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo})
-	deviceRules := fu.NewDeviceRules()
-
-	ingressHop := route[0]
-	egressHop := route[1]
-
-	if HasNextTable(flow) {
-		log.Debugw("has-next-table", log.Fields{"table_id": flow.TableId})
-		if outPortNo != 0 {
-			log.Warnw("outPort-should-not-be-specified", log.Fields{"outPortNo": outPortNo})
-		}
-		var fa *fu.FlowArgs
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				InPort(ingressHop.Ingress),
-				TunnelId(uint64(inPortNo)),
-			},
-			Actions: GetActions(flow),
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-
-		// Augment the Actions
-		fa.Actions = append(fa.Actions, Output(ingressHop.Egress))
-
-		fg := fu.NewFlowsAndGroups()
-		fg.AddFlow(MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(ingressHop.DeviceID, fg)
-	} else {
-		var actions []ofp.OfpActionType
-		var isOutputTypeInActions bool
-		for _, action := range GetActions(flow) {
-			actions = append(actions, action.Type)
-			if !isOutputTypeInActions && action.Type == OUTPUT {
-				isOutputTypeInActions = true
-			}
-		}
-		if len(actions) == 1 && isOutputTypeInActions {
-			var fa *fu.FlowArgs
-			// child device flow
-			fa = &fu.FlowArgs{
-				KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-				MatchFields: []*ofp.OfpOxmOfbField{
-					InPort(ingressHop.Ingress),
-				},
-				Actions: []*ofp.OfpAction{
-					Output(ingressHop.Egress),
-				},
-			}
-			// Augment the matchfields with the ofpfields from the flow
-			fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-			fg := fu.NewFlowsAndGroups()
-			fg.AddFlow(MkFlowStat(fa))
-			deviceRules.AddFlowsAndGroup(ingressHop.DeviceID, fg)
-
-			// parent device flow
-			fa = &fu.FlowArgs{
-				KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-				MatchFields: []*ofp.OfpOxmOfbField{
-					InPort(egressHop.Ingress), //egress_hop.ingress_port.port_no
-					TunnelId(uint64(inPortNo)),
-				},
-				Actions: []*ofp.OfpAction{
-					Output(egressHop.Egress),
-				},
-			}
-			// Augment the matchfields with the ofpfields from the flow
-			fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-			fg = fu.NewFlowsAndGroups()
-			fg.AddFlow(MkFlowStat(fa))
-			deviceRules.AddFlowsAndGroup(egressHop.DeviceID, fg)
-		} else {
-			if outPortNo == 0 {
-				log.Warnw("outPort-should-be-specified", log.Fields{"outPortNo": outPortNo})
-			}
-			var fa *fu.FlowArgs
-			fa = &fu.FlowArgs{
-				KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-				MatchFields: []*ofp.OfpOxmOfbField{
-					InPort(egressHop.Ingress),
-					TunnelId(uint64(inPortNo)),
-				},
-			}
-			// Augment the matchfields with the ofpfields from the flow
-			fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-
-			//Augment the actions
-			filteredAction := GetActions(flow, OUTPUT)
-			filteredAction = append(filteredAction, Output(egressHop.Egress))
-			fa.Actions = filteredAction
-
-			fg := fu.NewFlowsAndGroups()
-			fg.AddFlow(MkFlowStat(fa))
-			deviceRules.AddFlowsAndGroup(egressHop.DeviceID, fg)
-		}
-	}
-	return deviceRules
-}
-
-// processDownstreamFlowWithNextTable decomposes downstream flows containing next table ID instructions
-func (fd *FlowDecomposer) processDownstreamFlowWithNextTable(agent coreIf.LogicalDeviceAgent, route []graph.RouteHop,
-	inPortNo uint32, outPortNo uint32, flow *ofp.OfpFlowStats) *fu.DeviceRules {
-
-	log.Debugw("downstream-flow-with-next-table", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo})
-	deviceRules := fu.NewDeviceRules()
-
-	if outPortNo != 0 {
-		log.Warnw("outPort-should-not-be-specified", log.Fields{"outPortNo": outPortNo})
-	}
-	ingressHop := route[0]
-	egressHop := route[1]
-
-	if GetMetaData(flow) != 0 {
-		log.Debugw("creating-metadata-flow", log.Fields{"flow": flow})
-		portNumber := uint32(GetPortNumberFromMetadata(flow))
-		if portNumber != 0 {
-			recalculatedRoute := agent.GetRoute(inPortNo, portNumber)
-			switch len(recalculatedRoute) {
-			case 0:
-				log.Errorw("no-route-double-tag", log.Fields{"inPortNo": inPortNo, "outPortNo": portNumber, "comment": "deleting-flow", "metadata": GetMetaData64Bit(flow)})
-				//	TODO: Delete flow
-				return deviceRules
-			case 2:
-				log.Debugw("route-found", log.Fields{"ingressHop": ingressHop, "egressHop": egressHop})
-				break
-			default:
-				log.Errorw("invalid-route-length", log.Fields{"routeLen": len(route)})
-				return deviceRules
-			}
-			ingressHop = recalculatedRoute[0]
-		}
-		innerTag := GetInnerTagFromMetaData(flow)
-		if innerTag == 0 {
-			log.Errorw("no-inner-route-double-tag", log.Fields{"inPortNo": inPortNo, "outPortNo": portNumber, "comment": "deleting-flow", "metadata": GetMetaData64Bit(flow)})
-			//	TODO: Delete flow
-			return deviceRules
-		}
-		var fa *fu.FlowArgs
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				InPort(ingressHop.Ingress),
-				Metadata_ofp(innerTag),
-				TunnelId(uint64(portNumber)),
-			},
-			Actions: GetActions(flow),
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT, METADATA)...)
-
-		// Augment the Actions
-		fa.Actions = append(fa.Actions, Output(ingressHop.Egress))
-
-		fg := fu.NewFlowsAndGroups()
-		fg.AddFlow(MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(ingressHop.DeviceID, fg)
-	} else { // Create standard flow
-		log.Debugw("creating-standard-flow", log.Fields{"flow": flow})
-		var fa *fu.FlowArgs
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				InPort(ingressHop.Ingress),
-				TunnelId(uint64(inPortNo)),
-			},
-			Actions: GetActions(flow),
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-
-		// Augment the Actions
-		fa.Actions = append(fa.Actions, Output(ingressHop.Egress))
-
-		fg := fu.NewFlowsAndGroups()
-		fg.AddFlow(MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(ingressHop.DeviceID, fg)
-	}
-
-	return deviceRules
-}
-
-// processUnicastFlow decomposes unicast flows
-func (fd *FlowDecomposer) processUnicastFlow(agent coreIf.LogicalDeviceAgent, route []graph.RouteHop,
-	inPortNo uint32, outPortNo uint32, flow *ofp.OfpFlowStats) *fu.DeviceRules {
-
-	log.Debugw("unicast-flow", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo})
-	deviceRules := fu.NewDeviceRules()
-
-	ingressHop := route[0]
-	egressHop := route[1]
-
-	var actions []ofp.OfpActionType
-	var isOutputTypeInActions bool
-	for _, action := range GetActions(flow) {
-		actions = append(actions, action.Type)
-		if !isOutputTypeInActions && action.Type == OUTPUT {
-			isOutputTypeInActions = true
-		}
-	}
-	if len(actions) == 1 && isOutputTypeInActions {
-		var fa *fu.FlowArgs
-		// Parent device flow
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				InPort(ingressHop.Ingress),
-				TunnelId(uint64(inPortNo)),
-			},
-			Actions: []*ofp.OfpAction{
-				Output(ingressHop.Egress),
-			},
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-
-		fg := fu.NewFlowsAndGroups()
-		fg.AddFlow(MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(ingressHop.DeviceID, fg)
-
-		// Child device flow
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				InPort(egressHop.Ingress),
-			},
-			Actions: []*ofp.OfpAction{
-				Output(egressHop.Egress),
-			},
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-
-		fg = fu.NewFlowsAndGroups()
-		fg.AddFlow(MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(egressHop.DeviceID, fg)
-	} else {
-		var fa *fu.FlowArgs
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				InPort(egressHop.Ingress),
-			},
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-
-		// Augment the Actions
-		filteredAction := GetActions(flow, OUTPUT)
-		filteredAction = append(filteredAction, Output(egressHop.Egress))
-		fa.Actions = filteredAction
-
-		fg := fu.NewFlowsAndGroups()
-		fg.AddFlow(MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(egressHop.DeviceID, fg)
-	}
-	return deviceRules
-}
-
-// processMulticastFlow decompose multicast flows
-func (fd *FlowDecomposer) processMulticastFlow(agent coreIf.LogicalDeviceAgent, route []graph.RouteHop,
-	inPortNo uint32, outPortNo uint32, flow *ofp.OfpFlowStats, grpId uint32,
-	groupMap map[uint32]*ofp.OfpGroupEntry) *fu.DeviceRules {
-
-	log.Debugw("multicast-flow", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo})
-	deviceRules := fu.NewDeviceRules()
-
-	//having no Group yet is the same as having a Group with no buckets
-	var grp *ofp.OfpGroupEntry
-	var ok bool
-	if grp, ok = groupMap[grpId]; !ok {
-		log.Warnw("Group-id-not-present-in-map", log.Fields{"grpId": grpId, "groupMap": groupMap})
-		return deviceRules
-	}
-	if grp == nil || grp.Desc == nil {
-		log.Warnw("Group-or-desc-nil", log.Fields{"grpId": grpId, "grp": grp})
-		return deviceRules
-	}
-	for _, bucket := range grp.Desc.Buckets {
-		otherActions := make([]*ofp.OfpAction, 0)
-		for _, action := range bucket.Actions {
-			if action.Type == OUTPUT {
-				outPortNo = action.GetOutput().Port
-			} else if action.Type != POP_VLAN {
-				otherActions = append(otherActions, action)
-			}
-		}
-
-		route2 := agent.GetRoute(inPortNo, outPortNo)
-		switch len(route2) {
-		case 0:
-			log.Errorw("mc-no-route", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo, "comment": "deleting flow"})
-			//	TODO: Delete flow
-			return deviceRules
-		case 2:
-			log.Debugw("route-found", log.Fields{"ingressHop": route2[0], "egressHop": route2[1]})
-			break
-		default:
-			log.Errorw("invalid-route-length", log.Fields{"routeLen": len(route)})
-			return deviceRules
-		}
-
-		ingressHop := route[0]
-		ingressHop2 := route2[0]
-		egressHop := route2[1]
-
-		if ingressHop.Ingress != ingressHop2.Ingress {
-			log.Errorw("mc-ingress-hop-hop2-mismatch", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo, "comment": "ignoring flow"})
-			return deviceRules
-		}
-		// Set the parent device flow
-		var fa *fu.FlowArgs
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				InPort(ingressHop.Ingress),
-			},
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT)...)
-
-		// Augment the Actions
-		filteredAction := GetActions(flow, GROUP)
-		filteredAction = append(filteredAction, PopVlan())
-		filteredAction = append(filteredAction, Output(route2[1].Ingress))
-		fa.Actions = filteredAction
-
-		fg := fu.NewFlowsAndGroups()
-		fg.AddFlow(MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(ingressHop.DeviceID, fg)
-
-		// Set the child device flow
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				InPort(egressHop.Ingress),
-			},
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, GetOfbFields(flow, IN_PORT, VLAN_VID, VLAN_PCP)...)
-
-		// Augment the Actions
-		otherActions = append(otherActions, Output(egressHop.Egress))
-		fa.Actions = otherActions
-
-		fg = fu.NewFlowsAndGroups()
-		fg.AddFlow(MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(egressHop.DeviceID, fg)
-	}
-	return deviceRules
-}
-
-// decomposeFlow decomposes a flow for a logical device into flows for each physical device
-func (fd *FlowDecomposer) decomposeFlow(agent coreIf.LogicalDeviceAgent, flow *ofp.OfpFlowStats,
-	groupMap map[uint32]*ofp.OfpGroupEntry) *fu.DeviceRules {
-
-	inPortNo := GetInPort(flow)
-	outPortNo := GetOutPort(flow)
-	deviceRules := fu.NewDeviceRules()
-	route := agent.GetRoute(inPortNo, outPortNo)
-
-	switch len(route) {
-	case 0:
-		log.Errorw("no-route", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo, "comment": "deleting-flow"})
-		//	TODO: Delete flow
-		return deviceRules
-	case 2:
-		log.Debugw("route-found", log.Fields{"ingressHop": route[0], "egressHop": route[1]})
-		break
-	default:
-		log.Errorw("invalid-route-length", log.Fields{"routeLen": len(route)})
-		return deviceRules
-	}
-
-	// Process controller bound flow
-	if outPortNo != 0 && (outPortNo&0x7fffffff) == uint32(ofp.OfpPortNo_OFPP_CONTROLLER) {
-		deviceRules = fd.processControllerBoundFlow(agent, route, inPortNo, outPortNo, flow)
-	} else {
-		var ingressDevice *voltha.Device
-		var err error
-		if ingressDevice, err = fd.deviceMgr.GetDevice(route[0].DeviceID); err != nil {
-			log.Errorw("ingress-device-not-found", log.Fields{"deviceId": route[0].DeviceID, "flow": flow})
-			return deviceRules
-		}
-		isUpstream := !ingressDevice.Root
-		if isUpstream {
-			deviceRules = fd.processUpstreamNonControllerBoundFlow(agent, route, inPortNo, outPortNo, flow)
-		} else if HasNextTable(flow) {
-			deviceRules = fd.processDownstreamFlowWithNextTable(agent, route, inPortNo, outPortNo, flow)
-		} else if outPortNo != 0 { // Unicast
-			deviceRules = fd.processUnicastFlow(agent, route, inPortNo, outPortNo, flow)
-		} else if grpId := GetGroup(flow); grpId != 0 { //Multicast
-			deviceRules = fd.processMulticastFlow(agent, route, inPortNo, outPortNo, flow, grpId, groupMap)
-		}
-	}
-	deviceRules = fd.updateOutputPortForControllerBoundFlowForParentDevide(flow, deviceRules)
-	return deviceRules
-}
diff --git a/vendor/github.com/opencord/voltha-go/rw_core/graph/device_graph.go b/vendor/github.com/opencord/voltha-go/rw_core/graph/device_graph.go
deleted file mode 100644
index 5583023..0000000
--- a/vendor/github.com/opencord/voltha-go/rw_core/graph/device_graph.go
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package graph
-
-import (
-	"errors"
-	"fmt"
-	"github.com/gyuho/goraph"
-	"github.com/opencord/voltha-go/common/log"
-	"github.com/opencord/voltha-protos/go/voltha"
-	"strconv"
-	"strings"
-	"sync"
-)
-
-func init() {
-	log.AddPackage(log.JSON, log.WarnLevel, nil)
-}
-
-type RouteHop struct {
-	DeviceID string
-	Ingress  uint32
-	Egress   uint32
-}
-
-type OFPortLink struct {
-	Ingress uint32
-	Egress  uint32
-}
-
-type ofPortLinkToPath struct {
-	link OFPortLink
-	path []RouteHop
-}
-
-type GetDeviceFunc func(id string) (*voltha.Device, error)
-
-type DeviceGraph struct {
-	logicalDeviceId    string
-	GGraph             goraph.Graph
-	getDeviceFromModel GetDeviceFunc
-	logicalPorts       []*voltha.LogicalPort
-	rootPortsString    map[string]uint32
-	nonRootPortsString map[string]uint32
-	RootPorts          map[uint32]uint32
-	rootPortsLock      sync.RWMutex
-	Routes             map[OFPortLink][]RouteHop
-	graphBuildLock     sync.RWMutex
-	boundaryPorts      map[string]uint32
-	boundaryPortsLock  sync.RWMutex
-	cachedDevices      map[string]*voltha.Device
-	cachedDevicesLock  sync.RWMutex
-	devicesAdded       map[string]string
-	portsAdded         map[string]string
-}
-
-func NewDeviceGraph(logicalDeviceId string, getDevice GetDeviceFunc) *DeviceGraph {
-	var dg DeviceGraph
-	dg.logicalDeviceId = logicalDeviceId
-	dg.GGraph = goraph.NewGraph()
-	dg.getDeviceFromModel = getDevice
-	dg.graphBuildLock = sync.RWMutex{}
-	dg.cachedDevicesLock = sync.RWMutex{}
-	dg.rootPortsLock = sync.RWMutex{}
-	dg.devicesAdded = make(map[string]string)
-	dg.portsAdded = make(map[string]string)
-	dg.rootPortsString = make(map[string]uint32)
-	dg.nonRootPortsString = make(map[string]uint32)
-	dg.RootPorts = make(map[uint32]uint32)
-	dg.boundaryPorts = make(map[string]uint32)
-	dg.Routes = make(map[OFPortLink][]RouteHop)
-	dg.cachedDevices = make(map[string]*voltha.Device)
-	log.Debug("new device graph created ...")
-	return &dg
-}
-
-//IsRootPort returns true if the port is a root port on a logical device
-func (dg *DeviceGraph) IsRootPort(port uint32) bool {
-	dg.rootPortsLock.RLock()
-	defer dg.rootPortsLock.RUnlock()
-	_, exist := dg.RootPorts[port]
-	return exist
-}
-
-//GetDeviceNodeIds retrieves all the nodes in the device graph
-func (dg *DeviceGraph) GetDeviceNodeIds() map[string]string {
-	dg.graphBuildLock.RLock()
-	defer dg.graphBuildLock.RUnlock()
-	nodeIds := make(map[string]string)
-	nodesMap := dg.GGraph.GetNodes()
-	for id, node := range nodesMap {
-		if len(strings.Split(node.String(), ":")) != 2 { // not port node
-			nodeIds[id.String()] = id.String()
-		}
-	}
-	return nodeIds
-}
-
-//ComputeRoutes creates a device graph from the logical ports and then calculates all the routes
-//between the logical ports.  This will clear up the graph and routes if there were any.
-func (dg *DeviceGraph) ComputeRoutes(lps []*voltha.LogicalPort) {
-	if dg == nil {
-		return
-	}
-	dg.graphBuildLock.Lock()
-	defer dg.graphBuildLock.Unlock()
-
-	// Clear the graph
-	dg.reset()
-
-	dg.logicalPorts = lps
-
-	// Set the root, non-root ports and boundary ports
-	for _, lp := range lps {
-		portId := concatDeviceIdPortId(lp.DeviceId, lp.DevicePortNo)
-		if lp.RootPort {
-			dg.rootPortsString[portId] = lp.OfpPort.PortNo
-			dg.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
-		} else {
-			dg.nonRootPortsString[portId] = lp.OfpPort.PortNo
-		}
-		dg.boundaryPorts[portId] = lp.OfpPort.PortNo
-	}
-
-	// Build the graph
-	var device *voltha.Device
-	for _, logicalPort := range dg.logicalPorts {
-		device, _ = dg.getDevice(logicalPort.DeviceId, false)
-		dg.GGraph = dg.addDevice(device, dg.GGraph, &dg.devicesAdded, &dg.portsAdded, dg.boundaryPorts)
-	}
-
-	dg.Routes = dg.buildRoutes()
-}
-
-// AddPort adds a port to the graph.  If the graph is empty it will just invoke ComputeRoutes function
-func (dg *DeviceGraph) AddPort(lp *voltha.LogicalPort) {
-	log.Debugw("Addport", log.Fields{"logicalPort": lp})
-	//  If the graph does not exist invoke ComputeRoutes.
-	if len(dg.boundaryPorts) == 0 {
-		dg.ComputeRoutes([]*voltha.LogicalPort{lp})
-		return
-	}
-
-	dg.graphBuildLock.Lock()
-	defer dg.graphBuildLock.Unlock()
-
-	portId := concatDeviceIdPortId(lp.DeviceId, lp.DevicePortNo)
-
-	//	If the port is already part of the boundary ports, do nothing
-	if dg.portExist(portId) {
-		return
-	}
-	// Add the port to the set of boundary ports
-	dg.boundaryPorts[portId] = lp.OfpPort.PortNo
-
-	// Add the device where this port is located to the device graph. If the device is already added then
-	// only the missing port will be added
-	device, _ := dg.getDevice(lp.DeviceId, false)
-	dg.GGraph = dg.addDevice(device, dg.GGraph, &dg.devicesAdded, &dg.portsAdded, dg.boundaryPorts)
-
-	if lp.RootPort {
-		// Compute the route from this root port to all non-root ports
-		dg.rootPortsString[portId] = lp.OfpPort.PortNo
-		dg.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
-		dg.Routes = dg.buildPathsToAllNonRootPorts(lp)
-	} else {
-		// Compute the route from this port to all root ports
-		dg.nonRootPortsString[portId] = lp.OfpPort.PortNo
-		dg.Routes = dg.buildPathsToAllRootPorts(lp)
-	}
-
-	dg.Print()
-}
-
-func (dg *DeviceGraph) Print() error {
-	log.Debugw("Print", log.Fields{"graph": dg.logicalDeviceId, "boundaryPorts": dg.boundaryPorts})
-	if level, err := log.GetPackageLogLevel(); err == nil && level == log.DebugLevel {
-		output := ""
-		routeNumber := 1
-		for k, v := range dg.Routes {
-			key := fmt.Sprintf("LP:%d->LP:%d", k.Ingress, k.Egress)
-			val := ""
-			for _, i := range v {
-				val += fmt.Sprintf("{%d->%s->%d},", i.Ingress, i.DeviceID, i.Egress)
-			}
-			val = val[:len(val)-1]
-			output += fmt.Sprintf("%d:{%s=>%s}   ", routeNumber, key, fmt.Sprintf("[%s]", val))
-			routeNumber += 1
-		}
-		if len(dg.Routes) == 0 {
-			log.Debugw("no-routes-found", log.Fields{"lDeviceId": dg.logicalDeviceId, "Graph": dg.GGraph.String()})
-		} else {
-			log.Debugw("graph_routes", log.Fields{"lDeviceId": dg.logicalDeviceId, "Routes": output})
-		}
-	}
-	return nil
-}
-
-//getDevice returns the device either from the local cache (default) or from the model.
-//TODO: Set a cache timeout such that we do not use invalid data.  The full device lifecycle should also
-//be taken in consideration
-func (dg *DeviceGraph) getDevice(id string, useCache bool) (*voltha.Device, error) {
-	if useCache {
-		dg.cachedDevicesLock.RLock()
-		if d, exist := dg.cachedDevices[id]; exist {
-			dg.cachedDevicesLock.RUnlock()
-			//log.Debugw("getDevice - returned from cache", log.Fields{"deviceId": id})
-			return d, nil
-		}
-		dg.cachedDevicesLock.RUnlock()
-	}
-	//	Not cached
-	if d, err := dg.getDeviceFromModel(id); err != nil {
-		log.Errorw("device-not-found", log.Fields{"deviceId": id, "error": err})
-		return nil, err
-	} else { // cache it
-		dg.cachedDevicesLock.Lock()
-		dg.cachedDevices[id] = d
-		dg.cachedDevicesLock.Unlock()
-		//log.Debugw("getDevice - returned from model", log.Fields{"deviceId": id})
-		return d, nil
-	}
-}
-
-// addDevice adds a device to a device graph and setup edges that represent the device connections to its peers
-func (dg *DeviceGraph) addDevice(device *voltha.Device, g goraph.Graph, devicesAdded *map[string]string, portsAdded *map[string]string,
-	boundaryPorts map[string]uint32) goraph.Graph {
-
-	if device == nil {
-		return g
-	}
-
-	if _, exist := (*devicesAdded)[device.Id]; !exist {
-		g.AddNode(goraph.NewNode(device.Id))
-		(*devicesAdded)[device.Id] = device.Id
-	}
-
-	var portId string
-	var peerPortId string
-	for _, port := range device.Ports {
-		portId = concatDeviceIdPortId(device.Id, port.PortNo)
-		if _, exist := (*portsAdded)[portId]; !exist {
-			(*portsAdded)[portId] = portId
-			g.AddNode(goraph.NewNode(portId))
-			g.AddEdge(goraph.StringID(device.Id), goraph.StringID(portId), 1)
-			g.AddEdge(goraph.StringID(portId), goraph.StringID(device.Id), 1)
-		}
-		for _, peer := range port.Peers {
-			if _, exist := (*devicesAdded)[peer.DeviceId]; !exist {
-				d, _ := dg.getDevice(peer.DeviceId, true)
-				g = dg.addDevice(d, g, devicesAdded, portsAdded, boundaryPorts)
-			}
-			peerPortId = concatDeviceIdPortId(peer.DeviceId, peer.PortNo)
-			g.AddEdge(goraph.StringID(portId), goraph.StringID(peerPortId), 1)
-			g.AddEdge(goraph.StringID(peerPortId), goraph.StringID(portId), 1)
-
-		}
-	}
-	return g
-}
-
-//portExist returns true if the port ID is already part of the boundary ports map.
-func (dg *DeviceGraph) portExist(id string) bool {
-	dg.boundaryPortsLock.RLock()
-	defer dg.boundaryPortsLock.RUnlock()
-	_, exist := dg.boundaryPorts[id]
-	return exist
-}
-
-// buildPathsToAllRootPorts builds all the paths from the non-root logical port to all root ports
-// on the logical device
-func (dg *DeviceGraph) buildPathsToAllRootPorts(lp *voltha.LogicalPort) map[OFPortLink][]RouteHop {
-	paths := dg.Routes
-	source := concatDeviceIdPortId(lp.DeviceId, lp.DevicePortNo)
-	sourcePort := lp.OfpPort.PortNo
-	ch := make(chan *ofPortLinkToPath)
-	numBuildRequest := 0
-	for target, targetPort := range dg.rootPortsString {
-		go dg.buildRoute(source, target, sourcePort, targetPort, ch)
-		numBuildRequest += 1
-	}
-	responseReceived := 0
-forloop:
-	for {
-		if responseReceived == numBuildRequest {
-			break
-		}
-		select {
-		case res, ok := <-ch:
-			if !ok {
-				log.Debug("channel closed")
-				break forloop
-			}
-			if res != nil && len(res.path) > 0 {
-				paths[res.link] = res.path
-				paths[OFPortLink{Ingress: res.link.Egress, Egress: res.link.Ingress}] = getReverseRoute(res.path)
-			}
-		}
-		responseReceived += 1
-	}
-	return paths
-}
-
-// buildPathsToAllNonRootPorts builds all the paths from the root logical port to all non-root ports
-// on the logical device
-func (dg *DeviceGraph) buildPathsToAllNonRootPorts(lp *voltha.LogicalPort) map[OFPortLink][]RouteHop {
-	paths := dg.Routes
-	source := concatDeviceIdPortId(lp.DeviceId, lp.DevicePortNo)
-	sourcePort := lp.OfpPort.PortNo
-	ch := make(chan *ofPortLinkToPath)
-	numBuildRequest := 0
-	for target, targetPort := range dg.nonRootPortsString {
-		go dg.buildRoute(source, target, sourcePort, targetPort, ch)
-		numBuildRequest += 1
-	}
-	responseReceived := 0
-forloop:
-	for {
-		if responseReceived == numBuildRequest {
-			break
-		}
-		select {
-		case res, ok := <-ch:
-			if !ok {
-				log.Debug("channel closed")
-				break forloop
-			}
-			if res != nil && len(res.path) > 0 {
-				paths[res.link] = res.path
-				paths[OFPortLink{Ingress: res.link.Egress, Egress: res.link.Ingress}] = getReverseRoute(res.path)
-			}
-		}
-		responseReceived += 1
-	}
-	return paths
-}
-
-//buildRoute builds a route between a source and a target logical port
-func (dg *DeviceGraph) buildRoute(sourceId, targetId string, sourcePort, targetPort uint32, ch chan *ofPortLinkToPath) {
-	var pathIds []goraph.ID
-	path := make([]RouteHop, 0)
-	var err error
-	var hop RouteHop
-	var result *ofPortLinkToPath
-
-	if sourceId == targetId {
-		ch <- result
-		return
-	}
-	//Ignore Root - Root Routes
-	if dg.IsRootPort(sourcePort) && dg.IsRootPort(targetPort) {
-		ch <- result
-		return
-	}
-
-	//Ignore non-Root - non-Root Routes
-	if !dg.IsRootPort(sourcePort) && !dg.IsRootPort(targetPort) {
-		ch <- result
-		return
-	}
-
-	if pathIds, _, err = goraph.Dijkstra(dg.GGraph, goraph.StringID(sourceId), goraph.StringID(targetId)); err != nil {
-		log.Errorw("no-path", log.Fields{"sourceId": sourceId, "targetId": targetId, "error": err})
-		ch <- result
-		return
-	}
-	if len(pathIds)%3 != 0 {
-		ch <- result
-		return
-	}
-	var deviceId string
-	var ingressPort uint32
-	var egressPort uint32
-	for i := 0; i < len(pathIds); i = i + 3 {
-		if deviceId, ingressPort, err = splitIntoDeviceIdPortId(pathIds[i].String()); err != nil {
-			log.Errorw("id-error", log.Fields{"sourceId": sourceId, "targetId": targetId, "error": err})
-			break
-		}
-		if _, egressPort, err = splitIntoDeviceIdPortId(pathIds[i+2].String()); err != nil {
-			log.Errorw("id-error", log.Fields{"sourceId": sourceId, "targetId": targetId, "error": err})
-			break
-		}
-		hop = RouteHop{Ingress: ingressPort, DeviceID: deviceId, Egress: egressPort}
-		path = append(path, hop)
-	}
-	result = &ofPortLinkToPath{link: OFPortLink{Ingress: sourcePort, Egress: targetPort}, path: path}
-	ch <- result
-}
-
-//buildRoutes build all routes between all the ports on the logical device
-func (dg *DeviceGraph) buildRoutes() map[OFPortLink][]RouteHop {
-	paths := make(map[OFPortLink][]RouteHop)
-	ch := make(chan *ofPortLinkToPath)
-	numBuildRequest := 0
-	for source, sourcePort := range dg.boundaryPorts {
-		for target, targetPort := range dg.boundaryPorts {
-			go dg.buildRoute(source, target, sourcePort, targetPort, ch)
-			numBuildRequest += 1
-		}
-	}
-	responseReceived := 0
-forloop:
-	for {
-		if responseReceived == numBuildRequest {
-			break
-		}
-		select {
-		case res, ok := <-ch:
-			if !ok {
-				log.Debug("channel closed")
-				break forloop
-			}
-			if res != nil && len(res.path) > 0 {
-				paths[res.link] = res.path
-			}
-		}
-		responseReceived += 1
-	}
-	return paths
-}
-
-// reset cleans up the device graph
-func (dg *DeviceGraph) reset() {
-	dg.devicesAdded = make(map[string]string)
-	dg.portsAdded = make(map[string]string)
-	dg.rootPortsString = make(map[string]uint32)
-	dg.nonRootPortsString = make(map[string]uint32)
-	dg.RootPorts = make(map[uint32]uint32)
-	dg.boundaryPorts = make(map[string]uint32)
-	dg.Routes = make(map[OFPortLink][]RouteHop)
-	dg.cachedDevices = make(map[string]*voltha.Device)
-}
-
-//concatDeviceIdPortId formats a portid using the device id and the port number
-func concatDeviceIdPortId(deviceId string, portNo uint32) string {
-	return fmt.Sprintf("%s:%d", deviceId, portNo)
-}
-
-// splitIntoDeviceIdPortId extracts the device id and port number from the portId
-func splitIntoDeviceIdPortId(id string) (string, uint32, error) {
-	result := strings.Split(id, ":")
-	if len(result) != 2 {
-		return "", 0, errors.New(fmt.Sprintf("invalid-id-%s", id))
-	}
-	if temp, err := strconv.ParseInt(result[1], 10, 32); err != nil {
-		return "", 0, errors.New(fmt.Sprintf("invalid-id-%s-%s", id, err.Error()))
-	} else {
-		return result[0], uint32(temp), nil
-	}
-}
-
-//getReverseRoute returns the reverse of the route in param
-func getReverseRoute(route []RouteHop) []RouteHop {
-	reverse := make([]RouteHop, len(route))
-	for i, j := 0, len(route)-1; i < j; i, j = i+1, j-1 {
-		reverse[i], reverse[j] = route[j], route[i]
-	}
-	return reverse
-}
diff --git a/vendor/github.com/opencord/voltha-go/rw_core/utils/core_utils.go b/vendor/github.com/opencord/voltha-go/rw_core/utils/core_utils.go
index cf77d59..813c978 100644
--- a/vendor/github.com/opencord/voltha-go/rw_core/utils/core_utils.go
+++ b/vendor/github.com/opencord/voltha-go/rw_core/utils/core_utils.go
@@ -59,12 +59,12 @@
 		if !ok { // closed channel
 			//Set the channel at that index to nil to disable this case, hence preventing it from interfering with other cases.
 			cases[index].Chan = reflect.ValueOf(nil)
-			errors[index] = status.Errorf(codes.Internal, "channel closed")
+			errors[index] = status.Error(codes.Internal, "channel closed")
 			errorsReceived = true
 		} else if index == len(chnls) { // Timeout has occurred
 			for k := range errors {
 				if !resultsReceived[k] {
-					errors[k] = status.Errorf(codes.Aborted, "timeout")
+					errors[k] = status.Error(codes.Aborted, "timeout")
 				}
 			}
 			errorsReceived = true
diff --git a/vendor/github.com/opencord/voltha-go/rw_core/utils/flow_utils.go b/vendor/github.com/opencord/voltha-go/rw_core/utils/flow_utils.go
index c2c9287..c1ca18d 100644
--- a/vendor/github.com/opencord/voltha-go/rw_core/utils/flow_utils.go
+++ b/vendor/github.com/opencord/voltha-go/rw_core/utils/flow_utils.go
@@ -17,12 +17,746 @@
 
 import (
 	"bytes"
+	"crypto/md5"
+	"fmt"
 	"github.com/cevaris/ordered_map"
 	"github.com/gogo/protobuf/proto"
+	"github.com/opencord/voltha-go/common/log"
 	ofp "github.com/opencord/voltha-protos/go/openflow_13"
+	"math/big"
 	"strings"
 )
 
+var (
+	// Instructions shortcut
+	APPLY_ACTIONS = ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS
+
+	//OFPAT_* shortcuts
+	OUTPUT       = ofp.OfpActionType_OFPAT_OUTPUT
+	COPY_TTL_OUT = ofp.OfpActionType_OFPAT_COPY_TTL_OUT
+	COPY_TTL_IN  = ofp.OfpActionType_OFPAT_COPY_TTL_IN
+	SET_MPLS_TTL = ofp.OfpActionType_OFPAT_SET_MPLS_TTL
+	DEC_MPLS_TTL = ofp.OfpActionType_OFPAT_DEC_MPLS_TTL
+	PUSH_VLAN    = ofp.OfpActionType_OFPAT_PUSH_VLAN
+	POP_VLAN     = ofp.OfpActionType_OFPAT_POP_VLAN
+	PUSH_MPLS    = ofp.OfpActionType_OFPAT_PUSH_MPLS
+	POP_MPLS     = ofp.OfpActionType_OFPAT_POP_MPLS
+	SET_QUEUE    = ofp.OfpActionType_OFPAT_SET_QUEUE
+	GROUP        = ofp.OfpActionType_OFPAT_GROUP
+	SET_NW_TTL   = ofp.OfpActionType_OFPAT_SET_NW_TTL
+	NW_TTL       = ofp.OfpActionType_OFPAT_DEC_NW_TTL
+	SET_FIELD    = ofp.OfpActionType_OFPAT_SET_FIELD
+	PUSH_PBB     = ofp.OfpActionType_OFPAT_PUSH_PBB
+	POP_PBB      = ofp.OfpActionType_OFPAT_POP_PBB
+	EXPERIMENTER = ofp.OfpActionType_OFPAT_EXPERIMENTER
+
+	//OFPXMT_OFB_* shortcuts (incomplete)
+	IN_PORT         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IN_PORT
+	IN_PHY_PORT     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IN_PHY_PORT
+	METADATA        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_METADATA
+	ETH_DST         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_DST
+	ETH_SRC         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_SRC
+	ETH_TYPE        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_TYPE
+	VLAN_VID        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID
+	VLAN_PCP        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP
+	IP_DSCP         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_DSCP
+	IP_ECN          = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_ECN
+	IP_PROTO        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_PROTO
+	IPV4_SRC        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_SRC
+	IPV4_DST        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST
+	TCP_SRC         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TCP_SRC
+	TCP_DST         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TCP_DST
+	UDP_SRC         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC
+	UDP_DST         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_UDP_DST
+	SCTP_SRC        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_SCTP_SRC
+	SCTP_DST        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_SCTP_DST
+	ICMPV4_TYPE     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV4_TYPE
+	ICMPV4_CODE     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV4_CODE
+	ARP_OP          = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_OP
+	ARP_SPA         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_SPA
+	ARP_TPA         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_TPA
+	ARP_SHA         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_SHA
+	ARP_THA         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_THA
+	IPV6_SRC        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_SRC
+	IPV6_DST        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_DST
+	IPV6_FLABEL     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_FLABEL
+	ICMPV6_TYPE     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV6_TYPE
+	ICMPV6_CODE     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV6_CODE
+	IPV6_ND_TARGET  = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_TARGET
+	OFB_IPV6_ND_SLL = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_SLL
+	IPV6_ND_TLL     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_TLL
+	MPLS_LABEL      = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_LABEL
+	MPLS_TC         = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_TC
+	MPLS_BOS        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_BOS
+	PBB_ISID        = ofp.OxmOfbFieldTypes_OFPXMT_OFB_PBB_ISID
+	TUNNEL_ID       = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TUNNEL_ID
+	IPV6_EXTHDR     = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_EXTHDR
+)
+
+//ofp_action_* shortcuts
+
+func Output(port uint32, maxLen ...ofp.OfpControllerMaxLen) *ofp.OfpAction {
+	maxLength := ofp.OfpControllerMaxLen_OFPCML_MAX
+	if len(maxLen) > 0 {
+		maxLength = maxLen[0]
+	}
+	return &ofp.OfpAction{Type: OUTPUT, Action: &ofp.OfpAction_Output{Output: &ofp.OfpActionOutput{Port: port, MaxLen: uint32(maxLength)}}}
+}
+
+func MplsTtl(ttl uint32) *ofp.OfpAction {
+	return &ofp.OfpAction{Type: SET_MPLS_TTL, Action: &ofp.OfpAction_MplsTtl{MplsTtl: &ofp.OfpActionMplsTtl{MplsTtl: ttl}}}
+}
+
+func PushVlan(ethType uint32) *ofp.OfpAction {
+	return &ofp.OfpAction{Type: PUSH_VLAN, Action: &ofp.OfpAction_Push{Push: &ofp.OfpActionPush{Ethertype: ethType}}}
+}
+
+func PopVlan() *ofp.OfpAction {
+	return &ofp.OfpAction{Type: POP_VLAN}
+}
+
+func PopMpls(ethType uint32) *ofp.OfpAction {
+	return &ofp.OfpAction{Type: POP_MPLS, Action: &ofp.OfpAction_PopMpls{PopMpls: &ofp.OfpActionPopMpls{Ethertype: ethType}}}
+}
+
+func Group(groupId uint32) *ofp.OfpAction {
+	return &ofp.OfpAction{Type: GROUP, Action: &ofp.OfpAction_Group{Group: &ofp.OfpActionGroup{GroupId: groupId}}}
+}
+
+func NwTtl(nwTtl uint32) *ofp.OfpAction {
+	return &ofp.OfpAction{Type: NW_TTL, Action: &ofp.OfpAction_NwTtl{NwTtl: &ofp.OfpActionNwTtl{NwTtl: nwTtl}}}
+}
+
+func SetField(field *ofp.OfpOxmOfbField) *ofp.OfpAction {
+	actionSetField := &ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: &ofp.OfpOxmField_OfbField{OfbField: field}}
+	return &ofp.OfpAction{Type: SET_FIELD, Action: &ofp.OfpAction_SetField{SetField: &ofp.OfpActionSetField{Field: actionSetField}}}
+}
+
+func Experimenter(experimenter uint32, data []byte) *ofp.OfpAction {
+	return &ofp.OfpAction{Type: EXPERIMENTER, Action: &ofp.OfpAction_Experimenter{Experimenter: &ofp.OfpActionExperimenter{Experimenter: experimenter, Data: data}}}
+}
+
+//ofb_field generators (incomplete set)
+
+func InPort(inPort uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IN_PORT, Value: &ofp.OfpOxmOfbField_Port{Port: inPort}}
+}
+
+func InPhyPort(inPhyPort uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IN_PHY_PORT, Value: &ofp.OfpOxmOfbField_Port{Port: inPhyPort}}
+}
+
+func Metadata_ofp(tableMetadata uint64) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: METADATA, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: tableMetadata}}
+}
+
+// should Metadata_ofp used here ?????
+func EthDst(ethDst uint64) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ETH_DST, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: ethDst}}
+}
+
+// should Metadata_ofp used here ?????
+func EthSrc(ethSrc uint64) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ETH_SRC, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: ethSrc}}
+}
+
+func EthType(ethType uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ETH_TYPE, Value: &ofp.OfpOxmOfbField_EthType{EthType: ethType}}
+}
+
+func VlanVid(vlanVid uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: VLAN_VID, Value: &ofp.OfpOxmOfbField_VlanVid{VlanVid: vlanVid}}
+}
+
+func VlanPcp(vlanPcp uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: VLAN_PCP, Value: &ofp.OfpOxmOfbField_VlanPcp{VlanPcp: vlanPcp}}
+}
+
+func IpDscp(ipDscp uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IP_DSCP, Value: &ofp.OfpOxmOfbField_IpDscp{IpDscp: ipDscp}}
+}
+
+func IpEcn(ipEcn uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IP_ECN, Value: &ofp.OfpOxmOfbField_IpEcn{IpEcn: ipEcn}}
+}
+
+func IpProto(ipProto uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IP_PROTO, Value: &ofp.OfpOxmOfbField_IpProto{IpProto: ipProto}}
+}
+
+func Ipv4Src(ipv4Src uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IPV4_SRC, Value: &ofp.OfpOxmOfbField_Ipv4Src{Ipv4Src: ipv4Src}}
+}
+
+func Ipv4Dst(ipv4Dst uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IPV4_DST, Value: &ofp.OfpOxmOfbField_Ipv4Dst{Ipv4Dst: ipv4Dst}}
+}
+
+func TcpSrc(tcpSrc uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: TCP_SRC, Value: &ofp.OfpOxmOfbField_TcpSrc{TcpSrc: tcpSrc}}
+}
+
+func TcpDst(tcpDst uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: TCP_DST, Value: &ofp.OfpOxmOfbField_TcpDst{TcpDst: tcpDst}}
+}
+
+func UdpSrc(udpSrc uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: UDP_SRC, Value: &ofp.OfpOxmOfbField_UdpSrc{UdpSrc: udpSrc}}
+}
+
+func UdpDst(udpDst uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: UDP_DST, Value: &ofp.OfpOxmOfbField_UdpDst{UdpDst: udpDst}}
+}
+
+func SctpSrc(sctpSrc uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: SCTP_SRC, Value: &ofp.OfpOxmOfbField_SctpSrc{SctpSrc: sctpSrc}}
+}
+
+func SctpDst(sctpDst uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: SCTP_DST, Value: &ofp.OfpOxmOfbField_SctpDst{SctpDst: sctpDst}}
+}
+
+func Icmpv4Type(icmpv4Type uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ICMPV4_TYPE, Value: &ofp.OfpOxmOfbField_Icmpv4Type{Icmpv4Type: icmpv4Type}}
+}
+
+func Icmpv4Code(icmpv4Code uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ICMPV4_CODE, Value: &ofp.OfpOxmOfbField_Icmpv4Code{Icmpv4Code: icmpv4Code}}
+}
+
+func ArpOp(arpOp uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ARP_OP, Value: &ofp.OfpOxmOfbField_ArpOp{ArpOp: arpOp}}
+}
+
+func ArpSpa(arpSpa uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ARP_SPA, Value: &ofp.OfpOxmOfbField_ArpSpa{ArpSpa: arpSpa}}
+}
+
+func ArpTpa(arpTpa uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ARP_TPA, Value: &ofp.OfpOxmOfbField_ArpTpa{ArpTpa: arpTpa}}
+}
+
+func ArpSha(arpSha []byte) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ARP_SHA, Value: &ofp.OfpOxmOfbField_ArpSha{ArpSha: arpSha}}
+}
+
+func ArpTha(arpTha []byte) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ARP_THA, Value: &ofp.OfpOxmOfbField_ArpTha{ArpTha: arpTha}}
+}
+
+func Ipv6Src(ipv6Src []byte) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IPV6_SRC, Value: &ofp.OfpOxmOfbField_Ipv6Src{Ipv6Src: ipv6Src}}
+}
+
+func Ipv6Dst(ipv6Dst []byte) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IPV6_DST, Value: &ofp.OfpOxmOfbField_Ipv6Dst{Ipv6Dst: ipv6Dst}}
+}
+
+func Ipv6Flabel(ipv6Flabel uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IPV6_FLABEL, Value: &ofp.OfpOxmOfbField_Ipv6Flabel{Ipv6Flabel: ipv6Flabel}}
+}
+
+func Icmpv6Type(icmpv6Type uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ICMPV6_TYPE, Value: &ofp.OfpOxmOfbField_Icmpv6Type{Icmpv6Type: icmpv6Type}}
+}
+
+func Icmpv6Code(icmpv6Code uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: ICMPV6_CODE, Value: &ofp.OfpOxmOfbField_Icmpv6Code{Icmpv6Code: icmpv6Code}}
+}
+
+func Ipv6NdTarget(ipv6NdTarget []byte) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IPV6_ND_TARGET, Value: &ofp.OfpOxmOfbField_Ipv6NdTarget{Ipv6NdTarget: ipv6NdTarget}}
+}
+
+func OfbIpv6NdSll(ofbIpv6NdSll []byte) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: OFB_IPV6_ND_SLL, Value: &ofp.OfpOxmOfbField_Ipv6NdSsl{Ipv6NdSsl: ofbIpv6NdSll}}
+}
+
+func Ipv6NdTll(ipv6NdTll []byte) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IPV6_ND_TLL, Value: &ofp.OfpOxmOfbField_Ipv6NdTll{Ipv6NdTll: ipv6NdTll}}
+}
+
+func MplsLabel(mplsLabel uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: MPLS_LABEL, Value: &ofp.OfpOxmOfbField_MplsLabel{MplsLabel: mplsLabel}}
+}
+
+func MplsTc(mplsTc uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: MPLS_TC, Value: &ofp.OfpOxmOfbField_MplsTc{MplsTc: mplsTc}}
+}
+
+func MplsBos(mplsBos uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: MPLS_BOS, Value: &ofp.OfpOxmOfbField_MplsBos{MplsBos: mplsBos}}
+}
+
+func PbbIsid(pbbIsid uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: PBB_ISID, Value: &ofp.OfpOxmOfbField_PbbIsid{PbbIsid: pbbIsid}}
+}
+
+func TunnelId(tunnelId uint64) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: TUNNEL_ID, Value: &ofp.OfpOxmOfbField_TunnelId{TunnelId: tunnelId}}
+}
+
+func Ipv6Exthdr(ipv6Exthdr uint32) *ofp.OfpOxmOfbField {
+	return &ofp.OfpOxmOfbField{Type: IPV6_EXTHDR, Value: &ofp.OfpOxmOfbField_Ipv6Exthdr{Ipv6Exthdr: ipv6Exthdr}}
+}
+
+//frequently used extractors
+
+func excludeAction(action *ofp.OfpAction, exclude ...ofp.OfpActionType) bool {
+	for _, actionToExclude := range exclude {
+		if action.Type == actionToExclude {
+			return true
+		}
+	}
+	return false
+}
+
+func GetActions(flow *ofp.OfpFlowStats, exclude ...ofp.OfpActionType) []*ofp.OfpAction {
+	if flow == nil {
+		return nil
+	}
+	for _, instruction := range flow.Instructions {
+		if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS) {
+			instActions := instruction.GetActions()
+			if instActions == nil {
+				return nil
+			}
+			if len(exclude) == 0 {
+				return instActions.Actions
+			} else {
+				filteredAction := make([]*ofp.OfpAction, 0)
+				for _, action := range instActions.Actions {
+					if !excludeAction(action, exclude...) {
+						filteredAction = append(filteredAction, action)
+					}
+				}
+				return filteredAction
+			}
+		}
+	}
+	return nil
+}
+
+func UpdateOutputPortByActionType(flow *ofp.OfpFlowStats, actionType uint32, toPort uint32) *ofp.OfpFlowStats {
+	if flow == nil {
+		return nil
+	}
+	nFlow := (proto.Clone(flow)).(*ofp.OfpFlowStats)
+	nFlow.Instructions = nil
+	nInsts := make([]*ofp.OfpInstruction, 0)
+	for _, instruction := range flow.Instructions {
+		if instruction.Type == actionType {
+			instActions := instruction.GetActions()
+			if instActions == nil {
+				return nil
+			}
+			nActions := make([]*ofp.OfpAction, 0)
+			for _, action := range instActions.Actions {
+				if action.GetOutput() != nil {
+					nActions = append(nActions, Output(toPort))
+				} else {
+					nActions = append(nActions, action)
+				}
+			}
+			instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: nActions}}
+			nInsts = append(nInsts, &ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction})
+		} else {
+			nInsts = append(nInsts, instruction)
+		}
+	}
+	nFlow.Instructions = nInsts
+	return nFlow
+}
+
+func excludeOxmOfbField(field *ofp.OfpOxmOfbField, exclude ...ofp.OxmOfbFieldTypes) bool {
+	for _, fieldToExclude := range exclude {
+		if field.Type == fieldToExclude {
+			return true
+		}
+	}
+	return false
+}
+
+func GetOfbFields(flow *ofp.OfpFlowStats, exclude ...ofp.OxmOfbFieldTypes) []*ofp.OfpOxmOfbField {
+	if flow == nil || flow.Match == nil || flow.Match.Type != ofp.OfpMatchType_OFPMT_OXM {
+		return nil
+	}
+	ofbFields := make([]*ofp.OfpOxmOfbField, 0)
+	for _, field := range flow.Match.OxmFields {
+		if field.OxmClass == ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC {
+			ofbFields = append(ofbFields, field.GetOfbField())
+		}
+	}
+	if len(exclude) == 0 {
+		return ofbFields
+	} else {
+		filteredFields := make([]*ofp.OfpOxmOfbField, 0)
+		for _, ofbField := range ofbFields {
+			if !excludeOxmOfbField(ofbField, exclude...) {
+				filteredFields = append(filteredFields, ofbField)
+			}
+		}
+		return filteredFields
+	}
+}
+
+func GetPacketOutPort(packet *ofp.OfpPacketOut) uint32 {
+	if packet == nil {
+		return 0
+	}
+	for _, action := range packet.GetActions() {
+		if action.Type == OUTPUT {
+			return action.GetOutput().Port
+		}
+	}
+	return 0
+}
+
+func GetOutPort(flow *ofp.OfpFlowStats) uint32 {
+	if flow == nil {
+		return 0
+	}
+	for _, action := range GetActions(flow) {
+		if action.Type == OUTPUT {
+			out := action.GetOutput()
+			if out == nil {
+				return 0
+			}
+			return out.GetPort()
+		}
+	}
+	return 0
+}
+
+func GetInPort(flow *ofp.OfpFlowStats) uint32 {
+	if flow == nil {
+		return 0
+	}
+	for _, field := range GetOfbFields(flow) {
+		if field.Type == IN_PORT {
+			return field.GetPort()
+		}
+	}
+	return 0
+}
+
+func GetGotoTableId(flow *ofp.OfpFlowStats) uint32 {
+	if flow == nil {
+		return 0
+	}
+	for _, instruction := range flow.Instructions {
+		if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE) {
+			gotoTable := instruction.GetGotoTable()
+			if gotoTable == nil {
+				return 0
+			}
+			return gotoTable.GetTableId()
+		}
+	}
+	return 0
+}
+
+func GetTunnelId(flow *ofp.OfpFlowStats) uint64 {
+	if flow == nil {
+		return 0
+	}
+	for _, field := range GetOfbFields(flow) {
+		if field.Type == TUNNEL_ID {
+			return field.GetTunnelId()
+		}
+	}
+	return 0
+}
+
+//GetMetaData - legacy get method (only want lower 32 bits)
+func GetMetaData(flow *ofp.OfpFlowStats) uint32 {
+	if flow == nil {
+		return 0
+	}
+	for _, field := range GetOfbFields(flow) {
+		if field.Type == METADATA {
+			return uint32(field.GetTableMetadata() & 0xffffffff)
+		}
+	}
+	return 0
+}
+
+func GetMetaData64Bit(flow *ofp.OfpFlowStats) uint64 {
+	if flow == nil {
+		return 0
+	}
+	for _, field := range GetOfbFields(flow) {
+		if field.Type == METADATA {
+			return field.GetTableMetadata()
+		}
+	}
+	return 0
+}
+
+// GetPortNumberFromMetadata retrieves the port number from the Metadata_ofp. The port number (UNI on ONU) is in the
+// lower 32-bits of Metadata_ofp and the inner_tag is in the upper 32-bits. This is set in the ONOS OltPipeline as
+// a Metadata_ofp field
+func GetPortNumberFromMetadata(flow *ofp.OfpFlowStats) uint64 {
+	md := GetMetaData64Bit(flow)
+	if md == 0 {
+		return 0
+	}
+	if md <= 0xffffffff {
+		log.Debugw("onos-upgrade-suggested", log.Fields{"Metadata_ofp": md, "message": "Legacy MetaData detected form OltPipeline"})
+		return md
+	}
+	return md & 0xffffffff
+}
+
+//GetInnerTagFromMetaData retrieves the inner tag from the Metadata_ofp. The port number (UNI on ONU) is in the
+// lower 32-bits of Metadata_ofp and the inner_tag is in the upper 32-bits. This is set in the ONOS OltPipeline as
+//// a Metadata_ofp field
+func GetInnerTagFromMetaData(flow *ofp.OfpFlowStats) uint64 {
+	md := GetMetaData64Bit(flow)
+	if md == 0 {
+		return 0
+	}
+	if md <= 0xffffffff {
+		log.Debugw("onos-upgrade-suggested", log.Fields{"Metadata_ofp": md, "message": "Legacy MetaData detected form OltPipeline"})
+		return md
+	}
+	return (md >> 32) & 0xffffffff
+}
+
+// Extract the child device port from a flow that contains the parent device peer port.  Typically the UNI port of an
+// ONU child device.  Per TST agreement this will be the lower 32 bits of tunnel id reserving upper 32 bits for later
+// use
+func GetChildPortFromTunnelId(flow *ofp.OfpFlowStats) uint32 {
+	tid := GetTunnelId(flow)
+	if tid == 0 {
+		return 0
+	}
+	// Per TST agreement we are keeping any child port id (uni port id) in the lower 32 bits
+	return uint32(tid & 0xffffffff)
+}
+
+func HasNextTable(flow *ofp.OfpFlowStats) bool {
+	if flow == nil {
+		return false
+	}
+	return GetGotoTableId(flow) != 0
+}
+
+func GetGroup(flow *ofp.OfpFlowStats) uint32 {
+	if flow == nil {
+		return 0
+	}
+	for _, action := range GetActions(flow) {
+		if action.Type == GROUP {
+			grp := action.GetGroup()
+			if grp == nil {
+				return 0
+			}
+			return grp.GetGroupId()
+		}
+	}
+	return 0
+}
+
+func HasGroup(flow *ofp.OfpFlowStats) bool {
+	return GetGroup(flow) != 0
+}
+
+// GetNextTableId returns the next table ID if the "table_id" is present in the map, otherwise return nil
+func GetNextTableId(kw OfpFlowModArgs) *uint32 {
+	if val, exist := kw["table_id"]; exist {
+		ret := uint32(val)
+		return &ret
+	}
+	return nil
+}
+
+// Return unique 64-bit integer hash for flow covering the following attributes:
+// 'table_id', 'priority', 'flags', 'cookie', 'match', '_instruction_string'
+func HashFlowStats(flow *ofp.OfpFlowStats) uint64 {
+	if flow == nil { // Should never happen
+		return 0
+	}
+	// Create string with the instructions field first
+	var instructionString bytes.Buffer
+	for _, instruction := range flow.Instructions {
+		instructionString.WriteString(instruction.String())
+	}
+	var flowString = fmt.Sprintf("%d%d%d%d%s%s", flow.TableId, flow.Priority, flow.Flags, flow.Cookie, flow.Match.String(), instructionString.String())
+	h := md5.New()
+	h.Write([]byte(flowString))
+	hash := big.NewInt(0)
+	hash.SetBytes(h.Sum(nil))
+	return hash.Uint64()
+}
+
+// flowStatsEntryFromFlowModMessage maps an ofp_flow_mod message to an ofp_flow_stats message
+func FlowStatsEntryFromFlowModMessage(mod *ofp.OfpFlowMod) *ofp.OfpFlowStats {
+	flow := &ofp.OfpFlowStats{}
+	if mod == nil {
+		return flow
+	}
+	flow.TableId = mod.TableId
+	flow.Priority = mod.Priority
+	flow.IdleTimeout = mod.IdleTimeout
+	flow.HardTimeout = mod.HardTimeout
+	flow.Flags = mod.Flags
+	flow.Cookie = mod.Cookie
+	flow.Match = mod.Match
+	flow.Instructions = mod.Instructions
+	flow.Id = HashFlowStats(flow)
+	return flow
+}
+
+func GroupEntryFromGroupMod(mod *ofp.OfpGroupMod) *ofp.OfpGroupEntry {
+	group := &ofp.OfpGroupEntry{}
+	if mod == nil {
+		return group
+	}
+	group.Desc = &ofp.OfpGroupDesc{Type: mod.Type, GroupId: mod.GroupId, Buckets: mod.Buckets}
+	group.Stats = &ofp.OfpGroupStats{GroupId: mod.GroupId}
+	//TODO do we need to instantiate bucket bins?
+	return group
+}
+
+func MkOxmFields(matchFields []ofp.OfpOxmField) []*ofp.OfpOxmField {
+	oxmFields := make([]*ofp.OfpOxmField, 0)
+	for _, matchField := range matchFields {
+		oxmField := ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: matchField.Field}
+		oxmFields = append(oxmFields, &oxmField)
+	}
+	return oxmFields
+}
+
+func MkInstructionsFromActions(actions []*ofp.OfpAction) []*ofp.OfpInstruction {
+	instructions := make([]*ofp.OfpInstruction, 0)
+	instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: actions}}
+	instruction := ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction}
+	instructions = append(instructions, &instruction)
+	return instructions
+}
+
+// Convenience function to generare ofp_flow_mod message with OXM BASIC match composed from the match_fields, and
+// single APPLY_ACTIONS instruction with a list if ofp_action objects.
+func MkSimpleFlowMod(matchFields []*ofp.OfpOxmField, actions []*ofp.OfpAction, command *ofp.OfpFlowModCommand, kw OfpFlowModArgs) *ofp.OfpFlowMod {
+
+	// Process actions instructions
+	instructions := make([]*ofp.OfpInstruction, 0)
+	instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: actions}}
+	instruction := ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction}
+	instructions = append(instructions, &instruction)
+
+	// Process next table
+	if tableId := GetNextTableId(kw); tableId != nil {
+		var instGotoTable ofp.OfpInstruction_GotoTable
+		instGotoTable.GotoTable = &ofp.OfpInstructionGotoTable{TableId: *tableId}
+		inst := ofp.OfpInstruction{Type: uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE), Data: &instGotoTable}
+		instructions = append(instructions, &inst)
+	}
+
+	// Process match fields
+	oxmFields := make([]*ofp.OfpOxmField, 0)
+	for _, matchField := range matchFields {
+		oxmField := ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: matchField.Field}
+		oxmFields = append(oxmFields, &oxmField)
+	}
+	var match ofp.OfpMatch
+	match.Type = ofp.OfpMatchType_OFPMT_OXM
+	match.OxmFields = oxmFields
+
+	// Create ofp_flow_message
+	msg := &ofp.OfpFlowMod{}
+	if command == nil {
+		msg.Command = ofp.OfpFlowModCommand_OFPFC_ADD
+	} else {
+		msg.Command = *command
+	}
+	msg.Instructions = instructions
+	msg.Match = &match
+
+	// Set the variadic argument values
+	msg = setVariadicModAttributes(msg, kw)
+
+	return msg
+}
+
+func MkMulticastGroupMod(groupId uint32, buckets []*ofp.OfpBucket, command *ofp.OfpGroupModCommand) *ofp.OfpGroupMod {
+	group := &ofp.OfpGroupMod{}
+	if command == nil {
+		group.Command = ofp.OfpGroupModCommand_OFPGC_ADD
+	} else {
+		group.Command = *command
+	}
+	group.Type = ofp.OfpGroupType_OFPGT_ALL
+	group.GroupId = groupId
+	group.Buckets = buckets
+	return group
+}
+
+//SetVariadicModAttributes sets only uint64 or uint32 fields of the ofp_flow_mod message
+func setVariadicModAttributes(mod *ofp.OfpFlowMod, args OfpFlowModArgs) *ofp.OfpFlowMod {
+	if args == nil {
+		return mod
+	}
+	for key, val := range args {
+		switch key {
+		case "cookie":
+			mod.Cookie = val
+		case "cookie_mask":
+			mod.CookieMask = val
+		case "table_id":
+			mod.TableId = uint32(val)
+		case "idle_timeout":
+			mod.IdleTimeout = uint32(val)
+		case "hard_timeout":
+			mod.HardTimeout = uint32(val)
+		case "priority":
+			mod.Priority = uint32(val)
+		case "buffer_id":
+			mod.BufferId = uint32(val)
+		case "out_port":
+			mod.OutPort = uint32(val)
+		case "out_group":
+			mod.OutGroup = uint32(val)
+		case "flags":
+			mod.Flags = uint32(val)
+		}
+	}
+	return mod
+}
+
+func MkPacketIn(port uint32, packet []byte) *ofp.OfpPacketIn {
+	packetIn := &ofp.OfpPacketIn{
+		Reason: ofp.OfpPacketInReason_OFPR_ACTION,
+		Match: &ofp.OfpMatch{
+			Type: ofp.OfpMatchType_OFPMT_OXM,
+			OxmFields: []*ofp.OfpOxmField{
+				{
+					OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC,
+					Field: &ofp.OfpOxmField_OfbField{
+						OfbField: InPort(port)},
+				},
+			},
+		},
+		Data: packet,
+	}
+	return packetIn
+}
+
+// MkFlowStat is a helper method to build flows
+func MkFlowStat(fa *FlowArgs) *ofp.OfpFlowStats {
+	//Build the matchfields
+	matchFields := make([]*ofp.OfpOxmField, 0)
+	for _, val := range fa.MatchFields {
+		matchFields = append(matchFields, &ofp.OfpOxmField{Field: &ofp.OfpOxmField_OfbField{OfbField: val}})
+	}
+	return FlowStatsEntryFromFlowModMessage(MkSimpleFlowMod(matchFields, fa.Actions, fa.Command, fa.KV))
+}
+
+func MkGroupStat(ga *GroupArgs) *ofp.OfpGroupEntry {
+	return GroupEntryFromGroupMod(MkMulticastGroupMod(ga.GroupId, ga.Buckets, ga.Command))
+}
+
 type OfpFlowModArgs map[string]uint64
 
 type FlowArgs struct {
@@ -127,6 +861,10 @@
 }
 
 func (fg *FlowsAndGroups) AddFlow(flow *ofp.OfpFlowStats) {
+	if flow == nil {
+		return
+	}
+
 	if fg.Flows == nil {
 		fg.Flows = ordered_map.NewOrderedMap()
 	}
@@ -139,6 +877,23 @@
 	}
 }
 
+func (fg *FlowsAndGroups) AddGroup(group *ofp.OfpGroupEntry) {
+	if group == nil {
+		return
+	}
+
+	if fg.Flows == nil {
+		fg.Flows = ordered_map.NewOrderedMap()
+	}
+	if fg.Groups == nil {
+		fg.Groups = ordered_map.NewOrderedMap()
+	}
+	//Add group only if absent
+	if _, exist := fg.Groups.Get(group.Desc.GroupId); !exist {
+		fg.Groups.Set(group.Desc.GroupId, group)
+	}
+}
+
 //AddFrom add flows and groups from the argument into this structure only if they do not already exist
 func (fg *FlowsAndGroups) AddFrom(from *FlowsAndGroups) {
 	iter := from.Flows.IterFunc()
@@ -266,6 +1021,9 @@
 //FlowMatch returns true if two flows matches on the following flow attributes:
 //TableId, Priority, Flags, Cookie, Match
 func FlowMatch(f1 *ofp.OfpFlowStats, f2 *ofp.OfpFlowStats) bool {
+	if f1 == nil || f2 == nil {
+		return false
+	}
 	keysMatter := []string{"TableId", "Priority", "Flags", "Cookie", "Match"}
 	for _, key := range keysMatter {
 		switch key {
@@ -297,6 +1055,9 @@
 //FlowMatchesMod returns True if given flow is "covered" by the wildcard flow_mod, taking into consideration of
 //both exact matches as well as masks-based match fields if any. Otherwise return False
 func FlowMatchesMod(flow *ofp.OfpFlowStats, mod *ofp.OfpFlowMod) bool {
+	if flow == nil || mod == nil {
+		return false
+	}
 	//Check if flow.cookie is covered by mod.cookie and mod.cookie_mask
 	if (flow.Cookie & mod.CookieMask) != (mod.Cookie & mod.CookieMask) {
 		return false
@@ -331,6 +1092,9 @@
 
 //FlowHasOutPort returns True if flow has a output command with the given out_port
 func FlowHasOutPort(flow *ofp.OfpFlowStats, outPort uint32) bool {
+	if flow == nil {
+		return false
+	}
 	for _, instruction := range flow.Instructions {
 		if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS) {
 			if instruction.GetActions() == nil {
@@ -351,6 +1115,9 @@
 
 //FlowHasOutGroup return True if flow has a output command with the given out_group
 func FlowHasOutGroup(flow *ofp.OfpFlowStats, groupID uint32) bool {
+	if flow == nil {
+		return false
+	}
 	for _, instruction := range flow.Instructions {
 		if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS) {
 			if instruction.GetActions() == nil {