Improve visuals and code structure
This commit is contained in:
parent
25adf0f374
commit
a5084c57f0
8 changed files with 485 additions and 184 deletions
115
internal/cache/datacache.go
vendored
Normal file
115
internal/cache/datacache.go
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"git.faercol.me/faercol/topology-map/internal/models"
|
||||
)
|
||||
|
||||
func NewDataCache() *DataCache {
|
||||
return &DataCache{
|
||||
lock: sync.Mutex{},
|
||||
devices: make(map[int]*models.Device),
|
||||
interfaces: make(map[int]*models.Interface),
|
||||
cables: make(map[int]*models.Cable),
|
||||
vms: make(map[int]*models.VM),
|
||||
}
|
||||
}
|
||||
|
||||
type DataCache struct {
|
||||
lock sync.Mutex
|
||||
devices map[int]*models.Device
|
||||
interfaces map[int]*models.Interface
|
||||
cables map[int]*models.Cable
|
||||
vms map[int]*models.VM
|
||||
}
|
||||
|
||||
func (d *DataCache) AddDevice(device models.Device) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
d.devices[device.ID] = &device
|
||||
}
|
||||
|
||||
func (d *DataCache) GetDevices() []*models.Device {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
var res []*models.Device
|
||||
for _, dev := range d.devices {
|
||||
res = append(res, dev)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DataCache) AddInterface(iface models.Interface) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
d.interfaces[iface.ID] = &iface
|
||||
}
|
||||
|
||||
func (d *DataCache) GetInterfaces() []*models.Interface {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
var res []*models.Interface
|
||||
for _, iface := range d.interfaces {
|
||||
res = append(res, iface)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DataCache) AddCable(cable models.Cable) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
d.cables[cable.ID] = &cable
|
||||
}
|
||||
|
||||
func (d *DataCache) GetCables() []*models.Cable {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
var res []*models.Cable
|
||||
for _, cable := range d.cables {
|
||||
res = append(res, cable)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DataCache) AddVM(vm models.VM) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
d.vms[vm.ID] = &vm
|
||||
}
|
||||
|
||||
func (d *DataCache) GetVMs() []*models.VM {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
var res []*models.VM
|
||||
for _, vm := range d.vms {
|
||||
res = append(res, vm)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *DataCache) ReconcileData() {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
for id, cable := range d.cables {
|
||||
ifaceA, ok := d.interfaces[cable.ATerminations[0].ObjectID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
cable.ATerminations[0].Object.Interface = *ifaceA
|
||||
|
||||
ifaceB, ok := d.interfaces[cable.BTerminations[0].ObjectID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
cable.BTerminations[0].Object.Interface = *ifaceB
|
||||
|
||||
d.cables[id] = cable
|
||||
|
||||
}
|
||||
}
|
14
internal/models/cytoscape.go
Normal file
14
internal/models/cytoscape.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package models
|
||||
|
||||
type ElementData struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Parent string `json:"parent,omitempty"`
|
||||
}
|
||||
|
||||
type Element struct {
|
||||
Data ElementData `json:"data"`
|
||||
Classes []string `json:"classes,omitempty"`
|
||||
}
|
145
internal/models/netbox.go
Normal file
145
internal/models/netbox.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package models
|
||||
|
||||
import "strconv"
|
||||
|
||||
const (
|
||||
Iface2dot5G = "2.5gbase-t"
|
||||
Iface1G = "1000base-t"
|
||||
Iface100M = "100base-tx"
|
||||
)
|
||||
|
||||
var speedAssociation map[string]int = map[string]int{
|
||||
"2.5gbase-t": 2500,
|
||||
"1000base-t": 1000,
|
||||
"100base-tx": 100,
|
||||
}
|
||||
|
||||
type DeviceRole struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Role DeviceRole `json:"role"`
|
||||
}
|
||||
|
||||
func (d Device) UniqueID() string {
|
||||
return "device-" + strconv.FormatInt(int64(d.ID), 10)
|
||||
}
|
||||
|
||||
func (d Device) Element() Element {
|
||||
return Element{
|
||||
Data: ElementData{
|
||||
ID: d.UniqueID(),
|
||||
Label: d.Name,
|
||||
},
|
||||
Classes: nil,
|
||||
}
|
||||
}
|
||||
|
||||
type InterfaceType struct {
|
||||
Value string `json:"value"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
type Interface struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Device Device `json:"device"`
|
||||
MacAddress string `json:"mac_address"`
|
||||
Type InterfaceType `json:"type"`
|
||||
}
|
||||
|
||||
func (i Interface) MaxSpeed() int {
|
||||
speed, ok := speedAssociation[i.Type.Value]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return speed
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
Device Device `json:"device"`
|
||||
Interface Interface
|
||||
}
|
||||
|
||||
type CableTermination struct {
|
||||
ObjectID int `json:"object_id"`
|
||||
Object Object `json:"object"`
|
||||
}
|
||||
|
||||
type Cable struct {
|
||||
ID int `json:"id"`
|
||||
ATerminations []CableTermination `json:"a_terminations"`
|
||||
BTerminations []CableTermination `json:"b_terminations"`
|
||||
}
|
||||
|
||||
func (c Cable) UniqueID() string {
|
||||
return "link-" + strconv.FormatInt(int64(c.ID), 10)
|
||||
}
|
||||
|
||||
func (c Cable) MaxSpeed() int {
|
||||
speedA := c.ATerminations[0].Object.Interface.MaxSpeed()
|
||||
speedB := c.BTerminations[0].Object.Interface.MaxSpeed()
|
||||
|
||||
if speedA > speedB {
|
||||
return speedB
|
||||
}
|
||||
return speedA
|
||||
}
|
||||
|
||||
func (c Cable) Element() Element {
|
||||
|
||||
var linkClass string
|
||||
switch c.MaxSpeed() {
|
||||
case 2500:
|
||||
linkClass = "link-fast"
|
||||
case 1000:
|
||||
linkClass = "link-normal"
|
||||
case 100:
|
||||
linkClass = "link-slow"
|
||||
default:
|
||||
linkClass = "link-speed-unknown"
|
||||
}
|
||||
|
||||
return Element{
|
||||
Data: ElementData{
|
||||
ID: c.UniqueID(),
|
||||
Source: c.ATerminations[0].Object.Device.UniqueID(),
|
||||
Target: c.BTerminations[0].Object.Device.UniqueID(),
|
||||
},
|
||||
Classes: []string{"link", linkClass},
|
||||
}
|
||||
}
|
||||
|
||||
type VM struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Device Device `json:"device"`
|
||||
}
|
||||
|
||||
func (v VM) UniqueID() string {
|
||||
return "vm-" + strconv.FormatInt(int64(v.ID), 10)
|
||||
}
|
||||
|
||||
func (v VM) Elements() []Element {
|
||||
return []Element{
|
||||
{
|
||||
Data: ElementData{
|
||||
ID: v.UniqueID(),
|
||||
Label: v.Name,
|
||||
},
|
||||
},
|
||||
{
|
||||
Data: ElementData{
|
||||
ID: "vm-cable" + strconv.FormatInt(int64(v.ID), 10),
|
||||
Source: v.UniqueID(),
|
||||
Target: v.Device.UniqueID(),
|
||||
},
|
||||
Classes: []string{"link", "link-virtual"},
|
||||
},
|
||||
}
|
||||
}
|
124
internal/netbox/netbox.go
Normal file
124
internal/netbox/netbox.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package netbox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"git.faercol.me/faercol/topology-map/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
devicesRoute = "dcim/devices"
|
||||
cablesRoute = "dcim/cables"
|
||||
vmsRoute = "virtualization/virtual-machines"
|
||||
interfaceRoute = "dcim/interfaces"
|
||||
)
|
||||
|
||||
type vmsResponse struct {
|
||||
Results []models.VM `json:"results"`
|
||||
}
|
||||
|
||||
type deviceResponse struct {
|
||||
Results []models.Device `json:"results"`
|
||||
}
|
||||
|
||||
type cableResponse struct {
|
||||
Results []models.Cable `json:"results"`
|
||||
}
|
||||
|
||||
type interfaceResponse struct {
|
||||
Results []models.Interface `json:"results"`
|
||||
}
|
||||
|
||||
type NetboxClient struct {
|
||||
httpClt *http.Client
|
||||
netboxBaseURL string
|
||||
netboxToken string
|
||||
}
|
||||
|
||||
func (c *NetboxClient) queryAPI(route string) ([]byte, error) {
|
||||
query, err := http.NewRequest("GET", c.netboxBaseURL+route, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query.Header.Set("Authorization", "Token "+c.netboxToken)
|
||||
|
||||
resp, err := c.httpClt.Do(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return respBody, nil
|
||||
}
|
||||
|
||||
func (c *NetboxClient) GetDevices() ([]models.Device, error) {
|
||||
var res deviceResponse
|
||||
|
||||
respBody, err := c.queryAPI(devicesRoute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(respBody, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Results, nil
|
||||
}
|
||||
|
||||
func (c *NetboxClient) GetInterfaces() ([]models.Interface, error) {
|
||||
var res interfaceResponse
|
||||
|
||||
respBody, err := c.queryAPI(interfaceRoute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(respBody, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.Results, nil
|
||||
}
|
||||
|
||||
func (c *NetboxClient) GetCables() ([]models.Cable, error) {
|
||||
var res cableResponse
|
||||
|
||||
respBody, err := c.queryAPI(cablesRoute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(respBody, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Results, nil
|
||||
}
|
||||
|
||||
func (c *NetboxClient) GetVMs() ([]models.VM, error) {
|
||||
var res vmsResponse
|
||||
|
||||
respBody, err := c.queryAPI(vmsRoute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(respBody, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Results, nil
|
||||
}
|
||||
|
||||
func NewClient(baseURL, token string) *NetboxClient {
|
||||
return &NetboxClient{
|
||||
httpClt: http.DefaultClient,
|
||||
netboxBaseURL: baseURL,
|
||||
netboxToken: token,
|
||||
}
|
||||
}
|
215
main.go
215
main.go
|
@ -4,142 +4,62 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.faercol.me/faercol/topology-map/internal/cache"
|
||||
"git.faercol.me/faercol/topology-map/internal/models"
|
||||
"git.faercol.me/faercol/topology-map/internal/netbox"
|
||||
)
|
||||
|
||||
const apiKey = "4e75b8927940adc29e2e1eac042bf92bcddd57fe"
|
||||
const netboxBaseURL = "https://netbox.internal.faercol.me/api/"
|
||||
|
||||
type Device struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
Device Device `json:"device"`
|
||||
}
|
||||
|
||||
type CableTermination struct {
|
||||
Object Object `json:"object"`
|
||||
}
|
||||
|
||||
type Cable struct {
|
||||
ID int `json:"id"`
|
||||
ATerminations []CableTermination `json:"a_terminations"`
|
||||
BTerminations []CableTermination `json:"b_terminations"`
|
||||
}
|
||||
|
||||
type VM struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Device Device `json:"device"`
|
||||
}
|
||||
|
||||
type vmsResponse struct {
|
||||
Results []VM `json:"results"`
|
||||
}
|
||||
|
||||
type deviceResponse struct {
|
||||
Results []Device `json:"results"`
|
||||
}
|
||||
|
||||
type cableResponse struct {
|
||||
Results []Cable `json:"results"`
|
||||
}
|
||||
|
||||
type ElementData struct {
|
||||
ID string `json:"id"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Parent string `json:"parent,omitempty"`
|
||||
}
|
||||
|
||||
type Element struct {
|
||||
Data ElementData `json:"data"`
|
||||
Classes []string `json:"classes"`
|
||||
}
|
||||
|
||||
func GetDevices() ([]Device, error) {
|
||||
query, err := http.NewRequest("GET", netboxBaseURL+"dcim/devices", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query.Header.Set("Authorization", "Token "+apiKey)
|
||||
|
||||
resp, err := http.DefaultClient.Do(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res deviceResponse
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(respBody, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Results, nil
|
||||
}
|
||||
|
||||
func GetCables() ([]Cable, error) {
|
||||
query, err := http.NewRequest("GET", netboxBaseURL+"dcim/cables", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query.Header.Set("Authorization", "Token "+apiKey)
|
||||
|
||||
resp, err := http.DefaultClient.Do(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res cableResponse
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(respBody, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Results, nil
|
||||
}
|
||||
|
||||
func GetVMs() ([]VM, error) {
|
||||
query, err := http.NewRequest("GET", netboxBaseURL+"virtualization/virtual-machines", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query.Header.Set("Authorization", "Token "+apiKey)
|
||||
|
||||
resp, err := http.DefaultClient.Do(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res vmsResponse
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(respBody, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Results, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
fmt.Println("Initializing data")
|
||||
dataCache := cache.NewDataCache()
|
||||
netboxClt := netbox.NewClient(netboxBaseURL, apiKey)
|
||||
|
||||
fmt.Println("Getting devices")
|
||||
devices, err := netboxClt.GetDevices()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, d := range devices {
|
||||
dataCache.AddDevice(d)
|
||||
}
|
||||
|
||||
fmt.Println("Getting VMs")
|
||||
vms, err := netboxClt.GetVMs()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range vms {
|
||||
dataCache.AddVM(v)
|
||||
}
|
||||
|
||||
fmt.Println("Getting cables")
|
||||
cables, err := netboxClt.GetCables()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, c := range cables {
|
||||
dataCache.AddCable(c)
|
||||
}
|
||||
|
||||
fmt.Println("Getting interfaces")
|
||||
interfaces, err := netboxClt.GetInterfaces()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, i := range interfaces {
|
||||
dataCache.AddInterface(i)
|
||||
}
|
||||
|
||||
fmt.Println("Reconciling data")
|
||||
dataCache.ReconcileData()
|
||||
fmt.Println("Done")
|
||||
|
||||
srv := http.NewServeMux()
|
||||
|
||||
srv.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -149,36 +69,16 @@ func main() {
|
|||
})
|
||||
srv.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("Serving API route")
|
||||
devices, err := GetDevices()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to get devices: %s\n", err)
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
cables, err := GetCables()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to get cables: %s\n", err)
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
resp := []models.Element{}
|
||||
for _, d := range dataCache.GetDevices() {
|
||||
resp = append(resp, d.Element())
|
||||
}
|
||||
|
||||
vms, err := GetVMs()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to get VMs: %s\n", err)
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
for _, c := range dataCache.GetCables() {
|
||||
resp = append(resp, c.Element())
|
||||
}
|
||||
|
||||
resp := []Element{}
|
||||
for _, d := range devices {
|
||||
resp = append(resp, Element{Data: ElementData{ID: d.Name}})
|
||||
}
|
||||
for _, c := range cables {
|
||||
resp = append(resp, Element{Data: ElementData{ID: "link-" + strconv.FormatInt(int64(c.ID), 10), Source: c.ATerminations[0].Object.Device.Name, Target: c.BTerminations[0].Object.Device.Name}})
|
||||
}
|
||||
for _, v := range vms {
|
||||
resp = append(resp, Element{Data: ElementData{ID: v.Name}}, Element{Data: ElementData{ID: "vm-" + strconv.FormatInt(int64(v.ID), 10), Source: v.Name, Target: v.Device.Name}, Classes: []string{"edge", "virtual-edge"}})
|
||||
for _, v := range dataCache.GetVMs() {
|
||||
resp = append(resp, v.Elements()...)
|
||||
}
|
||||
|
||||
respBody, err := json.Marshal(resp)
|
||||
|
@ -188,6 +88,7 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Write(respBody)
|
||||
})
|
||||
srv.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -206,7 +107,7 @@ func main() {
|
|||
}
|
||||
})
|
||||
|
||||
if err := http.ListenAndServe("127.0.0.1:5000", srv); err != nil {
|
||||
if err := http.ListenAndServe("0.0.0.0:5000", srv); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,28 +21,17 @@ function setupGraph() {
|
|||
.then(function (elements) {
|
||||
|
||||
var cy = window.cy = cytoscape({
|
||||
|
||||
container: document.getElementById('map-container'), // container to render in
|
||||
|
||||
container: document.getElementById('map-container'),
|
||||
elements: elements,
|
||||
// elements: [ // list of graph elements to start with
|
||||
// { // node a
|
||||
// data: { id: 'a' }
|
||||
// },
|
||||
// { // node b
|
||||
// data: { id: 'b' }
|
||||
// },
|
||||
// { // edge ab
|
||||
// data: { id: 'ab', source: 'a', target: 'b' }
|
||||
// }
|
||||
// ],
|
||||
|
||||
style: [ // the stylesheet for the graph
|
||||
{
|
||||
selector: 'node',
|
||||
style: {
|
||||
'background-color': '#666',
|
||||
'label': 'data(id)'
|
||||
'background-color': '#E1F5FE',
|
||||
'border-color': '#03A9F4',
|
||||
'border-width': 0.5,
|
||||
'label': 'data(label)'
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -50,23 +39,37 @@ function setupGraph() {
|
|||
selector: 'edge',
|
||||
style: {
|
||||
'width': 1,
|
||||
'line-color': '#1E88E5',
|
||||
'line-color': '#42A5F5',
|
||||
'target-arrow-color': '#1E88E5',
|
||||
'target-arrow-shape': 'none',
|
||||
'curve-style': 'bezier'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: ".virtual-edge",
|
||||
selector: ".link-virtual",
|
||||
style: {
|
||||
"line-color": "#BDBDBD",
|
||||
"line-style": "dashed",
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".link-fast",
|
||||
style: {
|
||||
"width": 1.5,
|
||||
"line-color": "#1565C0",
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".link-slow",
|
||||
style: {
|
||||
"line-color": "#FFC107",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
layout: {
|
||||
name: 'cola',
|
||||
name: 'cose',
|
||||
nodeDimensionsIncludeLabels: true,
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#map-container {
|
||||
width: 80%;
|
||||
height: 600px;
|
||||
width: 95%;
|
||||
height: 1200px;
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.edge {
|
||||
color: pink;
|
||||
background-color: #F5F5F5;
|
||||
border-color: #BDBDBD;
|
||||
}
|
BIN
topology-map
BIN
topology-map
Binary file not shown.
Loading…
Reference in a new issue