func TestGraphPathTraversal(t *testing.T) {
	g := newGraph(t)

	n1 := g.NewNode(graph.GenID(), graph.Metadata{"Type": "host", "Name": "localhost"})
	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, graph.Metadata{"RelationType": "ownership"})
	g.Link(n2, n3, graph.Metadata{"RelationType": "ownership"})

	query := `G.V().Has("Name", "N3").GraphPath()`

	tp := traversal.NewGremlinTraversalParser(strings.NewReader(query), g)
	tp.AddTraversalExtension(NewTopologyTraversalExtension())

	ts, err := tp.Parse()
	if err != nil {
		t.Fatal(err.Error())
	}

	res, err := ts.Exec()
	if err != nil {
		t.Fatal(err.Error())
	}

	if len(res.Values()) != 1 || res.Values()[0].(string) != "localhost[Type=host]/N2[Type=T2]/N3[Type=T3]" {
		t.Fatalf("Should return 1 path, returned: %v", res.Values())
	}
}
func newTransversalGraph(t *testing.T) *graph.Graph {
	g := newGraph(t)

	n1 := g.NewNode(graph.GenID(), graph.Metadata{"Value": 1, "Type": "intf", "Bytes": 1024})
	n2 := g.NewNode(graph.GenID(), graph.Metadata{"Value": 2, "Type": "intf", "Bytes": 2024})
	n3 := g.NewNode(graph.GenID(), graph.Metadata{"Value": 3})
	n4 := g.NewNode(graph.GenID(), graph.Metadata{"Value": 4, "Name": "Node4", "Bytes": 4024})

	g.Link(n1, n2, graph.Metadata{"Direction": "Left"})
	g.Link(n2, n3, graph.Metadata{"Direction": "Left"})
	g.Link(n3, n4)
	g.Link(n1, n4)
	g.Link(n1, n3, graph.Metadata{"Mode": "Direct"})

	return g
}
Example #3
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)
	}
}
Example #4
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 #5
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 {
			logging.GetLogger().Errorf("Error registering namespace %s: %s", path, err.Error())
			return nil
		}
		defer syscall.Close(fd)
		if err := syscall.Fstat(fd, &s); err != nil {
			logging.GetLogger().Errorf("Error reading namespace %s: %s", path, err.Error())
			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", "Path": path}
	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 #6
0
func (probe *DockerProbe) registerContainer(id string) {
	probe.Lock()
	defer probe.Unlock()

	if _, ok := probe.containerMap[id]; ok {
		return
	}
	info, err := probe.client.ContainerInspect(context.Background(), 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
	}
	defer nsHandle.Close()

	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 #7
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.GetNodes(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 #8
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 #9
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.linkPendingChildren(intf, index)

	return intf
}
func TestRegexPredicate(t *testing.T) {
	g := newGraph(t)
	g.NewNode(graph.GenID(), graph.Metadata{"Type": "host", "Name": "localhost"})

	query := `G.V().Has("Name", Regex("^local.*st$")).Count()`

	tp := traversal.NewGremlinTraversalParser(strings.NewReader(query), g)
	tp.AddTraversalExtension(NewTopologyTraversalExtension())

	ts, err := tp.Parse()
	if err != nil {
		t.Fatal(err.Error())
	}

	res, err := ts.Exec()
	if err != nil {
		t.Fatal(err.Error())
	}

	if len(res.Values()) != 1 || res.Values()[0].(int) != 1 {
		t.Fatalf("Regex should exactly match 1 node, returned: %v", res.Values())
	}
}
Example #11
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 #12
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})
	} else if index > 0 {
		// 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 #13
0
func (u *NetNSProbe) Register(path string, extraMetadata graph.Metadata) *graph.Node {
	// When a new network namespace has been seen by inotify, the path to
	// the namespace may still be a regular file, not a bind mount to the
	// file in /proc/<pid>/tasks/<tid>/ns/net yet, so we wait a bit for the
	// bind mount to be set up
	var newns *NetNs
	err := common.Retry(func() error {
		var stats syscall.Stat_t
		fd, err := syscall.Open(path, syscall.O_RDONLY, 0)
		if err != nil {
			return err
		}

		err = syscall.Fstat(fd, &stats)
		syscall.Close(fd)
		if err != nil {
			return err
		}

		if stats.Dev != u.rootNsDev {
			return fmt.Errorf("%s does not seem to be a valid namespace", path)
		}

		newns = &NetNs{path: path, dev: stats.Dev, ino: stats.Ino}
		return nil
	}, 10, time.Millisecond*20)

	if err != nil {
		logging.GetLogger().Errorf("Could not register namespace: %s", err.Error())
		return nil
	}

	_, ok := u.pathToNetNS[path]
	if !ok {
		u.pathToNetNS[path] = newns
	}

	u.Lock()
	defer u.Unlock()

	nsString := newns.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", "Path": path}
	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)
	nu.Start(newns)

	u.nsnlProbes[nsString] = nu

	return n
}