Skip to content
This repository has been archived by the owner on May 30, 2022. It is now read-only.

Hosts UI revamp #552

Merged
merged 5 commits into from
Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func NewAppWithDeps(config *Config, deps Dependencies) (*App, error) {
webEngine.GET("/", HomeHandler)
webEngine.GET("/about", NewAboutHandler(deps.subscriptionsService))
webEngine.GET("/hosts", NewHostListHandler(deps.hostsService))
webEngine.GET("/hosts/:name", NewHostHandler(deps.consul, deps.subscriptionsService))
webEngine.GET("/hosts/:name", NewHostHandler(deps.hostsService, deps.subscriptionsService))
webEngine.GET("/catalog", NewChecksCatalogHandler(deps.checksService))
webEngine.GET("/clusters", NewClusterListHandler(deps.clustersService))
webEngine.GET("/clusters/:id", NewClusterHandler(deps.clustersService))
Expand Down
39 changes: 7 additions & 32 deletions web/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import (
consulApi "github.com/hashicorp/consul/api"
"github.com/pkg/errors"

"github.com/trento-project/trento/internal/cloud"
"github.com/trento-project/trento/internal/consul"
"github.com/trento-project/trento/internal/hosts"
"github.com/trento-project/trento/internal/sapsystem"

"github.com/trento-project/trento/web/models"
"github.com/trento-project/trento/web/services"
Expand Down Expand Up @@ -127,35 +124,17 @@ func getTrentoAgentCheck(client consul.Client, node string) (*consulApi.HealthCh
return trentoAgentCheck, nil
}

func NewHostHandler(client consul.Client, subsService services.SubscriptionsService) gin.HandlerFunc {
func NewHostHandler(hostsService services.HostsService, subsService services.SubscriptionsService) gin.HandlerFunc {
return func(c *gin.Context) {
name := c.Param("name")
catalogNode, _, err := client.Catalog().Node(name, nil)
if err != nil {
_ = c.Error(err)
return
}

if catalogNode == nil {
_ = c.Error(NotFoundError("could not find host"))
return
}

trentoAgentCheck, err := getTrentoAgentCheck(client, name)
host, err := hostsService.GetByName(name)
if err != nil {
_ = c.Error(err)
return
}

systems, err := sapsystem.Load(client, name)
if err != nil {
_ = c.Error(err)
_ = c.Error(NotFoundError("could not find host"))
fabriziosestito marked this conversation as resolved.
Show resolved Hide resolved
return
}

cloudData, err := cloud.Load(client, name)
if err != nil {
_ = c.Error(err)
if host == nil {
_ = c.Error(NotFoundError("could not find host"))
return
}

Expand All @@ -165,13 +144,9 @@ func NewHostHandler(client consul.Client, subsService services.SubscriptionsServ
return
}

host := hosts.NewHost(*catalogNode.Node, client)
c.HTML(http.StatusOK, "host.html.tmpl", gin.H{
"Host": &host,
"TrentoAgentCheck": trentoAgentCheck,
"SAPSystems": systems,
"CloudData": cloudData,
"Subscriptions": subs,
"Host": &host,
"Subscriptions": subs,
})
}
}
11 changes: 11 additions & 0 deletions web/models/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,15 @@ type Host struct {
CloudData interface{}
}

type AzureCloudData struct {
VMName string `json:"vmname"`
ResourceGroup string `json:"resourceGroup"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha! camelCase vs snake_case strikes again :trollface:

Unfortunately we are not being consistent in the way we want json to look like.
And I am part of the problem, too 😞, for instance in #546 I have used snake_case

We did it differently throughout the code and I think that at a certain point we should sit, make an agreement on the style, and then commit to refactor all the codebase accordingly, which might not be trivial.
For reference: we have also an open issue for this #385

I don't think it is the right time to tackle such a refactor, though we can find an agreement for the immediate future.

I'd like to keep #546 consistent with this PR and since I don't have strong preferences, I can fix it to make camelCase compliant. Would that be ok?

Then we can have a wider discussion and consider refactoring as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nelsonkopliku @dottorblaster in the entities/models i personally have used snake case everywhere, maybe we can keep this consistent at least for this layer and think about a clean-up later. wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, whatever it works better. Anyway we'd need to find an agreement on such things. Just not now 😄

Copy link
Member

@fabriziosestito fabriziosestito Dec 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like better the camel case honestly, we can refactor all at once laterz

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit late but ok for me.

Location string `json:"location"`
VMSize string `json:"vmsize"`
DataDisksNumber int `json:"dataDisksNumber"`
Offer string `json:"offer"`
SKU string `json:"sku"`
AdminUsername string `json:"adminUsername"`
}

type HostList []*Host
32 changes: 32 additions & 0 deletions web/services/hosts.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package services

import (
"encoding/json"
"errors"
"time"

Expand All @@ -21,6 +22,7 @@ type HostsService interface {
GetAll(*HostsFilter, *Page) (models.HostList, error)
GetByID(string) (*models.Host, error)
GetAllBySAPSystemID(string) (models.HostList, error)
GetByName(string) (*models.Host, error)
GetCount() (int, error)
GetAllSIDs() ([]string, error)
GetAllTags() ([]string, error)
Expand Down Expand Up @@ -133,6 +135,36 @@ func (s *hostsService) GetAllBySAPSystemID(id string) (models.HostList, error) {
return hostList, nil
}

func (s *hostsService) GetByName(name string) (*models.Host, error) {
var host entities.Host
fabriziosestito marked this conversation as resolved.
Show resolved Hide resolved
err := s.db.
Model(entities.Host{}).
Preload("Heartbeat").
Preload("SAPSystemInstances").
Where("name = ?", name).
First(&host).
Error

if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}

hostHealth := computeHealth(&host)
modeledHost := host.ToModel()
modeledHost.Health = hostHealth

if modeledHost.CloudProvider == "azure" {
var cloudData models.AzureCloudData
json.Unmarshal(host.CloudData, &cloudData)
modeledHost.CloudData = cloudData
}

return modeledHost, nil
}

func (s *hostsService) GetCount() (int, error) {
var count int64
err := s.db.Model(&entities.Host{}).Count(&count).Error
Expand Down
39 changes: 13 additions & 26 deletions web/templates/blocks/sap_instance.html.tmpl
Original file line number Diff line number Diff line change
@@ -1,40 +1,27 @@
{{ define "sap_instance" }}
<dl class="inline">
<dt class="inline">Name</dt>
<dd class="inline">{{ (index .Properties "INSTANCE_NAME").Value }}</dd>
<dt class="inline">SID</dt>
<dd class="inline">{{ (index .Properties "SAPSYSTEMNAME").Value }}</dd>
<dt class="inline">Instance number</dt>
<dd class="inline">{{ (index .Properties "SAPSYSTEM").Value }}</dd>
</dl>
<div class='table-responsive'>
<table class='table eos-table'>
<thead>
<tr>
<th scope='col'>Process name</th>
<th scope='col'>Description</th>
<th scope='col'>Start time</th>
<th scope='col'>Elapsed time</th>
<th scope='col'>Pid</th>
<th scope='col'>Status</th>
<th scope='col'>ID</th>
<th scope='col'>SID</th>
<th scope='col'>Type</th>
<th scope='col'>Features</th>
<th scope='col'>InstanceNumber</th>
</tr>
</thead>
<tbody>
{{- range .Processes }}
{{- $SAPSystem := . }}
{{- range .Instances }}
<tr>
<td>{{ .Name }}</td>
<td>{{ .Description }}</td>
<td>{{ .Starttime }}</td>
<td>{{ .Elapsedtime }}</td>
<td>{{ .Pid }}</td>
<td>
<span class='badge badge-pill badge-{{ if eq .Dispstatus "SAPControl-GREEN" }}primary{{ else if eq .Dispstatus "SAPControl-YELLOW" }}warning{{ else if eq .Dispstatus "SAPControl-RED" }}danger{{ else if eq .Dispstatus "SAPControl-GRAY" }}secondary{{ end }}'>
{{ .Textstatus }}
</span>
</td>
<td>{{ $SAPSystem.ID }}</td>
<td>{{ $SAPSystem.SID }}</td>
<td>{{ $SAPSystem.Type }}</td>
<td>{{ .Features }}</td>
<td>{{ .InstanceNumber }}</td>
</tr>
{{- else }}
{{ template "empty_table_body" 6}}
{{ template "empty_table_body" 5}}
{{- end }}
</tbody>
</table>
Expand Down
171 changes: 103 additions & 68 deletions web/templates/host.html.tmpl
Original file line number Diff line number Diff line change
@@ -1,49 +1,88 @@
{{ define "content" }}
<div class="col">
<h6><a href="/hosts">Hosts</a> > {{ .Host.Name }}</h6>
<h1>Host details</h1>
<dl class="inline">
<dt class="inline">Name</dt>
<dd class="inline">{{ .Host.Name }}</dd>
<dt class="inline">SAP System</dt>
<dd class="inline">
{{- $Host := .Host }}
{{- range $i, $v := split (index $Host.TrentoMeta "trento-sap-systems") "," }}{{- if $i }},{{- end }}
<a href="/sapsystems/{{ index (split (index $Host.TrentoMeta "trento-sap-systems-id") ",") $i }}">{{ $v }}</a>{{- end }}
</dd>
<h6><a href="/hosts">Hosts</a> > {{ .Host.Name }}</h6>

<dt class="inline">Cluster</dt>
<dd class="inline"><a
href='/clusters/{{ index .Host.TrentoMeta "trento-ha-cluster-id" }}'>{{ index .Host.TrentoMeta "trento-ha-cluster" }}</a>
</dd>
<dt class="inline">Agent version</dt>
<dd class="inline">
{{ .Host.GetAgentVersionString }}
</dd>
</dl>
<hr/>
{{- if eq .CloudData.Provider "azure" }}
<div class="border-top mb-4">
<div class="row">
<div class="col-sm-12">
<div class="row mt-5 mb-5">
<div class="col-3">
<strong>Name:</strong><br>
<span class="text-muted">{{ .Host.Name }}</span>
</div>
<div class="col-3">
<strong>SAP Systems:</strong><br>
<span class="text-muted">
{{ range .Host.SAPSystems }}
<a href="/sapsystems/{{ .ID }}">{{ .SID }}</a>
{{ end }}
</span>
</div>
<div class="col-3">
<strong>Cluster:</strong><br>
<span class="text-muted">
<a href="/clusters/{{ .Host.ClusterID }}">{{ .Host.ClusterName }}</a>
</span>
</div>
<div class="col-3">
<strong>Agent version:</strong><br>
<span class="text-muted">{{ .Host.AgentVersion }}</span>
</div>
</div>
</div>
</div>
</div>

{{- if eq .Host.CloudProvider "azure" }}
<h1>Cloud details</h1>
{{- $Compute := .CloudData.Metadata.Compute }}
<dl class="inline">
<dt class="inline">VM Name</dt>
<dd class="inline"><a target="_blank" href="{{ .CloudData.Metadata.GetVmUrl }}"
target="_blank">{{ $Compute.Name }}</a></dd>
<dt class="inline">Resource group</dt>
<dd class="inline"><a href="{{ .CloudData.Metadata.GetResourceGroupUrl }}"
target="_blank">{{ $Compute.ResourceGroupName }}</a></dd>
<dt class="inline">Location</dt>
<dd class="inline">{{ $Compute.Location }}</dd>
<dt class="inline">VM Size</dt>
<dd class="inline">{{ $Compute.VmSize }}</dd>
<dt class="inline">Data disks number</dt>
<dd class="inline">{{ len $Compute.StorageProfile.DataDisks }}</dd>
<dt class="inline">Offer</dt>
<dd class="inline">{{ $Compute.Offer }}</dd>
<dt class="inline">SKU</dt>
<dd class="inline">{{ $Compute.Sku }}</dd>
</dl>
<hr/>
{{- $CloudData := .Host.CloudData }}
<div class="mb-4">
<div class="row">
<div class="col-sm-12">
<div class="row mt-5 mb-5">
<div class="col-3">
<strong>VM Name:</strong><br>
<span class="text-muted">{{ $CloudData.VMName }}</span>
</div>
<div class="col-3">
<strong>Resource group:</strong><br>
<span class="text-muted">
{{ $CloudData.ResourceGroup }}
</span>
</div>
<div class="col-3">
<strong>Location:</strong><br>
<span class="text-muted">
{{ $CloudData.Location }}
</span>
</div>
<div class="col-3">
<strong>VM Size:</strong><br>
<span class="text-muted">{{ $CloudData.VMSize }}</span>
</div>
</div>
<div class="row mt-5 mb-5">
<div class="col-3">
<strong>Data disks number:</strong><br>
<span class="text-muted">
{{ $CloudData.DataDisksNumber }}
</span>
</div>
<div class="col-3">
<strong>Offer:</strong><br>
<span class="text-muted">
{{ $CloudData.Offer }}
</span>
</div>
<div class="col-3">
<strong>SKU:</strong><br>
<span class="text-muted">{{ $CloudData.SKU }}</span>
</div>
</div>
</div>
</div>
</div>
{{- end }}
<h1>SUSE subscription details</h1>
<div class='table-responsive'>
Expand Down Expand Up @@ -79,39 +118,35 @@
</table>
</div>
<hr/>
{{- if ne (len .SAPSystems) 0 }}
{{- if ne (len .Host.SAPSystems) 0 }}
<p class='clearfix'></p>
<h2>SAP instances</h2>
{{- range $Key, $SAPSystem := .SAPSystems }}
{{- range $Key, $SAPInstance := $SAPSystem.Instances }}
{{ template "sap_instance" $SAPInstance.SAPControl }}
{{- end }}
{{- range $Key, $SAPSystem := .Host.SAPSystems }}
{{ template "sap_instance" $SAPSystem }}
{{- end }}
<hr/>
{{- end }}
<p class='clearfix'></p>
<h2>Trento Agent status</h2>
{{- if .TrentoAgentCheck }}
<div class='table-responsive'>
<table class='table eos-table'>
<thead>
<tr>
<th scope='col'>Status</th>
<th scope='col'>Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span class='badge badge-pill badge-{{ if eq .TrentoAgentCheck.Status "passing" }}primary{{ else if eq .TrentoAgentCheck.Status "warning" }}warning{{ else }}danger{{ end }}'>{{ .TrentoAgentCheck.Status }}</span>
</td>
<td class='show-white-space'>{{ .TrentoAgentCheck.Output }}</td>
</tr>
</tbody>
</table>
</div>
{{- else }}
Agent not running
{{- end }}
<div class='table-responsive'>
<table class='table eos-table'>
<thead>
<tr>
<th scope='col'>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>
{{ if eq .Host.Health "passing" }}
<span class='badge badge-pill badge-primary'>running</span>
{{ else }}
<span class='badge badge-pill badge-danger'>not running</span>
{{ end }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
{{ end }}