Show AC app data visualization in new metrics tab 97/697/10
authorSarkar, Anand (as0481) <as0481@att.com>
Tue, 13 Aug 2019 14:15:30 +0000 (10:15 -0400)
committerLott, Christopher (cl778h) <cl778h@att.com>
Tue, 20 Aug 2019 21:52:32 +0000 (17:52 -0400)
Signed-off-by: Sarkar, Anand (as0481) <as0481@att.com>
Issue-Id: RICPLT-958
Change-Id: Ib06da274321bcb8fd28918314ca275f6b4c6a048
Signed-off-by: Lott, Christopher (cl778h) <cl778h@att.com>
docs/config-deploy.rst
docs/release-notes.rst
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java
webapp-backend/src/main/resources/application.properties
webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AdminControllerTest.java
webapp-frontend/src/app/services/stats/stats.service.ts
webapp-frontend/src/app/stats/stats.component.html
webapp-frontend/src/app/stats/stats.component.scss
webapp-frontend/src/app/stats/stats.component.ts

index d89aae9..e11c1e4 100644 (file)
@@ -113,6 +113,10 @@ Application user name expected at ONAP portal. No default value.
 
 Port where the Tomcat server listens for requests. Default is ``8080``
 
+``metrics.url.ac``
+
+Url to the kibana source which visualizes AC App metrics. No default value and needs to be replaced with actual value during deployment time.
+
 ``userfile``
 
 Path of file that stores user details. Default is ``users.json``
index 19c9101..1eb16cb 100644 (file)
@@ -43,6 +43,7 @@ Version 1.2.0, 20 Aug 2019
 * Add xapp dynamic configuration feature
 * Disable x-frame-options response header
 * Repair app manager undeploy-app method
+* Display AC xAPP metrics data via Kibana source (metrics.url.ac) on dashboard
 
 Version 1.0.5, 5 July 2019
 --------------------------
index bb093cd..1ceb0cb 100644 (file)
@@ -26,11 +26,10 @@ public abstract class DashboardConstants {
        }
 
        public static final String ENDPOINT_PREFIX = "/api";
-
        public static final String LOGIN_PAGE = "/login.html";
-
        // Factor out method names used in multiple controllers
        public static final String VERSION_METHOD = "version";
+       public static final String APP_NAME_AC = "AC";
 
        // The role names are defined by ONAP Portal.
        // The prefix "ROLE_" is required by Spring.
index 262f999..8313d73 100644 (file)
@@ -21,9 +21,13 @@ package org.oransc.ric.portal.dashboard.controller;
 
 import java.lang.invoke.MethodHandles;
 
+import javax.servlet.http.HttpServletResponse;
+
 import org.oransc.ric.portal.dashboard.DashboardApplication;
 import org.oransc.ric.portal.dashboard.DashboardConstants;
 import org.oransc.ric.portal.dashboard.model.DashboardUser;
+import org.oransc.ric.portal.dashboard.model.ErrorTransport;
+import org.oransc.ric.portal.dashboard.model.IDashboardResponse;
 import org.oransc.ric.portal.dashboard.model.SuccessTransport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,6 +36,8 @@ import org.springframework.security.access.annotation.Secured;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.beans.factory.annotation.Value;
 
 import io.swagger.annotations.ApiOperation;
 
@@ -49,12 +55,16 @@ public class AdminController {
        public static final String USER_METHOD = "user";
        public static final String HEALTH_METHOD = "health";
        public static final String VERSION_METHOD = DashboardConstants.VERSION_METHOD;
+       public static final String XAPPMETRICS_METHOD = "metrics";
 
        private final DashboardUser[] users;
 
        private static final String ACTIVE = "Active";
        private static final String INACTIVE = "Inactive";
 
+       @Value("${metrics.url.ac}")
+       private String acAppMetricsUrl;
+
        public AdminController() {
                // Mock data
                users = new DashboardUser[] { //
@@ -92,4 +102,15 @@ public class AdminController {
                return users;
        }
 
+       @ApiOperation(value = "Gets the kibana metrics URL for the specified app.", response = SuccessTransport.class)
+       @GetMapping(XAPPMETRICS_METHOD)
+       public IDashboardResponse getAppMetricsUrl(@RequestParam String app, HttpServletResponse response) {
+               if (DashboardConstants.APP_NAME_AC.equals(app)) {
+                       logger.debug("getAppMetricsUrl: acAppMetricsUrl {}", acAppMetricsUrl);
+                       return new SuccessTransport(200, acAppMetricsUrl);
+               } else {
+                       response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                       return new ErrorTransport(400, "Client provided app name is invalid as: " + app);
+               }
+       }
 }
index 95a3573..d69d816 100644 (file)
@@ -126,8 +126,7 @@ public class PortalAuthenticationFilter implements Filter {
                        PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(userDetails,
                                        "fakeCredentials", userDetails.getAuthorities());
                        SecurityContextHolder.getContext().setAuthentication(authToken);
-               }
-               else {
+               } else {
                        logger.debug("doFilter: authorities {}", auth.getAuthorities());
                }
                chain.doFilter(req, res);
index 1b7bd1c..50da1cd 100644 (file)
@@ -54,3 +54,6 @@ e2mgr.url.suffix = /v1
 
 # Mimic slow endpoints by defining sleep period, in milliseconds
 mock.config.delay = 0
+
+#Representative URL to Kibana source displaying the AC App metrics
+metrics.url.ac = http://jar-app-props-kibana-url
index 018350d..8c61a4e 100644 (file)
@@ -21,11 +21,16 @@ package org.oransc.ric.portal.dashboard.controller;
 
 import java.lang.invoke.MethodHandles;
 import java.net.URI;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.oransc.ric.portal.dashboard.DashboardConstants;
 import org.oransc.ric.portal.dashboard.model.DashboardUser;
+import org.oransc.ric.portal.dashboard.model.ErrorTransport;
+import org.oransc.ric.portal.dashboard.model.IDashboardResponse;
 import org.oransc.ric.portal.dashboard.model.SuccessTransport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -72,4 +77,29 @@ public class AdminControllerTest extends AbstractControllerTest {
                Assertions.assertTrue(response.getStatusCode().is4xxClientError());
        }
 
+       @Test
+       public void getxAppMetricsUrlTest() {
+               Map<String, String> metricsQueryParms = new HashMap<String, String>();
+               metricsQueryParms.put("app", DashboardConstants.APP_NAME_AC);
+               URI uri = buildUri(metricsQueryParms, AdminController.CONTROLLER_PATH, AdminController.XAPPMETRICS_METHOD);
+               logger.debug("Invoking {}", uri);
+               ResponseEntity<SuccessTransport> successResponse = testRestTemplateStandardRole().exchange(uri, HttpMethod.GET, null,
+                               SuccessTransport.class);
+               Assertions.assertFalse(successResponse.getBody().getData().toString().isEmpty());
+               Assertions.assertTrue(successResponse.getStatusCode().is2xxSuccessful());
+       }
+
+       @Test
+       public void getxAppMetricsUrlTestFail() {
+               Map<String, String> metricsQueryParms = new HashMap<String, String>(); 
+               //Providing a bogus value for application name in query parameter to test failure 
+               metricsQueryParms.put("app", "ABCD");
+               URI uri = buildUri(metricsQueryParms, AdminController.CONTROLLER_PATH, AdminController.XAPPMETRICS_METHOD);
+               logger.debug("Invoking {}", uri);
+               ResponseEntity<ErrorTransport> errorResponse = testRestTemplateStandardRole().exchange(uri, HttpMethod.GET, null,
+                               ErrorTransport.class);
+               logger.debug("{}", errorResponse.getBody().getError().toString());
+               Assertions.assertTrue(errorResponse.getStatusCode().is4xxClientError());
+       }
+
 }
index e4e667a..b611732 100644 (file)
@@ -18,7 +18,7 @@
  * ========================LICENSE_END===================================
  */
 import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpParams } from '@angular/common/http';
 import { HttpHeaders } from '@angular/common/http';
 import { Observable } from 'rxjs';
 import { HttpErrorResponse } from '@angular/common/http';
@@ -50,6 +50,8 @@ export class StatsService {
             })
           };
 
+    private basePath = 'api/admin/';
+
     constructor(private httpClient: HttpClient) {
         // this.loadConfig();
         // this.getLoad();
@@ -103,6 +105,14 @@ export class StatsService {
         return Math.round((Math.random() * (20 - 0)) + 0);
     }
 
+    // Gets xApp metrics kibana url for the named application
+    getAppMetricsUrl(appName: string)  {
+        return this.httpClient.get(this.basePath + 'metrics', {
+            params: new HttpParams()
+                .set('app', appName)
+        });
+    }
+
     saveConfig(key: string, value: string) {
         if (key === 'jsonURL') {
             this.baseJSONServerUrl = value;
index 4b3d2ef..2245829 100644 (file)
@@ -7,9 +7,9 @@
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
-  
+
        http://www.apache.org/licenses/LICENSE-2.0
-  
+
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -27,8 +27,7 @@
                </mat-card-header>
                <mat-card-content>
                        <canvas baseChart [datasets]="cpuChartData" [labels]="cpuChartLabels" [options]="cpuChartOptions"
-                               [colors]="cpuChartColors" [legend]="cpuChartLegend" [chartType]="cpuChartType"
-                               (click)="loadClickData()">
+                               [colors]="cpuChartColors" [legend]="cpuChartLegend" [chartType]="cpuChartType" (click)="loadClickData()">
                        </canvas>
                </mat-card-content>
        </mat-card>
@@ -52,8 +51,8 @@
                                                </mat-card-header>
                                                <mat-card-content>
                                                        <canvas baseChart [datasets]="latencyChartData" [labels]="latencyChartLabels"
-                                                               [options]="latencyChartOptions" [colors]="latencyChartColors"
-                                                               [legend]="latencyChartLegend" [chartType]="latencyChartType" (click)="loadClickData()">
+                                                               [options]="latencyChartOptions" [colors]="latencyChartColors" [legend]="latencyChartLegend"
+                                                               [chartType]="latencyChartType" (click)="loadClickData()">
                                                        </canvas>
                                                </mat-card-content>
                                        </mat-card>
@@ -64,9 +63,9 @@
                                                        <mat-card-title>Pendulum Control Load</mat-card-title>
                                                </mat-card-header>
                                                <mat-card-content>
-                                                       <canvas baseChart [datasets]="loadChartData" [labels]="loadChartLabels"
-                                                               [options]="loadChartOptions" [colors]="loadChartColors" [legend]="loadChartLegend"
-                                                               [chartType]="loadChartType" (click)="loadClickData()">
+                                                       <canvas baseChart [datasets]="loadChartData" [labels]="loadChartLabels" [options]="loadChartOptions"
+                                                               [colors]="loadChartColors" [legend]="loadChartLegend" [chartType]="loadChartType"
+                                                               (click)="loadClickData()">
                                                        </canvas>
                                                </mat-card-content>
                                        </mat-card>
                                </mat-grid-tile>
                        </mat-grid-list>
                </mat-tab>
+               <mat-tab label="AC xApp">
+                       <mat-grid-list cols="3" rowHeight="3:2">
+                               <mat-grid-tile>
+                                       <mat-card>
+                                               <iframe [src]="metricsUrlAc"></iframe>
+                                       </mat-card>
+                               </mat-grid-tile>
+                       </mat-grid-list>
+               </mat-tab>
        </mat-tab-group>
 
 </div>
 
-<rd-app-config-event></rd-app-config-event>
+<rd-app-config-event></rd-app-config-event>
\ No newline at end of file
index 9ba1bad..809ab1c 100644 (file)
@@ -7,9 +7,9 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
 .mat-grid-tile {
   background: transparent;
+}
+
+iframe {
+  width: 500px;
+  height: 420px;
+  border: 1px solid black;
 }
\ No newline at end of file
index aa4cd4a..515d4ff 100644 (file)
@@ -22,6 +22,8 @@ import { BaseChartDirective } from 'ng2-charts/ng2-charts';
 import { StatsService } from '../services/stats/stats.service';
 import { HttpClient } from '@angular/common/http';
 import { map } from 'rxjs/operators';
+import { DashboardSuccessTransport } from '../interfaces/dashboard.types';
+import { DomSanitizer, SafeUrl, SafeResourceUrl } from '@angular/platform-browser';
 
 @Component({
     selector: 'rd-stats',
@@ -36,6 +38,7 @@ export class StatsComponent implements OnInit {
     checked = false;
     load;
     delay;
+    metricsUrlAc : SafeResourceUrl;
 
     public latencyChartColors: Array<any> = [
         { // blue
@@ -304,7 +307,7 @@ export class StatsComponent implements OnInit {
         return value;
     }
 
-    constructor(private service: StatsService, private httpClient: HttpClient) {
+    constructor(private service: StatsService, private httpClient: HttpClient, private sanitize: DomSanitizer) {
         this.sliderLoadMax = Number(this.service.loadMax) || 0;
         this.sliderDelayMax = Number(this.service.delayMax) || 0;
         // console.log('this.sliderLoadMax: ' + this.sliderLoadMax);
@@ -322,6 +325,9 @@ export class StatsComponent implements OnInit {
         this.fetchMetrics().subscribe(metricsv => {
             // console.log('metricsv.load: ' + metricsv['load']);
         });
+        this.service.getAppMetricsUrl('AC').subscribe((res:DashboardSuccessTransport) => {
+            this.metricsUrlAc = this.sanitize.bypassSecurityTrustResourceUrl(res.data);
+        });
     }
 
     startLoadTimer() {
@@ -347,34 +353,25 @@ export class StatsComponent implements OnInit {
 
     fetchMetrics() {
         return this.httpClient.get<any[]>(this.service.hostURL + this.service.metricsPath, this.service.httpOptions).pipe(map(res => {
-            // console.log(res);
-            // console.log(res['load']);
             return res;
         }));
     }
 
     fetchDelay() {
         return this.httpClient.get<any[]>(this.service.hostURL + this.service.delayPath, this.service.httpOptions).pipe(map(res => {
-            // console.log(res);
-            // console.log(res['delay']);
             const delayv = res['delay'];
-            // console.log(delayv);
             this.delay = delayv;
             return this.delay;
         }));
     }
 
     saveDelay() {
-        // console.log(this.delay);
         this.service.putDelay(this.delay);
     }
 
     fetchLoad() {
         return this.httpClient.get<any[]>(this.service.hostURL + this.service.loadPath, this.service.httpOptions).pipe(map(res => {
-            // console.log(res);
-            // console.log(res['load']);
             const loadv = res['load'];
-            // console.log(loadv);
             this.load = loadv;
             return this.load;
         }));
@@ -382,7 +379,6 @@ export class StatsComponent implements OnInit {
     }
 
     saveLoad() {
-        // console.log(this.load);
         this.service.putLoad(this.load);
     }