* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ExceptionHandler({RestClientResponseException.class})
public final ResponseEntity<ErrorTransport> handleProxyMethodException(Exception ex, WebRequest request) {
// Capture the full stack trace in the log.
- log.error("handleProxyMethodException: request {}, exception {}", request.getDescription(false), ex);
+ log.error("handleProxyMethodException: request {}, exception {}", request.getDescription(false),
+ ex.getMessage());
if (ex instanceof HttpStatusCodeException) {
HttpStatusCodeException hsce = (HttpStatusCodeException) ex;
return new ResponseEntity<>(new ErrorTransport(hsce.getRawStatusCode(), hsce.getResponseBodyAsString(),
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* 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.
* Gets a list of authorities (roles) for this user. To keep Spring happy, every
* item has prefix ROLE_.
*/
+ @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> roleList = new ArrayList<>();
Iterator<EcompRole> roleIter = ecompUser.getRoles().iterator();
return roleList;
}
+ @Override
public String getPassword() {
return null;
}
+ @Override
public String getUsername() {
return ecompUser.getLoginId();
}
+ @Override
public boolean isAccountNonExpired() {
return true;
}
+ @Override
public boolean isAccountNonLocked() {
return true;
}
+ @Override
public boolean isCredentialsNonExpired() {
return true;
}
+ @Override
public boolean isEnabled() {
return ecompUser.isActive();
}
* ========================LICENSE_START=================================
* O-RAN-SC
* %%
- * Copyright (C) 2019 AT&T Intellectual Property
+ * Copyright (C) 2019 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* ========================LICENSE_START=================================
* O-RAN-SC
* %%
- * Copyright (C) 2019 AT&T Intellectual Property
+ * Copyright (C) 2019 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
private static final HostnameVerifier jvmHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
- private static final HostnameVerifier trivialHostnameVerifier = (hostname, sslSession) -> true;
+ private static final HostnameVerifier trivialHostnameVerifier =
+ (hostname, sslSession) -> hostname.equalsIgnoreCase(sslSession.getPeerHost());
private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[] {new X509TrustManager() {
- @SuppressWarnings("squid:S1168") // Must return null to get wanted behaviour.
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
- return null;
+ return new java.security.cert.X509Certificate[0];
}
@Override
public static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException {
HttpsURLConnection.setDefaultHostnameVerifier(trivialHostnameVerifier);
// Install the all-trusting trust manager
- SSLContext sc = SSLContext.getInstance("SSL");
+ SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, UNQUESTIONING_TRUST_MANAGER, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
public static void turnOnSslChecking() throws KeyManagementException, NoSuchAlgorithmException {
HttpsURLConnection.setDefaultHostnameVerifier(jvmHostnameVerifier);
// Return it to the initial state (discovered by reflection, now hardcoded)
- SSLContext sc = SSLContext.getInstance("SSL");
+ SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, null, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
* O-RAN-SC
* %%
* Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
--- /dev/null
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property
+ * %%
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+package org.oransc.ric.portal.dashboard;
+
+import java.lang.invoke.MethodHandles;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+import org.onap.portalsdk.core.onboarding.exception.PortalAPIException;
+import org.onap.portalsdk.core.restful.domain.EcompRole;
+import org.onap.portalsdk.core.restful.domain.EcompUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ActiveProfiles;
+
+@ActiveProfiles("test")
+public class DashboardUserManagerTest {
+
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static EcompUser createEcompUser(String loginId) {
+ EcompUser user = new EcompUser();
+ user.setActive(true);
+ user.setLoginId(loginId);
+ user.setFirstName("First");
+ user.setLastName("Last");
+ EcompRole role = new EcompRole();
+ role.setId(1L);
+ role.setName(DashboardConstants.ROLE_NAME_ADMIN);
+ Set<EcompRole> roles = new HashSet<>();
+ roles.add(role);
+ user.setRoles(roles);
+ return user;
+ }
+
+ @Test
+ public void testUserMgr() throws Exception {
+ final String loginId = "demo";
+ DashboardUserManager dum = new DashboardUserManager(true);
+ EcompUser user = createEcompUser(loginId);
+ dum.createUser(user);
+ logger.info("Created user {}", user);
+ try {
+ dum.createUser(user);
+ throw new Exception("Unexpected success");
+ } catch (PortalAPIException ex) {
+ logger.info("caught expected exception: {}", ex.toString());
+ }
+ Assert.assertFalse(dum.getUsers().isEmpty());
+ EcompUser fetched = dum.getUser(loginId);
+ Assert.assertEquals(fetched, user);
+ fetched.setLastName("Lastier");
+ dum.updateUser(loginId, fetched);
+ EcompUser missing = dum.getUser("foo");
+ Assert.assertNull(missing);
+ EcompUser unk = createEcompUser("unknown");
+ try {
+ dum.updateUser("unk", unk);
+ } catch (PortalAPIException ex) {
+ logger.info("caught expected exception: {}", ex.toString());
+ }
+ }
+
+}
* O-RAN-SC
* %%
* Copyright (C) 2019 Nordix Foundation
+ * Modifications Copyright (C) 2020 Nordix Foundation
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
--- /dev/null
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property
+ * %%
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+package org.oransc.ric.portal.dashboard.portalapi;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.onap.portalsdk.core.onboarding.util.PortalApiConstants;
+import org.oransc.ric.portal.dashboard.DashboardUserManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+public class PortalAuthManagerTest {
+
+ @Value("${portalapi.decryptor}")
+ private String decryptor;
+
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ @Test
+ public void testPortalStuff() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException, IOException, ServletException {
+
+ PortalAuthManager m = new PortalAuthManager("app", "user", "secret", decryptor, "cookie");
+ Assert.assertNotNull(m.getAppCredentials());
+ String s = null;
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ s = m.validateEcompSso(request);
+ logger.debug("validateEcompSso answers {}", s);
+ Assert.assertNull(s);
+
+ Cookie cookie = new Cookie(PortalApiConstants.EP_SERVICE, "bogus");
+ request.setCookies(cookie);
+ s = m.validateEcompSso(request);
+ logger.debug("validateEcompSso answers {}", s);
+ Assert.assertNull(s);
+
+ DashboardUserManager dum = new DashboardUserManager(true);
+ PortalAuthenticationFilter filter = new PortalAuthenticationFilter(false, m, dum);
+ filter.init(null);
+ filter.destroy();
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ try {
+ filter.doFilter(request, response, null);
+ } catch (NullPointerException ex) {
+ logger.debug("chain is null");
+ }
+
+ filter = new PortalAuthenticationFilter(true, m, dum);
+ try {
+ filter.doFilter(request, response, null);
+ } catch (NullPointerException ex) {
+ logger.debug("chain is null");
+ }
+ }
+
+}
TC_ONELINE_DESCR="dockerirze the test, setup docker container for policy agent, cbs, consul, near realtime ric simulator"
-. ../common/testcase_common.sh $1 $2
+. ../common/testcase_common.sh $1 $2 $3
#### TEST BEGIN ####
consul_config_app "../simulator-group/consul_cbs/config.json"
+start_dashboard
+
+start_sdnc
+
start_policy_agent
check_policy_agent_logs
# Set the images for the Policy agent app to use for the auto tests. Do not add the image tag.
#
-# Local image and tag, shall point to locally built image (non-nexus path)
+# Local Policy Agent image and tag, shall point to locally built image (non-nexus path)
export POLICY_AGENT_LOCAL_IMAGE=o-ran-sc/nonrtric-policy-agent
# Remote image
export POLICY_AGENT_REMOTE_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-policy-agent
+# SDNC A1 Adapter remote image
+export SDNC_A1_ADAPTER_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-a1-controller:1.7.4
+# Dashboard remote image
+export DASHBOARD_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-dashboard:1.0.1
# Common env var for auto-test.
. ../common/test_env.sh
-echo "Test case started as: ${BASH_SOURCE[$i+1]} "$1 $2
+echo "Test case started as: ${BASH_SOURCE[$i+1]} "$1 $2 $3
echo "Numbers of ric simulator started" $2
# This is a script that contains all the functions needed for auto test
IMAGE_TAG="1.0.0-SNAPSHOT"
IMAGE_TAG_REMOTE="latest"
RIC_NUMBER=$2
+SDNC=$3
-if [ $# -lt 1 ] || [ $# -gt 2 ]; then
- echo "Expected arg: local | remote and numbers of the rics "
+if [ $# -lt 1 ] || [ $# -gt 4 ]; then
+ echo "Expected arg: local | remote and numbers of the rics and SDNC "
exit 1
elif [ $1 == "local" ]; then
if [ -z $POLICY_AGENT_LOCAL_IMAGE ]; then
echo ""
}
+start_dashboard() {
+
+ DOCKER_SIM_NWNAME="nonrtric-docker-net"
+ echo "Creating docker network $DOCKER_SIM_NWNAME, if needed"
+ docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME
+
+ echo "start dashboard"
+ curdir=$PWD
+ cd $SIM_GROUP
+ cd dashboard/
+
+ docker-compose up -d
+
+ cd $curdir
+ echo ""
+}
+
+start_sdnc() {
+
+ if [ $SDNC == "sdnc" ]; then
+ DOCKER_SIM_NWNAME="nonrtric-docker-net"
+ echo "Creating docker network $DOCKER_SIM_NWNAME, if needed"
+ docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME
+
+ echo "start sdnc"
+ curdir=$PWD
+ cd $SIM_GROUP
+ cd sdnc/
+
+ docker-compose up -d a1-controller
+
+ cd $curdir
+ echo ""
+ fi
+}
+
prepare_consul_config() {
echo "prepare consul config"
curdir=$PWD
echo ""
}
+
+
clean_containers() {
echo "Stopping all containers, policy agent app(s) and simulators with name prefix 'policy_agent'"
docker stop $(docker ps -q --filter name=/policy-agent) &> /dev/null
docker stop $(docker ps -q --filter name=ric-simulator) &> /dev/null
echo "Removing all containers, policy agent app and simulators with name prefix 'ric-simulator'"
docker rm $(docker ps -a -q --filter name=ric-simulator) &> /dev/null
+ echo "Removing all containers, policy agent app and simulators with name prefix 'dashboard'"
+ docker rm $(docker ps -a -q --filter name=dashboard) &> /dev/null
+ echo "Removing all containers, policy agent app and simulators with name prefix 'a1-controller'"
+ docker rm $(docker ps -a -q --filter name=a1-controller) &> /dev/null
+ echo "Removing all containers, policy agent app and simulators with name prefix 'sdnc_db_container'"
+ docker rm $(docker ps -a -q --filter name=sdnc_db_container) &> /dev/null
+ echo "Removing all containers, policy agent app and simulators with name prefix 'cbs'"
+ docker rm $(docker ps -a -q --filter name=polman_cbs) &> /dev/null
+ echo "Removing all containers, policy agent app and simulators with name prefix 'consul'"
+ docker rm $(docker ps -a -q --filter name=polman_consul) &> /dev/null
echo "Removing unused docker networks with substring 'policy agent' in network name"
docker network rm $(docker network ls -q --filter name=nonrtric)
echo ""
policy_status[policyId] = ps
return("Status updated for policy: " + policyId, 200)
+#Metrics function
+
+@app.route('/counter/<string:countername>', methods=['GET'])
+def getCounter(countername):
+ if (countername == "num_instances"):
+ return str(len(policy_instances)),200
+ elif (countername == "num_types"):
+ return str(len(policy_types)),200
+ else:
+ return "Counter name: "+countername+" not found.",404
+
+
port_number = 8085
if len(sys.argv) >= 2:
if isinstance(sys.argv[1], int):
{
"ric": [
- {
- "name": "ric_ric-simulator_6",
- "baseUrl": "http://ric_ric-simulator_6:8085/",
- "managedElementIds": [
- "kista_ric_ric-simulator_6",
- "stockholm_ric_ric-simulator_6"
- ]
- },
- {
- "name": "ric_ric-simulator_2",
- "baseUrl": "http://ric_ric-simulator_2:8085/",
- "managedElementIds": [
- "kista_ric_ric-simulator_2",
- "stockholm_ric_ric-simulator_2"
- ]
- },
{
"name": "ric_ric-simulator_3",
"baseUrl": "http://ric_ric-simulator_3:8085/",
]
},
{
- "name": "ric_ric-simulator_7",
- "baseUrl": "http://ric_ric-simulator_7:8085/",
- "managedElementIds": [
- "kista_ric_ric-simulator_7",
- "stockholm_ric_ric-simulator_7"
- ]
- },
- {
- "name": "ric_ric-simulator_8",
- "baseUrl": "http://ric_ric-simulator_8:8085/",
- "managedElementIds": [
- "kista_ric_ric-simulator_8",
- "stockholm_ric_ric-simulator_8"
- ]
- },
- {
- "name": "ric_ric-simulator_5",
- "baseUrl": "http://ric_ric-simulator_5:8085/",
+ "name": "ric_ric-simulator_2",
+ "baseUrl": "http://ric_ric-simulator_2:8085/",
"managedElementIds": [
- "kista_ric_ric-simulator_5",
- "stockholm_ric_ric-simulator_5"
+ "kista_ric_ric-simulator_2",
+ "stockholm_ric_ric-simulator_2"
]
},
{
"kista_ric_ric-simulator_1",
"stockholm_ric_ric-simulator_1"
]
- },
- {
- "name": "ric_ric-simulator_9",
- "baseUrl": "http://ric_ric-simulator_9:8085/",
- "managedElementIds": [
- "kista_ric_ric-simulator_9",
- "stockholm_ric_ric-simulator_9"
- ]
- },
- {
- "name": "ric_ric-simulator_4",
- "baseUrl": "http://ric_ric-simulator_4:8085/",
- "managedElementIds": [
- "kista_ric_ric-simulator_4",
- "stockholm_ric_ric-simulator_4"
- ]
- },
- {
- "name": "ric_ric-simulator_10",
- "baseUrl": "http://ric_ric-simulator_10:8085/",
- "managedElementIds": [
- "kista_ric_ric-simulator_10",
- "stockholm_ric_ric-simulator_10"
- ]
}
],
"streams_publishes": {
"dmaap_publisher": {
"type": "message_router",
"dmaap_info": {
- "topic_url": "http://admin:admin@localhost:6845/events/A1-POLICY-AGENT-WRITE"
+ "topic_url": "http://message-router:3905/events/A1-POLICY-AGENT-WRITE"
}
}
},
"dmaap_subscriber": {
"type": "message_router",
"dmaap_info": {
- "topic_url": "http://admin:admin@localhost:6845/events/A1-POLICY-AGENT-READ/users/policy-agent"
+ "topic_url": "http://message-router:3905/events/A1-POLICY-AGENT-READ/users/policy-agent"
}
}
}
--- /dev/null
+networks:
+ nonrtric-docker-net:
+ external:
+ name: nonrtric-docker-net
+services:
+ dashboard:
+ image: nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-dashboard:1.0.1
+
+ networks:
+ nonrtric-docker-net: null
+ ports:
+ - 8080:8080
+version: '3.0'
+
+
--- /dev/null
+# ==================================================================================
+# Modifications Copyright (c) 2019 Nordix Foundation.
+#
+# 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.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==================================================================================
+version: '2.1'
+
+networks:
+ default:
+ driver: bridge
+ name: nonrtric-docker-net
+
+services:
+ db:
+ image: mysql/mysql-server:5.6
+ container_name: sdnc_db_container
+ networks:
+ - default
+ ports:
+ - "3306"
+ environment:
+ - MYSQL_ROOT_PASSWORD=openECOMP1.0
+ - MYSQL_ROOT_HOST=%
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "30m"
+ max-file: "5"
+
+ a1-controller:
+ image: nexus3.o-ran-sc.org:10002/o-ran-sc/nonrtric-a1-controller:1.7.4
+ depends_on :
+ - db
+ container_name: a1-controller-container
+ networks:
+ - default
+ entrypoint: ["/opt/onap/sdnc/bin/startODL.sh"]
+ ports:
+ - "8282:8181"
+ links:
+ - db:dbhost
+ - db:sdnctldb01
+ - db:sdnctldb02
+ environment:
+ - MYSQL_ROOT_PASSWORD=openECOMP1.0
+ - SDNC_CONFIG_DIR=/opt/onap/sdnc/data/properties
+ dns:
+ - ${DNS_IP_ADDR-10.0.100.1}
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "30m"
+ max-file: "5"
+ extra_hosts:
+ aaf.osaaf.org: 10.12.6.214
org.springframework: ERROR
org.springframework.data: ERROR
org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR
- org.oransc.policyagent: WARN
+ org.oransc.policyagent: INFO
file: /var/log/policy-agent/application.log
app:
filepath: /opt/app/policy-agent/config/application_configuration.json
swagger: '2.0'
info:
- description: This page lists all the rest apis for Policy server.
+ description: This page lists all the rest apis for the service.
version: '1.0'
- title: Policy server
+ title: A1 Policy management service
host: 'localhost:8081'
basePath: /
tags:
- - name: operation-handler
- description: Operation Handler
- - name: policy-controller
+ - name: A1 Policy Management
description: Policy Controller
- - name: ric-repository-controller
+ - name: Health check
+ description: Status Controller
+ - name: RIC Repository
description: Ric Repository Controller
- - name: service-controller
+ - name: Service registry and supervision
description: Service Controller
- - name: status-controller
- description: Status Controller
+ - name: operation-handler
+ description: Operation Handler
- name: web-mvc-links-handler
description: Web Mvc Links Handler
paths:
/policies:
get:
tags:
- - policy-controller
+ - A1 Policy Management
summary: Query policies
operationId: getPoliciesUsingGET
produces:
'403':
description: Forbidden
'404':
- description: Not Found
+ description: RIC or type not found
+ schema:
+ type: string
deprecated: false
/policy:
get:
tags:
- - policy-controller
+ - A1 Policy Management
summary: Returns a policy configuration
operationId: getPolicyUsingGET
produces:
description: Policy found
schema:
type: object
- '204':
- description: Policy is not found
- schema:
- type: string
'401':
description: Unauthorized
'403':
description: Forbidden
'404':
- description: Not Found
+ description: Policy is not found
deprecated: false
put:
tags:
- - policy-controller
+ - A1 Policy Management
summary: Put a policy
operationId: putPolicyUsingPUT
consumes:
type: string
responses:
'200':
- description: Policy created or updated
+ description: Policy updated
schema:
- type: string
+ type: object
'201':
- description: Created
+ description: Policy created
+ schema:
+ type: object
'401':
description: Unauthorized
'403':
description: Forbidden
'404':
- description: Not Found
+ description: RIC or policy type is not found
+ schema:
+ type: string
+ '405':
+ description: Change is not allowed
+ schema:
+ type: string
+ '423':
+ description: RIC is locked
+ schema:
+ type: string
deprecated: false
delete:
tags:
- - policy-controller
+ - A1 Policy Management
summary: Delete a policy
operationId: deletePolicyUsingDELETE
produces:
description: Unauthorized
'403':
description: Forbidden
+ '404':
+ description: Policy is not found
+ schema:
+ type: string
+ '423':
+ description: RIC is locked
+ schema:
+ type: string
deprecated: false
/policy_schema:
get:
tags:
- - policy-controller
+ - A1 Policy Management
summary: Returns one policy type schema definition
operationId: getPolicySchemaUsingGET
produces:
'403':
description: Forbidden
'404':
- description: Not Found
+ description: RIC is not found
+ schema:
+ type: string
deprecated: false
/policy_schemas:
get:
tags:
- - policy-controller
+ - A1 Policy Management
summary: Returns policy type schema definitions
operationId: getPolicySchemasUsingGET
produces:
'403':
description: Forbidden
'404':
- description: Not Found
+ description: RIC is not found
+ schema:
+ type: string
deprecated: false
/policy_status:
get:
tags:
- - policy-controller
+ - A1 Policy Management
summary: Returns a policy status
operationId: getPolicyStatusUsingGET
produces:
description: Policy status
schema:
type: object
- '204':
- description: Policy is not found
- schema:
- type: string
'401':
description: Unauthorized
'403':
description: Forbidden
'404':
- description: Not Found
+ description: Policy is not found
+ schema:
+ type: string
deprecated: false
/policy_types:
get:
tags:
- - policy-controller
+ - A1 Policy Management
summary: Query policy type names
operationId: getPolicyTypesUsingGET
produces:
'403':
description: Forbidden
'404':
- description: Not Found
+ description: RIC is not found
+ schema:
+ type: string
deprecated: false
/ric:
get:
tags:
- - ric-repository-controller
+ - RIC Repository
summary: Returns the name of a RIC managing one Mananged Element
operationId: getRicUsingGET
produces:
/rics:
get:
tags:
- - ric-repository-controller
+ - RIC Repository
summary: Query NearRT RIC information
operationId: getRicsUsingGET
produces:
'403':
description: Forbidden
'404':
- description: Not Found
+ description: Policy type is not found
+ schema:
+ type: string
deprecated: false
/service:
put:
tags:
- - service-controller
+ - Service registry and supervision
summary: Register a service
operationId: putServiceUsingPUT
consumes:
type: string
'201':
description: Created
+ '400':
+ description: Cannot parse the ServiceRegistrationInfo
+ schema:
+ type: string
'401':
description: Unauthorized
'403':
/services:
get:
tags:
- - service-controller
+ - Service registry and supervision
summary: Returns service information
operationId: getServicesUsingGET
produces:
'403':
description: Forbidden
'404':
- description: Not Found
+ description: Service is not found
+ schema:
+ type: string
deprecated: false
delete:
tags:
- - service-controller
+ - Service registry and supervision
summary: Delete a service
operationId: deleteServiceUsingDELETE
produces:
- '*/*'
parameters:
- - name: serviceName
+ - name: name
in: query
- description: serviceName
+ description: name
required: true
type: string
responses:
schema:
type: string
'204':
- description: No Content
+ description: OK
+ schema:
+ type: string
'401':
description: Unauthorized
'403':
description: Forbidden
+ '404':
+ description: Service not found
+ schema:
+ type: string
deprecated: false
/services/keepalive:
post:
tags:
- - service-controller
- summary: Keep the poilicies alive for a service
+ - Service registry and supervision
+ summary: Keep the policies alive for a service
operationId: keepAliveServiceUsingPOST
consumes:
- application/json
produces:
- '*/*'
parameters:
- - name: serviceName
+ - name: name
in: query
- description: serviceName
+ description: name
required: true
type: string
responses:
/status:
get:
tags:
- - status-controller
+ - Health check
summary: Returns status and statistics of this service
operationId: getStatusUsingGET
produces:
title: 'Map«string,Link»'
additionalProperties:
$ref: '#/definitions/Link'
- Mono«ResponseEntity«Void»»:
+ Mono«ResponseEntity«object»»:
type: object
- title: Mono«ResponseEntity«Void»»
+ title: Mono«ResponseEntity«object»»
Mono«ResponseEntity«string»»:
type: object
title: Mono«ResponseEntity«string»»
description: O1 identities for managed entities
items:
type: string
- name:
- type: string
- description: identity of the ric
policyTypes:
type: array
description: supported policy types
items:
type: string
+ ricName:
+ type: string
+ description: identity of the ric
title: RicInfo
ServiceRegistrationInfo:
type: object
+ required:
+ - serviceName
properties:
callbackUrl:
type: string
ServiceStatus:
type: object
properties:
+ callbackUrl:
+ type: string
+ description: callback for notifying of RIC recovery
keepAliveIntervalSeconds:
type: integer
format: int64
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
- * Swagger configuration class that uses swagger2 documentation type and scans all the controllers
- * under org.oransc.policyagent.controllers package. To access the swagger gui go to
- * http://ip:port/swagger-ui.html
+ * Swagger configuration class that uses swagger2 documentation type and scans
+ * all the controllers under org.oransc.policyagent.controllers package. To
+ * access the swagger gui go to http://ip:port/swagger-ui.html
*
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
- static final String API_TITLE = "Policy server";
- static final String DESCRIPTION = "This page lists all the rest apis for Policy server.";
+ static final String API_TITLE = "A1 Policy management service";
+ static final String DESCRIPTION = "This page lists all the rest apis for the service.";
static final String VERSION = "1.0";
static final String RESOURCES_PATH = "classpath:/META-INF/resources/";
static final String WEBJARS_PATH = RESOURCES_PATH + "webjars/";
import reactor.core.publisher.Mono;
/**
- * Factory for A1 clients that supports four different protocol versions of the A1 api.
+ * Factory for A1 clients that supports four different protocol versions of the
+ * A1 api.
*/
public class A1ClientFactory {
* Creates an A1 client with the correct A1 protocol for the provided Ric.
*
* <p>
- * It detects the protocol version by trial and error, since there is no getVersion method specified in the A1
- * api yet.
+ * It detects the protocol version by trial and error, since there is no
+ * getVersion method specified in the A1 api yet.
*
* <p>
- * As a side effect it also sets the protocol version in the provided Ric. This means that after the first
- * successful creation it won't have to try which protocol to use, but can create the client directly.
+ * As a side effect it also sets the protocol version in the provided Ric. This
+ * means that after the first successful creation it won't have to try which
+ * protocol to use, but can create the client directly.
*
- * @param ric The Ric to get a client for.
- * @return a client with the correct protocol, or a ServiceException if none of the protocols are supported by the
- * Ric.
+ * @param ric The RIC to get a client for.
+ * @return a client with the correct protocol, or a ServiceException if none of
+ * the protocols are supported by the Ric.
*/
public Mono<A1Client> createA1Client(Ric ric) {
return getProtocolVersion(ric) //
return Mono.just(createOscA1Client(ric));
} else if (version == A1ProtocolType.SDNC_OSC) {
return Mono.just(createSdncOscA1Client(ric));
- } else { // A1ProtocolType.SDNR_ONAP
- return Mono.just(createSdnrOnapA1Client(ric));
+ } else { // A1ProtocolType.SDNC_ONAP
+ return Mono.just(createSdncOnapA1Client(ric));
}
}
private Mono<A1Client.A1ProtocolType> getProtocolVersion(Ric ric) {
if (ric.getProtocolVersion() == A1ProtocolType.UNKNOWN) {
- return fetchVersion(createSdnrOnapA1Client(ric)) //
+ return fetchVersion(createSdncOnapA1Client(ric)) //
.onErrorResume(notUsed -> fetchVersion(createSdncOscA1Client(ric))) //
.onErrorResume(notUsed -> fetchVersion(createOscA1Client(ric))) //
.onErrorResume(notUsed -> fetchVersion(createStdA1ClientImpl(ric))) //
appConfig.getA1ControllerUsername(), appConfig.getA1ControllerPassword());
}
- protected A1Client createSdnrOnapA1Client(Ric ric) {
+ protected A1Client createSdncOnapA1Client(Ric ric) {
return new SdncOnapA1Client(ric.getConfig(), appConfig.getA1ControllerBaseUrl(),
appConfig.getA1ControllerUsername(), appConfig.getA1ControllerPassword());
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.lang.Nullable;
import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
import reactor.core.publisher.Mono;
public class AsyncRestClient {
private final WebClient client;
private final String baseUrl;
- public class AsyncRestClientException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- public AsyncRestClientException(String message) {
- super(message);
- }
- }
-
public AsyncRestClient(String baseUrl) {
this.client = WebClient.create(baseUrl);
this.baseUrl = baseUrl;
}
- public Mono<String> post(String uri, String body) {
+ public Mono<ResponseEntity<String>> postForEntity(String uri, @Nullable String body) {
logger.debug("POST uri = '{}{}''", baseUrl, uri);
- return client.post() //
+ Mono<String> bodyProducer = body != null ? Mono.just(body) : Mono.empty();
+ RequestHeadersSpec<?> request = client.post() //
.uri(uri) //
.contentType(MediaType.APPLICATION_JSON) //
- .bodyValue(body) //
- .retrieve() //
- .onStatus(HttpStatus::isError,
- response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
- .bodyToMono(String.class) //
- .defaultIfEmpty("");
+ .body(bodyProducer, String.class);
+ return retrieve(request);
+ }
+
+ public Mono<String> post(String uri, @Nullable String body) {
+ return postForEntity(uri, body) //
+ .flatMap(this::toBody);
}
public Mono<String> postWithAuthHeader(String uri, String body, String username, String password) {
logger.debug("POST (auth) uri = '{}{}''", baseUrl, uri);
- return client.post() //
+ RequestHeadersSpec<?> request = client.post() //
.uri(uri) //
.headers(headers -> headers.setBasicAuth(username, password)) //
.contentType(MediaType.APPLICATION_JSON) //
- .bodyValue(body) //
- .retrieve() //
- .onStatus(HttpStatus::isError,
- response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
- .bodyToMono(String.class) //
- .defaultIfEmpty("");
+ .bodyValue(body);
+ return retrieve(request) //
+ .flatMap(this::toBody);
}
- public Mono<String> put(String uri, String body) {
+ public Mono<ResponseEntity<String>> putForEntity(String uri, String body) {
logger.debug("PUT uri = '{}{}''", baseUrl, uri);
- return client.put() //
+ RequestHeadersSpec<?> request = client.put() //
.uri(uri) //
.contentType(MediaType.APPLICATION_JSON) //
- .bodyValue(body) //
- .retrieve() //
- .onStatus(HttpStatus::isError,
- response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
- .bodyToMono(String.class) //
- .defaultIfEmpty("");
+ .bodyValue(body);
+ return retrieve(request);
}
- public Mono<String> get(String uri) {
+ public Mono<String> put(String uri, String body) {
+ return putForEntity(uri, body) //
+ .flatMap(this::toBody);
+ }
+
+ public Mono<ResponseEntity<String>> getForEntity(String uri) {
logger.debug("GET uri = '{}{}''", baseUrl, uri);
- return client.get() //
- .uri(uri) //
- .retrieve() //
- .onStatus(HttpStatus::isError,
- response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
- .bodyToMono(String.class) //
- .defaultIfEmpty("");
+ RequestHeadersSpec<?> request = client.get().uri(uri);
+ return retrieve(request);
}
- public Mono<String> delete(String uri) {
+ public Mono<String> get(String uri) {
+ return getForEntity(uri) //
+ .flatMap(this::toBody);
+ }
+
+ public Mono<ResponseEntity<String>> deleteForEntity(String uri) {
logger.debug("DELETE uri = '{}{}''", baseUrl, uri);
- return client.delete() //
- .uri(uri) //
- .retrieve() //
- .onStatus(HttpStatus::isError,
- response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
- .bodyToMono(String.class) //
- .defaultIfEmpty("");
+ RequestHeadersSpec<?> request = client.delete().uri(uri);
+ return retrieve(request);
+ }
+
+ public Mono<String> delete(String uri) {
+ return deleteForEntity(uri) //
+ .flatMap(this::toBody);
}
+
+ private Mono<ResponseEntity<String>> retrieve(RequestHeadersSpec<?> request) {
+ return request.retrieve() //
+ .toEntity(String.class);
+ }
+
+ Mono<String> toBody(ResponseEntity<String> entity) {
+ if (entity.getBody() == null) {
+ return Mono.just("");
+ } else {
+ return Mono.just(entity.getBody());
+ }
+ }
+
}
public SdncOnapA1Client(RicConfig ricConfig, String baseUrl, String username, String password) {
this(ricConfig, username, password, new AsyncRestClient(baseUrl + "/restconf/operations"));
if (logger.isDebugEnabled()) {
- logger.debug("SdnrOnapA1Client for ric: {}, a1ControllerBaseUrl: {}", ricConfig.name(), baseUrl);
+ logger.debug("SdncOnapA1Client for ric: {}, a1ControllerBaseUrl: {}", ricConfig.name(), baseUrl);
}
}
import java.util.List;
import org.oransc.policyagent.clients.A1ClientFactory;
-import org.oransc.policyagent.configuration.ApplicationConfig;
import org.oransc.policyagent.exceptions.ServiceException;
import org.oransc.policyagent.repository.ImmutablePolicy;
import org.oransc.policyagent.repository.Lock.LockType;
import org.oransc.policyagent.repository.PolicyTypes;
import org.oransc.policyagent.repository.Ric;
import org.oransc.policyagent.repository.Rics;
+import org.oransc.policyagent.repository.Service;
+import org.oransc.policyagent.repository.Services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import reactor.core.publisher.Mono;
@RestController
-@Api(value = "Policy Management API")
+@Api(tags = "A1 Policy Management")
public class PolicyController {
- private final Rics rics;
- private final PolicyTypes policyTypes;
- private final Policies policies;
- private final A1ClientFactory a1ClientFactory;
+ @Autowired
+ private Rics rics;
+ @Autowired
+ private PolicyTypes policyTypes;
+ @Autowired
+ private Policies policies;
+ @Autowired
+ private A1ClientFactory a1ClientFactory;
+ @Autowired
+ private Services services;
private static Gson gson = new GsonBuilder() //
.serializeNulls() //
.create(); //
- @Autowired
- PolicyController(ApplicationConfig config, PolicyTypes types, Policies policies, Rics rics,
- A1ClientFactory a1ClientFactory) {
- this.policyTypes = types;
- this.policies = policies;
- this.rics = rics;
- this.a1ClientFactory = a1ClientFactory;
- }
-
@GetMapping("/policy_schemas")
@ApiOperation(value = "Returns policy type schema definitions")
@ApiResponses(
value = {
- @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List")})
+ @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List"), //
+ @ApiResponse(code = 404, message = "RIC is not found", response = String.class)})
public ResponseEntity<String> getPolicySchemas(@RequestParam(name = "ric", required = false) String ricName) {
synchronized (this.policyTypes) {
if (ricName == null) {
@GetMapping("/policy_schema")
@ApiOperation(value = "Returns one policy type schema definition")
- @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy schema", response = Object.class)})
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "Policy schema", response = Object.class),
+ @ApiResponse(code = 404, message = "RIC is not found", response = String.class)})
public ResponseEntity<String> getPolicySchema(@RequestParam(name = "id", required = true) String id) {
try {
PolicyType type = policyTypes.getType(id);
@GetMapping("/policy_types")
@ApiOperation(value = "Query policy type names")
@ApiResponses(
- value = {@ApiResponse(
- code = 200,
- message = "Policy type names",
- response = String.class,
- responseContainer = "List")})
+ value = {
+ @ApiResponse(
+ code = 200,
+ message = "Policy type names",
+ response = String.class,
+ responseContainer = "List"),
+ @ApiResponse(code = 404, message = "RIC is not found", response = String.class)})
public ResponseEntity<String> getPolicyTypes(@RequestParam(name = "ric", required = false) String ricName) {
synchronized (this.policyTypes) {
if (ricName == null) {
@ApiResponses(
value = { //
@ApiResponse(code = 200, message = "Policy found", response = Object.class), //
- @ApiResponse(code = 204, message = "Policy is not found")} //
+ @ApiResponse(code = 404, message = "Policy is not found")} //
)
public ResponseEntity<String> getPolicy( //
@RequestParam(name = "instance", required = true) String instance) {
Policy p = policies.getPolicy(instance);
return new ResponseEntity<>(p.json(), HttpStatus.OK);
} catch (ServiceException e) {
- return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
@DeleteMapping("/policy")
@ApiOperation(value = "Delete a policy", response = Object.class)
- @ApiResponses(value = {@ApiResponse(code = 204, message = "Policy deleted", response = Object.class)})
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 204, message = "Policy deleted", response = Object.class),
+ @ApiResponse(code = 404, message = "Policy is not found", response = String.class),
+ @ApiResponse(code = 423, message = "RIC is locked", response = String.class)})
public Mono<ResponseEntity<Object>> deletePolicy( //
@RequestParam(name = "instance", required = true) String id) {
- Policy policy = policies.get(id);
- if (policy != null && policy.ric().getState() == Ric.RicState.IDLE) {
+ Policy policy;
+ try {
+ policy = policies.getPolicy(id);
+ keepServiceAlive(policy.ownerServiceName());
+ if (policy.ric().getState() != Ric.RicState.IDLE) {
+ return Mono.just(new ResponseEntity<>("Busy, recovering", HttpStatus.LOCKED));
+ }
Ric ric = policy.ric();
return ric.getLock().lock(LockType.SHARED) // //
.flatMap(lock -> a1ClientFactory.createA1Client(policy.ric())) //
.doOnNext(notUsed -> ric.getLock().unlockBlocking()) //
.doOnError(notUsed -> ric.getLock().unlockBlocking()) //
.flatMap(notUsed -> Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT)));
- } else if (policy != null) {
- return Mono.just(new ResponseEntity<>("Busy, recovering", HttpStatus.LOCKED));
- } else {
+ } catch (ServiceException e) {
return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
}
@PutMapping(path = "/policy")
@ApiOperation(value = "Put a policy", response = String.class)
- @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy created or updated")})
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 201, message = "Policy created", response = Object.class), //
+ @ApiResponse(code = 200, message = "Policy updated", response = Object.class), //
+ @ApiResponse(code = 423, message = "RIC is locked", response = String.class), //
+ @ApiResponse(code = 404, message = "RIC or policy type is not found", response = String.class), //
+ @ApiResponse(code = 405, message = "Change is not allowed", response = String.class)})
public Mono<ResponseEntity<Object>> putPolicy( //
@RequestParam(name = "type", required = true) String typeName, //
@RequestParam(name = "instance", required = true) String instanceId, //
@RequestBody Object jsonBody) {
String jsonString = gson.toJson(jsonBody);
-
Ric ric = rics.get(ricName);
PolicyType type = policyTypes.get(typeName);
+ keepServiceAlive(service);
if (ric != null && type != null && ric.getState() == Ric.RicState.IDLE) {
Policy policy = ImmutablePolicy.builder() //
.id(instanceId) //
.onErrorResume(t -> Mono.just(new ResponseEntity<>(t.getMessage(), HttpStatus.METHOD_NOT_ALLOWED)));
}
- return ric == null && type == null ? Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND))
- : Mono.just(new ResponseEntity<>(HttpStatus.CONFLICT)); // Recovering
+ return ric == null || type == null ? Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND))
+ : Mono.just(new ResponseEntity<>(HttpStatus.LOCKED)); // Recovering
}
private Mono<Object> validateModifiedPolicy(Policy policy) {
@ApiOperation(value = "Query policies")
@ApiResponses(
value = {
- @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List")})
+ @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List"),
+ @ApiResponse(code = 404, message = "RIC or type not found", response = String.class)})
public ResponseEntity<String> getPolicies( //
@RequestParam(name = "type", required = false) String type, //
@RequestParam(name = "ric", required = false) String ric, //
@RequestParam(name = "service", required = false) String service) //
{
+ if ((type != null && this.policyTypes.get(type) == null)) {
+ return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND);
+ }
+ if ((ric != null && this.rics.get(ric) == null)) {
+ return new ResponseEntity<>("RIC not found", HttpStatus.NOT_FOUND);
+ }
synchronized (policies) {
Collection<Policy> result = null;
try {
policiesJson = policiesToJson(result);
} catch (ServiceException e) {
- return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(policiesJson, HttpStatus.OK);
}
@ApiResponses(
value = { //
@ApiResponse(code = 200, message = "Policy status", response = Object.class), //
- @ApiResponse(code = 204, message = "Policy is not found", response = String.class)} //
+ @ApiResponse(code = 404, message = "Policy is not found", response = String.class)} //
)
public Mono<ResponseEntity<String>> getPolicyStatus( //
@RequestParam(name = "instance", required = true) String instance) {
.flatMap(client -> client.getPolicyStatus(policy)) //
.flatMap(status -> Mono.just(new ResponseEntity<>(status, HttpStatus.OK)));
} catch (ServiceException e) {
- return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT));
+ return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND));
+ }
+ }
+
+ private void keepServiceAlive(String name) {
+ Service s = this.services.get(name);
+ if (s != null) {
+ s.keepAlive();
}
}
import java.util.List;
import java.util.Optional;
-import org.oransc.policyagent.configuration.ApplicationConfig;
+import org.oransc.policyagent.repository.PolicyTypes;
import org.oransc.policyagent.repository.Ric;
import org.oransc.policyagent.repository.Rics;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
@RestController
-@Api(value = "RIC Management API")
+@Api(tags = "RIC Repository")
public class RicRepositoryController {
@Autowired
private Rics rics;
+ @Autowired
+ PolicyTypes types;
+
private static Gson gson = new GsonBuilder() //
.serializeNulls() //
.create(); //
- @Autowired
- RicRepositoryController(ApplicationConfig appConfig) {
- }
-
/**
* Example: http://localhost:8081/rics?managedElementId=kista_1
*/
}
/**
- * @return a Json array of all RIC data
- * Example: http://localhost:8081/ric
+ * @return a Json array of all RIC data Example: http://localhost:8081/ric
*/
@GetMapping("/rics")
@ApiOperation(value = "Query NearRT RIC information")
@ApiResponses(
value = { //
- @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List") //
- })
+ @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List"), //
+ @ApiResponse(code = 404, message = "Policy type is not found", response = String.class)})
public ResponseEntity<String> getRics(
@RequestParam(name = "policyType", required = false) String supportingPolicyType) {
+ if ((supportingPolicyType != null) && (this.types.get(supportingPolicyType) == null)) {
+ return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND);
+ }
+
List<RicInfo> result = new ArrayList<>();
synchronized (rics) {
for (Ric ric : rics.getRics()) {
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.web.bind.annotation.RestController;
@RestController
+@Api(tags = "Service registry and supervision")
public class ServiceController {
private final Services services;
private final Policies policies;
private static Gson gson = new GsonBuilder() //
- .serializeNulls() //
.create(); //
@Autowired
@GetMapping("/services")
@ApiOperation(value = "Returns service information")
@ApiResponses(
- value = {@ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List")})
+ value = { //
+ @ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List"), //
+ @ApiResponse(code = 404, message = "Service is not found", response = String.class)})
public ResponseEntity<String> getServices(//
@RequestParam(name = "name", required = false) String name) {
+ if (name != null && this.services.get(name) == null) {
+ return new ResponseEntity<>("Service not found", HttpStatus.NOT_FOUND);
+ }
+
Collection<ServiceStatus> servicesStatus = new ArrayList<>();
synchronized (this.services) {
for (Service s : this.services.getAll()) {
}
private ServiceStatus toServiceStatus(Service s) {
- return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds());
+ return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds(),
+ s.getCallbackUrl());
+ }
+
+ private void validateRegistrationInfo(ServiceRegistrationInfo registrationInfo) throws ServiceException {
+ if (registrationInfo.serviceName.isEmpty()) {
+ throw new ServiceException("Missing mandatory parameter 'serviceName'");
+ }
}
@ApiOperation(value = "Register a service")
- @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = String.class)})
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "OK", response = String.class),
+ @ApiResponse(code = 400, message = "Cannot parse the ServiceRegistrationInfo", response = String.class)})
@PutMapping("/service")
public ResponseEntity<String> putService(//
@RequestBody ServiceRegistrationInfo registrationInfo) {
try {
+ validateRegistrationInfo(registrationInfo);
this.services.put(toService(registrationInfo));
return new ResponseEntity<>("OK", HttpStatus.OK);
} catch (Exception e) {
- return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
@ApiOperation(value = "Delete a service")
- @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")})
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 204, message = "OK"),
+ @ApiResponse(code = 404, message = "Service not found", response = String.class)})
@DeleteMapping("/services")
public ResponseEntity<String> deleteService(//
@RequestParam(name = "name", required = true) String serviceName) {
removePolicies(service);
return new ResponseEntity<>("OK", HttpStatus.NO_CONTENT);
} catch (Exception e) {
- return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
- @ApiOperation(value = "Keep the poilicies alive for a service")
+ @ApiOperation(value = "Heartbeat from a serice")
@ApiResponses(
- value = {@ApiResponse(code = 200, message = "Policies timeout supervision refreshed"),
+ value = { //
+ @ApiResponse(code = 200, message = "Service supervision timer refreshed, OK"),
@ApiResponse(code = 404, message = "The service is not found, needs re-registration")})
@PostMapping("/services/keepalive")
public ResponseEntity<String> keepAliveService(//
@RequestParam(name = "name", required = true) String serviceName) {
try {
- services.getService(serviceName).ping();
+ services.getService(serviceName).keepAlive();
return new ResponseEntity<>("OK", HttpStatus.OK);
- } catch (Exception e) {
+ } catch (ServiceException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
package org.oransc.policyagent.controllers;
+import com.google.gson.annotations.SerializedName;
+
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value = "ServiceRegistrationInfo")
public class ServiceRegistrationInfo {
- @ApiModelProperty(value = "identity of the service")
- public String serviceName;
+ @ApiModelProperty(value = "identity of the service", required = true, allowEmptyValue = false)
+ @SerializedName(value = "serviceName", alternate = {"name"})
- @ApiModelProperty(
- value = "keep alive interval for policies owned by the service. 0 means no timeout supervision."
- + " Polcies that are not refreshed within this time are removed")
- public long keepAliveIntervalSeconds;
+ public String serviceName = "";
- @ApiModelProperty(value = "callback for notifying of RIC recovery")
- public String callbackUrl;
+ @ApiModelProperty(
+ value = "keep alive interval for the service. This is a heartbeat supervision of the service, "
+ + "which in regular intevals must invoke a 'keepAlive' REST call. "
+ + "When a service does not invoke this call within the given time, it is considered unavailble. "
+ + "An unavailable service will be automatically deregistered and its policies will be deleted. "
+ + "Value 0 means no timeout supervision.")
+ @SerializedName("keepAliveIntervalSeconds")
+ public long keepAliveIntervalSeconds = 0;
+
+ @ApiModelProperty(value = "callback for notifying of RIC recovery", required = false, allowEmptyValue = true)
+ @SerializedName("callbackUrl")
+ public String callbackUrl = "";
public ServiceRegistrationInfo() {
}
public final long keepAliveIntervalSeconds;
@ApiModelProperty(value = "time since last invocation by the service")
- public final long timeSincePingSeconds;
+ public final long timeSinceLastActivitySeconds;
- ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds) {
+ @ApiModelProperty(value = "callback for notifying of RIC recovery")
+ public String callbackUrl;
+
+ ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds, String callbackUrl) {
this.serviceName = name;
this.keepAliveIntervalSeconds = keepAliveIntervalSeconds;
- this.timeSincePingSeconds = timeSincePingSeconds;
+ this.timeSinceLastActivitySeconds = timeSincePingSeconds;
+ this.callbackUrl = callbackUrl;
}
}
package org.oransc.policyagent.controllers;
+import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import reactor.core.publisher.Mono;
@RestController
+@Api(tags = "Health check")
public class StatusController {
@GetMapping("/status")
import com.google.common.collect.Iterables;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.time.Duration;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
+/**
+ * The class fetched incoming requests from DMAAP on regular intervals. Each
+ * received request is proceesed by DmaapMessageHandler.
+ */
@Component
public class DmaapMessageConsumer implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(DmaapMessageConsumer.class);
- private final static Duration TIME_BETWEEN_DMAAP_POLLS = Duration.ofSeconds(10);
+ private static final Duration TIME_BETWEEN_DMAAP_POLLS = Duration.ofSeconds(10);
private final ApplicationConfig applicationConfig;
thread.start();
}
- DmaapMessageConsumer(ApplicationConfig applicationConfig, boolean start) {
- this.applicationConfig = applicationConfig;
- }
-
private boolean isDmaapConfigured() {
Properties consumerCfg = applicationConfig.getDmaapConsumerConfig();
Properties producerCfg = applicationConfig.getDmaapPublisherConfig();
}
}
- MRConsumer getMessageRouterConsumer(Properties dmaapConsumerProperties) throws FileNotFoundException, IOException {
+ MRConsumer getMessageRouterConsumer(Properties dmaapConsumerProperties) throws IOException {
return MRClientFactory.createConsumer(dmaapConsumerProperties);
}
return new AsyncRestClient(agentBaseUrl);
}
- MRBatchingPublisher getMessageRouterPublisher(Properties dmaapPublisherProperties)
- throws FileNotFoundException, IOException {
+ MRBatchingPublisher getMessageRouterPublisher(Properties dmaapPublisherProperties) throws IOException {
return MRClientFactory.createBatchingPublisher(dmaapPublisherProperties);
}
}
* limitations under the License.
* ========================LICENSE_END===================================
*/
-
package org.oransc.policyagent.dmaap;
import com.google.gson.Gson;
import org.onap.dmaap.mr.client.MRBatchingPublisher;
import org.oransc.policyagent.clients.AsyncRestClient;
+import org.oransc.policyagent.dmaap.DmaapRequestMessage.Operation;
import org.oransc.policyagent.exceptions.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
+/**
+ * The class handles incoming requests from DMAAP.
+ * <p>
+ * That means: invoke a REST call towards this services and to send back a
+ * response though DMAAP
+ */
public class DmaapMessageHandler {
-
private static final Logger logger = LoggerFactory.getLogger(DmaapMessageHandler.class);
-
private static Gson gson = new GsonBuilder() //
.create(); //
-
private final MRBatchingPublisher dmaapClient;
private final AsyncRestClient agentClient;
Mono<String> createTask(String msg) {
try {
DmaapRequestMessage dmaapRequestMessage = gson.fromJson(msg, ImmutableDmaapRequestMessage.class);
-
return this.invokePolicyAgent(dmaapRequestMessage) //
.onErrorResume(t -> handleAgentCallError(t, msg, dmaapRequestMessage)) //
- .flatMap(response -> sendDmaapResponse(response, dmaapRequestMessage, HttpStatus.OK));
-
+ .flatMap(
+ response -> sendDmaapResponse(response.getBody(), dmaapRequestMessage, response.getStatusCode()));
} catch (Exception e) {
logger.warn("Received unparsable message from DMAAP: {}", msg);
- return Mono.error(e);
+ return Mono.error(e); // Cannot make any response
}
}
- private Mono<String> handleAgentCallError(Throwable t, String origianalMessage,
+ private Mono<ResponseEntity<String>> handleAgentCallError(Throwable t, String originalMessage,
DmaapRequestMessage dmaapRequestMessage) {
logger.debug("Agent call failed: {}", t.getMessage());
- if (t instanceof ServiceException) {
- String errorMessage = prepareBadOperationErrorMessage(t, origianalMessage);
- return sendDmaapResponse(errorMessage, dmaapRequestMessage, HttpStatus.NOT_FOUND) //
- .flatMap(notUsed -> Mono.empty());
- } else {
- return sendDmaapResponse(t.toString(), dmaapRequestMessage, HttpStatus.NOT_FOUND) //
- .flatMap(notUsed -> Mono.empty());
+ HttpStatus status = HttpStatus.NOT_FOUND;
+ String errorMessage = t.getMessage();
+ if (t instanceof WebClientResponseException) {
+ WebClientResponseException exception = (WebClientResponseException) t;
+ status = exception.getStatusCode();
+ errorMessage = exception.getResponseBodyAsString();
+ } else if (t instanceof ServiceException) {
+ status = HttpStatus.BAD_REQUEST;
+ errorMessage = prepareBadOperationErrorMessage(t, originalMessage);
+
}
+ return sendDmaapResponse(errorMessage, dmaapRequestMessage, status) //
+ .flatMap(notUsed -> Mono.empty());
}
- private String prepareBadOperationErrorMessage(Throwable t, String origianalMessage) {
- String badOperation = origianalMessage.substring(origianalMessage.indexOf("operation\":\"") + 12,
- origianalMessage.indexOf(",\"url\":"));
- String errorMessage = t.getMessage().replace("null", badOperation);
- return errorMessage;
+ private String prepareBadOperationErrorMessage(Throwable t, String originalMessage) {
+ String operationParameterStart = "operation\":\"";
+ int indexOfOperationStart = originalMessage.indexOf(operationParameterStart) + operationParameterStart.length();
+ int indexOfOperationEnd = originalMessage.indexOf("\",\"", indexOfOperationStart);
+ String badOperation = originalMessage.substring(indexOfOperationStart, indexOfOperationEnd);
+ return t.getMessage().replace("null", badOperation);
}
- private Mono<String> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) {
+ private Mono<ResponseEntity<String>> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) {
DmaapRequestMessage.Operation operation = dmaapRequestMessage.operation();
- if (operation == null) {
- return Mono.error(new ServiceException("Not implemented operation: " + operation));
- }
- Mono<String> result = null;
String uri = dmaapRequestMessage.url();
- switch (operation) {
- case DELETE:
- result = agentClient.delete(uri);
- break;
- case GET:
- result = agentClient.get(uri);
- break;
- case PUT:
- result = agentClient.put(uri, payload(dmaapRequestMessage));
- break;
- case POST:
- result = agentClient.post(uri, payload(dmaapRequestMessage));
- break;
- default:
- // Nothing, can never get here.
+
+ if (operation == Operation.DELETE) {
+ return agentClient.deleteForEntity(uri);
+ } else if (operation == Operation.GET) {
+ return agentClient.getForEntity(uri);
+ } else if (operation == Operation.PUT) {
+ return agentClient.putForEntity(uri, payload(dmaapRequestMessage));
+ } else if (operation == Operation.POST) {
+ return agentClient.postForEntity(uri, payload(dmaapRequestMessage));
+ } else {
+ return Mono.error(new ServiceException("Not implemented operation: " + operation));
}
- return result;
+
}
private String payload(DmaapRequestMessage message) {
private Mono<String> sendDmaapResponse(String response, DmaapRequestMessage dmaapRequestMessage,
HttpStatus status) {
- return getDmaapResponseMessage(dmaapRequestMessage, response, status) //
+ return createDmaapResponseMessage(dmaapRequestMessage, response, status) //
.flatMap(this::sendToDmaap) //
.onErrorResume(this::handleResponseCallError);
}
}
private Mono<String> handleResponseCallError(Throwable t) {
- logger.debug("Failed to send respons to DMaaP: {}", t.getMessage());
+ logger.debug("Failed to send response to DMaaP: {}", t.getMessage());
return Mono.empty();
}
- private Mono<String> getDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response,
+ private Mono<String> createDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response,
HttpStatus status) {
DmaapResponseMessage dmaapResponseMessage = ImmutableDmaapResponseMessage.builder() //
.status(status.toString()) //
.timestamp(dmaapRequestMessage.timestamp()) //
.build();
String str = gson.toJson(dmaapResponseMessage);
-
return Mono.just(str);
}
this.name = name;
this.keepAliveInterval = keepAliveInterval;
this.callbackUrl = callbackUrl;
- ping();
+ keepAlive();
}
public synchronized Duration getKeepAliveInterval() {
return this.keepAliveInterval;
}
- public synchronized void ping() {
+ public synchronized void keepAlive() {
this.lastPing = Instant.now();
}
package org.oransc.policyagent.repository;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
}
public synchronized Iterable<Service> getAll() {
- return registeredServices.values();
+ return Collections.unmodifiableCollection(registeredServices.values());
}
public synchronized void remove(String name) {
import javax.validation.constraints.NotNull;
+import lombok.AccessLevel;
+import lombok.Getter;
+
import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClient;
import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClientFactory;
import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsRequests;
public Properties systemEnvironment;
final ApplicationConfig appConfig;
+ @Getter(AccessLevel.PROTECTED)
private Disposable refreshTask = null;
private boolean isConsulUsed = false;
public void stop() {
if (refreshTask != null) {
refreshTask.dispose();
- refreshTask = null;
}
}
- Flux<ApplicationConfig> createRefreshTask() {
+ Flux<RicConfigUpdate.Type> createRefreshTask() {
Flux<JsonObject> loadFromFile = Flux.interval(Duration.ZERO, FILE_CONFIG_REFRESH_INTERVAL) //
.filter(notUsed -> configFileExists()) //
.filter(notUsed -> !this.isConsulUsed) //
.flatMap(this::parseConfiguration) //
.flatMap(this::updateConfig) //
.doOnNext(this::handleUpdatedRicConfig) //
- .flatMap(configUpdate -> Flux.just(this.appConfig)) //
+ .flatMap(configUpdate -> Flux.just(configUpdate.getType())) //
.doOnTerminate(() -> logger.error("Configuration refresh task is terminated"));
}
} else if (event == RicConfigUpdate.Type.CHANGED) {
Ric ric = this.rics.get(ricName);
if (ric == null) {
- // Should not happend,just for robustness
+ // Should not happen,just for robustness
addRic(updatedInfo.getRicConfig());
} else {
ric.setRicConfig(updatedInfo.getRicConfig());
*/
@Component
@EnableScheduling
-public class RepositorySupervision {
- private static final Logger logger = LoggerFactory.getLogger(RepositorySupervision.class);
+public class RicSupervision {
+ private static final Logger logger = LoggerFactory.getLogger(RicSupervision.class);
private final Rics rics;
private final Policies policies;
private final Services services;
@Autowired
- public RepositorySupervision(Rics rics, Policies policies, A1ClientFactory a1ClientFactory, PolicyTypes policyTypes,
+ public RicSupervision(Rics rics, Policies policies, A1ClientFactory a1ClientFactory, PolicyTypes policyTypes,
Services services) {
this.rics = rics;
this.policies = policies;
@Scheduled(fixedRate = 1000 * 60)
public void checkAllRics() {
logger.debug("Checking Rics starting");
- createTask().subscribe(this::onRicChecked, null, this::onComplete);
+ createTask().subscribe(ric -> logger.debug("Ric: {} checked", ric.ric.name()), //
+ null, //
+ () -> logger.debug("Checking Rics completed"));
}
private Flux<RicData> createTask() {
return Mono.error(new Exception("Syncronization started"));
}
- @SuppressWarnings("squid:S2629")
- private void onRicChecked(RicData ric) {
- logger.debug("Ric: {} checked", ric.ric.name());
- }
-
- private void onComplete() {
- logger.debug("Checking Rics completed");
- }
-
RicSynchronizationTask createSynchronizationTask() {
return new RicSynchronizationTask(a1ClientFactory, policyTypes, policies, services);
}
this.services = services;
}
- @SuppressWarnings("squid:S2629")
+ @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
public void run(Ric ric) {
logger.debug("Handling ric: {}", ric.getConfig().name());
return Flux.concat(recoverTypes, policiesDeletedInRic, policiesRecreatedInRic);
}
- @SuppressWarnings("squid:S2629")
+ @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
private void onSynchronizationComplete(Ric ric) {
- logger.debug("Synchronization completed for: {}", ric.name());
+ logger.info("Synchronization completed for: {}", ric.name());
ric.setState(RicState.IDLE);
notifyAllServices("Synchronization completed for:" + ric.name());
}
}
}
- @SuppressWarnings("squid:S2629")
+ @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
private void onSynchronizationError(Ric ric, Throwable t) {
logger.warn("Synchronization failed for ric: {}, reason: {}", ric.name(), t.getMessage());
// If synchronization fails, try to remove all instances
() -> onSynchronizationComplete(ric));
}
- @SuppressWarnings("squid:S2629")
+ @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
private void onRecoveryError(Ric ric, Throwable t) {
logger.warn("Synchronization failure recovery failed for ric: {}, reason: {}", ric.name(), t.getMessage());
ric.setState(RicState.UNDEFINED);
package org.oransc.policyagent.tasks;
+import java.time.Duration;
+
import org.oransc.policyagent.clients.A1ClientFactory;
+import org.oransc.policyagent.repository.Lock;
+import org.oransc.policyagent.repository.Lock.LockType;
import org.oransc.policyagent.repository.Policies;
import org.oransc.policyagent.repository.Policy;
import org.oransc.policyagent.repository.Service;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
-import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
- * Periodically checks that services with a keepAliveInterval set are alive. If a service is deemed not alive,
- * all the service's policies are deleted, both in the repository and in the affected Rics, and the service is
- * removed from the repository. This means that the service needs to register again after this.
+ * Periodically checks that services with a keepAliveInterval set are alive. If
+ * a service is deemed not alive, all the service's policies are deleted, both
+ * in the repository and in the affected Rics, and the service is removed from
+ * the repository. This means that the service needs to register again after
+ * this.
*/
@Component
@EnableScheduling
private final Services services;
private final Policies policies;
private A1ClientFactory a1ClientFactory;
+ private final Duration checkInterval;
@Autowired
public ServiceSupervision(Services services, Policies policies, A1ClientFactory a1ClientFactory) {
+ this(services, policies, a1ClientFactory, Duration.ofMinutes(1));
+ }
+
+ public ServiceSupervision(Services services, Policies policies, A1ClientFactory a1ClientFactory,
+ Duration checkInterval) {
this.services = services;
this.policies = policies;
this.a1ClientFactory = a1ClientFactory;
+ this.checkInterval = checkInterval;
+ start();
}
- @Scheduled(fixedRate = 1000 * 60)
- public void checkAllServices() {
+ private void start() {
logger.debug("Checking services starting");
- createTask().subscribe(this::onPolicyDeleted, null, this::onComplete);
+ createTask().subscribe(null, null, () -> logger.error("Checking services unexpectedly terminated"));
}
- @SuppressWarnings("squid:S2629")
- private void onPolicyDeleted(Policy policy) {
- logger.debug("Policy deleted due to inactivity: {}, service: {}", policy.id(), policy.ownerServiceName());
+ private Flux<?> createTask() {
+ return Flux.interval(this.checkInterval) //
+ .flatMap(notUsed -> checkAllServices());
}
- private void onComplete() {
- logger.debug("Checking services completed");
+ Flux<Policy> checkAllServices() {
+ return Flux.fromIterable(services.getAll()) //
+ .filter(Service::isExpired) //
+ .doOnNext(service -> logger.info("Service is expired: {}", service.getName())) //
+ .doOnNext(service -> services.remove(service.getName())) //
+ .flatMap(this::getAllPoliciesForService) //
+ .flatMap(this::deletePolicy);
}
- private Flux<Policy> createTask() {
- synchronized (services) {
- return Flux.fromIterable(services.getAll()) //
- .filter(Service::isExpired) //
- .doOnNext(service -> logger.info("Service is expired: {}", service.getName())) //
- .doOnNext(service -> services.remove(service.getName())) //
- .flatMap(this::getAllPoliciesForService) //
- .doOnNext(policies::remove) //
- .flatMap(this::deletePolicyInRic);
- }
+ @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+ private Flux<Policy> deletePolicy(Policy policy) {
+ Lock lock = policy.ric().getLock();
+ return lock.lock(LockType.SHARED) //
+ .doOnNext(notUsed -> policies.remove(policy)) //
+ .flatMap(notUsed -> deletePolicyInRic(policy))
+ .doOnNext(notUsed -> logger.debug("Policy deleted due to service inactivity: {}, service: {}", policy.id(),
+ policy.ownerServiceName())) //
+ .doOnNext(notUsed -> lock.unlockBlocking()) //
+ .doOnError(throwable -> lock.unlockBlocking()) //
+ .doOnError(throwable -> logger.debug("Failed to delete inactive policy: {}, reason: {}", policy.id(),
+ throwable.getMessage())) //
+ .flatMapMany(notUsed -> Flux.just(policy)) //
+ .onErrorResume(throwable -> Flux.empty());
}
private Flux<Policy> getAllPoliciesForService(Service service) {
.map(nothing -> policy));
}
- @SuppressWarnings("squid:S2629")
+ @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
private Mono<String> handleDeleteFromRicFailure(Policy policy, Throwable e) {
logger.warn("Could not delete policy: {} from ric: {}", policy.id(), policy.ric().name(), e);
return Mono.empty();
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
-import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.oransc.policyagent.clients.AsyncRestClient;
import org.oransc.policyagent.configuration.ApplicationConfig;
import org.oransc.policyagent.configuration.ImmutableRicConfig;
import org.oransc.policyagent.configuration.RicConfig;
import org.oransc.policyagent.repository.Ric.RicState;
import org.oransc.policyagent.repository.Rics;
import org.oransc.policyagent.repository.Services;
-import org.oransc.policyagent.tasks.RepositorySupervision;
+import org.oransc.policyagent.tasks.RicSupervision;
+import org.oransc.policyagent.tasks.ServiceSupervision;
import org.oransc.policyagent.utils.MockA1Client;
import org.oransc.policyagent.utils.MockA1ClientFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
-import org.springframework.http.HttpStatus.Series;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
-import org.springframework.http.client.ClientHttpResponse;
import org.springframework.test.context.junit.jupiter.SpringExtension;
-import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
+ private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
+
@Autowired
ApplicationContext context;
MockA1ClientFactory a1ClientFactory;
@Autowired
- RepositorySupervision supervision;
+ RicSupervision supervision;
@Autowired
Services services;
@TestConfiguration
static class TestBeanFactory {
private final PolicyTypes policyTypes = new PolicyTypes();
+ private final Services services = new Services();
+ private final Policies policies = new Policies();
+ MockA1ClientFactory a1ClientFactory = null;
@Bean
public ApplicationConfig getApplicationConfig() {
@Bean
MockA1ClientFactory getA1ClientFactory() {
- return new MockA1ClientFactory(this.policyTypes);
+ if (a1ClientFactory == null) {
+ this.a1ClientFactory = new MockA1ClientFactory(this.policyTypes);
+ }
+ return this.a1ClientFactory;
}
@Bean
public PolicyTypes getPolicyTypes() {
return this.policyTypes;
}
- }
-
- @LocalServerPort
- private int port;
- private final RestTemplate restTemplate = new RestTemplate();
-
- public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
+ @Bean
+ Policies getPolicies() {
+ return this.policies;
+ }
- @Override
- public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
- return (httpResponse.getStatusCode().series() == Series.CLIENT_ERROR
- || httpResponse.getStatusCode().series() == Series.SERVER_ERROR);
+ @Bean
+ Services getServices() {
+ return this.services;
}
- @Override
- public void handleError(ClientHttpResponse httpResponse) throws IOException {
- System.out.println("Error " + httpResponse.toString());
+ @Bean
+ public ServiceSupervision getServiceSupervision() {
+ Duration checkInterval = Duration.ofMillis(1);
+ return new ServiceSupervision(this.services, this.policies, this.getA1ClientFactory(), checkInterval);
}
}
- private void setRestErrorhandler() {
- restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
- }
+ @LocalServerPort
+ private int port;
@BeforeEach
public void reset() {
@Test
public void testGetRics() throws Exception {
addRic("kista_1");
- String url = baseUrl() + "/rics";
- String rsp = this.restTemplate.getForObject(url, String.class);
- System.out.println(rsp);
+ this.addPolicyType("type1", "kista_1");
+ String url = "/rics?policyType=type1";
+ String rsp = restClient().get(url).block();
assertThat(rsp).contains("kista_1");
- url = baseUrl() + "/rics?policyType=STD_PolicyModelUnconstrained_0.2.0";
- rsp = this.restTemplate.getForObject(url, String.class);
- assertThat(rsp).isEqualTo("[]");
+ // Non existing policy type
+ url = "/rics?policyType=XXXX";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
@Test
String managedElementId = "kista_1";
addRic(ricName, managedElementId);
- String url = baseUrl() + "/ric?managedElementId=" + managedElementId;
- String rsp = this.restTemplate.getForObject(url, String.class);
-
+ String url = "/ric?managedElementId=" + managedElementId;
+ String rsp = restClient().get(url).block();
assertThat(rsp).isEqualTo(ricName);
+
+ // test GET RIC for ManagedElement that does not exist
+ url = "/ric?managedElementId=" + "junk";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
- @Test
- public void testGetRicForManagedElementThatDoesNotExist() throws Exception {
- this.setRestErrorhandler();
- String url = baseUrl() + "/ric?managedElementId=kista_1";
- ResponseEntity<String> entity = this.restTemplate.getForEntity(url, String.class);
- assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+ private String putPolicyUrl(String serviceName, String ricName, String policyTypeName, String policyInstanceId) {
+ String url = "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName
+ + "&service=" + serviceName;
+ return url;
}
@Test
putService(serviceName);
addPolicyType(policyTypeName, ricName);
- String url = baseUrl() + "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName
- + "&service=" + serviceName;
- final String json = jsonString();
+ String url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId);
+ final String policyBody = jsonString();
this.rics.getRic(ricName).setState(Ric.RicState.IDLE);
- this.restTemplate.put(url, createJsonHttpEntity(json));
+ restClient().put(url, policyBody).block();
Policy policy = policies.getPolicy(policyInstanceId);
assertThat(policy).isNotNull();
assertThat(policy.ownerServiceName()).isEqualTo(serviceName);
assertThat(policy.ric().name()).isEqualTo("ric1");
- url = baseUrl() + "/policies";
- String rsp = this.restTemplate.getForObject(url, String.class);
+ url = "/policies";
+ String rsp = restClient().get(url).block();
assertThat(rsp.contains(policyInstanceId)).isTrue();
+ // Test of error codes
+ url = putPolicyUrl(serviceName, ricName + "XX", policyTypeName, policyInstanceId);
+ testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
+
+ url = putPolicyUrl(serviceName, ricName, policyTypeName + "XX", policyInstanceId);
+ testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
+
+ url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId);
+ this.rics.getRic(ricName).setState(Ric.RicState.SYNCHRONIZING);
+ testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED);
+ this.rics.getRic(ricName).setState(Ric.RicState.IDLE);
}
@Test
// In this case service is attempted to be changed
this.addRic("ric1");
this.addRic("ricXXX");
-
this.addPolicy("instance1", "type1", "service1", "ric1");
- this.setRestErrorhandler();
- String urlWrongRic = baseUrl() + "/policy?type=type1&instance=instance1&ric=ricXXX&service=service1";
- ResponseEntity<String> entity = this.putForEntity(urlWrongRic, jsonString());
- assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.METHOD_NOT_ALLOWED);
- Policy policy = policies.getPolicy("instance1");
- assertThat(policy.ric().name()).isEqualTo("ric1"); // Not changed
+ // Try change ric1 -> ricXXX
+ String urlWrongRic = putPolicyUrl("service1", "ricXXX", "type1", "instance1");
+ testErrorCode(restClient().put(urlWrongRic, jsonString()), HttpStatus.METHOD_NOT_ALLOWED);
}
@Test
public void testGetPolicy() throws Exception {
- String url = baseUrl() + "/policy?instance=id";
+ String url = "/policy?instance=id";
Policy policy = addPolicy("id", "typeName", "service1", "ric1");
{
- String rsp = this.restTemplate.getForObject(url, String.class);
+ String rsp = restClient().get(url).block();
assertThat(rsp).isEqualTo(policy.json());
}
{
policies.remove(policy);
- ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class);
- assertThat(rsp.getStatusCodeValue()).isEqualTo(HttpStatus.NO_CONTENT.value());
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
}
@Test
public void testDeletePolicy() throws Exception {
- String url = baseUrl() + "/policy?instance=id";
addPolicy("id", "typeName", "service1", "ric1");
assertThat(policies.size()).isEqualTo(1);
- this.restTemplate.delete(url);
+ String url = "/policy?instance=id";
+ ResponseEntity<String> entity = restClient().deleteForEntity(url).block();
+ assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
assertThat(policies.size()).isEqualTo(0);
+
+ // Delete a non existing policy
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
@Test
addPolicyType("type1", "ric1");
addPolicyType("type2", "ric2");
- String url = baseUrl() + "/policy_schemas";
- String rsp = this.restTemplate.getForObject(url, String.class);
- System.out.println("*** " + rsp);
+ String url = "/policy_schemas";
+ String rsp = this.restClient().get(url).block();
assertThat(rsp).contains("type1");
assertThat(rsp).contains("[{\"title\":\"type2\"}");
List<String> info = parseSchemas(rsp);
assertThat(info.size()).isEqualTo(2);
- url = baseUrl() + "/policy_schemas?ric=ric1";
- rsp = this.restTemplate.getForObject(url, String.class);
+ url = "/policy_schemas?ric=ric1";
+ rsp = restClient().get(url).block();
assertThat(rsp).contains("type1");
info = parseSchemas(rsp);
assertThat(info.size()).isEqualTo(1);
+
+ // Get schema for non existing RIC
+ url = "/policy_schemas?ric=ric1XXX";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
@Test
addPolicyType("type1", "ric1");
addPolicyType("type2", "ric2");
- String url = baseUrl() + "/policy_schema?id=type1";
- String rsp = this.restTemplate.getForObject(url, String.class);
- System.out.println(rsp);
+ String url = "/policy_schema?id=type1";
+ String rsp = restClient().get(url).block();
+ logger.info(rsp);
assertThat(rsp).contains("type1");
assertThat(rsp).contains("title");
+
+ // Get non existing schema
+ url = "/policy_schema?id=type1XX";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
@Test
addPolicyType("type1", "ric1");
addPolicyType("type2", "ric2");
- String url = baseUrl() + "/policy_types";
- String rsp = this.restTemplate.getForObject(url, String.class);
+ String url = "/policy_types";
+ String rsp = restClient().get(url).block();
assertThat(rsp).isEqualTo("[\"type2\",\"type1\"]");
- url = baseUrl() + "/policy_types?ric=ric1";
- rsp = this.restTemplate.getForObject(url, String.class);
+ url = "/policy_types?ric=ric1";
+ rsp = restClient().get(url).block();
assertThat(rsp).isEqualTo("[\"type1\"]");
+
+ // Get policy types for non existing RIC
+ url = "/policy_types?ric=ric1XXX";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
@Test
public void testGetPolicies() throws Exception {
reset();
- String url = baseUrl() + "/policies";
addPolicy("id1", "type1", "service1");
- String rsp = this.restTemplate.getForObject(url, String.class);
- System.out.println(rsp);
+ String url = "/policies";
+ String rsp = restClient().get(url).block();
+ logger.info(rsp);
List<PolicyInfo> info = parseList(rsp, PolicyInfo.class);
assertThat(info).size().isEqualTo(1);
PolicyInfo policyInfo = info.get(0);
addPolicy("id2", "type1", "service2");
addPolicy("id3", "type2", "service1");
- String url = baseUrl() + "/policies?type=type1";
- String rsp = this.restTemplate.getForObject(url, String.class);
- System.out.println(rsp);
+ String url = "/policies?type=type1";
+ String rsp = restClient().get(url).block();
+ logger.info(rsp);
assertThat(rsp).contains("id1");
assertThat(rsp).contains("id2");
assertThat(rsp.contains("id3")).isFalse();
- url = baseUrl() + "/policies?type=type1&service=service2";
- rsp = this.restTemplate.getForObject(url, String.class);
- System.out.println(rsp);
+ url = "/policies?type=type1&service=service2";
+ rsp = restClient().get(url).block();
+ logger.info(rsp);
assertThat(rsp.contains("id1")).isFalse();
assertThat(rsp).contains("id2");
assertThat(rsp.contains("id3")).isFalse();
+
+ // Test get policies for non existing type
+ url = "/policies?type=type1XXX";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
+
+ // Test get policies for non existing RIC
+ url = "/policies?ric=XXX";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
@Test
public void testPutAndGetService() throws Exception {
// PUT
- putService("name");
+ putService("name", 0);
- // GET
- String url = baseUrl() + "/services?serviceName=name";
- String rsp = this.restTemplate.getForObject(url, String.class);
+ // GET one service
+ String url = "/services?name=name";
+ String rsp = restClient().get(url).block();
List<ServiceStatus> info = parseList(rsp, ServiceStatus.class);
assertThat(info.size()).isEqualTo(1);
ServiceStatus status = info.iterator().next();
- assertThat(status.keepAliveIntervalSeconds).isEqualTo(1);
+ assertThat(status.keepAliveIntervalSeconds).isEqualTo(0);
assertThat(status.serviceName).isEqualTo("name");
// GET (all)
- url = baseUrl() + "/services";
- rsp = this.restTemplate.getForObject(url, String.class);
+ url = "/services";
+ rsp = restClient().get(url).block();
assertThat(rsp.contains("name")).isTrue();
- System.out.println(rsp);
+ logger.info(rsp);
// Keep alive
- url = baseUrl() + "/services/keepalive?name=name";
- ResponseEntity<String> entity = this.restTemplate.postForEntity(url, null, String.class);
+ url = "/services/keepalive?name=name";
+ ResponseEntity<String> entity = restClient().postForEntity(url, null).block();
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
- // DELETE
+ // DELETE service
assertThat(services.size()).isEqualTo(1);
- url = baseUrl() + "/services?name=name";
- this.restTemplate.delete(url);
+ url = "/services?name=name";
+ restClient().delete(url).block();
assertThat(services.size()).isEqualTo(0);
// Keep alive, no registerred service
- url = baseUrl() + "/services/keepalive?name=name";
- setRestErrorhandler();
- entity = this.restTemplate.postForEntity(url, null, String.class);
- assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+ testErrorCode(restClient().post("/services/keepalive?name=name", ""), HttpStatus.NOT_FOUND);
+
+ // PUT servive with crap payload
+ testErrorCode(restClient().put("/service", "crap"), HttpStatus.BAD_REQUEST);
+ testErrorCode(restClient().put("/service", "{}"), HttpStatus.BAD_REQUEST);
+
+ // GET non existing servive
+ testErrorCode(restClient().get("/services?name=XXX"), HttpStatus.NOT_FOUND);
+ }
+
+ @Test
+ public void testServiceSupervision() throws Exception {
+ putService("service1", 1);
+ addPolicyType("type1", "ric1");
+
+ String url = putPolicyUrl("service1", "ric1", "type1", "instance1");
+ final String policyBody = jsonString();
+ restClient().put(url, policyBody).block();
+
+ assertThat(policies.size()).isEqualTo(1);
+ assertThat(services.size()).isEqualTo(1);
+
+ // Timeout after ~1 second
+ await().untilAsserted(() -> assertThat(policies.size()).isEqualTo(0));
+ assertThat(services.size()).isEqualTo(0);
}
@Test
addPolicy("id", "typeName", "service1", "ric1");
assertThat(policies.size()).isEqualTo(1);
- String url = baseUrl() + "/policy_status?instance=id";
- String rsp = this.restTemplate.getForObject(url, String.class);
+ String url = "/policy_status?instance=id";
+ String rsp = restClient().get(url).block();
assertThat(rsp.equals("OK")).isTrue();
+
+ // GET non existing policy status
+ url = "/policy_status?instance=XXX";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
}
private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
return addPolicy(id, typeName, service, "ric");
}
- private String createServiceJson(String name) {
- ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, 1, "callbackUrl");
+ private String createServiceJson(String name, long keepAliveIntervalSeconds) {
+ ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, "callbackUrl");
String json = gson.toJson(service);
return json;
}
private void putService(String name) {
- String url = baseUrl() + "/service";
- HttpEntity<String> entity = createJsonHttpEntity(createServiceJson(name));
- this.restTemplate.put(url, entity);
+ putService(name, 0);
+ }
+
+ private void putService(String name, long keepAliveIntervalSeconds) {
+ String url = "/service";
+ String body = createServiceJson(name, keepAliveIntervalSeconds);
+ restClient().put(url, body).block();
}
private String baseUrl() {
private final String baseUrl;
static AtomicInteger nextCount = new AtomicInteger(0);
private final int count;
- private final RepositorySupervision supervision;
+ private final RicSupervision supervision;
- ConcurrencyTestRunnable(String baseUrl, RepositorySupervision supervision) {
+ ConcurrencyTestRunnable(String baseUrl, RicSupervision supervision) {
this.baseUrl = baseUrl;
this.count = nextCount.incrementAndGet();
this.supervision = supervision;
private void putPolicy(String name) {
String putUrl = baseUrl + "/policy?type=type1&instance=" + name + "&ric=ric1&service=service1";
- this.restTemplate.put(putUrl, createJsonHttpEntity("{}"));
+ restTemplate.put(putUrl, createJsonHttpEntity("{}"));
}
private void deletePolicy(String name) {
String deleteUrl = baseUrl + "/policy?instance=" + name;
- this.restTemplate.delete(deleteUrl);
+ restTemplate.delete(deleteUrl);
}
}
t.join();
}
assertThat(policies.size()).isEqualTo(0);
- System.out.println("Concurrency test took " + Duration.between(startTime, Instant.now()));
+ logger.info("Concurrency test took " + Duration.between(startTime, Instant.now()));
+ }
+
+ private AsyncRestClient restClient() {
+ return new AsyncRestClient(baseUrl());
+ }
+
+ private void testErrorCode(Mono<?> request, HttpStatus expStatus) {
+ StepVerifier.create(request) //
+ .expectSubscription() //
+ .expectErrorMatches(t -> checkWebClientError(t, expStatus)) //
+ .verify();
+ }
+
+ private boolean checkWebClientError(Throwable t, HttpStatus expStatus) {
+ assertTrue(t instanceof WebClientResponseException);
+ WebClientResponseException e = (WebClientResponseException) t;
+ assertThat(e.getStatusCode()).isEqualTo(expStatus);
+ return true;
}
private MockA1Client getA1Client(String ricName) throws ServiceException {
return new HttpEntity<String>(content, headers);
}
- private ResponseEntity<String> putForEntity(String url, String jsonBody) {
- return restTemplate.exchange(url, HttpMethod.PUT, createJsonHttpEntity(jsonBody), String.class);
- }
-
private static <T> List<T> parseList(String jsonString, Class<T> clazz) {
List<T> result = new ArrayList<>();
JsonArray jsonArr = JsonParser.parseString(jsonString).getAsJsonArray();
import org.oransc.policyagent.repository.PolicyTypes;
import org.oransc.policyagent.repository.Rics;
import org.oransc.policyagent.utils.MockA1ClientFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class MockPolicyAgent {
+ private static final Logger logger = LoggerFactory.getLogger(MockPolicyAgent.class);
@Autowired
Rics rics;
PolicyType type = ImmutablePolicyType.builder().name(typeName).schema(schema).build();
policyTypes.put(type);
} catch (Exception e) {
- System.out.println("Could not load json schema " + e);
+ logger.error("Could not load json schema ", e);
}
}
}
private int port;
private void keepServerAlive() {
- System.out.println("Keeping server alive!");
+ logger.info("Keeping server alive!");
try {
synchronized (this) {
this.wait();
}
} catch (Exception ex) {
- System.out.println("Unexpected: " + ex.toString());
+ logger.error("Unexpected: " + ex);
}
}
}
@Test
+ @SuppressWarnings("squid:S2699") // Tests should include assertions. This test is only for keeping the server alive,
+ // so it will only be confusing to add an assertion.
public void runMock() throws Exception {
keepServerAlive();
}
A1Client sdncOscA1ClientMock;
@Mock
- A1Client sdnrOnapA1ClientMock;
+ A1Client sdncOnapA1ClientMock;
private ImmutableRicConfig ricConfig =
ImmutableRicConfig.builder().name(RIC_NAME).baseUrl("baseUrl").managedElementIds(new Vector<>()).build();
@Test
public void createStd_ok() {
- whenGetProtocolVersionSdnrOnapA1ClientThrowException();
+ whenGetProtocolVersionSdncOnapA1ClientThrowException();
whenGetProtocolVersionSdncOscA1ClientThrowException();
whenGetProtocolVersionOscA1ClientThrowException();
whenGetProtocolVersionStdA1ClientReturnCorrectProtocol();
@Test
public void createOsc_ok() {
- whenGetProtocolVersionSdnrOnapA1ClientThrowException();
+ whenGetProtocolVersionSdncOnapA1ClientThrowException();
whenGetProtocolVersionSdncOscA1ClientThrowException();
whenGetProtocolVersionOscA1ClientReturnCorrectProtocol();
@Test
public void createSdncOsc_ok() {
- whenGetProtocolVersionSdnrOnapA1ClientThrowException();
+ whenGetProtocolVersionSdncOnapA1ClientThrowException();
whenGetProtocolVersionSdncOscA1ClientReturnCorrectProtocol();
StepVerifier.create(factoryUnderTest.createA1Client(ric)) //
}
@Test
- public void createSdnrOnap_ok() {
- whenGetProtocolVersionSdnrOnapA1ClientReturnCorrectProtocol();
+ public void createSdncOnap_ok() {
+ whenGetProtocolVersionSdncOnapA1ClientReturnCorrectProtocol();
StepVerifier.create(factoryUnderTest.createA1Client(ric)) //
.expectSubscription() //
- .expectNext(sdnrOnapA1ClientMock) //
+ .expectNext(sdncOnapA1ClientMock) //
.verifyComplete();
assertEquals(A1ProtocolType.SDNC_ONAP, ric.getProtocolVersion(), "Not correct protocol");
@Test
public void createWithNoProtocol_error() {
- whenGetProtocolVersionSdnrOnapA1ClientThrowException();
+ whenGetProtocolVersionSdncOnapA1ClientThrowException();
whenGetProtocolVersionSdncOscA1ClientThrowException();
whenGetProtocolVersionOscA1ClientThrowException();
whenGetProtocolVersionStdA1ClientThrowException();
assertEquals(A1ProtocolType.STD_V1, ric.getProtocolVersion(), "Not correct protocol");
- verifyNoMoreInteractions(sdnrOnapA1ClientMock);
+ verifyNoMoreInteractions(sdncOnapA1ClientMock);
verifyNoMoreInteractions(sdncOscA1ClientMock);
verifyNoMoreInteractions(oscA1ClientMock);
verifyNoMoreInteractions(stdA1ClientMock);
}
- private void whenGetProtocolVersionSdnrOnapA1ClientThrowException() {
- doReturn(sdnrOnapA1ClientMock).when(factoryUnderTest).createSdnrOnapA1Client(ric);
- when(sdnrOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.error(new Exception(EXCEPTION_MESSAGE)));
+ private void whenGetProtocolVersionSdncOnapA1ClientThrowException() {
+ doReturn(sdncOnapA1ClientMock).when(factoryUnderTest).createSdncOnapA1Client(ric);
+ when(sdncOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.error(new Exception(EXCEPTION_MESSAGE)));
}
- private void whenGetProtocolVersionSdnrOnapA1ClientReturnCorrectProtocol() {
- doReturn(sdnrOnapA1ClientMock).when(factoryUnderTest).createSdnrOnapA1Client(any(Ric.class));
- when(sdnrOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.just(A1ProtocolType.SDNC_ONAP));
+ private void whenGetProtocolVersionSdncOnapA1ClientReturnCorrectProtocol() {
+ doReturn(sdncOnapA1ClientMock).when(factoryUnderTest).createSdncOnapA1Client(any(Ric.class));
+ when(sdncOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.just(A1ProtocolType.SDNC_ONAP));
}
private void whenGetProtocolVersionSdncOscA1ClientThrowException() {
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-import org.oransc.policyagent.clients.AsyncRestClient.AsyncRestClientException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
Mono<String> returnedMono = clientUnderTest.get(REQUEST_URL);
- StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
- .verify();
+ StepVerifier.create(returnedMono)
+ .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
}
@Test
mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
Mono<String> returnedMono = clientUnderTest.put(REQUEST_URL, TEST_JSON);
- StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
- .verify();
+ StepVerifier.create(returnedMono)
+ .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
}
@Test
mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
Mono<String> returnedMono = clientUnderTest.delete(REQUEST_URL);
- StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
- .verify();
+ StepVerifier.create(returnedMono)
+ .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
}
@Test
mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
Mono<String> returnedMono = clientUnderTest.post(REQUEST_URL, TEST_JSON);
- StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
- .verify();
+ StepVerifier.create(returnedMono)
+ .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
}
@Test
mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
Mono<String> returnedMono = clientUnderTest.postWithAuthHeader(REQUEST_URL, TEST_JSON, USERNAME, PASSWORD);
- StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
- .verify();
+ StepVerifier.create(returnedMono)
+ .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
}
}
RicConfigUpdate update =
appConfigUnderTest.setConfiguration(Arrays.asList(RIC_CONFIG_1), null, null).blockFirst();
assertEquals(RicConfigUpdate.Type.ADDED, update.getType());
- assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configuraions.");
+ assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configurations.");
assertEquals(RIC_CONFIG_1, appConfigUnderTest.getRic(RIC_CONFIG_1.name()),
"Not correct Ric retrieved from configurations.");
@Test
public void dmaapNotConfigured_thenDoNothing() {
- messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false));
+ messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock));
doReturn(true).when(messageConsumerUnderTest).sleep(any(Duration.class));
@Test
public void dmaapConfiguredAndNoMessages_thenPollOnce() throws Exception {
- messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false));
+ messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock));
doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class));
@Test
public void dmaapConfiguredAndErrorGettingMessages_thenLogWarning() throws Exception {
- messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false));
+ messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock));
doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class));
@Test
public void dmaapConfiguredAndOneMessage_thenPollOnceAndProcessMessage() throws Exception {
- messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false));
+ messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock));
doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class));
import org.oransc.policyagent.repository.ImmutablePolicyType;
import org.oransc.policyagent.repository.PolicyType;
import org.oransc.policyagent.utils.LoggingUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
public class DmaapMessageHandlerTest {
-
+ private static final Logger logger = LoggerFactory.getLogger(DmaapMessageHandlerTest.class);
private static final String URL = "url";
private final MRBatchingPublisher dmaapClient = mock(MRBatchingPublisher.class);
return gson.toJson(dmaapRequestMessage(operation));
}
+ private Mono<ResponseEntity<String>> okResponse() {
+ ResponseEntity<String> entity = new ResponseEntity<>("OK", HttpStatus.OK);
+ return Mono.just(entity);
+ }
+
@Test
public void testMessageParsing() {
String message = dmaapInputMessage(Operation.DELETE);
- System.out.println(message);
+ logger.info(message);
DmaapRequestMessage parsedMessage = gson.fromJson(message, ImmutableDmaapRequestMessage.class);
assertTrue(parsedMessage != null);
assertFalse(parsedMessage.payload().isPresent());
message = dmaapInputMessage(Operation.PUT);
- System.out.println(message);
+ logger.info(message);
parsedMessage = gson.fromJson(message, ImmutableDmaapRequestMessage.class);
assertTrue(parsedMessage != null);
assertTrue(parsedMessage.payload().isPresent());
@Test
public void successfulDelete() throws IOException {
- doReturn(Mono.just("OK")).when(agentClient).delete(anyString());
+ doReturn(okResponse()).when(agentClient).deleteForEntity(anyString());
doReturn(1).when(dmaapClient).send(anyString());
doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
.expectNext("OK") //
.verifyComplete(); //
- verify(agentClient).delete(URL);
+ verify(agentClient).deleteForEntity(URL);
verifyNoMoreInteractions(agentClient);
verify(dmaapClient).send(anyString());
@Test
public void successfulGet() throws IOException {
- doReturn(Mono.just("OK")).when(agentClient).get(anyString());
+ doReturn(okResponse()).when(agentClient).getForEntity(anyString());
doReturn(1).when(dmaapClient).send(anyString());
doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
.expectNext("OK") //
.verifyComplete(); //
- verify(agentClient).get(URL);
+ verify(agentClient).getForEntity(URL);
verifyNoMoreInteractions(agentClient);
verify(dmaapClient).send(anyString());
@Test
public void successfulPut() throws IOException {
- doReturn(Mono.just("OK")).when(agentClient).put(anyString(), anyString());
+ doReturn(okResponse()).when(agentClient).putForEntity(anyString(), anyString());
doReturn(1).when(dmaapClient).send(anyString());
doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
.expectNext("OK") //
.verifyComplete(); //
- verify(agentClient).put(URL, payloadAsString());
+ verify(agentClient).putForEntity(URL, payloadAsString());
verifyNoMoreInteractions(agentClient);
verify(dmaapClient).send(anyString());
@Test
public void successfulPost() throws IOException {
- doReturn(Mono.just("OK")).when(agentClient).post(anyString(), anyString());
+ doReturn(okResponse()).when(agentClient).postForEntity(anyString(), anyString());
doReturn(1).when(dmaapClient).send(anyString());
doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
.expectNext("OK") //
.verifyComplete(); //
- verify(agentClient).post(URL, payloadAsString());
+ verify(agentClient).postForEntity(URL, payloadAsString());
verifyNoMoreInteractions(agentClient);
verify(dmaapClient).send(anyString());
@Test
public void exceptionWhenCallingPolicyAgent_thenNotFoundResponse() throws IOException {
- String errorCause = "Refused";
- doReturn(Mono.error(new Exception(errorCause))).when(agentClient).put(anyString(), any());
+ WebClientResponseException except = new WebClientResponseException(400, "Refused", null, null, null, null);
+ doReturn(Mono.error(except)).when(agentClient).putForEntity(anyString(), any());
doReturn(1).when(dmaapClient).send(anyString());
doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
.expectSubscription() //
.verifyComplete(); //
- verify(agentClient).put(anyString(), anyString());
+ verify(agentClient).putForEntity(anyString(), anyString());
verifyNoMoreInteractions(agentClient);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(dmaapClient).send(captor.capture());
String actualMessage = captor.getValue();
- assertThat(actualMessage.contains(HttpStatus.NOT_FOUND + "\",\"message\":\"java.lang.Exception: " + errorCause))
- .isTrue();
+ assertThat(actualMessage.contains(HttpStatus.BAD_REQUEST.toString())).isTrue();
verify(dmaapClient).sendBatchWithResponse();
verifyNoMoreInteractions(dmaapClient);
verify(dmaapClient).send(captor.capture());
String actualMessage = captor.getValue();
assertThat(actualMessage
- .contains(HttpStatus.NOT_FOUND + "\",\"message\":\"Not implemented operation: " + badOperation)).isTrue();
+ .contains(HttpStatus.BAD_REQUEST + "\",\"message\":\"Not implemented operation: " + badOperation)).isTrue();
verify(dmaapClient).sendBatchWithResponse();
verifyNoMoreInteractions(dmaapClient);
Lock lock = new Lock();
Mono<Lock> seq = lock.lock(LockType.EXCLUSIVE) //
- .doOnNext(l -> System.out.println("1 " + l)) //
.flatMap(l -> lock.lock(LockType.EXCLUSIVE)) //
- .flatMap(l -> lock.unlock()) //
- .doOnNext(l -> System.out.println("2 " + l)); //
+ .flatMap(l -> lock.unlock());
asynchUnlock(lock);
StepVerifier.create(seq) //
package org.oransc.policyagent.tasks;
+import static ch.qos.logback.classic.Level.ERROR;
+import static ch.qos.logback.classic.Level.WARN;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Properties;
import java.util.Vector;
import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.ImmutableEnvProperties;
import org.oransc.policyagent.clients.A1ClientFactory;
import org.oransc.policyagent.configuration.ApplicationConfig;
+import org.oransc.policyagent.configuration.ApplicationConfig.RicConfigUpdate.Type;
import org.oransc.policyagent.configuration.ApplicationConfigParser;
import org.oransc.policyagent.configuration.ImmutableRicConfig;
import org.oransc.policyagent.configuration.RicConfig;
+import org.oransc.policyagent.repository.ImmutablePolicy;
+import org.oransc.policyagent.repository.ImmutablePolicyType;
import org.oransc.policyagent.repository.Policies;
+import org.oransc.policyagent.repository.Policy;
import org.oransc.policyagent.repository.PolicyTypes;
+import org.oransc.policyagent.repository.Ric;
import org.oransc.policyagent.repository.Rics;
import org.oransc.policyagent.repository.Services;
import org.oransc.policyagent.utils.LoggingUtils;
@ExtendWith(MockitoExtension.class)
public class RefreshConfigTaskTest {
+ private static final boolean CONFIG_FILE_EXISTS = true;
+ private static final boolean CONFIG_FILE_DOES_NOT_EXIST = false;
+
private RefreshConfigTask refreshTaskUnderTest;
@Spy
}
private RefreshConfigTask createTestObject(boolean configFileExists) {
- RefreshConfigTask obj = spy(new RefreshConfigTask(appConfig, new Rics(), new Policies(), new Services(),
- new PolicyTypes(), new A1ClientFactory(appConfig)));
- doReturn(configFileExists).when(obj).configFileExists();
+ return createTestObject(configFileExists, new Rics(), new Policies(), true);
+ }
+
+ private RefreshConfigTask createTestObject(boolean configFileExists, Rics rics, Policies policies,
+ boolean stubConfigFileExists) {
+ RefreshConfigTask obj = spy(new RefreshConfigTask(appConfig, rics, policies, new Services(), new PolicyTypes(),
+ new A1ClientFactory(appConfig)));
+ if (stubConfigFileExists) {
+ doReturn(configFileExists).when(obj).configFileExists();
+ }
return obj;
}
+ @Test
+ public void startWithStubbedRefresh_thenTerminationLogged() {
+ refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false);
+ doReturn(Flux.empty()).when(refreshTaskUnderTest).createRefreshTask();
+
+ final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, ERROR);
+
+ refreshTaskUnderTest.start();
+
+ assertThat(logAppender.list.toString().contains("Configuration refresh terminated")).isTrue();
+ }
+
+ @Test
+ public void startWithStubbedRefreshReturnError_thenErrorAndTerminationLogged() {
+ refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false);
+ doReturn(Flux.error(new Exception("Error"))).when(refreshTaskUnderTest).createRefreshTask();
+
+ final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, ERROR);
+
+ refreshTaskUnderTest.start();
+
+ ILoggingEvent event = logAppender.list.get(0);
+ assertThat(event.getThrowableProxy().getMessage()).isEqualTo("Error");
+ assertThat(event.toString().contains("Configuration refresh terminated due to exception")).isTrue();
+ }
+
+ @Test
+ public void stop_thenTaskIsDisposed() throws Exception {
+ refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false);
+ refreshTaskUnderTest.systemEnvironment = new Properties();
+
+ refreshTaskUnderTest.start();
+ refreshTaskUnderTest.stop();
+
+ assertThat(refreshTaskUnderTest.getRefreshTask().isDisposed()).isTrue();
+ }
+
@Test
public void whenTheConfigurationFits_thenConfiguredRicsArePutInRepository() throws Exception {
- refreshTaskUnderTest = this.createTestObject(true);
+ refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_EXISTS);
refreshTaskUnderTest.systemEnvironment = new Properties();
// When
doReturn(getCorrectJson()).when(refreshTaskUnderTest).createInputStream(any());
StepVerifier.create(refreshTaskUnderTest.createRefreshTask()) //
.expectSubscription() //
- .expectNext(this.appConfig) //
- .expectNext(this.appConfig) //
+ .expectNext(Type.ADDED) //
+ .expectNext(Type.ADDED) //
.thenCancel() //
.verify();
// Then
- verify(refreshTaskUnderTest, times(1)).loadConfigurationFromFile();
+ verify(refreshTaskUnderTest).loadConfigurationFromFile();
+
+ verify(refreshTaskUnderTest, times(2)).runRicSynchronization(any(Ric.class));
Iterable<RicConfig> ricConfigs = appConfig.getRicConfigs();
RicConfig ricConfig = ricConfigs.iterator().next();
@Test
public void whenFileExistsButJsonIsIncorrect_thenNoRicsArePutInRepository() throws Exception {
- refreshTaskUnderTest = this.createTestObject(true);
+ refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_EXISTS);
refreshTaskUnderTest.systemEnvironment = new Properties();
// When
.verify();
// Then
- verify(refreshTaskUnderTest, times(1)).loadConfigurationFromFile();
+ verify(refreshTaskUnderTest).loadConfigurationFromFile();
assertThat(appConfig.getRicConfigs().size()).isEqualTo(0);
}
@Test
public void whenPeriodicConfigRefreshNoConsul_thenErrorIsLogged() {
- refreshTaskUnderTest = this.createTestObject(false);
+ refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST);
refreshTaskUnderTest.systemEnvironment = new Properties();
EnvProperties props = properties();
doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any());
doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props);
- Flux<?> err = Flux.error(new IOException());
- doReturn(err).when(cbsClient).updates(any(), any(), any());
+ when(cbsClient.updates(any(), any(), any())).thenReturn(Flux.error(new IOException()));
- final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class);
- Flux<?> task = refreshTaskUnderTest.createRefreshTask();
+ final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, WARN);
+ Flux<Type> task = refreshTaskUnderTest.createRefreshTask();
StepVerifier //
.create(task) //
}
@Test
- public void whenPeriodicConfigRefreshSuccess_thenNewConfigIsCreated() throws Exception {
- refreshTaskUnderTest = this.createTestObject(false);
+ public void whenPeriodicConfigRefreshSuccess_thenNewConfigIsCreatedAndRepositoryUpdated() throws Exception {
+ Rics rics = new Rics();
+ Policies policies = new Policies();
+ refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, rics, policies, false);
refreshTaskUnderTest.systemEnvironment = new Properties();
+ RicConfig changedRicConfig = getRicConfig(RIC_1_NAME);
+ rics.put(new Ric(changedRicConfig));
+ RicConfig removedRicConfig = getRicConfig("removed");
+ Ric removedRic = new Ric(removedRicConfig);
+ rics.put(removedRic);
+ appConfig.setConfiguration(Arrays.asList(changedRicConfig, removedRicConfig), null, null);
+
+ Policy policy = getPolicy(removedRic);
+ policies.put(policy);
+
EnvProperties props = properties();
doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any());
doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props);
JsonObject configAsJson = getJsonRootObject();
String newBaseUrl = "newBaseUrl";
modifyTheRicConfiguration(configAsJson, newBaseUrl);
- Flux<JsonObject> json = Flux.just(configAsJson);
- doReturn(json).when(cbsClient).updates(any(), any(), any());
+ when(cbsClient.updates(any(), any(), any())).thenReturn(Flux.just(configAsJson));
+ doNothing().when(refreshTaskUnderTest).runRicSynchronization(any(Ric.class));
- Flux<ApplicationConfig> task = refreshTaskUnderTest.createRefreshTask();
+ Flux<Type> task = refreshTaskUnderTest.createRefreshTask();
StepVerifier //
.create(task) //
.expectSubscription() //
- .expectNext(appConfig) //
- .expectNext(appConfig) //
+ .expectNext(Type.CHANGED) //
+ .expectNext(Type.ADDED) //
+ .expectNext(Type.REMOVED) //
.thenCancel() //
.verify();
- verify(refreshTaskUnderTest, times(2)).runRicSynchronization(any());
- assertThat(appConfig.getRicConfigs()).isNotNull();
+ assertThat(appConfig.getRicConfigs().size()).isEqualTo(2);
assertThat(appConfig.getRic(RIC_1_NAME).baseUrl()).isEqualTo(newBaseUrl);
+ String ric2Name = "ric2";
+ assertThat(appConfig.getRic(ric2Name)).isNotNull();
+
+ assertThat(rics.size()).isEqualTo(2);
+ assertThat(rics.get(RIC_1_NAME).getConfig().baseUrl()).isEqualTo(newBaseUrl);
+ assertThat(rics.get(ric2Name)).isNotNull();
+
+ assertThat(policies.size()).isEqualTo(0);
+ }
+
+ private RicConfig getRicConfig(String name) {
+ RicConfig ricConfig = ImmutableRicConfig.builder() //
+ .name(name) //
+ .baseUrl("url") //
+ .managedElementIds(Collections.emptyList()) //
+ .build();
+ return ricConfig;
+ }
+
+ private Policy getPolicy(Ric ric) {
+ ImmutablePolicyType type = ImmutablePolicyType.builder() //
+ .name("type") //
+ .schema("{}") //
+ .build();
+ Policy policy = ImmutablePolicy.builder() //
+ .id("id") //
+ .type(type) //
+ .lastModified("lastModified") //
+ .ric(ric) //
+ .json("{}") //
+ .ownerServiceName("ownerServiceName") //
+ .build();
+ return policy;
}
private void modifyTheRicConfiguration(JsonObject configAsJson, String newBaseUrl) {
import reactor.core.publisher.Mono;
@ExtendWith(MockitoExtension.class)
-public class RepositorySupervisionTest {
+public class RicSupervisionTest {
private static final String POLICY_TYPE_1_NAME = "type1";
private static final PolicyType POLICY_TYPE_1 = ImmutablePolicyType.builder() //
.name(POLICY_TYPE_1_NAME) //
setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID)));
setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME)));
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
supervisorUnderTest.checkAllRics();
RIC_1.setState(RicState.UNDEFINED);
rics.put(RIC_1);
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
RIC_1.setState(RicState.SYNCHRONIZING);
rics.put(RIC_1);
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
supervisorUnderTest.checkAllRics();
setUpGetPolicyIdentitiesToReturn(new Exception("Failed"));
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
supervisorUnderTest.checkAllRics();
verify(supervisorUnderTest).checkAllRics();
setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID)));
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID, "Another_policy")));
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
setUpGetPolicyIdentitiesToReturn(Collections.emptyList());
setUpGetPolicyTypeIdentitiesToReturn(new Exception("Failed"));
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
supervisorUnderTest.checkAllRics();
verify(supervisorUnderTest).checkAllRics();
setUpGetPolicyIdentitiesToReturn(Collections.emptyList());
setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME, "another_policy_type")));
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
setUpGetPolicyIdentitiesToReturn(Collections.emptyList());
setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME, "another_policy_type")));
- RepositorySupervision supervisorUnderTest =
- spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+ RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
await().atMost(Durations.FIVE_SECONDS).with().pollInterval(Durations.ONE_SECOND).until(service::isExpired);
- serviceSupervisionUnderTest.checkAllServices();
+ serviceSupervisionUnderTest.checkAllServices().blockLast();
assertThat(policies.size()).isEqualTo(0);
assertThat(services.size()).isEqualTo(0);
final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ServiceSupervision.class, WARN);
- serviceSupervisionUnderTest.checkAllServices();
+ serviceSupervisionUnderTest.checkAllServices().blockLast();
assertThat(policies.size()).isEqualTo(0);
assertThat(services.size()).isEqualTo(0);
ServiceSupervision serviceSupervisionUnderTest =
new ServiceSupervision(services, policies, a1ClientFactoryMock);
- serviceSupervisionUnderTest.checkAllServices();
+ serviceSupervisionUnderTest.checkAllServices().blockLast();
assertThat(policies.size()).isEqualTo(1);
assertThat(services.size()).isEqualTo(1);
ServiceSupervision serviceSupervisionUnderTest =
new ServiceSupervision(services, policies, a1ClientFactoryMock);
- serviceSupervisionUnderTest.checkAllServices();
+ serviceSupervisionUnderTest.checkAllServices().blockLast();
assertThat(policies.size()).isEqualTo(1);
assertThat(services.size()).isEqualTo(1);