From 296d4fec534846782aabfbd22b1d4094dfbc708e Mon Sep 17 00:00:00 2001 From: Michal Nowikowski Date: Mon, 30 Dec 2019 18:23:09 +0100 Subject: [PATCH 1/6] [#120] reenabled dashboard page with apps info - added retrieving all apps from database - moved manual conversion of app details from maps to structs from several places to one place: AfterScan go-pg hook function - added ReST function for retrieving apps stats - reenabled dashboard page - added high level information about apps on dashboard: - total number of kea apps - number of misbehaving kea apps --- Rakefile | 2 +- api/swagger.yaml | 29 +++++ backend/go.mod | 6 +- backend/go.sum | 21 ++++ backend/server/database/model/app.go | 69 ++++++------ backend/server/database/model/app_test.go | 101 ++++++++++++++++++ backend/server/restservice/restimpl.go | 61 ++++++++--- backend/server/restservice/restimpl_test.go | 80 +++++++++++++- webui/src/app/app-routing.module.ts | 7 +- webui/src/app/app.component.ts | 2 +- .../app/dashboard/dashboard.component.html | 59 +++++++++- .../src/app/dashboard/dashboard.component.ts | 39 ++++++- 12 files changed, 408 insertions(+), 68 deletions(-) diff --git a/Rakefile b/Rakefile index 6bc973323..579a482fa 100644 --- a/Rakefile +++ b/Rakefile @@ -444,7 +444,7 @@ end desc 'Run container with Stork Agent and BIND 9 and mount current Agent binary' task :run_bind9_container do # host[9999]->agent[8080] - sh 'docker run --rm -ti -p 9999:8080 -v `pwd`/backend/cmd/stork-agent:/agent agent-bind9' + sh 'docker run --rm -ti -p 9999:8080 -h agent-bind9 -v `pwd`/backend/cmd/stork-agent:/agent agent-bind9' end diff --git a/api/swagger.yaml b/api/swagger.yaml index 89e68b10a..a5280c98d 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -413,6 +413,23 @@ paths: schema: $ref: "#/definitions/ApiError" + /apps-stats: + get: + summary: Get applications statistics. + description: It returns number of apps of given type, number of not fully active apps, etc. + operationId: getAppsStats + tags: + - Services + responses: + 200: + description: Application statistics + schema: + $ref: "#/definitions/AppsStats" + default: + description: generic error response + schema: + $ref: "#/definitions/ApiError" + /apps/{id}/services/status: get: summary: Get services status for a given application. @@ -744,6 +761,18 @@ definitions: total: type: integer + AppsStats: + type: object + properties: + keaAppsTotal: + type: integer + keaAppsNotOk: + type: integer + bind9AppsTotal: + type: integer + bind9AppsNotOk: + type: integer + KeaStatus: type: object properties: diff --git a/backend/go.mod b/backend/go.mod index 3eb4ca6c4..b3ed258ab 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -16,7 +16,7 @@ require ( github.com/go-openapi/validate v0.19.3 github.com/go-pg/migrations/v7 v7.1.6 github.com/go-pg/pg v8.0.6+incompatible - github.com/go-pg/pg/v9 v9.0.0-beta.15 + github.com/go-pg/pg/v9 v9.1.0 github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.2 github.com/jessevdk/go-flags v1.4.0 @@ -25,8 +25,8 @@ require ( github.com/shirou/gopsutil v2.19.9+incompatible github.com/sirupsen/logrus v1.4.2 github.com/stretchr/testify v1.4.0 - golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc - golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 + golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 + golang.org/x/net v0.0.0-20190923162816-aa69164e4478 google.golang.org/grpc v1.24.0 gopkg.in/h2non/gock.v1 v1.0.15 ) diff --git a/backend/go.sum b/backend/go.sum index 99ab1a82f..4e9efee2f 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -16,6 +16,8 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA= +github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -84,10 +86,16 @@ github.com/go-pg/pg v8.0.6+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrx github.com/go-pg/pg/v9 v9.0.0-beta.14/go.mod h1:T2Sr6bpTCOr2lUqOUMiXLMJqZHSUBKk1LdgSqjwhZfA= github.com/go-pg/pg/v9 v9.0.0-beta.15 h1:fcwHlBivDKP+ILdcv49bRApfb1fmQgxB9RnFXtzLbPI= github.com/go-pg/pg/v9 v9.0.0-beta.15/go.mod h1:JtAtFggZZ97a9GoyKBYWYO9Vd4zWyk4DQ/2EONhmlIs= +github.com/go-pg/pg/v9 v9.0.3/go.mod h1:Tm/Q3Vt6gdQOH6TTN1H/xLlIXc+Qrka7TZ6uREtu/eA= +github.com/go-pg/pg/v9 v9.1.0 h1:yRXwMxYpbT3IxYZndQ2M2pTVGpV3ba9kbLcDVn51gew= +github.com/go-pg/pg/v9 v9.1.0/go.mod h1:R3slXUnppC1am3XiioEeszbPnU6uAzGHipxNyvpfpr4= github.com/go-pg/urlstruct v0.1.0/go.mod h1:2Nag+BIny6G/KYCkdt++ZnqU/VinzimGapKfs4kwlN0= github.com/go-pg/urlstruct v0.1.4/go.mod h1:2Nag+BIny6G/KYCkdt++ZnqU/VinzimGapKfs4kwlN0= github.com/go-pg/urlstruct v0.2.5 h1:XCElG08e8pONmvg2c+n1bTkf1Qx7UsVI+wjv3Z8WYtI= github.com/go-pg/urlstruct v0.2.5/go.mod h1:dxENwVISWSOX+k87hDt0ueEJadD+gZWv3tHzwfmZPu8= +github.com/go-pg/urlstruct v0.2.6/go.mod h1:dxENwVISWSOX+k87hDt0ueEJadD+gZWv3tHzwfmZPu8= +github.com/go-pg/urlstruct v0.2.8 h1:pasKiKzYyAtJ9YEpGe6G+3PB0M5Ez0qsMtjSA3gsw/g= +github.com/go-pg/urlstruct v0.2.8/go.mod h1:/XKyiUOUUS3onjF+LJxbfmSywYAdl6qMfVbX33Q8rgg= github.com/go-pg/zerochecker v0.1.1 h1:av77Qe7Gs+1oYGGh51k0sbZ0bUaxJEdeP0r8YE64Dco= github.com/go-pg/zerochecker v0.1.1/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= @@ -160,6 +168,8 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/vmihailenco/tagparser v0.1.0 h1:u6yzKTY6gW/KxL/K2NTEQUOSXZipyGiIRarGjJKmQzU= github.com/vmihailenco/tagparser v0.1.0/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1 h1:Sq1fR+0c58RME5EoqKdjkiQAmPjmfHlZOoRI6fTUOcs= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= @@ -172,6 +182,10 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -182,6 +196,8 @@ golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -193,6 +209,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -211,6 +229,7 @@ google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRn gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= @@ -220,6 +239,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w= mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= diff --git a/backend/server/database/model/app.go b/backend/server/database/model/app.go index f6eb68d6e..909da2018 100644 --- a/backend/server/database/model/app.go +++ b/backend/server/database/model/app.go @@ -1,10 +1,14 @@ package dbmodel import ( + "context" "encoding/json" + "github.com/go-pg/pg/v9" "github.com/go-pg/pg/v9/orm" "github.com/pkg/errors" + //log "github.com/sirupsen/logrus" + "isc.org/stork/util" "time" ) @@ -53,35 +57,40 @@ type App struct { Details interface{} // here we have either AppKea or AppBind9 } -func AddApp(db *pg.DB, app *App) error { - err := db.Insert(app) - if err != nil { - return errors.Wrapf(err, "problem with inserting app %v", app) +func (app *App) AfterScan(ctx context.Context) error { + if app.Details == nil { + return nil } - return nil -} -func ReconvertAppDetails(app *App) error { bytes, err := json.Marshal(app.Details) if err != nil { - return errors.Wrapf(err, "problem with getting app from the database: %v ", app) + return errors.Wrapf(err, "problem with marshaling app details: %v ", app.Details) } - if (app.Type == "kea") { + + switch app.Type { + case "kea": var keaDetails AppKea err = json.Unmarshal(bytes, &keaDetails) if err != nil { - return errors.Wrapf(err, "problem with getting Kea app from the database: %v ", app) + return errors.Wrapf(err, "problem with unmarshaling app details") } app.Details = keaDetails - } else if (app.Type == "bind9") { + + case "bind9": var bind9Details AppBind9 err = json.Unmarshal(bytes, &bind9Details) if err != nil { - return errors.Wrapf(err, "problem with getting BIND 9 app from the database: %v ", app) + return errors.Wrapf(err, "problem with unmarshaling app details") } app.Details = bind9Details - } else { - return errors.Wrapf(err, "unknown app type: %v ", app) + } + return nil +} + +func AddApp(db *pg.DB, app *App) error { + err := db.Insert(app) + if err != nil { + return errors.Wrapf(err, "problem with inserting app %v", app) } return nil } @@ -96,10 +105,6 @@ func GetAppById(db *pg.DB, id int64) (*App, error) { } else if err != nil { return nil, errors.Wrapf(err, "problem with getting app %v", id) } - err = ReconvertAppDetails(&app) - if err != nil { - return nil, err - } return &app, nil } @@ -112,13 +117,6 @@ func GetAppsByMachine(db *pg.DB, machineId int64) ([]App, error) { if err != nil { return nil, errors.Wrapf(err, "problem with getting apps") } - - for idx := range apps { - err = ReconvertAppDetails(&apps[idx]) - if err != nil { - return nil, err - } - } return apps, nil } @@ -158,12 +156,6 @@ func GetAppsByPage(db *pg.DB, offset int64, limit int64, text string, appType st if err != nil { return nil, 0, errors.Wrapf(err, "problem with getting apps") } - for idx := range apps { - err = ReconvertAppDetails(&apps[idx]) - if err != nil { - return nil, 0, err - } - } return apps, int64(total), nil } @@ -176,6 +168,21 @@ func DeleteApp(db *pg.DB, app *App) error { return nil } +func GetAllApps(db *pg.DB) ([]App, error) { + var apps []App + + // prepare query + q := db.Model(&apps) + q = q.Where("app.deleted is NULL") + + // retrieve apps from db + err := q.Select() + if err != nil { + return nil, errors.Wrapf(err, "problem with getting apps") + } + return apps, nil +} + // Returns a list of names of active DHCP deamons. This is useful for // creating commands to be send to active DHCP servers. func (app *App) GetActiveDHCPDeamonNames() (deamons []string) { diff --git a/backend/server/database/model/app_test.go b/backend/server/database/model/app_test.go index 545a8979c..516776f08 100644 --- a/backend/server/database/model/app_test.go +++ b/backend/server/database/model/app_test.go @@ -1,9 +1,11 @@ package dbmodel import ( + "context" "testing" "github.com/stretchr/testify/require" + //log "github.com/sirupsen/logrus" "isc.org/stork/server/database/test" ) @@ -309,3 +311,102 @@ func TestGetActiveDHCPAppMismatch(t *testing.T) { daemons := a.GetActiveDHCPDeamonNames() require.Empty(t, daemons) } + + +func TestGetAllApps(t *testing.T) { + db, _, teardown := dbtest.SetupDatabaseTestCase(t) + defer teardown() + + // add first machine, should be no error + m := &Machine{ + Id: 0, + Address: "localhost", + AgentPort: 8080, + } + err := AddMachine(db, m) + require.NoError(t, err) + require.NotEqual(t, 0, m.Id) + + // add kea app, no error expected + aKea := &App{ + Id: 0, + MachineID: m.Id, + Type: "kea", + CtrlPort: 1234, + Active: true, + } + err = AddApp(db, aKea) + require.NoError(t, err) + require.NotEqual(t, 0, aKea.Id) + + // add bind app, no error expected + aBind := &App{ + Id: 0, + MachineID: m.Id, + Type: "bind", + CtrlPort: 4321, + Active: true, + } + err = AddApp(db, aBind) + require.NoError(t, err) + require.NotEqual(t, 0, aBind.Id) + + // get all apps + apps, err := GetAllApps(db) + require.NoError(t, err) + require.Len(t, apps, 2) +} + +func TestAfterScanKea(t *testing.T) { + ctx := context.Background() + + // for now details are nil + aKea := &App{ + Id: 0, + MachineID: 0, + Type: "kea", + CtrlPort: 1234, + Active: true, + } + err := aKea.AfterScan(ctx) + require.Nil(t, err) + require.Nil(t, aKea.Details) + + // add some details + aKea.Details = map[string]interface{}{ + "ExtendedVersion": "1.2.3", + "Daemons": []map[string]interface{}{ + { + "Pid": 123, + "Name": "dhcp4", + }, + }, + } + err = aKea.AfterScan(ctx) + require.Nil(t, err) + require.NotNil(t, aKea.Details) + require.Equal(t, "1.2.3", aKea.Details.(AppKea).ExtendedVersion) + require.Equal(t, "dhcp4", aKea.Details.(AppKea).Daemons[0].Name) +} + +func TestAfterScanBind(t *testing.T) { + ctx := context.Background() + + // for now details are nil + aBind := &App{ + Id: 0, + MachineID: 0, + Type: "bind", + CtrlPort: 1234, + Active: true, + } + err := aBind.AfterScan(ctx) + require.Nil(t, err) + require.Nil(t, aBind.Details) + + // add some details + aBind.Details = map[string]interface{}{} + err = aBind.AfterScan(ctx) + require.Nil(t, err) + require.NotNil(t, aBind.Details) +} diff --git a/backend/server/restservice/restimpl.go b/backend/server/restservice/restimpl.go index 24070f683..1f38d1080 100644 --- a/backend/server/restservice/restimpl.go +++ b/backend/server/restservice/restimpl.go @@ -40,21 +40,11 @@ func (r *RestAPI) GetVersion(ctx context.Context, params general.GetVersionParam func machineToRestApi(dbMachine dbmodel.Machine) (*models.Machine, error) { var apps []*models.MachineApp - for _, srv := range dbMachine.Apps { + for _, app := range dbMachine.Apps { active := true - if srv.Type == "bind9" { - err := dbmodel.ReconvertAppDetails(&srv) - if err != nil { - return nil, err - } - active = srv.Details.(dbmodel.AppBind9).Daemon.Active - } else if srv.Type == "kea" { - if srv.Active { - err := dbmodel.ReconvertAppDetails(&srv) - if err != nil { - return nil, err - } - for _, d := range srv.Details.(dbmodel.AppKea).Daemons { + if app.Type == "kea" { + if app.Active { + for _, d := range app.Details.(dbmodel.AppKea).Daemons { if !d.Active { active = false break @@ -65,9 +55,9 @@ func machineToRestApi(dbMachine dbmodel.Machine) (*models.Machine, error) { } } s := models.MachineApp{ - ID: srv.Id, - Type: srv.Type, - Version: srv.Meta.Version, + ID: app.Id, + Type: app.Type, + Version: app.Meta.Version, Active: active, } apps = append(apps, &s) @@ -882,3 +872,40 @@ func (r *RestAPI) GetAppServicesStatus(ctx context.Context, params services.GetA rsp := services.NewGetAppServicesStatusOK().WithPayload(servicesStatus) return rsp } + +// Get statistics about applications. +func (r *RestAPI) GetAppsStats(ctx context.Context, params services.GetAppsStatsParams) middleware.Responder { + dbApps, err := dbmodel.GetAllApps(r.Db) + if err != nil { + msg := fmt.Sprintf("cannot get all apps from db") + log.Error(err) + rsp := services.NewGetAppsStatsDefault(500).WithPayload(&models.APIError{ + Message: &msg, + }) + return rsp + } + + appsStats := models.AppsStats{ + KeaAppsTotal: 0, + KeaAppsNotOk: 0, + Bind9AppsTotal: 0, + Bind9AppsNotOk: 0, + } + for _, dbApp := range dbApps { + switch dbApp.Type { + case "kea": + appsStats.KeaAppsTotal += 1 + if !dbApp.Active { + appsStats.KeaAppsNotOk += 1 + } + case "bind9": + appsStats.Bind9AppsTotal += 1 + if !dbApp.Active { + appsStats.Bind9AppsNotOk += 1 + } + } + } + + rsp := services.NewGetAppsStatsOK().WithPayload(&appsStats) + return rsp +} diff --git a/backend/server/restservice/restimpl_test.go b/backend/server/restservice/restimpl_test.go index e05342756..6e32a6b54 100644 --- a/backend/server/restservice/restimpl_test.go +++ b/backend/server/restservice/restimpl_test.go @@ -206,6 +206,9 @@ func TestGetMachine(t *testing.T) { Type: "kea", CtrlPort: 1234, Active: true, + Details: dbmodel.AppKea{ + Daemons: []dbmodel.KeaDaemon{}, + }, } err = dbmodel.AddApp(db, s) require.NoError(t, err) @@ -393,6 +396,9 @@ func TestGetApp(t *testing.T) { Type: "kea", CtrlPort: 1234, Active: true, + Details: dbmodel.AppKea{ + Daemons: []dbmodel.KeaDaemon{}, + }, } err = dbmodel.AddApp(db, s) require.NoError(t, err) @@ -442,6 +448,9 @@ func TestRestGetApp(t *testing.T) { Type: "kea", CtrlPort: 1234, Active: true, + Details: dbmodel.AppKea{ + Daemons: []dbmodel.KeaDaemon{}, + }, } err = dbmodel.AddApp(db, keaApp) require.NoError(t, err) @@ -462,6 +471,7 @@ func TestRestGetApp(t *testing.T) { Type: "bind9", CtrlPort: 953, Active: true, + Details: dbmodel.AppBind9{}, } err = dbmodel.AddApp(db, bind9App) require.NoError(t, err) @@ -508,6 +518,9 @@ func TestRestGetApps(t *testing.T) { Type: "kea", CtrlPort: 1234, Active: true, + Details: dbmodel.AppKea{ + Daemons: []dbmodel.KeaDaemon{}, + }, } err = dbmodel.AddApp(db, s1) require.NoError(t, err) @@ -519,13 +532,13 @@ func TestRestGetApps(t *testing.T) { Type: "bind9", CtrlPort: 4321, Active: true, + Details: dbmodel.AppBind9{}, } err = dbmodel.AddApp(db, s2) require.NoError(t, err) // get added apps - params = services.GetAppsParams{ - } + params = services.GetAppsParams{} rsp = rapi.GetApps(ctx, params) require.IsType(t, &services.GetAppsOK{}, rsp) okRsp = rsp.(*services.GetAppsOK) @@ -686,3 +699,66 @@ func TestRestGetAppServicesStatus(t *testing.T) { require.True(t, haStatus.RemoteServer.InTouch) } + +func TestRestGetAppsStats(t *testing.T) { + db, dbSettings, teardown := dbtest.SetupDatabaseTestCase(t) + defer teardown() + + settings := RestApiSettings{} + fa := storktest.NewFakeAgents(nil) + rapi, err := NewRestAPI(&settings, dbSettings, db, fa) + require.NoError(t, err) + ctx := context.Background() + + // get empty list of app + params := services.GetAppsStatsParams{} + rsp := rapi.GetAppsStats(ctx, params) + require.IsType(t, &services.GetAppsStatsOK{}, rsp) + okRsp := rsp.(*services.GetAppsStatsOK) + require.Equal(t, int64(0), okRsp.Payload.KeaAppsTotal) + require.Equal(t, int64(0), okRsp.Payload.KeaAppsNotOk) + + // add machine + m := &dbmodel.Machine{ + Address: "localhost", + AgentPort: 8080, + } + err = dbmodel.AddMachine(db, m) + require.NoError(t, err) + + // add app kea to machine + s1 := &dbmodel.App{ + Id: 0, + MachineID: m.Id, + Type: "kea", + CtrlPort: 1234, + Active: true, + Details: dbmodel.AppKea{ + Daemons: []dbmodel.KeaDaemon{}, + }, + } + err = dbmodel.AddApp(db, s1) + require.NoError(t, err) + + // add app bind to machine + s2 := &dbmodel.App{ + Id: 0, + MachineID: m.Id, + Type: "bind9", + CtrlPort: 4321, + Active: false, + Details: dbmodel.AppBind9{}, + } + err = dbmodel.AddApp(db, s2) + require.NoError(t, err) + + // get added app + params = services.GetAppsStatsParams{} + rsp = rapi.GetAppsStats(ctx, params) + require.IsType(t, &services.GetAppsStatsOK{}, rsp) + okRsp = rsp.(*services.GetAppsStatsOK) + require.Equal(t, int64(1), okRsp.Payload.KeaAppsTotal) + require.Equal(t, int64(0), okRsp.Payload.KeaAppsNotOk) + require.Equal(t, int64(1), okRsp.Payload.Bind9AppsTotal) + require.Equal(t, int64(1), okRsp.Payload.Bind9AppsNotOk) +} diff --git a/webui/src/app/app-routing.module.ts b/webui/src/app/app-routing.module.ts index d77d8c027..f149fbebc 100644 --- a/webui/src/app/app-routing.module.ts +++ b/webui/src/app/app-routing.module.ts @@ -14,10 +14,8 @@ import { PasswordChangePageComponent } from './password-change-page/password-cha const routes: Routes = [ { path: '', - // component: DashboardComponent, - pathMatch: 'full', - // canActivate: [AuthGuard], - redirectTo: 'machines/all', + component: DashboardComponent, + canActivate: [AuthGuard], }, { path: 'login', @@ -71,6 +69,7 @@ const routes: Routes = [ path: 'users', redirectTo: 'users/', pathMatch: 'full', + canActivate: [AuthGuard], }, { path: 'users/:id', diff --git a/webui/src/app/app.component.ts b/webui/src/app/app.component.ts index 6c36f41a6..54e56f483 100644 --- a/webui/src/app/app.component.ts +++ b/webui/src/app/app.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core' -import { Router, UrlSegment } from '@angular/router' +import { Router } from '@angular/router' import { Observable } from 'rxjs' import { MenuItem } from 'primeng/api' diff --git a/webui/src/app/dashboard/dashboard.component.html b/webui/src/app/dashboard/dashboard.component.html index 49ee4f361..082d6d442 100644 --- a/webui/src/app/dashboard/dashboard.component.html +++ b/webui/src/app/dashboard/dashboard.component.html @@ -1,11 +1,21 @@
- - Content - +
+ +
- +
+ +
- +
diff --git a/webui/src/app/dashboard/dashboard.component.ts b/webui/src/app/dashboard/dashboard.component.ts index 5ae553256..30ddff179 100644 --- a/webui/src/app/dashboard/dashboard.component.ts +++ b/webui/src/app/dashboard/dashboard.component.ts @@ -1,6 +1,10 @@ import { Component, OnInit } from '@angular/core' +import { Router } from '@angular/router' -import { PanelModule } from 'primeng/panel' +import { MessageService } from 'primeng/api' + +import { ServicesService } from '../backend/api/api' +import { AppsStats } from '../backend/model/appsStats' @Component({ selector: 'app-dashboard', @@ -8,9 +12,36 @@ import { PanelModule } from 'primeng/panel' styleUrls: ['./dashboard.component.sass'], }) export class DashboardComponent implements OnInit { - constructor() {} + appsStats: AppsStats + + constructor(private router: Router, private servicesApi: ServicesService, private msgSrv: MessageService) {} - ngOnInit() {} + ngOnInit() { + this.appsStats = { + keaAppsTotal: 0, + keaAppsNotOk: 0, + } - handler() {} + this.servicesApi.getAppsStats().subscribe( + data => { + this.appsStats = { ...this.appsStats, ...data } + if (this.appsStats.keaAppsTotal === 0) { + // redirect to machines page so user can add some machine + this.router.navigate(['/machines/all']) + } + }, + err => { + let msg = err.statusText + if (err.error && err.error.message) { + msg = err.error.message + } + this.msgSrv.add({ + severity: 'error', + summary: 'Cannot get applications statistics', + detail: 'Getting applications statistics erred: ' + msg, + life: 10000, + }) + } + ) + } } -- GitLab From 683ac06ed18bf1aec39471add0bbdf44188f15d7 Mon Sep 17 00:00:00 2001 From: Michal Nowikowski Date: Fri, 10 Jan 2020 12:25:27 +0100 Subject: [PATCH 2/6] [#120] added entry to ChangeLog --- ChangeLog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 722aab285..9b03b5255 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,10 @@ +* 23 [func] godfryd + + Added presenting number of all and misbehaving application + on dashboard page. If there is no apps yet then dashboard + redirects to machines page. + (Gitlab #120) + * 22 [doc] marcin Updated Stork ARM. Added documentation of the High Availability -- GitLab From eb0b8d231c83cf1042a975c25eb2303d799f3a7c Mon Sep 17 00:00:00 2001 From: Michal Nowikowski Date: Fri, 10 Jan 2020 13:52:50 +0100 Subject: [PATCH 3/6] [#120] changes after review --- ChangeLog.md | 6 +- backend/server/database/model/app.go | 8 +- backend/server/database/model/app_test.go | 17 ++++- doc/usage.rst | 6 ++ .../app/dashboard/dashboard.component.html | 76 ++++++++----------- .../src/app/dashboard/dashboard.component.ts | 2 +- 6 files changed, 59 insertions(+), 56 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 9b03b5255..c0709891a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,8 +1,8 @@ * 23 [func] godfryd - Added presenting number of all and misbehaving application - on dashboard page. If there is no apps yet then dashboard - redirects to machines page. + Added presenting number of all and misbehaving applications + on the dashboard page. If there are no applications added yet, + the dashboard redirects to the list of connected machines. (Gitlab #120) * 22 [doc] marcin diff --git a/backend/server/database/model/app.go b/backend/server/database/model/app.go index 909da2018..8c5a1d37b 100644 --- a/backend/server/database/model/app.go +++ b/backend/server/database/model/app.go @@ -57,6 +57,8 @@ type App struct { Details interface{} // here we have either AppKea or AppBind9 } +// This is a hook to go-pg that is called just after reading rows from database. +// It reconverts app details from json string maps to particular Go structure. func (app *App) AfterScan(ctx context.Context) error { if app.Details == nil { return nil @@ -64,7 +66,7 @@ func (app *App) AfterScan(ctx context.Context) error { bytes, err := json.Marshal(app.Details) if err != nil { - return errors.Wrapf(err, "problem with marshaling app details: %v ", app.Details) + return errors.Wrapf(err, "problem with marshaling %s app details: %v ", app.Type, app.Details) } switch app.Type { @@ -72,7 +74,7 @@ func (app *App) AfterScan(ctx context.Context) error { var keaDetails AppKea err = json.Unmarshal(bytes, &keaDetails) if err != nil { - return errors.Wrapf(err, "problem with unmarshaling app details") + return errors.Wrapf(err, "problem with unmarshaling kea app details") } app.Details = keaDetails @@ -80,7 +82,7 @@ func (app *App) AfterScan(ctx context.Context) error { var bind9Details AppBind9 err = json.Unmarshal(bytes, &bind9Details) if err != nil { - return errors.Wrapf(err, "problem with unmarshaling app details") + return errors.Wrapf(err, "problem with unmarshaling bind9 app details") } app.Details = bind9Details } diff --git a/backend/server/database/model/app_test.go b/backend/server/database/model/app_test.go index 516776f08..3dcf80ea0 100644 --- a/backend/server/database/model/app_test.go +++ b/backend/server/database/model/app_test.go @@ -68,7 +68,7 @@ func TestAddApp(t *testing.T) { s = &App{ Id: 0, MachineID: m.Id, - Type: "bind", + Type: "bind9", CtrlAddress: "", CtrlPort: 1234, Active: true, @@ -343,7 +343,7 @@ func TestGetAllApps(t *testing.T) { aBind := &App{ Id: 0, MachineID: m.Id, - Type: "bind", + Type: "bind9", CtrlPort: 4321, Active: true, } @@ -355,6 +355,8 @@ func TestGetAllApps(t *testing.T) { apps, err := GetAllApps(db) require.NoError(t, err) require.Len(t, apps, 2) + require.True(t, apps[0].Type == "kea" || apps[1].Type == "kea") + require.True(t, apps[0].Type == "bind9" || apps[1].Type == "bind9") } func TestAfterScanKea(t *testing.T) { @@ -396,7 +398,7 @@ func TestAfterScanBind(t *testing.T) { aBind := &App{ Id: 0, MachineID: 0, - Type: "bind", + Type: "bind9", CtrlPort: 1234, Active: true, } @@ -405,8 +407,15 @@ func TestAfterScanBind(t *testing.T) { require.Nil(t, aBind.Details) // add some details - aBind.Details = map[string]interface{}{} + aBind.Details = map[string]interface{}{ + "Daemon": map[string]interface{}{ + "Pid": 123, + "Name": "named", + }, + } err = aBind.AfterScan(ctx) require.Nil(t, err) require.NotNil(t, aBind.Details) + require.Equal(t, "named", aBind.Details.(AppBind9).Daemon.Name) + require.Equal(t, int32(123), aBind.Details.(AppBind9).Daemon.Pid) } diff --git a/doc/usage.rst b/doc/usage.rst index df3ea8321..c0cd0185f 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -199,3 +199,9 @@ state which will be reflected in this view. If the local server crashes, this will manifest itself as a communication problem between Stork and the server. + +Dashboard +========= + +Main web page of Stork presents a dashboard. It presents statistics about applications. +They include total number of Kea and BIND 9 apps and a number of misbehaving apps. diff --git a/webui/src/app/dashboard/dashboard.component.html b/webui/src/app/dashboard/dashboard.component.html index 082d6d442..f9fbf7910 100644 --- a/webui/src/app/dashboard/dashboard.component.html +++ b/webui/src/app/dashboard/dashboard.component.html @@ -1,18 +1,4 @@
-
- -
- -
- -
-
- - -
-
{{ appsStats.keaAppsTotal }} Kea {{ appsStats.keaAppsTotal === 1 ? 'app' : 'apps' }}
-
- {{ appsStats.keaAppsNotOk }} {{ appsStats.keaAppsNotOk > 1 ? 'have' : 'has' }} problems - -
-
- all ok - -
+ +
+
{{ appsStats.keaAppsTotal }} Kea {{ appsStats.keaAppsTotal === 1 ? 'app' : 'apps' }}
+
+ {{ appsStats.keaAppsNotOk }} {{ appsStats.keaAppsNotOk > 1 ? 'have' : 'has' }} problems + +
+
+ all ok +
+
- -
-
- {{ appsStats.bind9AppsTotal }} BIND 9 {{ appsStats.bind9AppsTotal === 1 ? 'app' : 'apps' }} -
-
- {{ appsStats.bind9AppsNotOk }} {{ appsStats.bind9AppsNotOk > 1 ? 'have' : 'has' }} problems - -
-
- all ok - -
+ + +
{{ appsStats.bind9AppsTotal }} BIND 9 {{ appsStats.bind9AppsTotal === 1 ? 'app' : 'apps' }}
+
+ {{ appsStats.bind9AppsNotOk }} {{ appsStats.bind9AppsNotOk > 1 ? 'have' : 'has' }} problems + +
+
+ all ok +
diff --git a/webui/src/app/dashboard/dashboard.component.ts b/webui/src/app/dashboard/dashboard.component.ts index 30ddff179..30502e58d 100644 --- a/webui/src/app/dashboard/dashboard.component.ts +++ b/webui/src/app/dashboard/dashboard.component.ts @@ -25,7 +25,7 @@ export class DashboardComponent implements OnInit { this.servicesApi.getAppsStats().subscribe( data => { this.appsStats = { ...this.appsStats, ...data } - if (this.appsStats.keaAppsTotal === 0) { + if (this.appsStats.keaAppsTotal === 0 && this.appsStats.bind9AppsTotal === 0) { // redirect to machines page so user can add some machine this.router.navigate(['/machines/all']) } -- GitLab From e94f51e69df73ad77a48767e8fb88b0b7cb75f17 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 10 Jan 2020 16:12:01 +0100 Subject: [PATCH 4/6] [#120] Slightly updated the Dashboard section --- doc/usage.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/usage.rst b/doc/usage.rst index c0cd0185f..0953c9476 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -203,5 +203,6 @@ server. Dashboard ========= -Main web page of Stork presents a dashboard. It presents statistics about applications. -They include total number of Kea and BIND 9 apps and a number of misbehaving apps. +The Main Stork page presents a simple dashboard. It includes some statistics about the monitored +applications such as: a total number of Kea and BIND 9 applications and a number of misbehaving +applications. -- GitLab From c1caa2f9cdddbc5beae8889db32328d0190b3256 Mon Sep 17 00:00:00 2001 From: Michal Nowikowski Date: Fri, 10 Jan 2020 16:41:12 +0100 Subject: [PATCH 5/6] [#120] improved description of /apps-stats --- api/swagger.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/swagger.yaml b/api/swagger.yaml index a5280c98d..93f5b18c5 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -416,7 +416,7 @@ paths: /apps-stats: get: summary: Get applications statistics. - description: It returns number of apps of given type, number of not fully active apps, etc. + description: It returns a number of apps of a given type, a number of apps with some inactive daemons, etc. operationId: getAppsStats tags: - Services -- GitLab From 460c1be80cfb8a6cc0291a169821387269cdcb61 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 10 Jan 2020 18:00:49 +0100 Subject: [PATCH 6/6] [#120] Added Stork 0.3.0 released text In the ChangeLog. --- ChangeLog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index c0709891a..a3d832d78 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,5 @@ +Stork 0.3.0 released on 2020-01-10. + * 23 [func] godfryd Added presenting number of all and misbehaving applications -- GitLab