diff options
Diffstat (limited to 'src/main/java/org/onap/a1pesimulator/service')
25 files changed, 2051 insertions, 0 deletions
diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/A1Service.java b/src/main/java/org/onap/a1pesimulator/service/a1/A1Service.java new file mode 100644 index 0000000..aa2c407 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/A1Service.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +import java.io.IOException; +import java.net.URISyntaxException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestClientException; + +public interface A1Service { + + ResponseEntity<String> healthCheck() throws URISyntaxException; + + ResponseEntity<String> putPolicy(Integer policyTypeId, String policyId, String body) throws URISyntaxException; + + ResponseEntity<String> putPolicySchema(Integer policyTypeId, String body) throws URISyntaxException; + + ResponseEntity<String> deletePolicy(Integer policyTypeId, String policyId) throws URISyntaxException; + + ResponseEntity<String> getPolicyTypeIds() throws RestClientException, URISyntaxException; + + ResponseEntity<String> getPolicyType(Integer policyTypeId) throws RestClientException, URISyntaxException; + + ResponseEntity<String> getPolicyIdsOfType(Integer policyTypeId) + throws RestClientException, URISyntaxException, IOException; + + ResponseEntity<String> getPolicy(Integer policyTypeId, String policyInstanceId) + throws RestClientException, URISyntaxException; + + ResponseEntity<String> getAllPoliciesForType(Integer policyTypeId) + throws IOException, RestClientException, URISyntaxException; + +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/OnPolicyAction.java b/src/main/java/org/onap/a1pesimulator/service/a1/OnPolicyAction.java new file mode 100644 index 0000000..821e395 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/OnPolicyAction.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +public interface OnPolicyAction { + + boolean isForMe(Integer policyTypeId, String policyId, String body); + + void onPolicy(Integer policyTypeId, String policyId, String body); +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/PolicyInstancesHolder.java b/src/main/java/org/onap/a1pesimulator/service/a1/PolicyInstancesHolder.java new file mode 100644 index 0000000..a5e5a07 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/PolicyInstancesHolder.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.util.JsonUtils; +import org.springframework.stereotype.Service; + +@Service +public class PolicyInstancesHolder { + + Map<String, String> cellPolicyMap = new HashMap<>(); + + public void addPolicy(String policyId, String body) { + cellPolicyMap.put(policyId, body); + } + + public void removePolicy(String policyId) { + cellPolicyMap.remove(policyId); + } + + public boolean containsPoliciesForCell(String cell) { + return cellPolicyMap.values().stream().map(this::getCellListFromPolicyInstance).flatMap(List::stream) + .anyMatch(c -> c.equals(cell)); + } + + private List<String> getCellListFromPolicyInstance(String policyInstance) { + RanUeHandoverOnPolicyAction.UeHandoverPolicy policy = + JsonUtils.INSTANCE.deserialize(policyInstance, RanUeHandoverOnPolicyAction.UeHandoverPolicy.class); + return policy.getResources().stream().flatMap(resources -> resources.getCellIdList().stream()) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/RanA1ServiceLocalStoreImpl.java b/src/main/java/org/onap/a1pesimulator/service/a1/RanA1ServiceLocalStoreImpl.java new file mode 100644 index 0000000..feb481d --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/RanA1ServiceLocalStoreImpl.java @@ -0,0 +1,116 @@ +package org.onap.a1pesimulator.service.a1; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +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.stereotype.Service; +import org.springframework.web.client.RestClientException; + +/** + * A1 Service implementation which uses in-memory policy data store. + */ +@Service +public class RanA1ServiceLocalStoreImpl implements A1Service { + + private static final Logger log = LoggerFactory.getLogger(RanA1ServiceLocalStoreImpl.class); + + private Map<Integer, Map<String, String>> policyTypesMap = new HashMap<>(); + private Map<Integer, String> policySchemaMap = new HashMap<>(); + private ObjectMapper mapper; + + public RanA1ServiceLocalStoreImpl(ObjectMapper mapper) { + this.mapper = mapper; + } + + @Override + public ResponseEntity<String> healthCheck() throws RestClientException { + return ResponseEntity.ok().build(); + } + + @Override + public ResponseEntity<String> putPolicySchema(Integer policyTypeId, String body) { + policySchemaMap.put(policyTypeId, body); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Override + public ResponseEntity<String> putPolicy(final Integer policyTypeId, final String policyId, final String body) { + log.debug("Create or update policy id {} of policy type id {} with following content {} ", policyId, + policyTypeId, body); + if (policyTypesMap.containsKey(policyTypeId)) { + policyTypesMap.get(policyTypeId).put(policyId, body); + } else { + Map<String, String> policies = new HashMap<>(); + policies.put(policyId, body); + policyTypesMap.put(policyTypeId, policies); + } + return ResponseEntity.accepted().build(); + } + + @Override + public ResponseEntity<String> deletePolicy(final Integer policyTypeId, final String policyId) { + log.debug("Delete policy id {} of policy type id {}", policyId, policyTypeId); + if (policyTypesMap.containsKey(policyTypeId)) { + policyTypesMap.get(policyTypeId).remove(policyId); + return ResponseEntity.accepted().build(); + } else { + return ResponseEntity.notFound().build(); + } + } + + @Override + public ResponseEntity<String> getPolicyTypeIds() throws RestClientException { + return getRestAsString(policySchemaMap.keySet()); + } + + @Override + public ResponseEntity<String> getPolicyType(final Integer policyTypeId) throws RestClientException { + if (policySchemaMap.isEmpty() || !policySchemaMap.containsKey(policyTypeId)) { + return ResponseEntity.notFound().build(); + } else { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(policySchemaMap.get(policyTypeId)); + } + } + + @Override + public ResponseEntity<String> getPolicyIdsOfType(final Integer policyTypeId) throws RestClientException { + Set<String> result = new HashSet<>(); + if (policyTypesMap.containsKey(policyTypeId)) { + result = policyTypesMap.get(policyTypeId).keySet(); + } + return getRestAsString(result); + } + + @Override + public ResponseEntity<String> getPolicy(final Integer policyTypeId, final String policyId) + throws RestClientException { + if (policyTypesMap.containsKey(policyTypeId) && policyTypesMap.get(policyTypeId).containsKey(policyId)) { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON) + .body(policyTypesMap.get(policyTypeId).get(policyId)); + } else { + return ResponseEntity.notFound().build(); + } + } + + @Override + public ResponseEntity<String> getAllPoliciesForType(final Integer policyTypeId) throws RestClientException { + return getRestAsString(policyTypesMap.get(policyTypeId)); + } + + private ResponseEntity<String> getRestAsString(Object obj) throws RestClientException { + try { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(mapper.writeValueAsString(obj)); + } catch (JsonProcessingException e) { + throw new RuntimeException("Cannot serialize object", e); + } + } + +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/RanUeHandoverOnPolicyAction.java b/src/main/java/org/onap/a1pesimulator/service/a1/RanUeHandoverOnPolicyAction.java new file mode 100644 index 0000000..fdbbd97 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/RanUeHandoverOnPolicyAction.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import lombok.Getter; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.data.ue.UserEquipmentNotification; +import org.onap.a1pesimulator.service.cell.RanCellService; +import org.onap.a1pesimulator.service.ue.RanUeService; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.JsonUtils.JsonUtilsException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +@Service +public class RanUeHandoverOnPolicyAction implements OnPolicyAction { + + private static final String TOPIC_UE = "/topic/userEquipment"; + private static final String POLICY_EXAMPLE = + "{ \"scope\": { \"ueId\": \"emergency_samsung_s10_01\" }, \"resources\": [ { \"cellIdList\": [ \"Cell1\" ], \"preference\": \"AVOID\" } ] }"; + private static final Logger log = LoggerFactory.getLogger(RanUeHandoverOnPolicyAction.class); + + private final RanUeService ranUeService; + private final SimpMessagingTemplate messagingTemplate; + private final RanCellService ranCellService; + private final PolicyInstancesHolder policyHolder; + + public RanUeHandoverOnPolicyAction(SimpMessagingTemplate messagingTemplate, RanUeService ranUeService, + RanCellService ranCellService, PolicyInstancesHolder policyHolder) { + this.messagingTemplate = messagingTemplate; + this.ranUeService = ranUeService; + this.ranCellService = ranCellService; + this.policyHolder = policyHolder; + } + + @Override + public boolean isForMe(Integer policyTypeId, String policyId, String body) { + try { + JsonUtils.INSTANCE.deserialize(body, UeHandoverPolicy.class); + return true; + } catch (JsonUtilsException ex) { + log.info( + "Policy {} is not for me because policy body doesn't comply with Ue Handover policy. Follow example: {}", + policyId, POLICY_EXAMPLE); + return false; + } + } + + @Override + public void onPolicy(Integer policyTypeId, String policyId, String body) { + UeHandoverPolicy policy = JsonUtils.INSTANCE.deserialize(body, UeHandoverPolicy.class); + String ueId = policy.getScope().getUeId(); + List<String> cellId = policy.getResources().stream().flatMap(resources -> resources.getCellIdList().stream()) + .collect(Collectors.toList()); + + if (ueId == null || cellId.isEmpty()) { + log.warn("Cannot handover because {} is not provided in preload! Follow example: {}", + ueId == null ? "ueId" : "cellId", POLICY_EXAMPLE); + return; + } + + Optional<String> activeCellId = getActiveCellForUE(ueId); + + if (!activeCellId.isPresent()) { + log.warn("Cannot handover ue {} because there is no active cell in range", ueId); + return; + } + + ranUeService.handover(ueId, activeCellId.get()); + messagingTemplate.convertAndSend(TOPIC_UE, new UserEquipmentNotification(ueId, activeCellId.get())); + } + + private Optional<String> getActiveCellForUE(String ue) { + Optional<UserEquipment> equipment = ranUeService.getUserEquipment(ue); + if (!equipment.isPresent()) { + log.warn("Cannot handover because is not ue with id: {}", ue); + return Optional.empty(); + } + + return ranCellService.getAllCellsWithStatus().stream().filter(c -> !c.isFailureMode()) + .map(cellWithStatus -> cellWithStatus.getCell().getIdentifier()) + .filter(cell -> ranUeService.canHandover(ue, cell)) + .filter(cell -> !policyHolder.containsPoliciesForCell(cell)).findFirst(); + } + + @Getter + public static class UeHandoverPolicy { + + private Scope scope; + private List<Resources> resources; + } + + @Getter + public static class Scope { + + private String ueId; + } + + @Getter + public static class Resources { + + private List<String> cellIdList; + private Preference preference; + } + + public enum Preference { + SHALL("SHALL"), PREFER("PREFER"), AVOID("AVOID"), FORBID("FORBID"); + public final String value; + + Preference(String stateName) { + this.value = stateName; + } + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/a1/SetLowRangeValuesOnPolicyAction.java b/src/main/java/org/onap/a1pesimulator/service/a1/SetLowRangeValuesOnPolicyAction.java new file mode 100644 index 0000000..e608aa5 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/a1/SetLowRangeValuesOnPolicyAction.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.a1; + +import java.util.List; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.onap.a1pesimulator.service.ves.RanVesBrokerService; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.RanVesUtils; +import org.springframework.stereotype.Service; + +@Service +public class SetLowRangeValuesOnPolicyAction implements OnPolicyAction { + + private final RanVesBrokerService vesBrokerService; + + public SetLowRangeValuesOnPolicyAction(RanVesBrokerService vesBrokerService) { + this.vesBrokerService = vesBrokerService; + } + + @Override + public boolean isForMe(Integer policyTypeId, String policyId, String body) { + // disabling for now + return false; + } + + @Override + public void onPolicy(Integer policyTypeId, String policyId, String body) { + vesBrokerService.getPeriodicEventsCache().values().forEach(this::updateEvent); + } + + private void updateEvent(RanPeriodicVesEvent periodicEvent) { + List<AdditionalMeasurement> lowRangeValues = RanVesUtils.setLowRangeValues( + periodicEvent.getEvent().getMeasurementFields().getAdditionalMeasurements()); + Event clonedEvent = JsonUtils.INSTANCE.clone(periodicEvent.getEvent()); + clonedEvent.getMeasurementFields().setAdditionalMeasurements(lowRangeValues); + periodicEvent.getSendVesRunnable().updateEvent(clonedEvent); + } + +} diff --git a/src/main/java/org/onap/a1pesimulator/service/cell/RanCellService.java b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellService.java new file mode 100644 index 0000000..a12ed2c --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellService.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.cell; + +import java.util.Collection; +import java.util.Set; +import org.onap.a1pesimulator.data.Topology; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.CellWithStatus; +import org.onap.a1pesimulator.data.cell.RanCell; + +public interface RanCellService { + + Topology getTopology(); + + Set<String> getCellIds(); + + CellDetails getCellById(String id); + + RanCell getCells(); + + void failure(String id); + + void recoverFromFailure(String id); + + Collection<CellWithStatus> getAllCellsWithStatus(); +}
\ No newline at end of file diff --git a/src/main/java/org/onap/a1pesimulator/service/cell/RanCellServiceImpl.java b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellServiceImpl.java new file mode 100644 index 0000000..0c0ab00 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellServiceImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.cell; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.data.Topology; +import org.onap.a1pesimulator.data.cell.Cell; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.CellWithStatus; +import org.onap.a1pesimulator.data.cell.RanCell; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.onap.a1pesimulator.service.ves.RanVesHolder; +import org.springframework.stereotype.Service; + +@Service +public class RanCellServiceImpl implements RanCellService { + + private final RanCellsHolder ranCellsHolder; + private final RanUeHolder ueHolder; + private final RanVesHolder vesHolder; + + public RanCellServiceImpl(RanCellsHolder ranCellsHolder, RanUeHolder ueHolder, RanVesHolder vesHolder) { + this.ranCellsHolder = ranCellsHolder; + this.ueHolder = ueHolder; + this.vesHolder = vesHolder; + } + + @Override + public Set<String> getCellIds() { + return ranCellsHolder.getCellIds(); + } + + @Override + public CellDetails getCellById(String id) { + CellDetails cellDetails = ranCellsHolder.getCellById(id); + cellDetails.setConnectedUserEquipments(getConnectedUserEquipments(cellDetails.getId())); + return cellDetails; + } + + @Override + public RanCell getCells() { + Collection<CellDetails> cellDetails = ranCellsHolder.getAllCells(); + cellDetails.forEach(cell -> cell.setConnectedUserEquipments(getConnectedUserEquipments(cell.getId()))); + return new RanCell(cellDetails, cellDetails.size()); + } + + @Override + public Topology getTopology() { + Collection<CellDetails> cellList = ranCellsHolder.getCellDetailsList(); + cellList.forEach(cell -> cell.setConnectedUserEquipments(getConnectedUserEquipments(cell.getId()))); + return Topology.builder().cells(cellList).userEquipments(ueHolder.getUserEquipments()).build(); + } + + private Set<String> getConnectedUserEquipments(String cellId) { + Collection<UserEquipment> cellUes = ueHolder.getUserEquipmentsConnectedToCell(cellId); + return cellUes.stream().map(UserEquipment::getId).collect(Collectors.toSet()); + } + + @Override + public void failure(String id) { + ranCellsHolder.markCellInFailure(id); + } + + @Override + public void recoverFromFailure(String id) { + ranCellsHolder.unmarkCellInFailure(id); + } + + @Override + public Collection<CellWithStatus> getAllCellsWithStatus() { + return ranCellsHolder.getAllCells().stream().map(this::wrapCellWithStatus).collect(Collectors.toList()); + } + + private CellWithStatus wrapCellWithStatus(CellDetails cell) { + Cell c = Cell.builder().identifier(cell.getId()).build(); + return CellWithStatus.builder().cell(c).failureMode(ranCellsHolder.isInFailureMode(cell.getId())) + .vesEnabled(vesHolder.isEventEnabled(cell.getId())).state(cell.getCurrentState()).build(); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/cell/RanCellStateService.java b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellStateService.java new file mode 100644 index 0000000..d69629e --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellStateService.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.cell; + +import java.util.Optional; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +@Service +public class RanCellStateService { + + private static final Logger log = LoggerFactory.getLogger(RanCellStateService.class); + + private final RanCellsHolder cellsHolder; + private final SimpMessagingTemplate messagingTemplate; + + public static final String TOPIC_CELL = "/topic/cellStatus"; + + public RanCellStateService(RanCellsHolder cellsHolder, SimpMessagingTemplate messagingTemplate) { + this.cellsHolder = cellsHolder; + this.messagingTemplate = messagingTemplate; + } + + public void activateState(String identifier) { + Optional<CellDetails> cellDetails = getCell(identifier); + if (cellExist(cellDetails, identifier, "Activate")) { + boolean changed = nextStateIfPossible(cellDetails.get(), CellStateEnum.INACTIVE); + if (changed) { + sendCellNotification(cellDetails.get()); + } + } + } + + public void failingState(String identifier) { + Optional<CellDetails> cellDetails = getCell(identifier); + if (cellExist(cellDetails, identifier, "Failing")) { + boolean changed = nextStateIfPossible(cellDetails.get(), CellStateEnum.ACTIVE); + if (changed) { + sendCellNotification(cellDetails.get()); + } + } + } + + public void stopState(String identifier) { + Optional<CellDetails> cellDetails = getCell(identifier); + if (cellExist(cellDetails, identifier, "Stop")) { + boolean changed = previousStateIfPossible(cellDetails.get()); + if (changed) { + sendCellNotification(cellDetails.get()); + } + } + } + + private boolean cellExist(Optional<CellDetails> cellDetails, String identifier, String actionName) { + if (cellDetails.isEmpty()) { + log.info("Cell not found for {} identifier! '{}' action won't be executed!", identifier, actionName); + return false; + } + + return true; + } + + private boolean previousStateIfPossible(CellDetails cell) { + + CellStateEnum state = cell.getCellStateMachine().getState(); + if (state == CellStateEnum.SLEEPING || state == CellStateEnum.GOING_TO_SLEEP || state == CellStateEnum.ACTIVE) { + cell.previousState(); + } else { + log.info("Cell {} is in {} state! The changing of the state isn't allowed." + + "Supported states are: GOING_TO_SLEEP, SLEEPING, ACTIVE.", cell.getId(), + cell.getCellStateMachine().getState().value); + return false; + } + + return true; + } + + private boolean nextStateIfPossible(CellDetails cellDetails, CellStateEnum shouldBe) { + + if (cellDetails.getCellStateMachine().getState() == shouldBe) { + cellDetails.nextState(); + } else { + log.info( + "Cell {} is in {} state. The changing of the state isn't allowed. " + "The supported state is: {}!", + cellDetails.getId(), cellDetails.getCellStateMachine().getState().value, shouldBe.value); + return false; + } + + return true; + } + + private Optional<CellDetails> getCell(String identifier) { + CellDetails cell = null; + try { + cell = cellsHolder.getCellById(identifier); + } catch (RuntimeException e) { + log.info("Exception was thrown: ", e); + } + + if (cell == null) { + return Optional.empty(); + } + + return Optional.of(cell); + } + + private void sendCellNotification(CellDetails cellDetails) { + messagingTemplate.convertAndSend(TOPIC_CELL, cellDetails); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/cell/RanCellsHolder.java b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellsHolder.java new file mode 100644 index 0000000..86fa32c --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/cell/RanCellsHolder.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.cell; + +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.BinaryOperator; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.CellList.Cell; +import org.onap.a1pesimulator.data.cell.CellList.CellData; +import org.onap.a1pesimulator.util.TopologyReader; +import org.springframework.stereotype.Service; + +@Service +public class RanCellsHolder { + + private Map<String, CellDetails> cellDetailsById; + private final Collection<CellInFailureMode> cellsInFailureMode = new HashSet<>(); + + private final TopologyReader topologyReader; + + public RanCellsHolder(TopologyReader topologyReader) { + this.topologyReader = topologyReader; + refresh(); + } + + public Set<String> getCellIds() { + return cellDetailsById.keySet(); + } + + public CellDetails getCellById(String id) { + if (!cellDetailsById.containsKey(id)) { + throw new RuntimeException(MessageFormat.format("Cell not found: {0}", id)); + } + return cellDetailsById.get(id); + } + + public Collection<CellDetails> getCellDetailsList() { + return cellDetailsById.values(); + } + + public Collection<CellDetails> getAllCells() { + return cellDetailsById.values(); + } + + public void markCellInFailure(String id) { + cellsInFailureMode.add(CellInFailureMode.builder().id(id).build()); + } + + public boolean isInFailureMode(String id) { + return cellsInFailureMode.stream().anyMatch(byIdPredicate(id)); + } + + public void unmarkCellInFailure(String id) { + cellsInFailureMode.removeIf(byIdPredicate(id)); + } + + public Optional<CellInFailureMode> getCellsInFailureMode(String id) { + return cellsInFailureMode.stream().filter(byIdPredicate(id)).findFirst(); + } + + @Getter + @Builder + public static class CellInFailureMode { + + private final String id; + @Setter + private Long sleepingModeDetectedTime; + } + + public void refresh() { + List<CellData> cellDatas = topologyReader.loadCellTopology().getCellList(); + cellDetailsById = cellDatas.stream().collect(Collectors.toMap(cellData -> cellData.getCell().getNodeId(), + this::toCellDetails, throwingMerger(), TreeMap::new)); + } + + public boolean hasChanged() { + return topologyReader.topologyCellHasChanged(); + } + + private <T> BinaryOperator<T> throwingMerger() { + return (u, v) -> { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }; + } + + private CellDetails toCellDetails(CellData data) { + Cell cell = data.getCell(); + return CellDetails.builder().id(cell.getNodeId()).latitude(cell.getLatitude()).longitude(cell.getLongitude()) + .build(); + } + + public static Predicate<CellInFailureMode> byIdPredicate(String id) { + return cell -> cell.getId().equals(id); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/distance/DistanceService.java b/src/main/java/org/onap/a1pesimulator/service/distance/DistanceService.java new file mode 100644 index 0000000..18462e5 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/distance/DistanceService.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.distance; + +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.util.DistanceCalculator; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class DistanceService { + + private Double cellRange; + + public DistanceService(@Value("${topology.cell.range}") Double cellRange) { + this.cellRange = cellRange; + } + + public boolean isInRange(CellDetails cell, UserEquipment ue) { + return DistanceCalculator + .isInRange(cell.getLatitude(), cell.getLongitude(), ue.getLatitude(), ue.getLongitude(), + cellRange); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ue/RanUeHolder.java b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeHolder.java new file mode 100644 index 0000000..d31135d --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeHolder.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ue; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.cell.RanCellsHolder; +import org.onap.a1pesimulator.service.distance.DistanceService; +import org.onap.a1pesimulator.util.TopologyReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class RanUeHolder { + + private static final Logger log = LoggerFactory.getLogger(RanUeHolder.class); + + private Map<String, UserEquipment> userEquipmentsById; + + private final TopologyReader topologyReader; + private final DistanceService distanceService; + + private final RanCellsHolder ranCellsHolder; + + public RanUeHolder(TopologyReader topologyReader, DistanceService distanceService, RanCellsHolder ranCellsHolder) { + this.topologyReader = topologyReader; + this.distanceService = distanceService; + this.ranCellsHolder = ranCellsHolder; + refresh(); + } + + public Collection<UserEquipment> getUserEquipments() { + return userEquipmentsById.values(); + } + + public Collection<UserEquipment> getUserEquipmentsConnectedToCell(String cellId) { + return userEquipmentsById.values().stream().filter(ue -> cellId.equalsIgnoreCase(ue.getCellId())) + .collect(Collectors.toList()); + } + + public Optional<UserEquipment> getUserEquipment(String id) { + return userEquipmentsById.values().stream().filter(ue -> id.equalsIgnoreCase(ue.getId())).findAny(); + } + + public void refresh() { + Collection<UserEquipment> ues = topologyReader.loadUeTopology(); + userEquipmentsById = ues.stream().filter(this::validate) + .collect(Collectors.toMap(UserEquipment::getId, Function.identity())); + } + + public boolean hasChanged() { + return topologyReader.topologyUeHasChanged(); + } + + private boolean validate(UserEquipment ue) { + CellDetails cell = ranCellsHolder.getCellById(ue.getCellId()); + boolean inRange = distanceService.isInRange(cell, ue); + if (!inRange) { + log.warn("UE {} is not in range of preferred cell {}", ue.getId(), cell.getId()); + } + return true; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ue/RanUeService.java b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeService.java new file mode 100644 index 0000000..556ed23 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeService.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ue; + +import java.util.Collection; +import java.util.Optional; +import org.onap.a1pesimulator.data.ue.RanUserEquipment; +import org.onap.a1pesimulator.data.ue.UserEquipment; + +public interface RanUeService { + + Collection<UserEquipment> getUserEquipments(); + + Collection<UserEquipment> getUserEquipmentsConnectedToCell(String cellId); + + Optional<UserEquipment> getUserEquipment(String id); + + RanUserEquipment getUes(); + + void handover(String ueId, String cellId); + + boolean canHandover(String ueId, String cellId); +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ue/RanUeServiceImpl.java b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeServiceImpl.java new file mode 100644 index 0000000..da6f5bc --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ue/RanUeServiceImpl.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ue; + +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.ue.RanUserEquipment; +import org.onap.a1pesimulator.data.ue.UserEquipment; +import org.onap.a1pesimulator.service.cell.RanCellsHolder; +import org.onap.a1pesimulator.service.distance.DistanceService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class RanUeServiceImpl implements RanUeService { + + private static final Logger log = LoggerFactory.getLogger(RanUeServiceImpl.class); + + private final RanUeHolder ueHolder; + private final RanCellsHolder ranCellsHolder; + private final DistanceService distanceService; + + public RanUeServiceImpl(RanUeHolder ueHolder, RanCellsHolder ranCellsHolder, DistanceService distanceService) { + this.ueHolder = ueHolder; + this.ranCellsHolder = ranCellsHolder; + this.distanceService = distanceService; + } + + @Override + public Collection<UserEquipment> getUserEquipments() { + return ueHolder.getUserEquipments(); + } + + @Override + public RanUserEquipment getUes() { + Collection<UserEquipment> uesCollection = ueHolder.getUserEquipments(); + return new RanUserEquipment(uesCollection, uesCollection.size()); + } + + @Override + public Collection<UserEquipment> getUserEquipmentsConnectedToCell(String cellId) { + return ueHolder.getUserEquipmentsConnectedToCell(cellId); + } + + @Override + public Optional<UserEquipment> getUserEquipment(String id) { + Optional<UserEquipment> userEquipment = ueHolder.getUserEquipment(id); + userEquipment.ifPresent(ue -> ue.setCellsInRange(getCellsIdsInRange(ue))); + return userEquipment; + } + + @Override + public void handover(String ueId, String cellId) { + Optional<UserEquipment> userEquipment = getUserEquipment(ueId); + if (!userEquipment.isPresent()) { + log.warn("Cannot handover ue {} to cell {}, because ue does not exist!", ueId, cellId); + return; + } + userEquipment.get().setCellId(cellId); + } + + @Override + public boolean canHandover(String ueId, String cellId) { + Optional<UserEquipment> userEquipment = getUserEquipment(ueId); + return userEquipment.map(equipment -> equipment.getCellsInRange().stream().anyMatch(cellId::equalsIgnoreCase)) + .orElse(false); + } + + private Collection<String> getCellsIdsInRange(UserEquipment ue) { + return ranCellsHolder.getAllCells().stream().filter(cell -> distanceService.isInRange(cell, ue)) + .map(CellDetails::getId).collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/OnEventAction.java b/src/main/java/org/onap/a1pesimulator/service/ves/OnEventAction.java new file mode 100644 index 0000000..b34594a --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/OnEventAction.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import org.onap.a1pesimulator.data.ves.Event; + +@FunctionalInterface +public interface OnEventAction { + + void onEvent(Event event); +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanCellEventCustomizer.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanCellEventCustomizer.java new file mode 100644 index 0000000..9922329 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanCellEventCustomizer.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.util.List; +import java.util.Optional; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable.EventCustomizer; +import org.onap.a1pesimulator.util.Constants; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.RanVesUtils; +import org.springframework.stereotype.Service; + +@Service +public class RanCellEventCustomizer implements EventCustomizer { + + private static final String UE_PARAM_TRAFFIC_MODEL_RANGE = "[[20-50]]"; + private final RanUeHolder ranUeHolder; + + public RanCellEventCustomizer(RanUeHolder ueHolder) { + this.ranUeHolder = ueHolder; + } + + @Override + public Event apply(Event t) { + Event event = JsonUtils.INSTANCE.clone(t); + return customizeEvent(event); + } + + private Event customizeEvent(Event event) { + RanVesUtils.updateHeader(event); + enrichWithUeData(event); + randomizeEvent(event); + return event; + } + + private void randomizeEvent(Event event) { + List<AdditionalMeasurement> additionalMeasurementsToRandomize = + event.getMeasurementFields().getAdditionalMeasurements(); + event.getMeasurementFields().setAdditionalMeasurements( + RanVesUtils.randomizeAdditionalMeasurements(additionalMeasurementsToRandomize)); + } + + private void enrichWithUeData(Event event) { + + Optional<AdditionalMeasurement> identity = event.getMeasurementFields().getAdditionalMeasurements().stream() + .filter(msrmnt -> Constants.MEASUREMENT_FIELD_IDENTIFIER + .equalsIgnoreCase( + msrmnt.getName())) + .findAny(); + identity.ifPresent(m -> addTrafficModelMeasurement(event, m)); + } + + private void addTrafficModelMeasurement(Event event, AdditionalMeasurement identity) { + AdditionalMeasurement trafficModelMeasurement = + RanVesUtils.buildTrafficModelMeasurement(identity, ranUeHolder, UE_PARAM_TRAFFIC_MODEL_RANGE); + event.getMeasurementFields().getAdditionalMeasurements().add(trafficModelMeasurement); + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanCellFailureEventCustomizer.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanCellFailureEventCustomizer.java new file mode 100644 index 0000000..ac2c4fc --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanCellFailureEventCustomizer.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.text.MessageFormat; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable.EventCustomizer; +import org.onap.a1pesimulator.util.Constants; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.RanVesUtils; + +public class RanCellFailureEventCustomizer implements EventCustomizer { + + private static final String UE_PARAM_TRAFFIC_MODEL_RANGE = "[[50->10]]"; + private final RanUeHolder ranUeHolder; + private final Event event; + + private final Map<Key, Value> additionalMeasurementsValues = new HashMap<>(); + private final ValueFactory valueFactory; + + public RanCellFailureEventCustomizer(Event event, RanUeHolder ranUeHolder) { + this.ranUeHolder = ranUeHolder; + this.event = event; + valueFactory = new ValueFactory(); + collectAdditionalMeasurementValues(event); + } + + @Override + public Event apply(Event t) { + return customizeEvent(JsonUtils.INSTANCE.clone(this.event)); + } + + private void collectAdditionalMeasurementValues(Event event) { + Collection<AdditionalMeasurement> additionalMeasurementsToResolve = + event.getMeasurementFields().getAdditionalMeasurements(); + additionalMeasurementsToResolve.forEach(this::collectAdditionalMeasurementValue); + } + + private void collectAdditionalMeasurementValue(AdditionalMeasurement m) { + for (Entry<String, String> entry : m.getHashMap().entrySet()) { + if (!RanVesUtils.isRange(entry.getValue())) { + continue; + } + additionalMeasurementsValues + .putIfAbsent(new Key(m.getName(), entry.getKey()), valueFactory.getInstance(entry.getValue())); + } + } + + private Event customizeEvent(Event event) { + RanVesUtils.updateHeader(event); + enrichWithUeData(event); + resolveRanges(event); + return event; + } + + private void resolveRanges(Event event) { + List<AdditionalMeasurement> additionalMeasurementsToResolve = + event.getMeasurementFields().getAdditionalMeasurements(); + + additionalMeasurementsToResolve.forEach(this::resolveRanges); + event.getMeasurementFields().setAdditionalMeasurements(additionalMeasurementsToResolve); + } + + private void resolveRanges(AdditionalMeasurement m) { + for (Entry<String, String> entry : m.getHashMap().entrySet()) { + Key key = new Key(m.getName(), entry.getKey()); + if (!additionalMeasurementsValues.containsKey(key)) { + continue; + } + Value value = additionalMeasurementsValues.get(key); + value.current = value.calculateCurrentValue(); + entry.setValue(value.current.toString()); + } + } + + private void enrichWithUeData(Event event) { + + Optional<AdditionalMeasurement> identity = event.getMeasurementFields().getAdditionalMeasurements().stream() + .filter(msrmnt -> Constants.MEASUREMENT_FIELD_IDENTIFIER + .equalsIgnoreCase( + msrmnt.getName())) + .findAny(); + identity.ifPresent(m -> addTrafficModelMeasurement(event, m)); + } + + private void addTrafficModelMeasurement(Event event, AdditionalMeasurement identity) { + AdditionalMeasurement trafficModelMeasurement = + RanVesUtils.buildTrafficModelMeasurement(identity, ranUeHolder, UE_PARAM_TRAFFIC_MODEL_RANGE); + event.getMeasurementFields().getAdditionalMeasurements().add(trafficModelMeasurement); + + collectAdditionalMeasurementValue(trafficModelMeasurement); + } + + // -----------helper classes + + private static class ValueFactory { + + public Value getInstance(String value) { + String[] split; + if (RanVesUtils.isRandomRange(value)) { + split = RanVesUtils.splitRandomRange(value); + return new RandomValue(Integer.valueOf(split[0]), Integer.valueOf(split[1])); + } + if (RanVesUtils.isTrandingRange(value)) { + split = RanVesUtils.splitTrendingRange(value); + Integer start = Integer.valueOf(split[0]); + Integer end = Integer.valueOf(split[1]); + if (start < end) { + return new RaisingValue(start, end); + } else if (start > end) { + return new DecreasingValue(start, end); + } + } + throw new RuntimeException(MessageFormat.format("Cannot instantiate Value from string: {0}", value)); + } + } + + private abstract static class Value { + + protected Integer start; + protected Integer end; + protected Integer current; + + public Value(Integer start, Integer end) { + this.start = start; + this.end = end; + } + + public abstract Integer calculateCurrentValue(); + } + + private static class RaisingValue extends Value { + + private int increment; + + public RaisingValue(Integer start, Integer end) { + super(start, end); + } + + @Override + public Integer calculateCurrentValue() { + if (current == null) { + return start; + } + if (increment == 0) { + increment = 1; + } else { + increment = increment * 2; + } + Integer result = start + increment; + if (result > end) { + increment = 1; + return end; + } + return result; + } + } + + private static class DecreasingValue extends Value { + + private int decrement; + + public DecreasingValue(Integer start, Integer end) { + super(start, end); + } + + @Override + public Integer calculateCurrentValue() { + if (current == null) { + return start; + } + if (decrement == 0) { + decrement = 1; + } else { + decrement = decrement * 2; + } + Integer result = start - decrement; + if (result < end) { + return end; + } + return result; + } + } + + private static class RandomValue extends Value { + + public RandomValue(Integer start, Integer end) { + super(start, end); + } + + @Override + public Integer calculateCurrentValue() { + return RanVesUtils.getRandomNumber(start, end); + } + } + + @AllArgsConstructor + @EqualsAndHashCode + private static class Key { + + private String paramName; + private String mapKey; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanCheckCellIsDeadOnEvent.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanCheckCellIsDeadOnEvent.java new file mode 100644 index 0000000..1330e04 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanCheckCellIsDeadOnEvent.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import static org.onap.a1pesimulator.service.cell.RanCellStateService.TOPIC_CELL; + +import java.util.Optional; +import org.onap.a1pesimulator.data.cell.CellDetails; +import org.onap.a1pesimulator.data.cell.state.CellStateEnum; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields; +import org.onap.a1pesimulator.service.cell.RanCellsHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +@Service +public class RanCheckCellIsDeadOnEvent implements OnEventAction { + + private static final Logger log = LoggerFactory.getLogger(RanCheckCellIsDeadOnEvent.class); + + private final RanCellsHolder ranCellsHolder; + private final SimpMessagingTemplate messagingTemplate; + + private final Integer failingModeThroughputValue; + private final Integer failingModeLatencyValue; + private final Integer failingCheckoutDelayTimeInSec; + + private static final int TO_MICRO_SEC = 1_000_000; + + public RanCheckCellIsDeadOnEvent(RanCellsHolder ranCellsHolder, SimpMessagingTemplate messagingTemplate, + @Value("${ves.failing.throughput}") Integer failingModeThroughputValue, + @Value("${ves.failing.latency}") Integer failingModeLatencyValue, + @Value("${ves.failing.checkout.delay}") Integer failingCheckoutDelayTimeInSec) { + this.ranCellsHolder = ranCellsHolder; + this.messagingTemplate = messagingTemplate; + + this.failingModeThroughputValue = failingModeThroughputValue; + this.failingModeLatencyValue = failingModeLatencyValue; + this.failingCheckoutDelayTimeInSec = failingCheckoutDelayTimeInSec; + } + + @Override + public void onEvent(Event event) { + Optional<String> cellId = getCellIdentifier(event); + Optional<String> throughput = getCellThroughput(event); + Optional<String> latency = getCellLatency(event); + + if (cellId.isPresent() && throughput.isPresent() && latency.isPresent()) { + checkCell(cellId.get(), Integer.parseInt(throughput.get()), Integer.parseInt(latency.get()), + event.getCommonEventHeader().getLastEpochMicrosec()); + } + } + + private void checkCell(String cellId, Integer throughput, Integer latency, Long lastEpochMicrosec) { + if (throughput <= failingModeThroughputValue && latency >= failingModeLatencyValue) { + log.info("Failure mode detected for cell {}", cellId); + processSleepingMode(cellId, lastEpochMicrosec); + } + } + + private void processSleepingMode(String cellId, Long lastEpochMicrosec) { + CellDetails cell = ranCellsHolder.getCellById(cellId); + if (cell.getCellStateMachine().getState() == CellStateEnum.GOING_TO_SLEEP) { + Optional<RanCellsHolder.CellInFailureMode> cellInFailureModeOpt = + ranCellsHolder.getCellsInFailureMode(cellId); + if (cellInFailureModeOpt.isPresent()) { + RanCellsHolder.CellInFailureMode cellInFailureMode = cellInFailureModeOpt.get(); + if (cellInFailureMode.getSleepingModeDetectedTime() == null) { + cellInFailureMode.setSleepingModeDetectedTime(lastEpochMicrosec); + } else { + long waitingEpochMicrosec = addDelayTime(cellInFailureMode.getSleepingModeDetectedTime()); + if (lastEpochMicrosec >= waitingEpochMicrosec) { + log.info("Cell {} is sleeping!", cellId); + cell.nextState(); + messagingTemplate.convertAndSend(TOPIC_CELL, cell); + } + } + } + } + } + + private Optional<String> getCellIdentifier(Event event) { + return getValueFromAdditionalMeasurement(event, "identifier"); + } + + private Optional<String> getCellThroughput(Event event) { + return getValueFromAdditionalMeasurement(event, "throughput"); + } + + private Optional<String> getCellLatency(Event event) { + return getValueFromAdditionalMeasurement(event, "latency"); + } + + private Optional<String> getValueFromAdditionalMeasurement(Event event, String key) { + Optional<MeasurementFields.AdditionalMeasurement> measurement = getAdditionalMeasurement(event, key); + return measurement.map(this::getValueFromAdditionalMeasurement); + } + + private String getValueFromAdditionalMeasurement(MeasurementFields.AdditionalMeasurement measurement) { + return measurement.getHashMap().get("value"); + } + + private Optional<MeasurementFields.AdditionalMeasurement> getAdditionalMeasurement(Event event, + String additionalMeasurement) { + return event.getMeasurementFields().getAdditionalMeasurements().stream() + .filter(e -> e.getName().equals(additionalMeasurement)).findFirst(); + } + + private long addDelayTime(long epoch) { + return epoch + failingCheckoutDelayTimeInSec * TO_MICRO_SEC; + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanEventCustomizerFactory.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanEventCustomizerFactory.java new file mode 100644 index 0000000..3fbeda9 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanEventCustomizerFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.text.MessageFormat; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.service.ue.RanUeHolder; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable.EventCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class RanEventCustomizerFactory { + + private final EventCustomizer regularEventCustomizer; + private final RanUeHolder ranUeHolder; + + public RanEventCustomizerFactory(EventCustomizer regularEventCustomizer, RanUeHolder ranUeHolder) { + this.ranUeHolder = ranUeHolder; + this.regularEventCustomizer = regularEventCustomizer; + } + + public EventCustomizer getEventCustomizer(Event event, Mode mode) { + switch (mode) { + case REGULAR: + return regularEventCustomizer; + case FAILURE: + return new RanCellFailureEventCustomizer(event, ranUeHolder); + default: + throw new RuntimeException( + MessageFormat.format("Cannot construct event customizer for mode: {0}", mode)); + } + } + + public enum Mode { + REGULAR, FAILURE + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanSendVesRunnable.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanSendVesRunnable.java new file mode 100644 index 0000000..7378bc0 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanSendVesRunnable.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.util.Collection; +import java.util.function.Function; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.exception.VesBrokerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RanSendVesRunnable implements Runnable { + + private static final Logger log = LoggerFactory.getLogger(RanSendVesRunnable.class); + + private final RanVesSender vesSender; + private Event event; + private final EventCustomizer eventCustomizer; + private final Collection<OnEventAction> onEventAction; + + public RanSendVesRunnable(RanVesSender vesSender, Event event, EventCustomizer eventCustomizer, + Collection<OnEventAction> onEventActions) { + this.vesSender = vesSender; + this.event = event; + this.eventCustomizer = eventCustomizer; + this.onEventAction = onEventActions; + } + + @Override + public void run() { + try { + Event customizedEvent = eventCustomizer.apply(event); + onEventAction.forEach(action -> action.onEvent(customizedEvent)); + vesSender.send(customizedEvent); + } catch (VesBrokerException e) { + log.error("Sending scheduled event failed: {}", e.getMessage()); + } + } + + public void updateEvent(Event event) { + this.event = event; + } + + @FunctionalInterface + public interface EventCustomizer extends Function<Event, Event> { } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerService.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerService.java new file mode 100644 index 0000000..8a90d46 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerService.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.springframework.http.ResponseEntity; + +public interface RanVesBrokerService { + + ResponseEntity<String> startSendingVesEvents(String identifier, Event vesEvent, Integer interval); + + Optional<RanPeriodicVesEvent> stopSendingVesEvents(String identifier); + + Map<String, RanPeriodicVesEvent> getPeriodicEventsCache(); + + Collection<String> getEnabledEventElementIdentifiers(); + + Event getEventStructure(String identifier); + + Event startSendingFailureVesEvents(String identifier); + + Event getGlobalPmVesStructure(); + + void setGlobalPmVesStructure(Event event); + + Integer getGlobalVesInterval(); + + void setGlobalVesInterval(Integer interval); +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerServiceImpl.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerServiceImpl.java new file mode 100644 index 0000000..861bd36 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesBrokerServiceImpl.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.MeasurementFields.AdditionalMeasurement; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.onap.a1pesimulator.util.Constants; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +@Service +public class RanVesBrokerServiceImpl implements RanVesBrokerService { + + private final RanVesDataProvider vesDataProvider; + + private final RanVesHolder vesHolder; + + public RanVesBrokerServiceImpl(RanVesDataProvider vesDataProvider, RanVesHolder vesHolder) { + this.vesDataProvider = vesDataProvider; + this.vesHolder = vesHolder; + } + + @Override + public Map<String, RanPeriodicVesEvent> getPeriodicEventsCache() { + return vesHolder.getPeriodicEventsCache(); + } + + @Override + public ResponseEntity<String> startSendingVesEvents(String identifier, Event vesEvent, Integer interval) { + + enrichWithIdentifier(identifier, vesEvent); + vesHolder.startSendingVesEvents(identifier, vesEvent, interval); + + return ResponseEntity.accepted().body("VES Event sending started"); + } + + @Override + public Event startSendingFailureVesEvents(String identifier) { + + Event vesEvent = vesDataProvider.getFailurePmVesEvent(); + + enrichWithIdentifier(identifier, vesEvent); + vesHolder.startSendingFailureVesEvents(identifier, vesEvent); + return vesEvent; + } + + @Override + public Optional<RanPeriodicVesEvent> stopSendingVesEvents(String identifier) { + return vesHolder.stopSendingVesEvents(identifier); + } + + @Override + public Collection<String> getEnabledEventElementIdentifiers() { + return vesHolder.getEnabledEventElementIdentifiers(); + } + + @Override + public Event getEventStructure(String identifier) { + return vesHolder.getEventStructure(identifier); + } + + @Override + public Event getGlobalPmVesStructure() { + return vesDataProvider.getPmVesEvent(); + } + + @Override + public void setGlobalPmVesStructure(Event event) { + vesDataProvider.setPmVesEvent(event); + } + + @Override + public Integer getGlobalVesInterval() { + return vesDataProvider.getRegularVesInterval(); + } + + @Override + public void setGlobalVesInterval(Integer interval) { + vesDataProvider.setInterval(interval); + } + + private void enrichWithIdentifier(String identifier, Event event) { + if (event.getMeasurementFields() == null || event.getMeasurementFields().getAdditionalMeasurements() == null) { + return; + } + Collection<AdditionalMeasurement> additionalMeasurements = + event.getMeasurementFields().getAdditionalMeasurements(); + Optional<AdditionalMeasurement> identityOpt = additionalMeasurements.stream() + .filter(m -> Constants.MEASUREMENT_FIELD_IDENTIFIER + .equalsIgnoreCase(m.getName())) + .findAny(); + if (identityOpt.isPresent()) { + identityOpt.get().getHashMap().put(Constants.MEASUREMENT_FIELD_IDENTIFIER, identifier); + } else { + AdditionalMeasurement measurement = new AdditionalMeasurement(); + measurement.setName(Constants.MEASUREMENT_FIELD_IDENTIFIER); + measurement.setHashMap(Collections.singletonMap(Constants.MEASUREMENT_FIELD_VALUE, identifier)); + additionalMeasurements.add(measurement); + } + } + +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesDataProvider.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesDataProvider.java new file mode 100644 index 0000000..95743f3 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesDataProvider.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.io.IOException; +import java.net.URL; +import lombok.Setter; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.util.JsonUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Service; + +@Service +public class RanVesDataProvider { + + private static final String PM_VES_LOCATION = "classpath:pmVes.json"; + private static final String PM_FAILURE_VES_LOCATION = "classpath:failurePmVes.json"; + + @Setter + private Event pmVesEvent; + @Setter + private Event failurePmVesEvent; + @Setter + private Integer interval; + + private final Integer defaultInterval; + private final ResourceLoader resourceLoader; + + public RanVesDataProvider(@Value("${ves.defaultInterval}") Integer defaultInterval, ResourceLoader resourceLoader) { + this.defaultInterval = defaultInterval; + this.resourceLoader = resourceLoader; + } + + @Cacheable("pmVes") + public Event loadPmVesEvent() { + URL resourceUrl = getResourceURL(resourceLoader.getResource(PM_VES_LOCATION)); + return JsonUtils.INSTANCE.deserializeFromFileUrl(resourceUrl, Event.class); + } + + @Cacheable("failurePmVes") + public Event loadFailurePmVesEvent() { + URL resourceUrl = getResourceURL(resourceLoader.getResource(PM_FAILURE_VES_LOCATION)); + return JsonUtils.INSTANCE.deserializeFromFileUrl(resourceUrl, Event.class); + } + + public Integer getRegularVesInterval() { + if (interval == null) { + return defaultInterval; + } + return interval; + } + + public Integer getFailureVesInterval() { + return defaultInterval; + } + + public Event getPmVesEvent() { + if (pmVesEvent == null) { + return loadPmVesEvent(); + } + return pmVesEvent; + } + + public Event getFailurePmVesEvent() { + if (failurePmVesEvent == null) { + return loadFailurePmVesEvent(); + } + return failurePmVesEvent; + } + + private URL getResourceURL(Resource resource) { + try { + return resource.getURL(); + } catch (IOException e) { + throw new RuntimeException("Cannot get resource URL for: " + resource.getFilename()); + } + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesHolder.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesHolder.java new file mode 100644 index 0000000..d53d8dd --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesHolder.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.function.BiFunction; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.data.ves.RanPeriodicVesEvent; +import org.onap.a1pesimulator.service.ves.RanEventCustomizerFactory.Mode; +import org.onap.a1pesimulator.service.ves.RanSendVesRunnable.EventCustomizer; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Service; + +@Service +public class RanVesHolder { + + private final Map<String, RanPeriodicVesEvent> periodicEventsCache = new ConcurrentHashMap<>(); + + private final RanVesDataProvider vesDataProvider; + private final RanEventCustomizerFactory eventCustomizerFactory; + private final ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler; + private final Collection<OnEventAction> onEventActions; + private final RanVesSender vesSender; + + public RanVesHolder(ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler, RanVesSender vesSender, + RanEventCustomizerFactory eventCustomizerFactory, RanVesDataProvider vesDataProvider, + Collection<OnEventAction> onEventActions) { + this.vesPmThreadPoolTaskScheduler = vesPmThreadPoolTaskScheduler; + this.vesSender = vesSender; + this.eventCustomizerFactory = eventCustomizerFactory; + this.vesDataProvider = vesDataProvider; + this.onEventActions = onEventActions; + } + + Map<String, RanPeriodicVesEvent> getPeriodicEventsCache() { + return periodicEventsCache; + } + + ResponseEntity<String> startSendingVesEvents(String identifier, Event vesEvent, Integer interval) { + + periodicEventsCache.compute(identifier, + new ThreadCacheUpdateFunction(vesPmThreadPoolTaskScheduler, vesEvent, interval, + eventCustomizerFactory.getEventCustomizer(vesEvent, Mode.REGULAR), onEventActions, vesSender)); + return ResponseEntity.accepted().body("VES Event sending started"); + } + + ResponseEntity<String> startSendingFailureVesEvents(String identifier, Event vesEvent) { + + periodicEventsCache.compute(identifier, new ThreadCacheUpdateFunction(vesPmThreadPoolTaskScheduler, vesEvent, + vesDataProvider.getFailureVesInterval(), + eventCustomizerFactory.getEventCustomizer(vesEvent, Mode.FAILURE), onEventActions, vesSender)); + return ResponseEntity.accepted().body("Failure VES Event sending started"); + } + + Optional<RanPeriodicVesEvent> stopSendingVesEvents(String identifier) { + RanPeriodicVesEvent periodicEvent = periodicEventsCache.remove(identifier); + if (periodicEvent == null) { + return Optional.empty(); + } + periodicEvent.getScheduledFuture().cancel(false); + return Optional.of(periodicEvent); + } + + Collection<String> getEnabledEventElementIdentifiers() { + return periodicEventsCache.keySet(); + } + + public boolean isEventEnabled(String identifier) { + return periodicEventsCache.containsKey(identifier); + } + + Event getEventStructure(String identifier) { + if (!periodicEventsCache.containsKey(identifier)) { + throw new IllegalArgumentException( + MessageFormat.format("Cannot find event for given source {0}", identifier)); + } + return periodicEventsCache.get(identifier).getEvent(); + } + + private static class ThreadCacheUpdateFunction + implements BiFunction<String, RanPeriodicVesEvent, RanPeriodicVesEvent> { + + private final Integer interval; + private final ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler; + private final Event vesEvent; + private final EventCustomizer eventCustomizer; + private final Collection<OnEventAction> onEventActions; + private final RanVesSender vesSender; + + public ThreadCacheUpdateFunction(ThreadPoolTaskScheduler vesPmThreadPoolTaskScheduler, Event vesEvent, + Integer interval, EventCustomizer eventCustomizer, Collection<OnEventAction> onEventActions, + RanVesSender vesSender) { + this.vesPmThreadPoolTaskScheduler = vesPmThreadPoolTaskScheduler; + this.vesEvent = vesEvent; + this.interval = interval; + this.eventCustomizer = eventCustomizer; + this.onEventActions = onEventActions; + this.vesSender = vesSender; + } + + @Override + public RanPeriodicVesEvent apply(String key, RanPeriodicVesEvent value) { + if (value != null) { + // if thread is registered then cancel it and schedule a new one + value.getScheduledFuture().cancel(false); + } + RanSendVesRunnable sendVesRunnable = + new RanSendVesRunnable(vesSender, vesEvent, eventCustomizer, onEventActions); + ScheduledFuture<?> scheduledFuture = + vesPmThreadPoolTaskScheduler.scheduleAtFixedRate(sendVesRunnable, interval * 1000L); + return RanPeriodicVesEvent.builder().event(vesEvent).interval(interval).scheduledFuture(scheduledFuture) + .sendVesRunnable(sendVesRunnable).build(); + } + + } +} diff --git a/src/main/java/org/onap/a1pesimulator/service/ves/RanVesSender.java b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesSender.java new file mode 100644 index 0000000..9c50197 --- /dev/null +++ b/src/main/java/org/onap/a1pesimulator/service/ves/RanVesSender.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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 + */ + +package org.onap.a1pesimulator.service.ves; + +import org.onap.a1pesimulator.data.VnfConfig; +import org.onap.a1pesimulator.data.ves.CommonEventHeader; +import org.onap.a1pesimulator.data.ves.Event; +import org.onap.a1pesimulator.exception.VesBrokerException; +import org.onap.a1pesimulator.util.JsonUtils; +import org.onap.a1pesimulator.util.VnfConfigReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class RanVesSender { + + private static final Logger log = LoggerFactory.getLogger(RanVesSender.class); + + private RestTemplate restTemplate; + + private String vesCollectorProtocol; + + private String vesCollectorPath; + + private VnfConfigReader vnfConfigReader; + + public RanVesSender(RestTemplate restTemplate, VnfConfigReader vnfConfigReader, + @Value("${ves.collector.protocol}") String vesCollectorProtocol, + @Value("${ves.collector.endpoint}") String vesCollectorPath) { + this.restTemplate = restTemplate; + this.vnfConfigReader = vnfConfigReader; + this.vesCollectorProtocol = vesCollectorProtocol; + this.vesCollectorPath = vesCollectorPath; + } + + public ResponseEntity<String> send(Event vesEvent) throws VesBrokerException { + VnfConfig vnfConfig = vnfConfigReader.getVnfConfig(); + String url = getVesCollectorUrl(vnfConfig); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBasicAuth(vnfConfig.getVesUser(), vnfConfig.getVesPassword()); + + setVnfInfo(vesEvent, vnfConfig); + String event = JsonUtils.INSTANCE.objectToPrettyString(vesEvent); + + log.info("Sending following VES event: {}", event); + + HttpEntity<String> entity = new HttpEntity<>(event, headers); + ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class); + + log.debug("Response received: {}", response); + + if (response.getStatusCode() == HttpStatus.OK || response.getStatusCode() == HttpStatus.ACCEPTED) { + return response; + } else { + String errorMsg = + "Failed to send VES event to the collector with response status code:" + response.getStatusCode(); + throw new VesBrokerException(errorMsg); + } + } + + private String getVesCollectorUrl(VnfConfig vnfConfig) { + return vesCollectorProtocol + "://" + vnfConfig.getVesHost() + ":" + vnfConfig.getVesPort() + vesCollectorPath; + } + + private void setVnfInfo(Event vesEvent, VnfConfig vnfConfig) { + CommonEventHeader header = vesEvent.getCommonEventHeader(); + header.setSourceId(vnfConfig.getVnfId()); + header.setSourceName(vnfConfig.getVnfName()); + vesEvent.setCommonEventHeader(header); + } +} |