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

Add HANA replication state in the Databases list view #338

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
79 changes: 72 additions & 7 deletions web/sapsystems.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,27 @@ type SAPSystemRow struct {
}

type InstanceRow struct {
SID string
InstanceNumber string
Features string
Host string
ClusterName string
ClusterId string
Type int
SID string
InstanceNumber string
Features string
SystemReplication SystemReplication // Only for Database type
Host string
ClusterName string
ClusterId string
}

type SystemReplication map[string]interface{}

type SAPSystemsTable []*SAPSystemRow

var systemTypeToTag = map[int]string{
sapsystem.Application: models.TagSAPSystemResourceType,
sapsystem.Database: models.TagDatabaseResourceType,
}

func NewSAPSystemsTable(sapSystemsList sapsystem.SAPSystemsList, hostsService services.HostsService, sapSystemsService services.SAPSystemsService, tagsService services.TagsService) (SAPSystemsTable, error) {
func NewSAPSystemsTable(sapSystemsList sapsystem.SAPSystemsList, hostsService services.HostsService,
sapSystemsService services.SAPSystemsService, tagsService services.TagsService) (SAPSystemsTable, error) {
var sapSystemsTable SAPSystemsTable
sids := make(map[string]int)
rowsBySID := make(map[string]*SAPSystemRow)
Expand Down Expand Up @@ -108,6 +113,7 @@ func NewSAPSystemsTable(sapSystemsList sapsystem.SAPSystemsList, hostsService se
clusterId = metadata["trento-ha-cluster-id"]

instance := &InstanceRow{
Type: i.Type,
SID: sid,
InstanceNumber: instanceNumber,
Features: features,
Expand All @@ -116,6 +122,11 @@ func NewSAPSystemsTable(sapSystemsList sapsystem.SAPSystemsList, hostsService se
ClusterId: clusterId,
}

if i.Type == sapsystem.Database {
// Cast to local struct to manage the data in this package
instance.SystemReplication = SystemReplication(i.SystemReplication)
}

sapSystem.InstancesTable = append(sapSystem.InstancesTable, instance)
}

Expand Down Expand Up @@ -201,6 +212,60 @@ func (t SAPSystemsTable) GetAllTags() []string {
return tags
}

func (r InstanceRow) IsDatabase() bool {
return bool(r.Type == sapsystem.Database)
}

// Find mode information at: https://help.sap.com/viewer/4e9b18c116aa42fc84c7dbfd02111aba/2.0.04/en-US/aefc55a27003440792e34ece2125dc89.html
func (s SystemReplication) GetReplicationMode() string {
localSite, ok := s["local_site_id"]
if !ok {
return ""
}

var mode string

site, ok := s["site"]
if !ok {
return ""
}

for siteId, site := range site.(map[string]interface{}) {
if siteId == localSite {
mode = fmt.Sprintf("%v", site.(map[string]interface{})["REPLICATION_MODE"])
break
}
}

switch mode {
case "PRIMARY":
return "Primary"
case "":
return ""
default: // SYNC, SYNCMEM, ASYNC, UNKNOWN
return "Secondary"
}
}

// Find status information at: https://help.sap.com/viewer/4e9b18c116aa42fc84c7dbfd02111aba/2.0.04/en-US/aefc55a27003440792e34ece2125dc89.html
func (s SystemReplication) GetReplicationStatus() string {
status, ok := s["overall_replication_status"]
if !ok {
return ""
}

status = fmt.Sprintf("%v", status)

switch status {
case "ACTIVE":
return "SOK"
case "ERROR":
return "SFAIL"
default: // UNKNOWN, INITIALIZING, SYNCING
return ""
}
}

func NewSAPSystemListHandler(client consul.Client, hostsService services.HostsService,
sapSystemsService services.SAPSystemsService, tagsService services.TagsService) gin.HandlerFunc {
return func(c *gin.Context) {
Expand Down
150 changes: 146 additions & 4 deletions web/sapsystems_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ var sapDatabasesList = sapsystem.SAPSystemsList{
Instances: map[string]*sapsystem.SAPInstance{
"HDB00": &sapsystem.SAPInstance{
Host: "hana01",
Type: sapsystem.Database,
SAPControl: &sapsystem.SAPControl{
Properties: map[string]*sapcontrol.InstanceProperty{
"SAPSYSTEMNAME": &sapcontrol.InstanceProperty{
Expand All @@ -148,6 +149,15 @@ var sapDatabasesList = sapsystem.SAPSystemsList{
},
},
},
SystemReplication: sapsystem.SystemReplication{
"local_site_id": "1",
"site": map[string]interface{}{
"1": map[string]interface{}{
"REPLICATION_MODE": "PRIMARY",
},
},
"overall_replication_status": "ACTIVE",
},
},
},
},
Expand Down Expand Up @@ -215,9 +225,9 @@ func TestSAPSystemsListHandler(t *testing.T) {
assert.Equal(t, 200, resp.Code)
assert.Contains(t, responseBody, "SAP Systems")
assert.Regexp(t, regexp.MustCompile("<td><a href=/sapsystems/systemId1>HA1</a></td><td></td><td><a href=/databases/systemId2>PRD</a></td><td>PRD</td><td>192.168.1.5</td><td>.*<input.*value=tag1.*>.*</td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("<td>HA1</td><td>MESSAGESERVER|ENQUE</td><td>00</td><td><a href=/clusters/e2f2eb50aef748e586a7baa85e0162cf>netweaver_cluster</a></td><td><a href=/hosts/netweaver01>netweaver01</a></td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("<td>HA1</td><td>ENQREP</td><td>10</td><td><a href=/clusters/e2f2eb50aef748e586a7baa85e0162cf>netweaver_cluster</a></td><td><a href=/hosts/netweaver02>netweaver02</a></td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("<td>PRD</td><td>HDB_WORKER</td><td>00</td><td><a href=/clusters/5dfbd28f35cbfb38969f9b99243ae8d4>hana_cluster</a></td><td><a href=/hosts/hana01>hana01</a></td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("<td>HA1</td><td>MESSAGESERVER|ENQUE</td><td>00</td><td></td><td><a href=/clusters/e2f2eb50aef748e586a7baa85e0162cf>netweaver_cluster</a></td><td><a href=/hosts/netweaver01>netweaver01</a></td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("<td>HA1</td><td>ENQREP</td><td>10</td><td></td><td><a href=/clusters/e2f2eb50aef748e586a7baa85e0162cf>netweaver_cluster</a></td><td><a href=/hosts/netweaver02>netweaver02</a></td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("(?s)<td>PRD</td><td>HDB_WORKER</td><td>00</td><td>.*HANA Primary.*SOK.*</td><td><a href=/clusters/5dfbd28f35cbfb38969f9b99243ae8d4>hana_cluster</a></td><td><a href=/hosts/hana01>hana01</a></td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("<td><i .*This SAP system SID exists multiple times.*warning.*<a href=/sapsystems/systemId2>DEV</a></td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("<td><i .*This SAP system SID exists multiple times.*warning.*<a href=/sapsystems/systemId3>DEV</a></td>"), responseBody)
}
Expand Down Expand Up @@ -267,7 +277,139 @@ func TestSAPDatabaseListHandler(t *testing.T) {
assert.Equal(t, 200, resp.Code)
assert.Contains(t, responseBody, "HANA Databases")
assert.Regexp(t, regexp.MustCompile("<td><a href=/databases/systemId2>PRD</a></td><td></td><td>.*<input.*value=tag1.*>.*</td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("<td>PRD</td><td>HDB_WORKER</td><td>00</td><td><a href=/clusters/e2f2eb50aef748e586a7baa85e0162cf>hana_cluster</a></td><td><a href=/hosts/hana01>hana01</a></td>"), responseBody)
assert.Regexp(t, regexp.MustCompile("(?s)<td>PRD</td><td>HDB_WORKER</td><td>00</td>.*HANA Primary.*SOK.*<td><a href=/clusters/e2f2eb50aef748e586a7baa85e0162cf>hana_cluster</a></td><td><a href=/hosts/hana01>hana01</a></td>"), responseBody)
}

func TestIsDatabase(t *testing.T) {
r := InstanceRow{
Type: sapsystem.Database,
}
assert.Equal(t, true, r.IsDatabase())

r = InstanceRow{
Type: sapsystem.Application,
}
assert.Equal(t, false, r.IsDatabase())
}

func TestGetReplicationModePrimary(t *testing.T) {
srData := SystemReplication{
"local_site_id": "1",
"site": map[string]interface{}{
"1": map[string]interface{}{
"REPLICATION_MODE": "PRIMARY",
},
},
"overall_replication_status": "ACTIVE",
}

mode := srData.GetReplicationMode()
assert.Equal(t, "Primary", mode)
}

func TestGetReplicationModeSecondary(t *testing.T) {
srData := SystemReplication{
"local_site_id": "1",
"site": map[string]interface{}{
"1": map[string]interface{}{
"REPLICATION_MODE": "SYNC",
},
},
"overall_replication_status": "ACTIVE",
}

mode := srData.GetReplicationMode()
assert.Equal(t, "Secondary", mode)
}

func TestGetReplicationModeEmpty(t *testing.T) {
srData := SystemReplication{
"site": map[string]interface{}{
"1": map[string]interface{}{
"REPLICATION_MODE": "SYNC",
},
},
"overall_replication_status": "ACTIVE",
}

mode := srData.GetReplicationMode()
assert.Equal(t, "", mode)

srData = SystemReplication{
"local_site_id": "1",
"overall_replication_status": "ACTIVE",
}

mode = srData.GetReplicationMode()
assert.Equal(t, "", mode)

srData = SystemReplication{
"local_site_id": "1",
"site": map[string]interface{}{
"2": map[string]interface{}{
"REPLICATION_MODE": "SYNC",
},
},
"overall_replication_status": "ACTIVE",
}

mode = srData.GetReplicationMode()
assert.Equal(t, "", mode)
}

func TestGetReplicationStatus(t *testing.T) {
srData := SystemReplication{
"local_site_id": "1",
"site": map[string]interface{}{
"1": map[string]interface{}{
"REPLICATION_MODE": "PRIMARY",
},
},
"overall_replication_status": "ACTIVE",
}

mode := srData.GetReplicationStatus()
assert.Equal(t, "SOK", mode)

srData = SystemReplication{
"local_site_id": "1",
"site": map[string]interface{}{
"1": map[string]interface{}{
"REPLICATION_MODE": "PRIMARY",
},
},
"overall_replication_status": "ERROR",
}

mode = srData.GetReplicationStatus()
assert.Equal(t, "SFAIL", mode)
}

func TestGetReplicationEmpty(t *testing.T) {
srData := SystemReplication{
"local_site_id": "1",
"site": map[string]interface{}{
"1": map[string]interface{}{
"REPLICATION_MODE": "PRIMARY",
},
},
}

mode := srData.GetReplicationStatus()
assert.Equal(t, "", mode)

srData = SystemReplication{
"local_site_id": "1",
"site": map[string]interface{}{
"1": map[string]interface{}{
"REPLICATION_MODE": "PRIMARY",
},
},
"overall_replication_status": "Other",
}

mode = srData.GetReplicationStatus()
assert.Equal(t, "", mode)
}

func TestSAPResourceHandler(t *testing.T) {
Expand Down
16 changes: 16 additions & 0 deletions web/templates/blocks/sapsystems_table.html.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
<th scope='col'>SID</th>
<th scope='col'>Features</th>
<th scope='col'>Instance number</th>
<th scope='col'>System replication</th>
<th scope='col'>Cluster name</th>
<th scope='col'>Host</th>
</tr>
Expand All @@ -59,6 +60,21 @@
<td>{{ .SID }}</td>
<td>{{ .Features }}</td>
<td>{{ .InstanceNumber }}</td>
{{- if .IsDatabase }}
{{- $mode := .SystemReplication.GetReplicationMode }}
{{- if ne $mode "" }}
<td>HANA {{ $mode }}
{{- else }}
<td></td>
{{- end }}
{{- if eq $mode "Primary" }}
{{- $status := .SystemReplication.GetReplicationStatus }}
<span class="badge badge-pill badge-{{ if eq $status "SOK" }}success{{ else }}danger{{ end }}">{{ $status }}</span>
{{- end }}
</td>
{{- else }}
<td></td>
{{- end }}
<td><a href="/clusters/{{ .ClusterId }}">{{ .ClusterName }}</a></td>
<td><a href="/hosts/{{ .Host }}">{{ .Host }}</a></td>
<tr>
Expand Down