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") } }
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() }
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 }
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 }
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++ } }() } } } }
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 }
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 }
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, } }
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 }
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 }
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 } } }
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 }
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 }
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) } }
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) } }
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) } }