func (spec EnvLink) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) { _, e, success = findEnv(g, src) // Whether we find a match or not, have to merge in the EnvLink e.Props = maputil.FillPropMap(mid, false, pp("hostname", spec.Address.Hostname), pp("ipv4", spec.Address.Ipv4), pp("ipv6", spec.Address.Ipv6), pp("nick", spec.Nick), ) // If we already found the matching edge, bail out now if success { return } rv := g.VerticesWith(q.Qbv(system.VType("environment"))) for _, vt := range rv { // TODO this'll be cross-package eventually - reorg needed if maputil.AnyMatch(e.Props, vt.Vertex.Properties, "nick", "hostname", "ipv4", "ipv6") { success = true e.Target = vt.ID break } } return }
func findEnvironment(g system.CoreGraph, props ps.Map) (envid uint64, success bool) { rv := g.VerticesWith(q.Qbv(system.VType("environment"))) for _, vt := range rv { if maputil.AnyMatch(props, vt.Vertex.Props(), "hostname", "ipv4", "ipv6", "nick") { return vt.ID, true } } return }
func envUnify(g system.CoreGraph, u system.UnifyInstructionForm) uint64 { matches := g.VerticesWith(q.Qbv(system.VType("environment"))) for _, e := range matches { if maputil.AnyMatch(e.Vertex.Properties, u.Vertex().Properties(), "hostname", "ipv4", "ipv6") { return e.ID } } return 0 }
func (spec DataProvenance) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) { // FIXME this presents another weird case where "success" is not binary. We *could* // find an already-existing data-provenance edge, but then have some net-addr params // change which cause it to fail to resolve to an environment. If we call that successful, // then it won't try to resolve again later...though, hm, just call it unsuccessful and // then try again one more time. Maybe it is fine. THINK IT THROUGH. e = system.StdEdge{ Source: src.ID, Props: ps.NewMap(), EType: "data-provenance", } e.Props = assignAddress(mid, spec.Address, e.Props, false) re := g.OutWith(src.ID, q.Qbe(system.EType("data-provenance"))) if len(re) == 1 { reresolve := maputil.AnyMatch(e.Props, re[0].Props, "hostname", "ipv4", "ipv6") e = re[0] if spec.SnapTime != "" { e.Props = e.Props.Set("snap-time", system.Property{MsgSrc: mid, Value: spec.SnapTime}) } if reresolve { e.Props = assignAddress(mid, spec.Address, e.Props, true) } else { return e, true } } envid, found := findEnvironment(g, e.Props) if !found { // TODO returning this already-modified edge necessitates that the core system // disregard 'failed' edges. which should be fine, that should be a guarantee return e, false } e.Target, success = findDataset(g, envid, spec.Dataset) return }
func (spec DataLink) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) { e = system.StdEdge{ Source: src.ID, Props: ps.NewMap(), EType: "datalink", } // DataLinks have a 'name' field that is expected to be unique for the source, if present if spec.Name != "" { // TODO 'name' is a traditional unique key; a change in it inherently denotes a new edge. how to handle this? // FIXME this approach just always updates the mid, which is weird? e.Props = e.Props.Set("name", system.Property{MsgSrc: mid, Value: spec.Name}) re := g.OutWith(src.ID, q.Qbe(system.EType("datalink"), "name", spec.Name)) if len(re) == 1 { success = true e = re[0] } } if spec.Type != "" { e.Props = e.Props.Set("type", system.Property{MsgSrc: mid, Value: spec.Type}) } if spec.Subset != "" { e.Props = e.Props.Set("subset", system.Property{MsgSrc: mid, Value: spec.Subset}) } if spec.Interaction != "" { e.Props = e.Props.Set("interaction", system.Property{MsgSrc: mid, Value: spec.Interaction}) } // Special bits: if we have ConnUnix data, eliminate ConnNet data, and vice-versa. var isLocal bool if spec.ConnUnix.Path != "" { isLocal = true e.Props = e.Props.Set("path", system.Property{MsgSrc: mid, Value: spec.ConnUnix.Path}) e.Props = e.Props.Delete("hostname") e.Props = e.Props.Delete("ipv4") e.Props = e.Props.Delete("ipv6") e.Props = e.Props.Delete("port") e.Props = e.Props.Delete("proto") } else { e.Props = e.Props.Set("port", system.Property{MsgSrc: mid, Value: spec.ConnNet.Port}) e.Props = e.Props.Set("proto", system.Property{MsgSrc: mid, Value: spec.ConnNet.Proto}) // can only be one of hostname, ipv4 or ipv6 if spec.ConnNet.Hostname != "" { e.Props = e.Props.Set("hostname", system.Property{MsgSrc: mid, Value: spec.ConnNet.Hostname}) } else if spec.ConnNet.Ipv4 != "" { e.Props = e.Props.Set("ipv4", system.Property{MsgSrc: mid, Value: spec.ConnNet.Ipv4}) } else { e.Props = e.Props.Set("ipv6", system.Property{MsgSrc: mid, Value: spec.ConnNet.Ipv6}) } } if success { return } var sock system.VertexTuple var rv system.VertexTupleVector // just for reuse // If net, must scan; if local, a bit easier. if !isLocal { // First, find the environment vertex rv = g.VerticesWith(q.Qbv(system.VType("environment"))) var envid uint64 for _, vt := range rv { if maputil.AnyMatch(e.Props, vt.Vertex.Properties, "hostname", "ipv4", "ipv6") { envid = vt.ID break } } // No matching env found, bail out if envid == 0 { return } // Now, walk the environment's edges to find the vertex representing the port rv = g.PredecessorsWith(envid, q.Qbv(system.VType("comm"), "type", "port", "port", spec.ConnNet.Port).And(q.Qbe(system.EType("envlink")))) if len(rv) != 1 { return } sock = rv[0] // With sock in hand, now find its proc rv = g.PredecessorsWith(sock.ID, q.Qbe(system.EType("listening"), "proto", spec.ConnNet.Proto).And(q.Qbv(system.VType("process")))) if len(rv) != 1 { // TODO could/will we ever allow >1? return } } else { envid, _, exists := findEnv(g, src) if !exists { // this is would be a pretty weird case return } // Walk the graph to find the vertex representing the unix socket rv = g.PredecessorsWith(envid, q.Qbv(system.VType("comm"), "path", spec.ConnUnix.Path).And(q.Qbe(system.EType("envlink")))) if len(rv) != 1 { return } sock = rv[0] // With sock in hand, now find its proc rv = g.PredecessorsWith(sock.ID, q.Qbv(system.VType("process")).And(q.Qbe(system.EType("listening")))) if len(rv) != 1 { // TODO could/will we ever allow >1? return } } rv = g.SuccessorsWith(rv[0].ID, q.Qbv(system.VType("parent-dataset"))) // FIXME this absolutely could be more than 1 if len(rv) != 1 { return } dataset := rv[0] // if the spec indicates a subset, find it if spec.Subset != "" { rv = g.PredecessorsWith(rv[0].ID, q.Qbv(system.VType("dataset"), "name", spec.Subset).And(q.Qbe(system.EType("dataset-hierarchy")))) if len(rv) != 1 { return } dataset = rv[0] } // FIXME only recording the final target id is totally broken; see https://github.com/pipeviz/pipeviz/issues/37 // Aaaand we found our target. success = true e.Target = dataset.ID return }