Example #1
0
func TestMarshal(t *testing.T) {
	g := newGraph(t)

	n1 := g.NewNode(graph.GenID(), graph.Metadata{"Name": "N1", "Type": "T1"})
	n2 := g.NewNode(graph.GenID(), graph.Metadata{"Name": "N2", "Type": "T2"})
	n3 := g.NewNode(graph.GenID(), graph.Metadata{"Name": "N3", "Type": "T3"})

	g.Link(n1, n2)
	g.Link(n2, n3)

	r := g.LookupShortestPath(n3, graph.Metadata{"Name": "N1"})
	if len(r) == 0 {
		t.Errorf("Wrong nodes returned: %v", r)
	}

	path := NodePath{r}.Marshal()
	if path != "N1[Type=T1]/N2[Type=T2]/N3[Type=T3]" {
		t.Errorf("Wrong path returned: %s", path)
	}

	node := LookupNodeFromNodePathString(g, path)
	if node == nil || node.ID != n3.ID {
		t.Errorf("Wrong node returned: %s", node)
	}

	node = LookupNodeFromNodePathString(g, "N1[Type=T1]/N2[Type=T1]/N3[Type=T3]")
	if node != nil {
		t.Errorf("Shouldn't have any nodes returned")
	}
}
Example #2
0
func (u *NetNSProbe) Register(path string, extraMetadata *graph.Metadata) {
	name := getNetNSName(path)

	u.RLock()
	_, ok := u.nsnlProbes[name]
	u.RUnlock()
	if ok {
		return
	}

	u.Graph.Lock()
	defer u.Graph.Unlock()

	logging.GetLogger().Debugf("Network Namespace added: %s", name)
	metadata := graph.Metadata{"Name": name, "Type": "netns"}
	if extraMetadata != nil {
		for k, v := range *extraMetadata {
			metadata[k] = v
		}
	}
	n := u.Graph.NewNode(graph.GenID(), metadata)
	u.Graph.Link(u.Root, n)

	nu := NewNetNsNetLinkTopoUpdater(u.Graph, n)
	go nu.Start(path)

	u.Lock()
	u.nsnlProbes[name] = nu
	u.Unlock()
}
Example #3
0
func (u *NetLinkProbe) addGenericLinkToTopology(link netlink.Link, m graph.Metadata) *graph.Node {
	name := link.Attrs().Name
	index := int64(link.Attrs().Index)

	var intf *graph.Node
	if name != "lo" {
		intf = u.Graph.LookupFirstChild(u.Root, graph.Metadata{
			"IfIndex": index,
		})
	}

	if intf == nil {
		intf = u.Graph.NewNode(graph.GenID(), m)
	}

	if intf == nil {
		return nil
	}

	if !u.Graph.AreLinked(u.Root, intf) {
		u.Graph.Link(u.Root, intf)
	}

	u.handleIntfIsChild(intf, link)
	u.handleIntfIsVeth(intf, link)
	u.handleIntfIsBond(intf, link)

	return intf
}
Example #4
0
func (u *NetLinkProbe) addGenericLinkToTopology(link netlink.Link, m graph.Metadatas) *graph.Node {
	name := link.Attrs().Name
	index := int64(link.Attrs().Index)

	var intf *graph.Node
	if name != "lo" {
		intf = u.Graph.LookupFirstNode(graph.Metadatas{
			"Name":    name,
			"IfIndex": index,
			"MAC":     link.Attrs().HardwareAddr.String()})
	}

	if intf == nil {
		intf = u.Graph.NewNode(graph.GenID(), m)
	}

	if intf == nil {
		return nil
	}

	if !u.Graph.AreLinked(u.Root, intf) {
		u.Graph.Link(u.Root, intf)
	}

	u.handleIntfIsBridgeMember(intf, link)
	u.handleIntfIsVeth(intf, link)

	return intf
}
Example #5
0
func (u *NetLinkProbe) handleIntfIsVeth(intf *graph.Node, link netlink.Link) {
	if link.Type() != "veth" {
		return
	}

	stats, err := ethtool.Stats(link.Attrs().Name)
	if err != nil {
		logging.GetLogger().Errorf("Unable get stats from ethtool: %s", err.Error())
		return
	}

	if index, ok := stats["peer_ifindex"]; ok {
		peerResolver := func() bool {
			// re get the interface from the graph since the interface could have been deleted
			if u.Graph.GetNode(intf.ID) == nil {
				return false
			}

			// got more than 1 peer, unable to find the right one, wait for the other to discover
			peer := u.Graph.LookupFirstNode(graph.Metadata{"IfIndex": int64(index), "Type": "veth"})
			if peer != nil && !u.Graph.AreLinked(peer, intf) {
				u.Graph.NewEdge(graph.GenID(), peer, intf, graph.Metadata{"Type": "veth"})
				return true
			}
			return false
		}

		if int64(index) > intf.Metadata()["IfIndex"].(int64) {
			ok := peerResolver()
			if !ok {
				// retry few seconds later since the right peer can be insert later
				go func() {
					ok := false
					try := 0

					for {
						if ok || try > 10 {
							return
						}
						time.Sleep(time.Millisecond * 200)

						u.Graph.Lock()
						ok = peerResolver()
						u.Graph.Unlock()

						try++
					}
				}()
			}
		}
	}
}
Example #6
0
func (u *NetLinkProbe) addOvsLinkToTopology(link netlink.Link, m graph.Metadata) *graph.Node {
	name := link.Attrs().Name

	intf := u.Graph.LookupFirstNode(graph.Metadata{"Name": name, "Driver": "openvswitch"})
	if intf == nil {
		intf = u.Graph.NewNode(graph.GenID(), m)
	}

	if !u.Graph.AreLinked(u.Root, intf) {
		u.Graph.Link(u.Root, intf, graph.Metadata{"RelationType": "ownership"})
	}

	return intf
}
Example #7
0
func (u *NetNSProbe) Register(path string, extraMetadata graph.Metadata) *graph.Node {
	ns, ok := u.pathToNetNS[path]
	if !ok {
		var s syscall.Stat_t
		fd, err := syscall.Open(path, syscall.O_RDONLY, 0)
		if err != nil {
			return nil
		}
		defer syscall.Close(fd)
		if err := syscall.Fstat(fd, &s); err != nil {
			return nil
		}
		ns = &NetNs{path: path, dev: s.Dev, ino: s.Ino}
		u.pathToNetNS[path] = ns
	}

	u.Lock()
	defer u.Unlock()

	nsString := ns.String()
	probe, ok := u.nsnlProbes[nsString]
	if ok {
		probe.useCount++
		logging.GetLogger().Debugf("Increasing counter for namespace %s to %d", nsString, probe.useCount)
		return probe.Root
	}

	u.Graph.Lock()
	defer u.Graph.Unlock()

	logging.GetLogger().Debugf("Network Namespace added: %s", nsString)
	metadata := graph.Metadata{"Name": getNetNSName(path), "Type": "netns"}
	if extraMetadata != nil {
		for k, v := range extraMetadata {
			metadata[k] = v
		}
	}
	n := u.Graph.NewNode(graph.GenID(), metadata)
	u.Graph.Link(u.Root, n, graph.Metadata{"RelationType": "ownership"})

	nu := NewNetNsNetLinkTopoUpdater(u.Graph, n)
	go nu.Start(ns)

	u.nsnlProbes[nsString] = nu

	return n
}
Example #8
0
func (probe *DockerProbe) registerContainer(id string) {
	probe.Lock()
	defer probe.Unlock()

	if _, ok := probe.containerMap[id]; ok {
		return
	}
	info, err := probe.client.InspectContainer(id)
	if err != nil {
		logging.GetLogger().Errorf("Failed to inspect Docker container %s: %s", id, err.Error())
		return
	}

	nsHandle, err := netns.GetFromPid(info.State.Pid)
	if err != nil {
		return
	}

	namespace := probe.containerNamespace(info.State.Pid)
	logging.GetLogger().Debugf("Register docker container %s and PID %d", info.Id, info.State.Pid)

	var n *graph.Node
	if probe.hostNs.Equal(nsHandle) {
		// The container is in net=host mode
		n = probe.Root
	} else {
		n = probe.Register(namespace, graph.Metadata{"Name": info.Name[1:], "Manager": "docker"})

	}

	probe.Graph.Lock()
	metadata := graph.Metadata{
		"Type":                 "container",
		"Name":                 info.Name[1:],
		"Docker.ContainerID":   info.Id,
		"Docker.ContainerName": info.Name,
		"Docker.ContainerPID":  info.State.Pid,
	}
	containerNode := probe.Graph.NewNode(graph.GenID(), metadata)
	probe.Graph.Link(n, containerNode, graph.Metadata{"RelationType": "membership"})
	probe.Graph.Unlock()

	probe.containerMap[info.Id] = ContainerInfo{
		Pid:  info.State.Pid,
		Node: containerNode,
	}
}
Example #9
0
func (u *NetLinkProbe) addGenericLinkToTopology(link netlink.Link, m graph.Metadata) *graph.Node {
	name := link.Attrs().Name
	index := int64(link.Attrs().Index)

	var intf *graph.Node
	intf = u.Graph.LookupFirstChild(u.Root, graph.Metadata{
		"IfIndex": index,
	})

	// could be a member of ovs
	intfs := u.Graph.LookupNodes(graph.Metadata{
		"Name":    name,
		"IfIndex": index,
	})
	for _, i := range intfs {
		if _, ok := i.Metadata()["UUID"]; ok {
			intf = i
			break
		}
	}

	if intf == nil {
		intf = u.Graph.NewNode(graph.GenID(), m)
	}

	if intf == nil {
		return nil
	}

	if !u.Graph.AreLinked(u.Root, intf) {
		u.Graph.Link(u.Root, intf, graph.Metadata{"RelationType": "ownership"})
	}

	// ignore ovs-system interface as it doesn't make any sense according to
	// the following thread:
	// http://openvswitch.org/pipermail/discuss/2013-October/011657.html
	if name == "ovs-system" {
		return intf
	}

	u.handleIntfIsChild(intf, link)
	u.handleIntfIsVeth(intf, link)
	u.handleIntfIsBond(intf, link)

	return intf
}
Example #10
0
func (u *NetLinkProbe) addBridgeLinkToTopology(link netlink.Link, m graph.Metadatas) *graph.Node {
	name := link.Attrs().Name
	index := int64(link.Attrs().Index)

	intf := u.Graph.LookupFirstNode(graph.Metadatas{
		"Name":    name,
		"IfIndex": index})

	if intf == nil {
		intf = u.Graph.NewNode(graph.GenID(), m)
	}

	if !u.Graph.AreLinked(u.Root, intf) {
		u.Graph.Link(u.Root, intf)
	}

	return intf
}
Example #11
0
func (o *OvsdbProbe) OnOvsBridgeAdd(monitor *ovsdb.OvsMonitor, uuid string, row *libovsdb.RowUpdate) {
	o.Lock()
	defer o.Unlock()

	name := row.New.Fields["name"].(string)

	o.Graph.Lock()
	defer o.Graph.Unlock()

	bridge := o.Graph.LookupFirstNode(graph.Metadata{"UUID": uuid})
	if bridge == nil {
		bridge = o.Graph.NewNode(graph.GenID(), graph.Metadata{"Name": name, "UUID": uuid, "Type": "ovsbridge"})
		o.Graph.Link(o.Root, bridge, graph.Metadata{"RelationType": "ownership"})
	}

	switch row.New.Fields["ports"].(type) {
	case libovsdb.OvsSet:
		set := row.New.Fields["ports"].(libovsdb.OvsSet)

		for _, i := range set.GoSet {
			u := i.(libovsdb.UUID).GoUuid

			port, ok := o.uuidToPort[u]
			if ok && !o.Graph.AreLinked(bridge, port) {
				o.Graph.Link(bridge, port, graph.Metadata{"RelationType": "layer2"})
			} else {
				/* will be filled later when the port update for this port will be triggered */
				o.portBridgeQueue[u] = bridge
			}
		}

	case libovsdb.UUID:
		u := row.New.Fields["ports"].(libovsdb.UUID).GoUuid

		port, ok := o.uuidToPort[u]
		if ok && !o.Graph.AreLinked(bridge, port) {
			o.Graph.Link(bridge, port, graph.Metadata{"RelationType": "layer2"})
		} else {
			/* will be filled later when the port update for this port will be triggered */
			o.portBridgeQueue[u] = bridge
		}
	}
}
Example #12
0
func (u *NetLinkProbe) addBridgeLinkToTopology(link netlink.Link, m graph.Metadata) *graph.Node {
	name := link.Attrs().Name
	index := int64(link.Attrs().Index)

	intf := u.Graph.LookupFirstChild(u.Root, graph.Metadata{
		"Name":    name,
		"IfIndex": index,
	})

	if intf == nil {
		intf = u.Graph.NewNode(graph.GenID(), m)
	}

	if !u.Graph.AreLinked(u.Root, intf) {
		u.Graph.Link(u.Root, intf, graph.Metadata{"RelationType": "ownership"})
	}

	u.linkMasterChildren(intf, index)

	return intf
}
Example #13
0
func (u *NetNSProbe) onNetNsCreated(path string) {
	name := getNetNSName(path)

	_, ok := u.nsnlProbes[name]
	if ok {
		return
	}

	u.Graph.Lock()
	defer u.Graph.Unlock()

	logging.GetLogger().Debug("Network Namespace added: %s", name)
	n := u.Graph.NewNode(graph.GenID(), graph.Metadatas{"Name": name, "Type": "netns"})
	u.Graph.Link(u.Root, n)

	nu := NewNetNsNetLinkTopoUpdater(u.Graph, n)
	go nu.Start(path)

	u.Lock()
	defer u.Unlock()
	u.nsnlProbes[name] = nu
}
Example #14
0
func (o *OvsdbProbe) OnOvsPortAdd(monitor *ovsdb.OvsMonitor, uuid string, row *libovsdb.RowUpdate) {
	o.Lock()
	defer o.Unlock()

	o.Graph.Lock()
	defer o.Graph.Unlock()

	port, ok := o.uuidToPort[uuid]
	if !ok {
		port = o.Graph.NewNode(graph.GenID(), graph.Metadata{
			"UUID": uuid,
			"Name": row.New.Fields["name"].(string),
			"Type": "ovsport",
		})
		o.uuidToPort[uuid] = port
	}

	// bond mode
	if mode, ok := row.New.Fields["bond_mode"]; ok {
		switch mode.(type) {
		case string:
			o.Graph.AddMetadata(port, "BondMode", mode.(string))
		}
	}

	// lacp
	if lacp, ok := row.New.Fields["lacp"]; ok {
		switch lacp.(type) {
		case string:
			o.Graph.AddMetadata(port, "LACP", lacp.(string))
		}
	}

	// vlan tag
	if tag, ok := row.New.Fields["tag"]; ok {
		switch tag.(type) {
		case libovsdb.OvsSet:
			set := tag.(libovsdb.OvsSet)
			if len(set.GoSet) > 0 {
				o.Graph.AddMetadata(port, "Vlans", set.GoSet)
			}
		case float64:
			o.Graph.AddMetadata(port, "Vlans", int(tag.(float64)))
		}
	}

	switch row.New.Fields["interfaces"].(type) {
	case libovsdb.OvsSet:
		set := row.New.Fields["interfaces"].(libovsdb.OvsSet)

		for _, i := range set.GoSet {
			u := i.(libovsdb.UUID).GoUuid
			intf, ok := o.uuidToIntf[u]
			if ok && !o.Graph.AreLinked(port, intf) {
				o.Graph.Link(port, intf, graph.Metadata{"RelationType": "layer2"})
			} else {
				/* will be filled later when the interface update for this interface will be triggered */
				o.intfPortQueue[u] = port
			}
		}
	case libovsdb.UUID:
		u := row.New.Fields["interfaces"].(libovsdb.UUID).GoUuid
		intf, ok := o.uuidToIntf[u]
		if ok && !o.Graph.AreLinked(port, intf) {
			o.Graph.Link(port, intf, graph.Metadata{"RelationType": "layer2"})
		} else {
			/* will be filled later when the interface update for this interface will be triggered */
			o.intfPortQueue[u] = port
		}
	}

	/* set pending port of a container */
	if bridge, ok := o.portBridgeQueue[uuid]; ok {
		o.Graph.Link(bridge, port, graph.Metadata{"RelationType": "layer2"})
		delete(o.portBridgeQueue, uuid)
	}
}
Example #15
0
func (o *OvsdbProbe) OnOvsInterfaceAdd(monitor *ovsdb.OvsMonitor, uuid string, row *libovsdb.RowUpdate) {
	o.Lock()
	defer o.Unlock()

	// NOTE(safchain) is it a workaround ???, seems that the interface is not fully set
	switch row.New.Fields["ofport"].(type) {
	case float64:
	default:
		return
	}

	var mac string
	switch row.New.Fields["mac_in_use"].(type) {
	case string:
		mac = row.New.Fields["mac_in_use"].(string)
	}

	var index int64
	if i, ok := row.New.Fields["ifindex"]; ok {
		switch row.New.Fields["ifindex"].(type) {
		case float64:
			index = int64(i.(float64))
		case libovsdb.OvsSet:
			set := row.New.Fields["ifindex"].(libovsdb.OvsSet)
			if len(set.GoSet) > 0 {
				index = set.GoSet[0].(int64)
			}
		}
	}

	var driver string
	if d, ok := row.New.Fields["status"].(libovsdb.OvsMap).GoMap["driver_name"]; ok {
		driver = d.(string)
	}

	var itype string
	if t, ok := row.New.Fields["type"]; ok {
		itype = t.(string)
	}

	name := row.New.Fields["name"].(string)

	o.Graph.Lock()
	defer o.Graph.Unlock()

	intf := o.Graph.LookupFirstNode(graph.Metadata{"UUID": uuid})
	if intf == nil {
		// added before by netlink ?
		intf = o.Graph.LookupFirstNode(graph.Metadata{"Name": name, "Driver": "openvswitch"})
		if intf != nil {
			o.Graph.AddMetadata(intf, "UUID", uuid)
		}
	}

	if intf == nil {
		// didn't find with the UUID nor with the driver, try with index and/or mac
		lm := graph.Metadata{"Name": name}
		if index > 0 {
			lm["IfIndex"] = index
		}
		if mac != "" {
			lm["MAC"] = mac
		}

		if len(lm) > 1 {
			intf = o.Graph.LookupFirstChild(o.Root, lm)
			if intf != nil {
				o.Graph.AddMetadata(intf, "UUID", uuid)
			}
		}
	}

	if intf == nil {
		intf = o.Graph.NewNode(graph.GenID(), graph.Metadata{"Name": name, "UUID": uuid, "DEBUG": "OVSDB"})
	} else {
		// the index can be added after the interface creation, during an update so
		// we need to check whether a interface with the same index exists at the first level
		// if this interface has no UUID it means that it has been added by NETLINK
		// and this is the same interface thus replace one by the other to keep only
		// one interface.
		nodes := o.Graph.LookupChildren(o.Root, graph.Metadata{"Name": name, "IfIndex": index})
		for _, node := range nodes {
			if node.Metadata()["UUID"] != uuid {
				m := node.Metadata()
				m["UUID"] = uuid
				intf = o.Graph.Replace(node, intf)
			}
		}
	}

	tr := o.Graph.StartMetadataTransaction(intf)
	defer tr.Commit()

	if index > 0 {
		tr.AddMetadata("IfIndex", index)
	}

	if mac != "" {
		tr.AddMetadata("MAC", mac)
	}

	if driver != "" {
		tr.AddMetadata("Driver", driver)
	}

	if itype != "" {
		tr.AddMetadata("Type", itype)
	}

	ext_ids := row.New.Fields["external_ids"].(libovsdb.OvsMap)
	for k, v := range ext_ids.GoMap {
		tr.AddMetadata("ExtID."+k.(string), v.(string))
	}

	o.uuidToIntf[uuid] = intf

	switch itype {
	case "gre", "vxlan", "geneve":
		tr.AddMetadata("Driver", "openvswitch")

		m := row.New.Fields["options"].(libovsdb.OvsMap)
		if ip, ok := m.GoMap["local_ip"]; ok {
			tr.AddMetadata("LocalIP", ip.(string))
		}
		if ip, ok := m.GoMap["remote_ip"]; ok {
			tr.AddMetadata("RemoteIP", ip.(string))
		}
		m = row.New.Fields["status"].(libovsdb.OvsMap)
		if iface, ok := m.GoMap["tunnel_egress_iface"]; ok {
			tr.AddMetadata("TunEgressIface", iface.(string))
		}
		if carrier, ok := m.GoMap["tunnel_egress_iface_carrier"]; ok {
			tr.AddMetadata("TunEgressIfaceCarrier", carrier.(string))
		}

	case "patch":
		// force the driver as it is not defined and we need it to delete properly
		tr.AddMetadata("Driver", "openvswitch")

		m := row.New.Fields["options"].(libovsdb.OvsMap)
		if p, ok := m.GoMap["peer"]; ok {

			peerName := p.(string)

			peer := o.Graph.LookupFirstNode(graph.Metadata{"Name": peerName, "Type": "patch"})
			if peer != nil {
				if !o.Graph.AreLinked(intf, peer) {
					o.Graph.Link(intf, peer, graph.Metadata{"RelationType": "layer2", "Type": "patch"})
				}
			} else {
				// lookup in the intf queue
				for _, peer := range o.uuidToIntf {
					if peer.Metadata()["Name"] == peerName && !o.Graph.AreLinked(intf, peer) {
						o.Graph.Link(intf, peer, graph.Metadata{"RelationType": "layer2", "Type": "patch"})
					}
				}
			}
		}
	}

	/* set pending interface for a port */
	if port, ok := o.intfPortQueue[uuid]; ok {
		o.Graph.Link(port, intf, graph.Metadata{"RelationType": "layer2"})
		delete(o.intfPortQueue, uuid)
	}
}
Example #16
0
func (o *OvsdbProbe) OnOvsInterfaceAdd(monitor *ovsdb.OvsMonitor, uuid string, row *libovsdb.RowUpdate) {
	o.Lock()
	defer o.Unlock()

	// NOTE(safchain) is it a workaround ???, seems that the interface is not fully set
	switch row.New.Fields["ofport"].(type) {
	case float64:
	default:
		return
	}

	var mac string
	switch row.New.Fields["mac_in_use"].(type) {
	case string:
		mac = row.New.Fields["mac_in_use"].(string)
	}

	var index int64
	if i, ok := row.New.Fields["ifindex"]; ok {
		switch row.New.Fields["ifindex"].(type) {
		case float64:
			index = int64(i.(float64))
		case libovsdb.OvsSet:
			set := row.New.Fields["ifindex"].(libovsdb.OvsSet)
			if len(set.GoSet) > 0 {
				index = set.GoSet[0].(int64)
			}
		}
	}

	var driver string
	if d, ok := row.New.Fields["status"].(libovsdb.OvsMap).GoMap["driver_name"]; ok {
		driver = d.(string)
	}

	var itype string
	if t, ok := row.New.Fields["type"]; ok {
		itype = t.(string)
	}

	name := row.New.Fields["name"].(string)

	o.Graph.Lock()
	defer o.Graph.Unlock()

	intf := o.Graph.LookupFirstNode(graph.Metadata{"UUID": uuid})
	if intf == nil {
		intf = o.Graph.LookupFirstNode(graph.Metadata{"IfIndex": index})
		if intf != nil {
			o.Graph.AddMetadata(intf, "UUID", uuid)
		}
	}

	if intf == nil {
		metadata := graph.Metadata{"Name": name, "UUID": uuid}
		if driver != "" {
			metadata["Driver"] = driver
		}

		if itype != "" {
			metadata["Type"] = itype
		}

		if index > 0 {
			metadata["IfIndex"] = index
		}
		intf = o.Graph.NewNode(graph.GenID(), metadata)
	}

	// an ovs interface can have no mac in its db,
	// so don't overrivde the netlink provided value with an empty value
	if mac != "" && mac != intf.Metadata()["MAC"] {
		// check wether a interface with the same mac exist, could have been added by netlink
		// in such case, replace the netlink node by the ovs one
		nl := o.Graph.LookupFirstNode(graph.Metadata{"MAC": mac})
		if nl != nil {
			m := intf.Metadata()
			m["UUID"] = uuid
			intf = o.Graph.Replace(intf, nl, m)
		} else {
			o.Graph.AddMetadata(intf, "MAC", mac)
		}
	}

	if driver != "" {
		o.Graph.AddMetadata(intf, "Driver", driver)
	}

	if itype != "" {
		o.Graph.AddMetadata(intf, "Type", itype)
	}

	o.uuidToIntf[uuid] = intf

	switch itype {
	case "gre", "vxlan":
		o.Graph.AddMetadata(intf, "Driver", "openvswitch")

		m := row.New.Fields["options"].(libovsdb.OvsMap)
		if ip, ok := m.GoMap["local_ip"]; ok {
			o.Graph.AddMetadata(intf, "LocalIP", ip.(string))
		}
		if ip, ok := m.GoMap["remote_ip"]; ok {
			o.Graph.AddMetadata(intf, "RemoteIP", ip.(string))
		}
		m = row.New.Fields["status"].(libovsdb.OvsMap)
		if iface, ok := m.GoMap["tunnel_egress_iface"]; ok {
			o.Graph.AddMetadata(intf, "TunEgressIface", iface.(string))
		}
		if carrier, ok := m.GoMap["tunnel_egress_iface_carrier"]; ok {
			o.Graph.AddMetadata(intf, "TunEgressIfaceCarrier", carrier.(string))
		}

	case "patch":
		// force the driver as it is not defined and we need it to delete properly
		o.Graph.AddMetadata(intf, "Driver", "openvswitch")

		m := row.New.Fields["options"].(libovsdb.OvsMap)
		if p, ok := m.GoMap["peer"]; ok {

			peerName := p.(string)

			peer := o.Graph.LookupFirstNode(graph.Metadata{"Name": peerName, "Type": "patch"})
			if peer != nil {
				if !o.Graph.AreLinked(intf, peer) {
					o.Graph.NewEdge(graph.GenID(), intf, peer, graph.Metadata{"Type": "patch"})
				}
			} else {
				// lookup in the intf queue
				for _, peer := range o.uuidToIntf {
					if peer.Metadata()["Name"] == peerName && !o.Graph.AreLinked(intf, peer) {
						o.Graph.NewEdge(graph.GenID(), intf, peer, graph.Metadata{"Type": "patch"})
					}
				}
			}
		}
	}

	/* set pending interface for a port */
	if port, ok := o.intfPortQueue[uuid]; ok {
		o.Graph.Link(port, intf)
		delete(o.intfPortQueue, uuid)
	}
}