From 81c5a43871449332f9a9560c7cf25d07cf714d8e Mon Sep 17 00:00:00 2001 From: "Lott, Christopher (cl778h)" Date: Mon, 17 Jun 2019 11:57:23 -0400 Subject: [PATCH] Pass thru error details from remote APIs Tweak RAN connection dialog behavior Change-Id: I597aa55e9a769e83d0a538a822e339cf8816bf62 Signed-off-by: Lott, Christopher (cl778h) --- docs/release-notes.rst | 3 +- .../dashboard/config/A1MediatorConfiguration.java | 3 +- .../config/A1MediatorMockConfiguration.java | 1 + .../dashboard/config/AnrXappConfiguration.java | 4 +- .../dashboard/config/AnrXappMockConfiguration.java | 4 +- .../dashboard/config/E2ManagerConfiguration.java | 8 ++- .../config/E2ManagerMockConfiguration.java | 6 +- .../dashboard/config/XappManagerConfiguration.java | 8 ++- .../config/XappManagerMockConfiguration.java | 6 +- .../dashboard/controller/AcXappController.java | 23 ++++-- .../dashboard/controller/AnrXappController.java | 84 ++++++++++++++++------ .../dashboard/controller/E2ManagerController.java | 81 +++++++++++++-------- .../controller/XappManagerController.java | 79 ++++++++++++-------- .../src/main/resources/application.properties | 8 +-- webapp-backend/src/main/resources/logback.xml | 3 + .../ran-connection-dialog.component.ts | 19 +++-- 16 files changed, 232 insertions(+), 108 deletions(-) diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 033bdf79..458a4ec4 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -20,7 +20,7 @@ RIC Dashboard Release Notes =========================== -Version 1.0.4, 13 June 2019 +Version 1.0.4, 17 June 2019 --------------------------- * Add AC xApp neighbor control screen * Add ANR xApp neighbor cell relation table @@ -32,6 +32,7 @@ Version 1.0.4, 13 June 2019 * Adjust CSS and HTML for main container positioning * Revise config property keys to use URL (not basepath) * Left menu overlap main content fix +* Extend back-end controllers to return error details Version 1.0.3, 28 May 2019 -------------------------- diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorConfiguration.java index 2ed6b9ac..240522b0 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorConfiguration.java @@ -49,7 +49,7 @@ public class A1MediatorConfiguration { @Autowired public A1MediatorConfiguration(@Value("${a1med.url}") final String url) throws MalformedURLException { - logger.info("Configuring A1 Mediator at URL {}", url); + logger.info("Configuring A1 Mediator at base URL {}", url); new URL(url); this.a1medUrl = url; } @@ -61,6 +61,7 @@ public class A1MediatorConfiguration { } @Bean + // The bean (method) name must be globally unique public A1MediatorApi a1MediatorApi() { return new A1MediatorApi(apiClient()); } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorMockConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorMockConfiguration.java index 485597a8..672e3043 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorMockConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1MediatorMockConfiguration.java @@ -55,6 +55,7 @@ public class A1MediatorMockConfiguration { } @Bean + // Use the same name as regular configuration public A1MediatorApi a1MediatorApi() { ApiClient apiClient = apiClient(); A1MediatorApi mockApi = mock(A1MediatorApi.class); diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappConfiguration.java index 5f2ae9bb..4573ff0f 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappConfiguration.java @@ -49,7 +49,7 @@ public class AnrXappConfiguration { @Autowired public AnrXappConfiguration(@Value("${anrxapp.url}") final String url) throws MalformedURLException { - logger.info("Configuring ANR client at URL {}", url); + logger.info("Configuring ANR client at base URL {}", url); new URL(url); this.anrXappUrl = url; } @@ -61,11 +61,13 @@ public class AnrXappConfiguration { } @Bean + // The bean (method) name must be globally unique public HealthApi anrHealthApi() { return new HealthApi(apiClient()); } @Bean + // The bean (method) name must be globally unique public NcrtApi anrNcrtApi() { return new NcrtApi(apiClient()); } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappMockConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappMockConfiguration.java index 4d80d4f3..340036aa 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappMockConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappMockConfiguration.java @@ -97,6 +97,7 @@ public class AnrXappMockConfiguration { } @Bean + // Use the same name as regular configuration public HealthApi anrHealthApi() { ApiClient apiClient = apiClient(); HealthApi mockApi = mock(HealthApi.class); @@ -111,7 +112,8 @@ public class AnrXappMockConfiguration { } @Bean - public NcrtApi ncrtMockApi() { + // Use the same name as regular configuration + public NcrtApi anrNcrtApi() { ApiClient apiClient = apiClient(); NcrtApi mockApi = mock(NcrtApi.class); when(mockApi.getApiClient()).thenReturn(apiClient); diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerConfiguration.java index 92bef319..6e791e3c 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerConfiguration.java @@ -49,7 +49,7 @@ public class E2ManagerConfiguration { @Autowired public E2ManagerConfiguration(@Value("${e2mgr.url}") final String url) throws MalformedURLException { - logger.info("Configuring E2 Manager at base path {}", url); + logger.info("Configuring E2 Manager at base URL {}", url); new URL(url); this.e2mgrUrl = url; } @@ -61,12 +61,14 @@ public class E2ManagerConfiguration { } @Bean - public HealthCheckApi e2HealthCheckApi() { + // The bean (method) name must be globally unique + public HealthCheckApi e2MgrHealthCheckApi() { return new HealthCheckApi(apiClient()); } @Bean - public NodebApi e2NodebApi() { + // The bean (method) name must be globally unique + public NodebApi e2MgrNodebApi() { return new NodebApi(apiClient()); } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerMockConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerMockConfiguration.java index 04bb8fce..334915c8 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerMockConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/E2ManagerMockConfiguration.java @@ -62,7 +62,8 @@ public class E2ManagerMockConfiguration { } @Bean - public HealthCheckApi e2HealthCheckApi() { + // Use the same name as regular configuration + public HealthCheckApi e2MgrHealthCheckApi() { ApiClient apiClient = apiClient(); HealthCheckApi mockApi = mock(HealthCheckApi.class); when(mockApi.getApiClient()).thenReturn(apiClient); @@ -75,7 +76,8 @@ public class E2ManagerMockConfiguration { } @Bean - public NodebApi e2NodebApi() { + // Use the same name as regular configuration + public NodebApi e2MgrNodebApi() { ApiClient apiClient = apiClient(); NodebApi mockApi = mock(NodebApi.class); when(mockApi.getApiClient()).thenReturn(apiClient); diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappManagerConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappManagerConfiguration.java index 7ecd95cf..1b699de6 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappManagerConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappManagerConfiguration.java @@ -50,7 +50,7 @@ public class XappManagerConfiguration { @Autowired public XappManagerConfiguration(@Value("${xappmgr.url}") final String url) throws MalformedURLException { - logger.info("Configuring xApp Manager at base path {}", url); + logger.info("Configuring xApp Manager at base URL {}", url); new URL(url); this.xappMgrUrl = url; } @@ -65,7 +65,8 @@ public class XappManagerConfiguration { * @return A HealthApi with an ApiClient configured from properties */ @Bean - public HealthApi xappHealthApi() { + // The bean (method) name must be globally unique + public HealthApi xappMgrHealthApi() { return new HealthApi(apiClient()); } @@ -73,7 +74,8 @@ public class XappManagerConfiguration { * @return An XappApi with an ApiClient configured from properties */ @Bean - public XappApi xappMgrApi() { + // The bean (method) name must be globally unique + public XappApi xappMgrXappApi() { return new XappApi(apiClient()); } } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappManagerMockConfiguration.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappManagerMockConfiguration.java index ed4f49cc..b944c959 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappManagerMockConfiguration.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappManagerMockConfiguration.java @@ -73,7 +73,8 @@ public class XappManagerMockConfiguration { } @Bean - public HealthApi xappHealthMockApi() { + // Use the same name as regular configuration + public HealthApi xappMgrHealthApi() { ApiClient mockClient = mock(ApiClient.class); when(mockClient.getStatusCode()).thenReturn(HttpStatus.OK); HealthApi mockApi = mock(HealthApi.class); @@ -88,7 +89,8 @@ public class XappManagerMockConfiguration { } @Bean - public XappApi xappMgrMockApi() { + // Use the same name as regular configuration + public XappApi xappMgrXappApi() { ApiClient mockClient = mock(ApiClient.class); when(mockClient.getStatusCode()).thenReturn(HttpStatus.OK); diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AcXappController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AcXappController.java index 37826564..279d504a 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AcXappController.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AcXappController.java @@ -31,11 +31,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpStatusCodeException; import com.fasterxml.jackson.databind.JsonNode; @@ -43,8 +45,10 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; /** - * Provides methods to manage policies of the Admission Control xApp, which - * initially defines just one. All requests go via the A1 Mediator. + * * Proxies calls from the front end to the AC xApp via the A1 Mediator API. + * All methods answer 502 on failure:
HTTP server received an + * invalid response from a server it consulted when acting as a proxy or + * gateway.
*/ @RestController @RequestMapping(value = DashboardConstants.ENDPOINT_PREFIX + "/xapp/ac", produces = MediaType.APPLICATION_JSON_VALUE) @@ -62,6 +66,8 @@ public class AcXappController { public AcXappController(final A1MediatorApi a1MediatorApi) { Assert.notNull(a1MediatorApi, "API must not be null"); this.a1MediatorApi = a1MediatorApi; + if (logger.isDebugEnabled()) + logger.debug("ctor: configured with client type {}", a1MediatorApi.getClass().getName()); } @ApiOperation(value = "Gets the A1 client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class) @@ -87,11 +93,18 @@ public class AcXappController { */ @ApiOperation(value = "Sets the admission control policy for AC xApp via the A1 Mediator") @RequestMapping(value = "catime", method = RequestMethod.PUT) - public void setAdmissionControlPolicy(@ApiParam(value = "Admission control policy") @RequestBody JsonNode acPolicy, // + public Object setAdmissionControlPolicy( + @ApiParam(value = "Admission control policy") @RequestBody JsonNode acPolicy, // HttpServletResponse response) { logger.debug("setAdmissionControlPolicy {}", acPolicy); - a1MediatorApi.a1ControllerPutHandler(AC_CONTROL_NAME, acPolicy); - response.setStatus(a1MediatorApi.getApiClient().getStatusCode().value()); + try { + a1MediatorApi.a1ControllerPutHandler(AC_CONTROL_NAME, acPolicy); + response.setStatus(a1MediatorApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("setAdmissionControlPolicy failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AnrXappController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AnrXappController.java index dae8ae25..9faeff72 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AnrXappController.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AnrXappController.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -43,6 +44,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpStatusCodeException; import io.swagger.annotations.ApiOperation; @@ -70,11 +72,14 @@ public class AnrXappController { private final NcrtApi ncrtApi; @Autowired - public AnrXappController(final HealthApi healthApi, final NcrtApi ncrtApi) { - Assert.notNull(healthApi, "API must not be null"); - Assert.notNull(ncrtApi, "API must not be null"); - this.healthApi = healthApi; - this.ncrtApi = ncrtApi; + public AnrXappController(final HealthApi anrHealthApi, final NcrtApi anrNcrtApi) { + Assert.notNull(anrHealthApi, "API must not be null"); + Assert.notNull(anrNcrtApi, "API must not be null"); + this.healthApi = anrHealthApi; + this.ncrtApi = anrNcrtApi; + if (logger.isDebugEnabled()) + logger.debug("ctor: configured with client types {} and {}", anrHealthApi.getClass().getName(), + anrNcrtApi.getClass().getName()); } @ApiOperation(value = "Gets the ANR client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class) @@ -85,57 +90,94 @@ public class AnrXappController { @ApiOperation(value = "Performs a liveness probe on the ANR xApp, result expressed as the response code.") @RequestMapping(value = "/health/alive", method = RequestMethod.GET) - public void getAnrXappHealthAlive(HttpServletResponse response) { - healthApi.getHealthAlive(); - response.setStatus(healthApi.getApiClient().getStatusCode().value()); + public Object getHealthAlive(HttpServletResponse response) { + logger.debug("getHealthAlive"); + try { + healthApi.getHealthAlive(); + response.setStatus(healthApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("getHealthAlive failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } @ApiOperation(value = "Performs a readiness probe on the ANR xApp, result expressed as the response code.") @RequestMapping(value = "/health/ready", method = RequestMethod.GET) - public void getAnrXappHealthReady(HttpServletResponse response) { - healthApi.getHealthReady(); - response.setStatus(healthApi.getApiClient().getStatusCode().value()); + public Object getHealthReady(HttpServletResponse response) { + logger.debug("getHealthReady"); + try { + healthApi.getHealthReady(); + response.setStatus(healthApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("getHealthAlive failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } @ApiOperation(value = "Returns list of gNodeB IDs based on NCRT in ANR", response = GgNodeBTable.class) @RequestMapping(value = "/gnodebs", method = RequestMethod.GET) - public GgNodeBTable getGnodebs() { - return ncrtApi.getgNodeB(); + public Object getGnodebs() { + logger.debug("getGnodebs"); + try { + return ncrtApi.getgNodeB(); + } catch (HttpStatusCodeException ex) { + logger.warn("getGnodebs failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } @ApiOperation(value = "Returns neighbor cell relation table for all gNodeBs or based on query parameters", response = NeighborCellRelationTable.class) @RequestMapping(value = "/ncrt", method = RequestMethod.GET) - public NeighborCellRelationTable getNcrt( // + public Object getNcrt( // @RequestParam(name = QP_NODEB, required = false) String ggnbId, // @RequestParam(name = QP_SERVING, required = false) String servingCellNrcgi, // @RequestParam(name = QP_NEIGHBOR, required = false) String neighborCellNrpci) { logger.debug("getNcrt: ggnbid {}, servingCellNrpci {}, neighborCellNrcgi {}", ggnbId, servingCellNrcgi, neighborCellNrpci); - return ncrtApi.getNcrt(ggnbId, servingCellNrcgi, neighborCellNrpci); + try { + return ncrtApi.getNcrt(ggnbId, servingCellNrcgi, neighborCellNrpci); + } catch (HttpStatusCodeException ex) { + logger.warn("getNcrt failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } // /ncrt/servingcells/{servCellNrcgi}/neighborcells/{neighCellNrpci} : @ApiOperation(value = "Modify neighbor cell relation based on Serving Cell NRCGI and Neighbor Cell NRPCI") @RequestMapping(value = "/ncrt/" + PP_SERVING + "/{" + PP_SERVING + "}/" + PP_NEIGHBOR + "/{" + PP_NEIGHBOR + "}", method = RequestMethod.PUT) - public void modifyNcrt(@PathVariable(PP_SERVING) String servingCellNrcgi, // + public Object modifyNcrt(@PathVariable(PP_SERVING) String servingCellNrcgi, // @PathVariable(PP_NEIGHBOR) String neighborCellNrpci, // @RequestBody NeighborCellRelationMod ncrMod, HttpServletResponse response) { logger.debug("modifyNcrt: servingCellNrcgi {}, neighborCellNrpci {}, ncrMod {}", servingCellNrcgi, neighborCellNrpci, ncrMod); - ncrtApi.modifyNcrt(servingCellNrcgi, neighborCellNrpci, ncrMod); - response.setStatus(healthApi.getApiClient().getStatusCode().value()); + try { + ncrtApi.modifyNcrt(servingCellNrcgi, neighborCellNrpci, ncrMod); + response.setStatus(healthApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("modifyNcrt failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } @ApiOperation(value = "Delete neighbor cell relation based on Serving Cell NRCGI and Neighbor Cell NRPCI") @RequestMapping(value = "/ncrt/" + PP_SERVING + "/{" + PP_SERVING + "}/" + PP_NEIGHBOR + "/{" + PP_NEIGHBOR + "}", method = RequestMethod.DELETE) - public void deleteNcrt(@PathVariable(PP_SERVING) String servingCellNrcgi, // + public Object deleteNcrt(@PathVariable(PP_SERVING) String servingCellNrcgi, // @PathVariable(PP_NEIGHBOR) String neighborCellNrpci, // HttpServletResponse response) { logger.debug("deleteNcrt: servingCellNrcgi {}, neighborCellNrpci {}", servingCellNrcgi, neighborCellNrpci); - ncrtApi.deleteNcrt(servingCellNrcgi, neighborCellNrpci); - response.setStatus(healthApi.getApiClient().getStatusCode().value()); + try { + ncrtApi.deleteNcrt(servingCellNrcgi, neighborCellNrpci); + response.setStatus(healthApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("modifyNcrt failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java index f4888fc7..ba98aed6 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java @@ -33,28 +33,30 @@ import org.oransc.ric.portal.dashboard.DashboardApplication; import org.oransc.ric.portal.dashboard.DashboardConstants; import org.oransc.ric.portal.dashboard.model.E2SetupRequestType; import org.oransc.ric.portal.dashboard.model.E2SetupResponse; -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.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpStatusCodeException; import io.swagger.annotations.ApiOperation; /** - * Provides methods to contact the E2 Manager. + * Proxies calls from the front end to the E2 Manager API. All methods answer + * 502 on failure:
HTTP server received an invalid response from a + * server it consulted when acting as a proxy or gateway.
* - * As of this writing the E2 interface only supports setup connection and check - * health actions, and query by RAN name. But it does not support get-all, oo - * this class mocks up some of that functionality. + * As of this writing the E2 interface does not support get-all, so this class + * mocks up some of that functionality. */ @Configuration @RestController @@ -101,11 +103,19 @@ public class E2ManagerController { @ApiOperation(value = "Gets the health from the E2 manager, expressed as the response code.") @RequestMapping(value = "/health", method = RequestMethod.GET) - public void getE2ManagerHealth(HttpServletResponse response) { - e2HealthCheckApi.healthGet(); - response.setStatus(e2HealthCheckApi.getApiClient().getStatusCode().value()); + public Object healthGet(HttpServletResponse response) { + logger.debug("healthGet"); + try { + e2HealthCheckApi.healthGet(); + response.setStatus(e2HealthCheckApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("healthGet failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } + // TODO replace with actual functionality @ApiOperation(value = "Gets the unique requests submitted to the E2 manager.", response = E2SetupResponse.class, responseContainer = "List") @RequestMapping(value = "/setup", method = RequestMethod.GET) public Iterable getRequests() { @@ -115,12 +125,17 @@ public class E2ManagerController { @ApiOperation(value = "Get RAN by name.", response = GetNodebResponse.class) @RequestMapping(value = "/nodeb/{" + PP_RANNAME + "}", method = RequestMethod.GET) - public GetNodebResponse getNb(@PathVariable(PP_RANNAME) String ranName) { + public Object getNb(@PathVariable(PP_RANNAME) String ranName) { logger.debug("getNb {}", ranName); - return e2NodebApi.getNb(ranName); + try { + return e2NodebApi.getNb(ranName); + } catch (HttpStatusCodeException ex) { + logger.warn("getNb failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } - // TODO replace with actual delete all RAN connections functionality + // TODO replace with actual functionality @ApiOperation(value = "Disconnect all RAN Connections.") @RequestMapping(value = "/disconnectAllRAN", method = RequestMethod.DELETE) public void disconnectAllRANConnections() { @@ -130,44 +145,48 @@ public class E2ManagerController { @ApiOperation(value = "Sets up an EN-DC RAN connection via the E2 manager.", response = E2SetupResponse.class) @RequestMapping(value = "/endcSetup", method = RequestMethod.POST) - public E2SetupResponse endcSetup(@RequestBody SetupRequest setupRequest, HttpServletResponse response) { + public Object endcSetup(@RequestBody SetupRequest setupRequest) { logger.debug("endcSetup {}", setupRequest); - int responseCode = -1; try { assertNotEmpty(setupRequest.getRanIp()); assertNotEmpty(setupRequest.getRanName()); assertNotNull(setupRequest.getRanPort()); - e2NodebApi.endcSetup(setupRequest); - responseCode = e2NodebApi.getApiClient().getStatusCode().value(); } catch (Exception ex) { - logger.warn("endcSetup failed", ex); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - responseCode = HttpServletResponse.SC_BAD_REQUEST; + return new E2SetupResponse(E2SetupRequestType.ENDC, setupRequest, HttpServletResponse.SC_BAD_REQUEST); + } + try { + e2NodebApi.endcSetup(setupRequest); + E2SetupResponse r = new E2SetupResponse(E2SetupRequestType.ENDC, setupRequest, + e2NodebApi.getApiClient().getStatusCode().value()); + responses.add(r); + return r; + } catch (HttpStatusCodeException ex) { + logger.warn("endcSetup failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); } - E2SetupResponse r = new E2SetupResponse(E2SetupRequestType.ENDC, setupRequest, responseCode); - responses.add(r); - return r; } @ApiOperation(value = "Sets up an X2 RAN connection via the E2 manager.", response = E2SetupResponse.class) @RequestMapping(value = "/x2Setup", method = RequestMethod.POST) - public IDashboardResponse x2Setup(@RequestBody SetupRequest setupRequest, HttpServletResponse response) { + public Object x2Setup(@RequestBody SetupRequest setupRequest) { logger.debug("x2Setup {}", setupRequest); - int responseCode = -1; try { assertNotEmpty(setupRequest.getRanIp()); assertNotEmpty(setupRequest.getRanName()); assertNotNull(setupRequest.getRanPort()); - e2NodebApi.x2Setup(setupRequest); - responseCode = e2NodebApi.getApiClient().getStatusCode().value(); } catch (Exception ex) { - logger.warn("x2Setup failed", ex); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - responseCode = HttpServletResponse.SC_BAD_REQUEST; + return new E2SetupResponse(E2SetupRequestType.ENDC, setupRequest, HttpServletResponse.SC_BAD_REQUEST); + } + try { + e2NodebApi.x2Setup(setupRequest); + E2SetupResponse r = new E2SetupResponse(E2SetupRequestType.X2, setupRequest, + e2NodebApi.getApiClient().getStatusCode().value()); + responses.add(r); + return r; + } catch (HttpStatusCodeException ex) { + logger.warn("x2Setup failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); } - E2SetupResponse r = new E2SetupResponse(E2SetupRequestType.X2, setupRequest, responseCode); - responses.add(r); - return r; } } diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappManagerController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappManagerController.java index efb296dc..e5249b19 100644 --- a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappManagerController.java +++ b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappManagerController.java @@ -25,7 +25,6 @@ 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.ErrorTransport; import org.oransc.ric.portal.dashboard.model.SuccessTransport; import org.oransc.ric.xappmgr.client.api.HealthApi; import org.oransc.ric.xappmgr.client.api.XappApi; @@ -37,19 +36,21 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpStatusCodeException; import io.swagger.annotations.ApiOperation; /** - * Mimics the xApp Manager API. These controller methods just proxy calls from - * the front-end thru to the real back-end. - * + * Proxies calls from the front end to the xApp Manager API. All methods answer + * 502 on failure:
HTTP server received an invalid response from a + * server it consulted when acting as a proxy or gateway.
*/ @Configuration @RestController @@ -66,11 +67,11 @@ public class XappManagerController { public XappManagerController(final HealthApi healthApi, final XappApi xappApi) { Assert.notNull(healthApi, "health API must not be null"); Assert.notNull(xappApi, "xapp API must not be null"); + this.healthApi = healthApi; + this.xappApi = xappApi; if (logger.isDebugEnabled()) logger.debug("ctor: configured with client types {} and {}", healthApi.getClass().getName(), xappApi.getClass().getName()); - this.healthApi = healthApi; - this.xappApi = xappApi; } @ApiOperation(value = "Gets the XApp manager client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class) @@ -81,57 +82,79 @@ public class XappManagerController { @ApiOperation(value = "Calls the xApp Manager liveness health check.") @RequestMapping(value = "/health/alive", method = RequestMethod.GET) - public void getHealth(HttpServletResponse response) { - logger.debug("getHealth"); - healthApi.getHealthAlive(); - response.setStatus(healthApi.getApiClient().getStatusCode().value()); + public Object getHealth(HttpServletResponse response) { + logger.debug("getHealthAlive"); + try { + healthApi.getHealthAlive(); + response.setStatus(healthApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("getHealthAlive failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } @ApiOperation(value = "Calls the xApp Manager readiness health check.") @RequestMapping(value = "/health/ready", method = RequestMethod.GET) - public void getHealthReady(HttpServletResponse response) { + public Object getHealthReady(HttpServletResponse response) { logger.debug("getHealthReady"); - healthApi.getHealthReady(); - response.setStatus(healthApi.getApiClient().getStatusCode().value()); + try { + healthApi.getHealthReady(); + response.setStatus(healthApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("getHealthReady failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } @ApiOperation(value = "Calls the xApp Manager to get the list of xApps.", response = AllXapps.class) @RequestMapping(value = "/xapps", method = RequestMethod.GET) - public AllXapps getAllXapps() { - if (logger.isDebugEnabled()) - logger.debug("getAllXapps via {}", xappApi.getApiClient().getBasePath()); - return xappApi.getAllXapps(); + public Object getAllXapps() { + logger.debug("getAllXapps"); + try { + return xappApi.getAllXapps(); + } catch (HttpStatusCodeException ex) { + logger.warn("getAllXapps failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } @ApiOperation(value = "Calls the xApp Manager to get the named xApp.", response = Xapp.class) @RequestMapping(value = "/xapps/{xAppName}", method = RequestMethod.GET) - public Xapp getXapp(@PathVariable("xAppName") String xAppName) { + public Object getXapp(@PathVariable("xAppName") String xAppName) { logger.debug("getXapp {}", xAppName); - return xappApi.getXappByName(xAppName); + try { + return xappApi.getXappByName(xAppName); + } catch (HttpStatusCodeException ex) { + logger.warn("getXapp failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); + } } @ApiOperation(value = "Calls the xApp Manager to deploy the specified Xapp.", response = Xapp.class) @RequestMapping(value = "/xapps", method = RequestMethod.POST) - public Object deployXapp(@RequestBody XAppInfo xAppInfo, HttpServletResponse response) { + public Object deployXapp(@RequestBody XAppInfo xAppInfo) { logger.debug("deployXapp {}", xAppInfo); try { return xappApi.deployXapp(xAppInfo); - } catch (Exception ex) { - logger.error("deployXapp failed", ex); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - return new ErrorTransport(500, "deployXapp failed", ex); + } catch (HttpStatusCodeException ex) { + logger.warn("deployXapp failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); } } @ApiOperation(value = "Calls the xApp Manager to undeploy the named Xapp.") @RequestMapping(value = "/xapps/{xAppName}", method = RequestMethod.DELETE) - public void undeployXapp(@PathVariable("xAppName") String xAppName, HttpServletResponse response) { + public Object undeployXapp(@PathVariable("xAppName") String xAppName, HttpServletResponse response) { logger.debug("undeployXapp {}", xAppName); try { xappApi.undeployXapp(xAppName); - } catch (Exception ex) { - logger.error("deployXapp failed", ex); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + response.setStatus(healthApi.getApiClient().getStatusCode().value()); + return null; + } catch (HttpStatusCodeException ex) { + logger.warn("undeployXapp failed: {}", ex.toString()); + return ResponseEntity.status(HttpServletResponse.SC_BAD_GATEWAY).body(ex.getResponseBodyAsString()); } } diff --git a/webapp-backend/src/main/resources/application.properties b/webapp-backend/src/main/resources/application.properties index 6015f519..fa2d5784 100644 --- a/webapp-backend/src/main/resources/application.properties +++ b/webapp-backend/src/main/resources/application.properties @@ -25,13 +25,13 @@ server.port = 8080 # A1 Mediator -a1med.url = http://A1-URL +a1med.url = http://jar-app-props-default-A1-URL # ANR xApp -anrxapp.url = http://ANR-URL +anrxapp.url = http://jar-app-props-default-ANR-URL # E2 Manager -e2mgr.url = http://E2-URL +e2mgr.url = http://jar-app-props-default-E2-URL # Xapp Manager -xappmgr.url = http://MGR-URL +xappmgr.url = http://jar-app-props-default-Xapp-Mgr-URL diff --git a/webapp-backend/src/main/resources/logback.xml b/webapp-backend/src/main/resources/logback.xml index 0d2348f9..26c9ffb2 100644 --- a/webapp-backend/src/main/resources/logback.xml +++ b/webapp-backend/src/main/resources/logback.xml @@ -65,4 +65,7 @@ > + + + diff --git a/webapp-frontend/src/app/ran-connection/ran-connection-dialog.component.ts b/webapp-frontend/src/app/ran-connection/ran-connection-dialog.component.ts index 52db4ba2..3867ef9c 100644 --- a/webapp-frontend/src/app/ran-connection/ran-connection-dialog.component.ts +++ b/webapp-frontend/src/app/ran-connection/ran-connection-dialog.component.ts @@ -21,6 +21,7 @@ import { Component, OnInit, Inject } from '@angular/core'; import { FormGroup, FormControl, Validators } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { E2ManagerService } from '../services/e2-mgr/e2-mgr.service'; +import { NotificationService } from './../services/ui/notification.service'; import { ErrorDialogService } from '../services/ui/error-dialog.service'; import { E2SetupRequest } from '../interfaces/e2-mgr.types'; import { HttpErrorResponse } from '@angular/common/http'; @@ -37,7 +38,9 @@ export class RANConnectionDialogComponent implements OnInit { constructor( private dialogRef: MatDialogRef, - private service: E2ManagerService, private errorService: ErrorDialogService, + private service: E2ManagerService, + private errorService: ErrorDialogService, + private notifService: NotificationService, @Inject(MAT_DIALOG_DATA) public data: E2SetupRequest) { } @@ -60,7 +63,6 @@ export class RANConnectionDialogComponent implements OnInit { } public setupConnection = (ranFormValue) => { - if (this.ranDialogForm.valid) { this.executeSetupConnection(ranFormValue); } @@ -75,21 +77,28 @@ export class RANConnectionDialogComponent implements OnInit { ranPort: ranFormValue.ranPort }; if (ranFormValue.ranType === 'endc') { - this.service.endcSetup(setupRequest).subscribe((val: any[]) => {}, + this.service.endcSetup(setupRequest).subscribe( + (response: any) => { + this.notifService.success('Endc connect succeeded!'); + this.dialogRef.close(); + }, (error => { httpErrRes = error; this.errorService.displayError(aboutError + httpErrRes.message); }) ); } else { - this.service.x2Setup(setupRequest).subscribe((val: any[]) => {}, + this.service.x2Setup(setupRequest).subscribe( + (response: any) => { + this.notifService.success('X2 connect succeeded!'); + this.dialogRef.close(); + }, (error => { httpErrRes = error; this.errorService.displayError(aboutError + httpErrRes.message); }) ); } - this.dialogRef.close(); } public hasError(controlName: string, errorName: string) { -- 2.16.6