From 6b79976523018d41b3643b15deeb6706ad4daa6e Mon Sep 17 00:00:00 2001 From: "Sarkar, Anand (as0481)" Date: Tue, 13 Aug 2019 10:15:30 -0400 Subject: [PATCH] Show AC app data visualization in new metrics tab Signed-off-by: Sarkar, Anand (as0481) Issue-Id: RICPLT-958 Change-Id: Ib06da274321bcb8fd28918314ca275f6b4c6a048 Signed-off-by: Lott, Christopher (cl778h) --- docs/config-deploy.rst | 4 +++ docs/release-notes.rst | 1 + .../ric/portal/dashboard/DashboardConstants.java | 3 +-- .../dashboard/controller/AdminController.java | 21 +++++++++++++++ .../portalapi/PortalAuthenticationFilter.java | 3 +-- .../src/main/resources/application.properties | 3 +++ .../dashboard/controller/AdminControllerTest.java | 30 ++++++++++++++++++++++ .../src/app/services/stats/stats.service.ts | 12 ++++++++- webapp-frontend/src/app/stats/stats.component.html | 28 ++++++++++++-------- webapp-frontend/src/app/stats/stats.component.scss | 10 ++++++-- webapp-frontend/src/app/stats/stats.component.ts | 18 +++++-------- 11 files changed, 105 insertions(+), 28 deletions(-) diff --git a/docs/config-deploy.rst b/docs/config-deploy.rst index d89aae93..e11c1e40 100644 --- a/docs/config-deploy.rst +++ b/docs/config-deploy.rst @@ -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`` diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 19c91019..1eb16cb8 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -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 -------------------------- diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java index bb093cda..1ceb0cb3 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java @@ -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. diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java index 262f999f..8313d736 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AdminController.java @@ -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); + } + } } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java index 95a3573b..d69d816a 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java @@ -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); diff --git a/webapp-backend/src/main/resources/application.properties b/webapp-backend/src/main/resources/application.properties index 1b7bd1c6..50da1cdb 100644 --- a/webapp-backend/src/main/resources/application.properties +++ b/webapp-backend/src/main/resources/application.properties @@ -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 diff --git a/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AdminControllerTest.java b/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AdminControllerTest.java index 018350d6..8c61a4e4 100644 --- a/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AdminControllerTest.java +++ b/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AdminControllerTest.java @@ -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 metricsQueryParms = new HashMap(); + metricsQueryParms.put("app", DashboardConstants.APP_NAME_AC); + URI uri = buildUri(metricsQueryParms, AdminController.CONTROLLER_PATH, AdminController.XAPPMETRICS_METHOD); + logger.debug("Invoking {}", uri); + ResponseEntity 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 metricsQueryParms = new HashMap(); + //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 errorResponse = testRestTemplateStandardRole().exchange(uri, HttpMethod.GET, null, + ErrorTransport.class); + logger.debug("{}", errorResponse.getBody().getError().toString()); + Assertions.assertTrue(errorResponse.getStatusCode().is4xxClientError()); + } + } diff --git a/webapp-frontend/src/app/services/stats/stats.service.ts b/webapp-frontend/src/app/services/stats/stats.service.ts index e4e667a5..b6117326 100644 --- a/webapp-frontend/src/app/services/stats/stats.service.ts +++ b/webapp-frontend/src/app/services/stats/stats.service.ts @@ -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; diff --git a/webapp-frontend/src/app/stats/stats.component.html b/webapp-frontend/src/app/stats/stats.component.html index 4b3d2ef2..2245829d 100644 --- a/webapp-frontend/src/app/stats/stats.component.html +++ b/webapp-frontend/src/app/stats/stats.component.html @@ -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 @@ + [colors]="cpuChartColors" [legend]="cpuChartLegend" [chartType]="cpuChartType" (click)="loadClickData()"> @@ -52,8 +51,8 @@ + [options]="latencyChartOptions" [colors]="latencyChartColors" [legend]="latencyChartLegend" + [chartType]="latencyChartType" (click)="loadClickData()"> @@ -64,9 +63,9 @@ Pendulum Control Load - + @@ -84,8 +83,17 @@ + + + + + + + + + - + \ No newline at end of file diff --git a/webapp-frontend/src/app/stats/stats.component.scss b/webapp-frontend/src/app/stats/stats.component.scss index 9ba1bada..809ab1c2 100644 --- a/webapp-frontend/src/app/stats/stats.component.scss +++ b/webapp-frontend/src/app/stats/stats.component.scss @@ -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. @@ -42,4 +42,10 @@ .mat-grid-tile { background: transparent; +} + +iframe { + width: 500px; + height: 420px; + border: 1px solid black; } \ No newline at end of file diff --git a/webapp-frontend/src/app/stats/stats.component.ts b/webapp-frontend/src/app/stats/stats.component.ts index aa4cd4ab..515d4ff3 100644 --- a/webapp-frontend/src/app/stats/stats.component.ts +++ b/webapp-frontend/src/app/stats/stats.component.ts @@ -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 = [ { // 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(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(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(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); } -- 2.16.6