Skip to content

Commit

Permalink
ui: allow to add external JS/CSS
Browse files Browse the repository at this point in the history
Refactor the graph-layout in order
to allow to add/modify icons via
nodeImgMap javascript map.

Closes: skydive-project#553
  • Loading branch information
safchain committed Nov 10, 2017
1 parent 4118b23 commit abc26c3
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 43 deletions.
5 changes: 5 additions & 0 deletions etc/skydive.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,8 @@ flow:
update: 60
# Protocol to use to send flows to the analyzer: websocket or udp
# protocol: udp

ui:
# Specify the extra assets folder. Javascript and CSS files present in this
# folder will be added in the WebUI.
# extra_assets: /usr/share/skydive/assets
88 changes: 77 additions & 11 deletions http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"crypto/tls"
"errors"
"fmt"
"html/template"
"io/ioutil"
"mime"
"net"
"net/http"
Expand Down Expand Up @@ -61,8 +63,17 @@ const (
TCP ConnectionType = 1 + iota
// TLS secure connection
TLS

// ExtraAssetPrefix is used for extra assets
ExtraAssetPrefix = "/extra-statics"
)

type ExtraAsset struct {
Filename string
Ext string
Content []byte
}

type Server struct {
http.Server
Host string
Expand All @@ -75,6 +86,7 @@ type Server struct {
listener net.Listener
CnxType ConnectionType
wg sync.WaitGroup
extraAssets map[string]ExtraAsset
}

func copyRequestVars(old, new *http.Request) {
Expand Down Expand Up @@ -153,17 +165,24 @@ func (s *Server) Stop() {
s.wg.Wait()
}

func serveStatics(w http.ResponseWriter, r *http.Request) {
func (s *Server) serveStatics(w http.ResponseWriter, r *http.Request) {
upath := r.URL.Path
if strings.HasPrefix(upath, "/") {
upath = strings.TrimPrefix(upath, "/")
}

content, err := statics.Asset(upath)
if err != nil {
logging.GetLogger().Errorf("Unable to find the asset: %s", upath)
w.WriteHeader(http.StatusNotFound)
return
var content []byte
var err error

if asset, ok := s.extraAssets[upath]; ok {
content = asset.Content
} else {
content, err = statics.Asset(upath)
if err != nil {
logging.GetLogger().Errorf("Unable to find the asset: %s", upath)
w.WriteHeader(http.StatusNotFound)
return
}
}

ext := filepath.Ext(upath)
Expand All @@ -182,10 +201,24 @@ func (s *Server) serveIndex(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
return
}

data := struct {
ExtraAssets map[string]ExtraAsset
}{
ExtraAssets: s.extraAssets,
}

w.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.WriteHeader(http.StatusOK)

setTLSHeader(w, r)
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.WriteHeader(http.StatusOK)
w.Write(html)

tmpl := template.Must(template.New("index").Delims("<<", ">>").Parse(string(html)))
if err := tmpl.Execute(w, data); err != nil {
logging.GetLogger().Criticalf("Unable to execute index template: %s", err)
}
}

func (s *Server) serveLogin(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -224,21 +257,53 @@ func (s *Server) HandleFunc(path string, f auth.AuthenticatedHandlerFunc) {
s.Router.HandleFunc(path, s.Auth.Wrap(f))
}

func NewServer(host string, serviceType common.ServiceType, addr string, port int, auth AuthenticationBackend) *Server {
func (s *Server) loadExtraAssets(folder string) {
files, err := ioutil.ReadDir(folder)
if err != nil {
logging.GetLogger().Errorf("Unable to load extra assets from %s: %s", folder, err)
return
}

for _, file := range files {
path := filepath.Join(folder, file.Name())

data, err := ioutil.ReadFile(path)
if err != nil {
logging.GetLogger().Errorf("Unable to load extra asset %s: %s", path, err)
return
}

ext := filepath.Ext(path)

key := strings.TrimPrefix(filepath.Join(ExtraAssetPrefix, file.Name()), "/")
s.extraAssets[key] = ExtraAsset{
Filename: filepath.Join(ExtraAssetPrefix, file.Name()),
Ext: ext,
Content: data,
}
}
}

func NewServer(host string, serviceType common.ServiceType, addr string, port int, auth AuthenticationBackend, assetsFolder string) *Server {
router := mux.NewRouter().StrictSlash(true)
router.Headers("X-Host-ID", host, "X-Service-Type", serviceType.String())

router.PathPrefix("/statics").HandlerFunc(serveStatics)

server := &Server{
Host: host,
ServiceType: serviceType,
Router: router,
Addr: addr,
Port: port,
Auth: auth,
extraAssets: make(map[string]ExtraAsset),
}

if assetsFolder != "" {
server.loadExtraAssets(assetsFolder)
}

router.PathPrefix("/statics").HandlerFunc(server.serveStatics)
router.PathPrefix(ExtraAssetPrefix).HandlerFunc(server.serveStatics)
router.HandleFunc("/login", server.serveLogin)
router.HandleFunc("/", server.serveIndex)

Expand All @@ -257,6 +322,7 @@ func NewServerFromConfig(serviceType common.ServiceType) (*Server, error) {
}

host := config.GetConfig().GetString("host_id")
assets := config.GetConfig().GetString("ui.extra_assets")

return NewServer(host, serviceType, sa.Addr, sa.Port, auth), nil
return NewServer(host, serviceType, sa.Addr, sa.Port, auth, assets), nil
}
13 changes: 12 additions & 1 deletion statics/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,23 @@

<!-- views -->
<script src="/statics/js/components/login.js"></script>
<script src="/statics/js/components/layout.js"></script>
<script src="/statics/js/components/graph-layout.js"></script>
<script src="/statics/js/components/topology.js"></script>
<script src="/statics/js/components/conversation.js"></script>
<script src="/statics/js/components/discovery.js"></script>

<script src="/statics/js/app.js"></script>

<!-- extra assets -->
<< range $i, $asset := .ExtraAssets >>
<< if eq $asset.Ext ".css" >>
<link rel="stylesheet" href="<< $asset.Filename >>" />
<< end >>
<< if eq $asset.Ext ".js" >>
<script src="<< $asset.Filename >>"></script>
<< end >>
<< end >>
</head>

<body>
Expand All @@ -81,7 +92,7 @@
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Skydive
<span class="mode">{{service}} {{version}}</span>
<span class="mode">{{service}} {{version}}</span>
</a>
</div>
<div class="collapse navbar-collapse" v-if="logged">
Expand Down
36 changes: 5 additions & 31 deletions statics/js/components/graph-layout.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
var hostImg = 'statics/img/host.png';
var switchImg = 'statics/img/switch.png';
var portImg = 'statics/img/port.png';
var intfImg = 'statics/img/intf.png';
var vethImg = 'statics/img/veth.png';
var nsImg = 'statics/img/ns.png';
var bridgeImg = 'statics/img/bridge.png';
var dockerImg = 'statics/img/docker.png';
var neutronImg = 'statics/img/openstack.png';

var minusImg = 'statics/img/minus-outline-16.png';
var plusImg = 'statics/img/plus-16.png';
var captureIndicatorImg = 'statics/img/media-record.png';
var pinIndicatorImg = 'statics/img/pin.png';

var TopologyGraphLayout = function(vm, selector) {
var self = this;

Expand Down Expand Up @@ -1313,26 +1298,15 @@ TopologyGraphLayout.prototype = {
},

nodeImg: function(d) {
switch(d.metadata.Type) {
case "host": return hostImg;
case "port":
case "ovsport": return portImg;
case "bridge": return bridgeImg;
case "switch":
case "ovsbridge": return switchImg;
case "netns": return nsImg;
case "veth": return vethImg;
case "bond": return portImg;
case "container": return dockerImg;
default: return intfImg;
if (nodeImgMap[d.metadata.Type]) {
return nodeImgMap[d.metadata.Type];
}

return intfImg;
},

managerImg: function(d) {
switch(d.metadata.Manager) {
case "docker": return dockerImg;
case "neutron": return neutronImg;
}
return managerImgMap[d.metadata.Type];
},

collapseImg: function(d) {
Expand Down
32 changes: 32 additions & 0 deletions statics/js/components/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
var hostImg = 'statics/img/host.png';
var switchImg = 'statics/img/switch.png';
var portImg = 'statics/img/port.png';
var intfImg = 'statics/img/intf.png';
var vethImg = 'statics/img/veth.png';
var nsImg = 'statics/img/ns.png';
var bridgeImg = 'statics/img/bridge.png';
var dockerImg = 'statics/img/docker.png';
var neutronImg = 'statics/img/openstack.png';

var minusImg = 'statics/img/minus-outline-16.png';
var plusImg = 'statics/img/plus-16.png';
var captureIndicatorImg = 'statics/img/media-record.png';
var pinIndicatorImg = 'statics/img/pin.png';

var nodeImgMap = {
"host": hostImg,
"port": portImg,
"ovsport": portImg,
"bridge": bridgeImg,
"switch": switchImg,
"ovsbridge": switchImg,
"netns": nsImg,
"veth": vethImg,
"bond": portImg,
"container": dockerImg
};

var managerImgMap = {
"docker": dockerImg,
"neutron": neutronImg
};

0 comments on commit abc26c3

Please sign in to comment.