@skydive-project/skydive-ui
Version:
Skydive WebUI v2
622 lines (592 loc) • 19.4 kB
text/typescript
import { Node, Link, NodeAttrs } from './Topology'
import Tools from './Tools'
const WEIGHT_NONE = 0
const WEIGHT_SUBMARINER_BROKER = 2
const WEIGHT_SUBMARINER_CLUSTER = 3
const WEIGHT_SUBMARINER_NODE = 4
const WEIGHT_SUBMARINER_POD = 5
const WEIGHT_FABRIC = 10
const WEIGHT_PHYSICAL = 13
const WEIGHT_BRIDGES = 14
const WEIGHT_PORTS = 15
const WEIGHT_VIRTUAL = 17
const WEIGHT_NAMESPACES = 18
const WEIGHT_VMS = 19
const WEIGHT_K8S_FEDERATION = 100
const WEIGHT_K8S_CLUSTER = 101
const WEIGHT_K8S_NODE = 102
const WEIGHT_K8S_POD = 103
var DefaultConfig = {
subTitle: "",
filters: [
{
id: "default",
label: "Default",
gremlin: ""
},
{
id: "namespaces",
label: "Namespaces",
gremlin: "G.V().Has('Type', 'host').as('host')" +
".out().Has('Type', 'netns').descendants().as('netns')" +
".select('host', 'netns').SubGraph()"
}
],
defaultFilter: 'default',
_newAttrs: function (node: Node): NodeAttrs {
var name = node.data.Name
if (name.length > 24) {
name = node.data.Name.substring(0, 24) + "."
}
var attrs = {
classes: [node.data.Type],
name: name,
icon: "\uf192",
href: '',
iconClass: '',
weight: 0,
badges: []
}
return attrs
},
_nodeAttrsK8s: function (node: Node): NodeAttrs {
var attrs = this._newAttrs(node)
switch (node.data.Type) {
case "cluster":
attrs.href = "assets/icons/cluster.png"
attrs.weight = WEIGHT_K8S_CLUSTER
break
case "configmap":
attrs.href = "assets/icons/configmap.png"
attrs.weight = WEIGHT_K8S_POD
break
case "container":
attrs.href = "assets/icons/container.png"
attrs.weight = WEIGHT_K8S_POD
break
case "cronjob":
attrs.href = "assets/icons/cronjob.png"
attrs.weight = WEIGHT_K8S_POD
break
case "daemonset":
attrs.href = "assets/icons/daemonset.png"
attrs.weight = WEIGHT_K8S_POD
break
case "deployment":
attrs.href = "assets/icons/deployment.png"
attrs.weight = WEIGHT_K8S_POD
break
case "endpoints":
attrs.href = "assets/icons/endpoints.png"
attrs.weight = WEIGHT_K8S_POD
break
case "ingress":
attrs.href = "assets/icons/ingress.png"
attrs.weight = WEIGHT_K8S_POD
break
case "job":
attrs.href = "assets/icons/job.png"
attrs.weight = WEIGHT_K8S_POD
break
case "node":
attrs.icon = "\uf109"
attrs.weight = WEIGHT_K8S_NODE
break
case "persistentvolume":
attrs.href = "assets/icons/persistentvolume.png"
attrs.weight = WEIGHT_K8S_POD
break
case "persistentvolumeclaim":
attrs.href = "assets/icons/persistentvolumeclaim.png"
attrs.weight = WEIGHT_K8S_POD
break
case "pod":
attrs.href = "assets/icons/pod.png"
attrs.weight = WEIGHT_K8S_POD
break
case "networkpolicy":
attrs.href = "assets/icons/networkpolicy.png"
attrs.weight = WEIGHT_K8S_POD
break
case "namespace":
attrs.icon = "\uf24d"
attrs.weight = WEIGHT_K8S_NODE
break
case "replicaset":
attrs.href = "assets/icons/replicaset.png"
attrs.weight = WEIGHT_K8S_POD
break
case "replicationcontroller":
attrs.href = "assets/icons/replicationcontroller.png"
attrs.weight = WEIGHT_K8S_POD
break
case "secret":
attrs.href = "assets/icons/secret.png"
attrs.weight = WEIGHT_K8S_POD
break
case "service":
attrs.href = "assets/icons/service.png"
attrs.weight = WEIGHT_K8S_POD
break
case "statefulset":
attrs.href = "assets/icons/statefulset.png"
attrs.weight = WEIGHT_K8S_POD
break
case "storageclass":
attrs.href = "assets/icons/storageclass.png"
attrs.weight = WEIGHT_K8S_NODE
break
default:
attrs.href = "assets/icons/k8s.png"
attrs.weight = WEIGHT_K8S_POD
}
return attrs
},
_nodeAttrsInfra: function (node: Node): NodeAttrs {
var attrs = this._newAttrs(node)
if (node.data.OfPort) {
attrs.weight = WEIGHT_PORTS
}
switch (node.data.Type) {
case "Broker":
attrs.icon = "\uf109"
attrs.weight = WEIGHT_SUBMARINER_BROKER
attrs.badges = ["\uf084"]
break
case "Cluster":
attrs.icon = "\uf6ff"
attrs.weight = WEIGHT_SUBMARINER_CLUSTER
break
case "Node":
attrs.icon = "\uf109"
attrs.weight = WEIGHT_SUBMARINER_NODE
break
case "Pod":
attrs.icon = "\uf24d"
attrs.weight = WEIGHT_SUBMARINER_POD
switch (node.data.Rule) {
case "routeagent":
attrs.badges = ["\uf084"]
break
case "gateway":
attrs.badges = ["\uf1e6","\uf084"]
break
}
break
case "host":
attrs.icon = "\uf109"
attrs.weight = WEIGHT_PHYSICAL
break
case "switch":
attrs.icon = "\uf6ff"
break
case "bridge":
case "ovsbridge":
attrs.icon = "\uf6ff"
attrs.weight = WEIGHT_BRIDGES
break
case "erspan":
attrs.icon = "\uf1e0"
break
case "geneve":
case "vxlan":
case "gre":
case "gretap":
attrs.icon = "\uf55b"
break
case "device":
case "internal":
case "interface":
case "tun":
case "tap":
attrs.icon = "\uf796"
attrs.weight = WEIGHT_VIRTUAL
break
case "veth":
attrs.icon = "\uf4d7"
attrs.weight = WEIGHT_VIRTUAL
break
case "switchport":
attrs.icon = "\uf0e8"
break
case "patch":
case "port":
case "ovsport":
attrs.icon = "\uf0e8"
attrs.weight = WEIGHT_PORTS
break
case "netns":
attrs.icon = "\uf24d"
attrs.weight = WEIGHT_NAMESPACES
break
case "libvirt":
attrs.icon = "\uf109"
attrs.weight = WEIGHT_VMS
break
}
if (node.data.Manager === "docker") {
attrs.icon = "\uf395"
attrs.iconClass = "font-brands"
}
if (node.data.IPV4 && node.data.IPV4.length) {
attrs.weight = WEIGHT_PHYSICAL
}
var virt = ["tap", "veth", "tun", "openvswitch"]
if (node.data.Driver && virt.indexOf(node.data.Driver) < 0) {
attrs.weight = WEIGHT_PHYSICAL
}
if (node.data.Probe === "fabric") {
attrs.weight = WEIGHT_FABRIC
}
if (node.data.Captures) {
attrs.badges = ["\uf03d"]
}
return attrs
},
nodeAttrs: function (node: Node): NodeAttrs {
switch (node.data.Manager) {
case "k8s":
return this._nodeAttrsK8s(node)
default:
return this._nodeAttrsInfra(node)
}
},
nodeSortFnc: function (a: Node, b: Node) {
return a.data.Name.localeCompare(b.data.Name)
},
nodeClicked: function (node: Node) {
window.App.tc.selectNode(node.id)
},
nodeDblClicked: function (node: Node) {
window.App.tc.expand(node)
},
nodeMenu: function (node: Node) {
return [
{
class: "", text: "Capture", disabled: false, callback: () => {
var api = new window.API.CapturesApi(window.App.apiConf)
api.createCapture({ GremlinQuery: `G.V('${node.id}')` }).then(result => {
console.log(result)
})
}
},
{ class: "", text: "Capture all", disabled: true, callback: () => { console.log("Capture all") } },
{ class: "", text: "Injection", disabled: false, callback: () => { console.log("Injection") } },
{ class: "", text: "Flows", disabled: false, callback: () => { console.log("Flows") } },
{ class: "", text: "Filter NS(demo)", disabled: false, callback: () => { window.App.loadExtraConfig("/assets/nsconfig.js") } }
]
},
nodeTags: function (data) {
if (data.Manager && data.Manager === "k8s") {
return ["kubernetes"]
} else {
return ["infrastructure"]
}
},
defaultNodeTag: "infrastructure",
nodeTabTitle: function (node: Node): string {
return node.data.Name.substring(0, 8)
},
groupSize: 3,
groupType: function (node: Node): string | undefined {
var nodeType = node.data.Type
if (!nodeType) {
return
}
switch (nodeType) {
case "configmap":
case "cronjob":
case "daemonset":
case "deployment":
case "endpoints":
case "ingress":
case "job":
case "persistentvolume":
case "persistentvolumeclaim":
case "pod":
case "networkpolicy":
case "replicaset":
case "replicationcontroller":
case "secret":
case "service":
case "statefulset":
return "app"
default:
return nodeType
}
},
groupName: function (node: Node): string | undefined {
if (node.data.K8s) {
var labels = node.data.K8s.Labels
if (!labels) {
return name
}
var app = labels["k8s-app"] || labels["app"]
if (!app) {
return "default"
}
return app
}
var nodeType = this.groupType(node)
if (!nodeType) {
return
}
return nodeType + "(s)"
},
weightTitles: function () {
return {
[WEIGHT_NONE]: "Not classified",
[WEIGHT_SUBMARINER_BROKER]: "Broker",
[WEIGHT_SUBMARINER_CLUSTER]: "Cluster",
[WEIGHT_SUBMARINER_NODE]: "Node",
[WEIGHT_SUBMARINER_POD]: "Pod",
[WEIGHT_FABRIC]: "Fabric",
[WEIGHT_PHYSICAL]: "Physical",
[WEIGHT_BRIDGES]: "Bridges",
[WEIGHT_PORTS]: "Ports",
[WEIGHT_VIRTUAL]: "Virtual",
[WEIGHT_NAMESPACES]: "Namespaces",
[WEIGHT_VMS]: "VMs",
[WEIGHT_K8S_FEDERATION]: "Federations",
[WEIGHT_K8S_CLUSTER]: "Clusters",
[WEIGHT_K8S_NODE]: "Nodes",
[WEIGHT_K8S_POD]: "Pods"
}
},
suggestions: [
"data.IPV4",
"data.MAC",
"data.Name",
"data.Rule",
"data.Node",
"data.vxlan"
],
nodeDataFields: [
{
field: "",
title: "General",
expanded: true,
icon: "\uf05a",
sortKeys: function (data) {
return ['Name', 'Type', 'MAC', 'Driver', 'State']
},
filterKeys: function (data) {
switch (data.Type) {
case "host":
return ['Name']
default:
return ['Name', 'Type', 'MAC', 'Driver', 'State']
}
}
},
{
field: "Sockets",
expanded: false,
icon: "\uf1e6"
},
{
field: "Captures",
expanded: false,
icon: "\uf51f",
normalizer: function (data) {
for (let capture of data) {
capture.ID = capture.ID.split('-')[0]
}
return data
}
},
{
field: "Injections",
expanded: false,
icon: "\uf48e"
},
{
field: "Docker",
expanded: false,
icon: "\uf395",
iconClass: "font-brands"
},
{
field: "IPV4",
expanded: true,
icon: "\uf1fa"
},
{
field: "IPV6",
expanded: true,
icon: "\uf1fa"
},
{
field: "LastUpdateMetric",
title: "Last metrics",
expanded: false,
icon: "\uf201",
normalizer: function (data) {
return {
RxPackets: data.RxPackets ? data.RxPackets.toLocaleString() : 0,
RxBytes: data.RxBytes ? Tools.prettyBytes(data.RxBytes) : 0,
TxPackets: data.TxPackets ? data.TxPackets.toLocaleString() : 0,
TxBytes: data.TxPackets ? Tools.prettyBytes(data.TxBytes) : 0,
Start: data.Start ? new Date(data.Start).toLocaleString() : 0,
Last: data.Last ? new Date(data.Last).toLocaleString() : 0
}
},
graph: function (data) {
return {
type: "LineChart",
data: [
[
{ type: "datetime", label: "time" },
"RxBytes",
"TxBytes"
],
[new Date(data.Last || 0), data.RxBytes || 0, data.TxBytes || 0]
]
}
}
},
{
field: "Metric",
title: "Total metrics",
expanded: false,
icon: "\uf201",
normalizer: function (data) {
return {
RxPackets: data.RxPackets ? data.RxPackets.toLocaleString() : 0,
RxBytes: data.RxBytes ? Tools.prettyBytes(data.RxBytes) : 0,
TxPackets: data.TxPackets ? data.TxPackets.toLocaleString() : 0,
TxBytes: data.TxPackets ? Tools.prettyBytes(data.TxBytes) : 0,
Last: data.Last ? new Date(data.Last).toLocaleString() : 0
}
}
},
{
field: "Features",
expanded: false,
icon: "\uf022"
},
{
field: "FDB",
expanded: false,
icon: "\uf0ce"
},
{
field: "Neighbors",
expanded: false,
icon: "\uf0ce"
},
{
field: "RoutingTables",
title: "Routing tables",
expanded: false,
icon: "\uf0ce",
normalizer: function (data) {
var rows = new Array<any>()
for (let table of data) {
if (!table.Routes) {
continue
}
for (let route of table.Routes) {
if (!route.NextHops) {
continue
}
for (let nh of route.NextHops) {
rows.push({
ID: table.ID,
Src: table.Src,
Protocol: route["Protocol"],
Prefix: route["Prefix"],
Priority: nh["Priority"],
IP: nh["IP"],
IfIndex: nh["IfIndex"]
})
}
}
}
return rows
}
}
],
linkAttrs: function (link: Link) {
var metric = link.source.data.LastUpdateMetric
var bandwidth = 0
if (metric) {
bandwidth = (metric.RxBytes + metric.TxBytes) * 8
bandwidth /= (metric.Last - metric.Start) / 1000
}
var attrs = {
classes: [link.data.RelationType],
icon: "\uf362",
directed: false,
href: '',
iconClass: '',
label: bandwidth ? Tools.prettyBandwidth(bandwidth) : ""
}
if (bandwidth > 0) {
attrs.classes.push('traffic')
}
if (link.data.RelationType === "layer2") {
attrs.classes.push("traffic")
}
if (link.data.Directed) {
attrs.directed = true
}
switch (link.data.RelationType) {
case "ipsec":
attrs.label = link.data.RelationType
attrs.classes.push("traffic")
attrs.classes.push('ipsec_traffic')
break
case "vxlan":
attrs.label = link.data.RelationType
attrs.classes.push("traffic")
attrs.classes.push('vxlan_traffic')
break
}
return attrs
},
linkTabTitle: function (link: Link) {
var src = link.source.data.Name
var dst = link.target.data.Name
if (src && dst) {
return src.substring(0, 8) + " / " + dst.substring(0, 8)
}
return link.id.split("-")[0]
},
linkDataFields: [
{
field: "",
title: "General",
expanded: true,
icon: "\uf05a",
},
{
field: "NSM",
title: "Network Service Mesh",
expanded: true,
icon: "\uf542",
},
{
field: "NSM.Source",
title: "Source",
expanded: false,
icon: "\uf018",
},
{
field: "NSM.Via",
title: "Via",
expanded: false,
icon: "\uf018",
},
{
field: "NSM.Destination",
title: "Destination",
expanded: false,
icon: "\uf018",
}
],
defaultLinkTagMode: function (tag: string): number {
return 2
}
}
export default DefaultConfig