squash of 11515 with some fixups - not tested

This commit is contained in:
Andrew Jackson (Ajax) 2023-12-18 19:23:40 -06:00
parent 223e08bb84
commit b12757485a
13 changed files with 414 additions and 98 deletions

View File

@ -164,7 +164,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context,
if err != nil {
return err
}
defer func() {
go func() {
<-ctx.Done()
_ = j.Close()
}()

View File

@ -116,10 +116,8 @@ var runCmd = &cli.Command{
}
}
fmt.Println("before populateRemainingDeps")
dependencies := &deps.Deps{}
err = dependencies.PopulateRemainingDeps(ctx, cctx, true)
fmt.Println("after popdeps")
if err != nil {
fmt.Println("err", err)
return err

View File

@ -0,0 +1,37 @@
package hapi
import (
"embed"
"html/template"
logging "github.com/ipfs/go-log/v2"
"github.com/filecoin-project/lotus/cmd/lotus-provider/deps"
"github.com/gorilla/mux"
"golang.org/x/xerrors"
)
//go:embed web/*
var templateFS embed.FS
func Routes(r *mux.Router, deps *deps.Deps) error {
t := new(template.Template)
t, err := t.ParseFS(templateFS, "web/*")
if err != nil {
return xerrors.Errorf("parse templates: %w", err)
}
a := &app{
db: deps.DB,
t: t,
}
r.HandleFunc("/simpleinfo/actorsummary", a.actorSummary)
r.HandleFunc("/simpleinfo/machines", a.indexMachines)
r.HandleFunc("/simpleinfo/tasks", a.indexTasks)
r.HandleFunc("/simpleinfo/taskhistory", a.indexTasksHistory)
return nil
}
var log = logging.Logger("lpweb")

View File

@ -0,0 +1,187 @@
package hapi
import (
"context"
"html/template"
"net/http"
"os"
"sync"
"time"
"github.com/filecoin-project/lotus/lib/harmony/harmonydb"
)
type app struct {
db *harmonydb.DB
t *template.Template
actorInfoLk sync.Mutex
actorInfos []actorInfo
}
type actorInfo struct {
Address string
CLayers []string
QualityAdjustedPower string
RawBytePower string
Deadlines []actorDeadline
}
type actorDeadline struct {
Empty bool
Current bool
Proven bool
PartFaulty bool
Faulty bool
}
func (a *app) actorSummary(w http.ResponseWriter, r *http.Request) {
a.actorInfoLk.Lock()
defer a.actorInfoLk.Unlock()
a.executeTemplate(w, "actor_summary", a.actorInfos)
}
func (a *app) indexMachines(w http.ResponseWriter, r *http.Request) {
s, err := a.clusterMachineSummary(r.Context())
if err != nil {
log.Errorf("cluster machine summary: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
a.executeTemplate(w, "cluster_machines", s)
}
func (a *app) indexTasks(w http.ResponseWriter, r *http.Request) {
s, err := a.clusterTaskSummary(r.Context())
if err != nil {
log.Errorf("cluster task summary: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
a.executeTemplate(w, "cluster_tasks", s)
}
func (a *app) indexTasksHistory(w http.ResponseWriter, r *http.Request) {
s, err := a.clusterTaskHistorySummary(r.Context())
if err != nil {
log.Errorf("cluster task history summary: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
a.executeTemplate(w, "cluster_task_history", s)
}
var templateDev = os.Getenv("LOTUS_HAPI_TEMPLATE_DEV") == "1"
func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) {
if templateDev {
fs := os.DirFS("./cmd/lotus-provider/web/hapi/web")
a.t = template.Must(template.ParseFS(fs, "web/*"))
}
if err := a.t.ExecuteTemplate(w, name, data); err != nil {
log.Errorf("execute template %s: %v", name, err)
http.Error(w, "internal server error", http.StatusInternalServerError)
}
}
type machineSummary struct {
Address string
ID int64
SinceContact string
}
type taskSummary struct {
Name string
SincePosted string
Owner *string
ID int64
}
type taskHistorySummary struct {
Name string
TaskID int64
Posted, Start, End string
Result bool
Err string
CompletedBy string
}
func (a *app) clusterMachineSummary(ctx context.Context) ([]machineSummary, error) {
rows, err := a.db.Query(ctx, "SELECT id, host_and_port, last_contact FROM harmony_machines")
if err != nil {
return nil, err // Handle error
}
defer rows.Close()
var summaries []machineSummary
for rows.Next() {
var m machineSummary
var lastContact time.Time
if err := rows.Scan(&m.ID, &m.Address, &lastContact); err != nil {
return nil, err // Handle error
}
m.SinceContact = time.Since(lastContact).Round(time.Second).String()
summaries = append(summaries, m)
}
return summaries, nil
}
func (a *app) clusterTaskSummary(ctx context.Context) ([]taskSummary, error) {
rows, err := a.db.Query(ctx, "SELECT id, name, update_time, owner_id FROM harmony_task")
if err != nil {
return nil, err // Handle error
}
defer rows.Close()
var summaries []taskSummary
for rows.Next() {
var t taskSummary
var posted time.Time
if err := rows.Scan(&t.ID, &t.Name, &posted, &t.Owner); err != nil {
return nil, err // Handle error
}
t.SincePosted = time.Since(posted).Round(time.Second).String()
summaries = append(summaries, t)
}
return summaries, nil
}
func (a *app) clusterTaskHistorySummary(ctx context.Context) ([]taskHistorySummary, error) {
rows, err := a.db.Query(ctx, "SELECT id, name, task_id, posted, work_start, work_end, result, err, completed_by_host_and_port FROM harmony_task_history ORDER BY work_end DESC LIMIT 15")
if err != nil {
return nil, err // Handle error
}
defer rows.Close()
var summaries []taskHistorySummary
for rows.Next() {
var t taskHistorySummary
var posted, start, end time.Time
if err := rows.Scan(&t.TaskID, &t.Name, &t.TaskID, &posted, &start, &end, &t.Result, &t.Err, &t.CompletedBy); err != nil {
return nil, err // Handle error
}
t.Posted = posted.Round(time.Second).Format("02 Jan 06 15:04")
t.Start = start.Round(time.Second).Format("02 Jan 06 15:04")
t.End = end.Round(time.Second).Format("02 Jan 06 15:04")
summaries = append(summaries, t)
}
return summaries, nil
}

View File

@ -0,0 +1,20 @@
{{define "actor_summary"}}
{{range .}}
<tr>
<td>{{.Address}}</td>
<td>
{{range .CLayers}}
<span>{{.}} </span>
{{end}}
</td>
<td>{{.QualityAdjustedPower}}</td>
<td>
<div class="deadline-box">
{{range .Deadlines}}
<div class="deadline-entry{{if .Current}} deadline-entry-cur{{end}}{{if .Proven}} deadline-proven{{end}}{{if .PartFaulty}} deadline-partially-faulty{{end}}{{if .Faulty}} deadline-faulty{{end}}"></div>
{{end}}
</div>
</td>
</tr>
{{end}}
{{end}}

View File

@ -0,0 +1,15 @@
{{define "chain_rpcs"}}
{{range .}}
<tr>
<td>{{.Address}}</td>
<td>
{{range .CLayers}}
<span>{{.}} </span>
{{end}}
</td>
<td>{{if .Reachable}}<span class="success">ok</span>{{else}}<span class="error">FAIL</span>{{end}}</td>
<td>{{if eq "ok" .SyncState}}<span class="success">ok</span>{{else}}<span class="warning">{{.SyncState}}</span>{{end}}</td>
<td>{{.Version}}</td>
</tr>
{{end}}
{{end}}

View File

@ -0,0 +1,10 @@
{{define "cluster_machines"}}
{{range .}}
<tr>
<td>{{.Address}}</td>
<td>{{.ID}}</td>
<td>todo</td>
<td>{{.SinceContact}}</td>
</tr>
{{end}}
{{end}}

View File

@ -0,0 +1,14 @@
{{define "cluster_task_history"}}
{{range .}}
<tr>
<td>{{.Name}}</td>
<td>{{.TaskID}}</td>
<td>{{.CompletedBy}}</td>
<td>{{.Posted}}</td>
<td>{{.Start}}</td>
<td>{{.End}}</td>
<td>{{if .Result}}<span class="success">success</span>{{else}}<span class="error">error</span>{{end}}</td>
<td>{{.Err}}</td>
</tr>
{{end}}
{{end}}

View File

@ -0,0 +1,10 @@
{{define "cluster_tasks"}}
{{range .}}
<tr>
<td>{{.Name}}</td>
<td>{{.ID}}</td>
<td>{{.SincePosted}}</td>
<td>{{.Owner}}</td>
</tr>
{{end}}
{{end}}

View File

@ -10,6 +10,7 @@ import (
"github.com/filecoin-project/lotus/cmd/lotus-provider/deps"
"github.com/filecoin-project/lotus/cmd/lotus-provider/web/api"
"github.com/filecoin-project/lotus/cmd/lotus-provider/web/hapi"
"github.com/filecoin-project/lotus/metrics"
"github.com/gorilla/mux"
"go.opencensus.io/tag"
@ -21,6 +22,10 @@ var static embed.FS
func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) {
mux := mux.NewRouter()
api.Routes(mux.PathPrefix("/api").Subrouter(), deps)
err := hapi.Routes(mux.PathPrefix("/hapi").Subrouter(), deps)
if err != nil {
return nil, err
}
mux.NotFoundHandler = http.FileServer(http.FS(static))
return &http.Server{

View File

@ -1,31 +1,45 @@
<html>
<head>
<title>Lotus Provider Cluster Overview</title>
<script src="https://unpkg.com/htmx.org@1.9.5" integrity="sha384-xcuj3WpfgjlKF+FXhSQFQ0ZNr39ln+hwjN3npfM9VBnUskLolQAcN80McRIVOPuO" crossorigin="anonymous"></script>
<script type="module" src="chain-connectivity.js"></script>
<style>
html,
body {
html, body {
background: #0f0f0f;
color: #ffffff;
padding: 0;
margin: 0;
font-family: monospace;
}
table td, table th {
font-size: 13px;
}
.app-head {
width: 100%;
}
.head-left {
display: inline-block;
}
.head-right {
display: inline-block;
float: right;
}
table {
border-collapse: collapse;
}
table td, table th {
border-left: 1px solid #f0f0f0;
padding: 1px 5px;
}
table tr td:first-child, table tr th:first-child {
border-left: none;
}
a:link {
color: #cfc;
@ -39,6 +53,16 @@
color: #af7;
}
.success {
color: green;
}
.warning {
color: yellow;
}
.error {
color: red;
}
.dash-tile {
display: flex;
flex-direction: column;
@ -73,109 +97,97 @@
.deadline-proven {
background-color: green;
}
.deadline-partially-faulty {
background-color: yellow;
}
.deadline-faulty {
background-color: red;
}
</style>
</head>
<body>
<div class="app-head">
<div class="head-left">
<h1>Lotus Provider Cluster</h1>
</div>
<div class="head-right">
version [todo]
</div>
<div class="app-head">
<div class="head-left">
<h1>Lotus Provider Cluster</h1>
</div>
<hr />
<div class="page">
<div class="info-block">
<h2>Chain Connectivity</h2>
<chain-connectivity></chain-connectivity>
</div>
<div class="head-right">
version [todo]
</div>
</div>
<hr/>
<div class="page">
<div class="info-block">
<h2>Chain Connectivity</h2>
<chain-connectivity></chain-connectivity>
</div>
<hr>
<div class="info-block">
<h2>Actor Summary</h2>
<table>
<thead>
<tr>
<th>Address</th>
<th>Config Layers</th>
<th>QaP</th>
<th>Deadlines</th>
</tr>
<tr>
<th>Address</th>
<th>Config Layers</th>
<th>QaP</th>
<th>Deadlines</th>
</tr>
</thead>
<tbody>
<tr>
<td>f01234</td>
<td>mig0</td>
<td>23TiB</td>
<td>
<div class="deadline-box">
<div class="deadline-entry deadline-proven"></div>
<div class="deadline-entry deadline-partially-faulty"></div>
<div class="deadline-entry deadline-faulty"></div>
<div class="deadline-entry deadline-proven"></div>
<div class="deadline-entry deadline-proven"></div>
<div class="deadline-entry deadline-entry-cur"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
<div class="deadline-entry"></div>
</div>
</td>
</tr>
<tbody hx-get="/hapi/simpleinfo/actorsummary" hx-trigger="load,every 5s">
</tbody>
</table>
</div>
<hr>
<div class="info-block">
<h2>Cluster Machines</h2>
<table>
<thead>
<tr>
<th>Host</th>
<th>ID</th>
<th>Config Layers</th>
<th>Last Contact</th>
</tr>
</thead>
<tbody hx-get="/hapi/simpleinfo/machines" hx-trigger="load,every 5s">
</tbody>
</table>
</div>
<hr>
<div class="info-block">
<h2>Recently Finished Tasks</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>ID</th>
<th>Executor</th>
<th>Posted</th>
<th>Start</th>
<th>End</th>
<th>Outcome</th>
<th>Message</th>
</tr>
</thead>
<tbody hx-get="/hapi/simpleinfo/taskhistory" hx-trigger="load, every 5s">
</tbody>
</table>
</div>
<hr>
<div class="info-block">
<h2>Cluster Tasks</h2>
<table>
<thead>
<tr>
<th>Task</th>
<th>ID</th>
<th>Posted</th>
<th>Owner</th>
</tr>
</thead>
<tbody hx-get="/hapi/simpleinfo/tasks" hx-trigger="load,every 5s">
</tbody>
</table>
</div>
</div>
</body>
</html>

View File

@ -184,6 +184,9 @@ retryRecordCompletion:
return false, fmt.Errorf("could not log completion: %w", err)
}
result = ""
if doErr != nil {
result = "non-failing error: " + doErr.Error()
}
} else {
if doErr != nil {
result = "error: " + doErr.Error()

View File

@ -156,13 +156,18 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don
ComputeTime: details.CompTime,
}
persistNoWin := func() error {
_, err := t.db.Exec(ctx, `UPDATE mining_base_block SET no_win = true WHERE task_id = $1`, taskID)
persistNoWin := func() (bool, error) {
n, err := t.db.Exec(ctx, `UPDATE mining_base_block SET no_win = true WHERE task_id = $1`, taskID)
if err != nil {
return xerrors.Errorf("marking base as not-won: %w", err)
return false, xerrors.Errorf("marking base as not-won: %w", err)
}
log.Debugw("persisted no-win", "rows", n)
if n == 0 {
return false, xerrors.Errorf("persist no win: no rows updated")
}
return nil
return false, nil
}
// ensure we have a beacon entry for the epoch we're mining on
@ -182,13 +187,13 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don
if mbi == nil {
// not eligible to mine on this base, we're done here
log.Debugw("WinPoSt not eligible to mine on this base", "tipset", types.LogCids(base.TipSet.Cids()))
return true, persistNoWin()
return persistNoWin()
}
if !mbi.EligibleForMining {
// slashed or just have no power yet, we're done here
log.Debugw("WinPoSt not eligible for mining", "tipset", types.LogCids(base.TipSet.Cids()))
return true, persistNoWin()
return persistNoWin()
}
if len(mbi.Sectors) == 0 {
@ -217,7 +222,7 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don
if eproof == nil {
// not a winner, we're done here
log.Debugw("WinPoSt not a winner", "tipset", types.LogCids(base.TipSet.Cids()))
return true, persistNoWin()
return persistNoWin()
}
}