add json
This commit is contained in:
56
api/api.go
56
api/api.go
@@ -1,10 +1,12 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"nvidiadashboard/pkg/constant"
|
||||||
"nvidiadashboard/pkg/nvidia"
|
"nvidiadashboard/pkg/nvidia"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -37,6 +39,7 @@ type (
|
|||||||
Username string
|
Username string
|
||||||
DriverVersion string
|
DriverVersion string
|
||||||
CUDAVersion string
|
CUDAVersion string
|
||||||
|
Version string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -72,6 +75,7 @@ func New(repo NVIDIARepository) *Server {
|
|||||||
s.mux.Use(middleware.URLFormat)
|
s.mux.Use(middleware.URLFormat)
|
||||||
|
|
||||||
s.mux.Get("/", s.handleRoot)
|
s.mux.Get("/", s.handleRoot)
|
||||||
|
s.mux.Get("/{uuid:GPU-(.*)}/json", s.handleGPUJSON)
|
||||||
s.mux.Get("/{uuid:GPU-(.*)}", s.handleGPU)
|
s.mux.Get("/{uuid:GPU-(.*)}", s.handleGPU)
|
||||||
s.mux.Get("/{path:(.*).html}", func(w http.ResponseWriter, r *http.Request) {
|
s.mux.Get("/{path:(.*).html}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
@@ -95,7 +99,7 @@ func (s *Server) Serve(port uint) error {
|
|||||||
func (s *Server) handleRoot(w http.ResponseWriter, _ *http.Request) {
|
func (s *Server) handleRoot(w http.ResponseWriter, _ *http.Request) {
|
||||||
gpus, err := s.repo.GetGPUs()
|
gpus, err := s.repo.GetGPUs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendInternalServerErrorHTML(w, err)
|
internalServerErrorHTML(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(gpus) > 0 {
|
if len(gpus) > 0 {
|
||||||
@@ -105,11 +109,13 @@ func (s *Server) handleRoot(w http.ResponseWriter, _ *http.Request) {
|
|||||||
}
|
}
|
||||||
wp := WebPack{
|
wp := WebPack{
|
||||||
Username: "anonymous",
|
Username: "anonymous",
|
||||||
|
Version: constant.Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := template.ParseFiles("static/no_gpu.html")
|
t, err := template.ParseFiles("static/no_gpu.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendInternalServerErrorHTML(w, err)
|
internalServerErrorHTML(w, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
err = t.Execute(w, wp)
|
err = t.Execute(w, wp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -122,7 +128,7 @@ func (s *Server) handleGPU(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
gpus, err := s.repo.GetGPUs()
|
gpus, err := s.repo.GetGPUs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendInternalServerErrorHTML(w, err)
|
internalServerErrorHTML(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
i := -1
|
i := -1
|
||||||
@@ -137,7 +143,7 @@ func (s *Server) handleGPU(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
gpu, err := s.repo.GetGPU(i)
|
gpu, err := s.repo.GetGPU(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendInternalServerErrorHTML(w, err)
|
internalServerErrorHTML(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wp := WebPack{
|
wp := WebPack{
|
||||||
@@ -146,11 +152,13 @@ func (s *Server) handleGPU(w http.ResponseWriter, r *http.Request) {
|
|||||||
GPU: gpu,
|
GPU: gpu,
|
||||||
DriverVersion: s.repo.DriverVersion(),
|
DriverVersion: s.repo.DriverVersion(),
|
||||||
CUDAVersion: s.repo.CUDAVersion(),
|
CUDAVersion: s.repo.CUDAVersion(),
|
||||||
|
Version: constant.Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := template.New("index").Funcs(templateFuncMap).ParseFiles("static/index.html")
|
t, err := template.New("index").Funcs(templateFuncMap).ParseFiles("static/index.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendInternalServerErrorHTML(w, err)
|
internalServerErrorHTML(w, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
err = t.ExecuteTemplate(w, "index.html", wp)
|
err = t.ExecuteTemplate(w, "index.html", wp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -158,6 +166,38 @@ func (s *Server) handleGPU(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleGPUJSON(w http.ResponseWriter, r *http.Request) {
|
||||||
|
uuid := chi.URLParam(r, "uuid")
|
||||||
|
|
||||||
|
gpus, err := s.repo.GetGPUs()
|
||||||
|
if err != nil {
|
||||||
|
internalServerErrorJSON(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := -1
|
||||||
|
for _, gpu := range gpus {
|
||||||
|
if gpu.UUID == uuid {
|
||||||
|
i = gpu.Index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i == -1 {
|
||||||
|
notFoundJSON(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gpu, err := s.repo.GetGPU(i)
|
||||||
|
if err != nil {
|
||||||
|
internalServerErrorJSON(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body, err := json.Marshal(gpu)
|
||||||
|
if err != nil {
|
||||||
|
internalServerErrorJSON(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("content-type", "application/json")
|
||||||
|
w.Write([]byte(body))
|
||||||
|
}
|
||||||
|
|
||||||
// FileServer conveniently sets up a http.FileServer handler to serve
|
// FileServer conveniently sets up a http.FileServer handler to serve
|
||||||
// static files from a http.FileSystem.
|
// static files from a http.FileSystem.
|
||||||
func fileServer(r chi.Router, path string, root http.FileSystem) {
|
func fileServer(r chi.Router, path string, root http.FileSystem) {
|
||||||
@@ -179,7 +219,7 @@ func fileServer(r chi.Router, path string, root http.FileSystem) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendInternalServerErrorHTML(w http.ResponseWriter, v any) {
|
func internalServerErrorHTML(w http.ResponseWriter, v any) {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
body := fmt.Sprintf(internalServerErrorPage, v)
|
body := fmt.Sprintf(internalServerErrorPage, v)
|
||||||
w.Write([]byte(body))
|
w.Write([]byte(body))
|
||||||
@@ -191,6 +231,6 @@ func convertByteSize(v int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func percentageRounded(a, b int) int {
|
func percentageRounded(a, b int) int {
|
||||||
p := (a / b) * 100
|
p := (float64(a) / float64(b)) * 100
|
||||||
return p
|
return int(p)
|
||||||
}
|
}
|
||||||
|
|||||||
58
api/json.go
Normal file
58
api/json.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
JSONBase struct {
|
||||||
|
Status int `json:"status"`
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONError struct {
|
||||||
|
JSONBase
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errLog = log.New(os.Stderr, log.Prefix(), log.Flags())
|
||||||
|
)
|
||||||
|
|
||||||
|
func internalServerErrorJSON(w http.ResponseWriter, v any) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
|
||||||
|
body, err := json.Marshal(JSONError{
|
||||||
|
JSONBase: JSONBase{
|
||||||
|
Status: http.StatusInternalServerError,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
},
|
||||||
|
Message: fmt.Sprintf("%v", v),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
errLog.Println("[ERROR]", err)
|
||||||
|
}
|
||||||
|
w.Write([]byte(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
func notFoundJSON(w http.ResponseWriter) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
|
||||||
|
body, err := json.Marshal(JSONError{
|
||||||
|
JSONBase: JSONBase{
|
||||||
|
Status: http.StatusNotFound,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
},
|
||||||
|
Message: "404 page not found",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
errLog.Println("[ERROR]", err)
|
||||||
|
}
|
||||||
|
w.Write([]byte(body))
|
||||||
|
}
|
||||||
4
main.go
4
main.go
@@ -1,12 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"nvidiadashboard/api"
|
"nvidiadashboard/api"
|
||||||
|
"nvidiadashboard/pkg/constant"
|
||||||
"nvidiadashboard/pkg/nvidia"
|
"nvidiadashboard/pkg/nvidia"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
fmt.Println("*** NVIDIA Web Dashboard -", constant.Version, "***")
|
||||||
|
|
||||||
r := nvidia.New()
|
r := nvidia.New()
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
|
|||||||
5
pkg/constant/constant.go
Normal file
5
pkg/constant/constant.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version = "0.1-alpha"
|
||||||
|
)
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
||||||
)
|
)
|
||||||
@@ -16,43 +17,43 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
GPU struct {
|
GPU struct {
|
||||||
Name string
|
Name string `json:"name"`
|
||||||
UUID string
|
UUID string `json:"uuid"`
|
||||||
Index int
|
Index int `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUDetail struct {
|
GPUDetail struct {
|
||||||
GPU
|
GPU
|
||||||
CoreTemperature int
|
CoreTemperature int `json:"coreTemperature"`
|
||||||
Utilization Utilization
|
Utilization Utilization `json:"usage"`
|
||||||
Processes []Process
|
Processes []Process `json:"processes"`
|
||||||
Memory Memory
|
Memory Memory `json:"memory"`
|
||||||
Fans []Fan
|
Fans []Fan `json:"fans"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Memory struct {
|
Memory struct {
|
||||||
Free int
|
Free int `json:"free"`
|
||||||
Reserved int
|
Reserved int `json:"reserved"`
|
||||||
Total int
|
Total int `json:"total"`
|
||||||
Used int
|
Used int `json:"used"`
|
||||||
Version int
|
Version int `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Process struct {
|
Process struct {
|
||||||
PID int
|
PID int `json:"pid"`
|
||||||
MemoryUsed int
|
MemoryUsed int `json:"memoryUsed"`
|
||||||
Name string
|
Name string `json:"commandLine"`
|
||||||
Type string
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Utilization struct {
|
Utilization struct {
|
||||||
Decode int
|
Decoder int `json:"decoder"`
|
||||||
Encode int
|
Encoder int `json:"encoder"`
|
||||||
Rate int
|
Compute int `json:"compute"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Fan struct {
|
Fan struct {
|
||||||
Speed int
|
Speed int `json:"speed"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -196,9 +197,10 @@ func (*Repository) GetGPU(ID int) (GPUDetail, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find process command line
|
// Find process command line
|
||||||
for _, process := range allProcess {
|
for i, process := range allProcess {
|
||||||
if cmdline, err := os.ReadFile("/proc/" + strconv.Itoa(process.PID) + "/cmdline"); err == nil {
|
if cmdline, err := os.ReadFile("/proc/" + strconv.Itoa(process.PID) + "/cmdline"); err == nil {
|
||||||
process.Name = string(cmdline)
|
process.Name = strings.ReplaceAll(string(cmdline), "\u0000", " ")
|
||||||
|
allProcess[i] = process
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,9 +213,9 @@ func (*Repository) GetGPU(ID int) (GPUDetail, error) {
|
|||||||
GPU: gpu,
|
GPU: gpu,
|
||||||
CoreTemperature: int(temp),
|
CoreTemperature: int(temp),
|
||||||
Utilization: Utilization{
|
Utilization: Utilization{
|
||||||
Decode: int(decUsage),
|
Decoder: int(decUsage),
|
||||||
Encode: int(encUsage),
|
Encoder: int(encUsage),
|
||||||
Rate: int(load.Gpu),
|
Compute: int(load.Gpu),
|
||||||
},
|
},
|
||||||
Processes: allProcess,
|
Processes: allProcess,
|
||||||
Memory: Memory{
|
Memory: Memory{
|
||||||
|
|||||||
2
static
2
static
Submodule static updated: de2d3dc85f...369059c925
Reference in New Issue
Block a user