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``
* 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
--------------------------
}
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.
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;
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;
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[] { //
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);
+ }
+ }
}
PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(userDetails,
"fakeCredentials", userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
- }
- else {
+ } else {
logger.debug("doFilter: authorities {}", auth.getAuthorities());
}
chain.doFilter(req, res);
# 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
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;
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());
+ }
+
}
* ========================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';
})
};
+ private basePath = 'api/admin/';
+
constructor(private httpClient: HttpClient) {
// this.loadConfig();
// this.getLoad();
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;
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-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>
</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>
<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
* 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
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',
checked = false;
load;
delay;
+ metricsUrlAc : SafeResourceUrl;
public latencyChartColors: Array<any> = [
{ // blue
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);
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() {
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;
}));
}
saveLoad() {
- // console.log(this.load);
this.service.putLoad(this.load);
}