diff --git a/CHANGES.md b/CHANGES.md
index 625f54b1..308680d8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,4 +1,7 @@
# OSCARS Release Notes
+### 1.2.3
+- OS-285: fix operational state bugs
+
### 1.2.2
> Mar 2024
- OS-227: add an ip interface frontend
diff --git a/backend/pom.xml b/backend/pom.xml
index 234b9da4..4d1c2988 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -14,7 +14,7 @@
net.es.oscars
backend
backend
- 1.2.2
+ 1.2.3
OSCARS backend
diff --git a/backend/src/main/java/net/es/oscars/sb/nso/LiveStatusOperationalStateCacheManager.java b/backend/src/main/java/net/es/oscars/sb/nso/LiveStatusOperationalStateCacheManager.java
index cad31b37..5c262dd8 100644
--- a/backend/src/main/java/net/es/oscars/sb/nso/LiveStatusOperationalStateCacheManager.java
+++ b/backend/src/main/java/net/es/oscars/sb/nso/LiveStatusOperationalStateCacheManager.java
@@ -95,6 +95,10 @@ public ArrayList refreshSdp(String device, int serviceId) {
ArrayList sdpsList = this.getDataLines(reply);
// extract SDP data - simple line format parsing
+ // incoming format:
+ // 7141:7087 Spok 134.55.200.173 Up Up 524100 524267
+ // sdpId : vcId
+
for (int i = 0; i < sdpsList.size(); i++) {
result = new LiveStatusSdpResult();
result.setDevice(device);
@@ -119,14 +123,13 @@ public ArrayList refreshSdp(String device, int serviceId) {
break;
}
- int vcId = 0;
int sdpId = 0;
+ int vcId = 0;
try {
- vcId = Integer.parseInt(sdpIdAndInfo[0]);
- sdpId = Integer.parseInt(sdpInfo[0]);
+ sdpId = Integer.parseInt(sdpIdAndInfo[0]);
+ vcId = Integer.parseInt(sdpInfo[0]);
} catch (NumberFormatException error) {
- log.error("Couldn't parse VC/SDP ID");
- error.printStackTrace();
+ log.error("Couldn't parse VC/SDP ID from "+line+" : "+error.getMessage());
}
result.setVcId(vcId);
result.setSdpId(sdpId);
@@ -396,10 +399,19 @@ public ArrayList getDataLines(String input) {
if (input == null) return null;
ArrayList data = new ArrayList<>();
String[] lines = input.split("\r\n");
- Pattern regex = Pattern.compile("^[0-9]{1}");
+
+ // starts with a number, i.e. for something like
+ // 1/1/c13/1:2012 7072 7001 none 7001 none Up Up
+ Pattern startsWithNumber = Pattern.compile("^[0-9]{1}");
+
+ // some SAP lines look different and we want those too
+ // lag-50:3603 7072 7005 none 7005 none Up Up
+ Pattern startsWithLag = Pattern.compile("^lag");
+
for (String line : lines) {
- Matcher m = regex.matcher(line);
- if (m.find()) {
+ Matcher numberMatch = startsWithNumber.matcher(line);
+ Matcher lagMatch = startsWithLag.matcher(line);
+ if (numberMatch.find() || lagMatch.find()) {
data.add(line);
}
}
diff --git a/backend/src/main/java/net/es/oscars/task/EsdbVlanSync.java b/backend/src/main/java/net/es/oscars/task/EsdbVlanSync.java
index 49b57e12..0a9730ac 100644
--- a/backend/src/main/java/net/es/oscars/task/EsdbVlanSync.java
+++ b/backend/src/main/java/net/es/oscars/task/EsdbVlanSync.java
@@ -39,7 +39,7 @@ public EsdbVlanSync(Startup startup, ConnectionRepository cr, ESDBProxy esdbProx
this.topologyStore = topologyStore;
}
- @Scheduled(initialDelay = 10000,fixedDelayString ="${esdb.vlan-sync-period}" )
+ @Scheduled(initialDelay = 120000, fixedDelayString ="${esdb.vlan-sync-period}" )
public void processingLoop() {
if (!esdbProperties.isEnabled()) {
return;
diff --git a/backend/src/main/java/net/es/oscars/web/rest/NsoLiveStatusController.java b/backend/src/main/java/net/es/oscars/web/rest/NsoLiveStatusController.java
index 06d961ab..89ed008a 100644
--- a/backend/src/main/java/net/es/oscars/web/rest/NsoLiveStatusController.java
+++ b/backend/src/main/java/net/es/oscars/web/rest/NsoLiveStatusController.java
@@ -1,5 +1,8 @@
package net.es.oscars.web.rest;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.transaction.Transactional;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -10,14 +13,11 @@
import net.es.oscars.resv.enums.DeploymentState;
import net.es.oscars.resv.enums.State;
import net.es.oscars.sb.nso.db.NsoSdpIdDAO;
-import net.es.oscars.sb.nso.db.NsoSdpVcIdDAO;
import net.es.oscars.sb.nso.ent.NsoSdpId;
-import net.es.oscars.sb.nso.ent.NsoSdpVcId;
import net.es.oscars.web.beans.NsoLiveStatusRequest;
import net.es.oscars.web.beans.OperationalState;
import net.es.oscars.web.beans.OperationalStateInfoResponse;
import net.es.oscars.web.beans.MacInfoResponse;
-import net.es.oscars.sb.nso.rest.LiveStatusLspResult;
import net.es.oscars.sb.nso.rest.LiveStatusSapResult;
import net.es.oscars.sb.nso.rest.LiveStatusSdpResult;
import net.es.oscars.sb.nso.rest.MacInfoResult;
@@ -31,13 +31,13 @@
import net.es.topo.common.devel.DevelUtils;
import net.es.topo.common.dto.nso.enums.NsoVplsSdpPrecedence;
+import org.checkerframework.checker.units.qual.A;
import org.springframework.web.bind.annotation.*;
import java.time.Instant;
import java.util.*;
-import java.util.function.Function;
-import java.util.stream.Collectors;
+import static net.es.oscars.web.beans.OperationalStateInfoResponse.UpDown.UP;
import static net.es.topo.common.dto.nso.enums.NsoVplsSdpPrecedence.PRIMARY;
import static net.es.topo.common.dto.nso.enums.NsoVplsSdpPrecedence.SECONDARY;
@@ -51,7 +51,6 @@ public class NsoLiveStatusController {
private final NsoSdpIdDAO nsoSdpIdDAO;
-
public NsoLiveStatusController(
LiveStatusOperationalStateCacheManager operationalStateCacheManager,
NsoVcIdDAO nsoVcIdDAO,
@@ -70,7 +69,7 @@ public MacInfoResponse getMacInfo(@RequestBody NsoLiveStatusRequest request) {
// filter and extract request data
RequestData requestData = getRequestData(request);
- if(requestData == null) {
+ if (requestData == null) {
log.info("Couldn't extract REST request data");
throw new NoSuchElementException();
}
@@ -95,11 +94,11 @@ public MacInfoResponse getMacInfo(@RequestBody NsoLiveStatusRequest request) {
.getMacs(device, serviceId, request.getRefreshIfOlderThan()).getMacInfoResult());
} else {
results.add(MacInfoResult.builder()
- .device(device)
- .errorMessage("Not deployed")
- .status(false)
- .timestamp(Instant.now())
- .fdbQueryResult("Not deployed")
+ .device(device)
+ .errorMessage("Not deployed")
+ .status(false)
+ .timestamp(Instant.now())
+ .fdbQueryResult("Not deployed")
.build());
}
}
@@ -115,7 +114,7 @@ public OperationalStateInfoResponse getOperationalStateInfo(@RequestBody NsoLive
// filter and extract request data
RequestData requestData = getRequestData(request);
- if(requestData == null) {
+ if (requestData == null) {
log.info("Couldn't extract REST request data");
throw new NoSuchElementException();
}
@@ -123,17 +122,8 @@ public OperationalStateInfoResponse getOperationalStateInfo(@RequestBody NsoLive
int serviceId = requestData.getServiceId();
Connection conn = requestData.getConn();
- // this collects nsoSdpIds keyed off the device
- Map> byDevice = nsoSdpIdDAO.findNsoSdpIdByConnectionId(conn.getConnectionId())
- .stream().collect(
- Collectors.groupingBy(
- NsoSdpId::getDevice,
- Collectors.mapping(
- Function.identity(),
- Collectors.toSet()
- )
- )
- );
+ // this collects nsoSdpIds that oscars would set up, keyed off the device
+ List nsoSdpIds = nsoSdpIdDAO.findNsoSdpIdByConnectionId(conn.getConnectionId());
// start building REST return
@@ -143,54 +133,63 @@ public OperationalStateInfoResponse getOperationalStateInfo(@RequestBody NsoLive
Instant timestamp = request.getRefreshIfOlderThan();
List results = new ArrayList<>();
- Map> allSdpsForDevice = new HashMap<>();
+ ArrayList allSdpsForAllDevices = new ArrayList<>();
Map> allSapsForDevice = new HashMap<>();
// Map> allLspsForDevice = new HashMap<>();
- log.debug("Run live-status request on devices for operational states");
+ log.debug("Run live-status request on devices and collect operational states");
for (String device : devices) {
if (conn.getState().equals(State.ACTIVE) &&
conn.getDeploymentState().equals(DeploymentState.DEPLOYED)) {
log.info("Fetch SDPs, SAPs, and LSPs from LiveStatusCacheManager for " + device + " service id " + serviceId);
+ List sdpsOnDevice = operationalStateCacheManager.getSdp(device, serviceId, timestamp);
+ List sapsOnDevice = operationalStateCacheManager.getSap(device, serviceId, timestamp);
// get SDPs, SAPs, and LSPs from cache manager
- allSdpsForDevice.put(device, operationalStateCacheManager.getSdp(device, serviceId, timestamp));
- allSapsForDevice.put(device, operationalStateCacheManager.getSap(device, serviceId, timestamp));
+ allSdpsForAllDevices.addAll(sdpsOnDevice);
+ allSapsForDevice.put(device, new ArrayList<>(sapsOnDevice));
// allLspsForDevice.put(device, operationalStateCacheManager.getLsp(device, timestamp));
-
+ // this raw output is the same for all SDPs on the device
String sdpRaw = "";
- for (LiveStatusSdpResult sdpResult : allSdpsForDevice.get(device)) {
+ for (LiveStatusSdpResult sdpResult : sdpsOnDevice) {
sdpRaw = sdpResult.getRaw();
}
+
String sapRaw = "";
for (LiveStatusSapResult sapResult : allSapsForDevice.get(device)) {
- sdpRaw = sapResult.getRaw();
+ sapRaw = sapResult.getRaw();
}
OperationalStateInfoResult resultElement = OperationalStateInfoResult.builder()
.device(device)
.timestamp(timestamp)
.status(true)
- .raw(sdpRaw+"\n"+sapRaw)
+ .sdps(sdpsOnDevice)
+ .saps(sapsOnDevice)
+ .raw(sdpRaw + "\n" + sapRaw)
.build();
- new OperationalStateInfoResult();
+ new OperationalStateInfoResult();
results.add(resultElement);
} else {
results.add(OperationalStateInfoResult.builder()
- .device(device)
- .errorMessage("Not deployed")
- .status(false)
- .raw("")
- .timestamp(timestamp)
- .build());
+ .device(device)
+ .errorMessage("Not deployed")
+ .status(false)
+ .sdps(new ArrayList<>())
+ .saps(new ArrayList<>())
+ .raw("")
+ .timestamp(timestamp)
+ .build());
}
}
response.setResults(results);
+ // dumpDebug("allsdps", allSdpsForAllDevices);
+ // dumpDebug("nsoSdpIds", nsoSdpIds);
for (OperationalStateInfoResult result : response.getResults()) {
String device = result.getDevice();
@@ -202,77 +201,73 @@ public OperationalStateInfoResponse getOperationalStateInfo(@RequestBody NsoLive
endpointState = OperationalState.UP;
}
OperationalStateInfoResponse.UpDown operState = sapResult.getOperationalState() ?
- OperationalStateInfoResponse.UpDown.UP : OperationalStateInfoResponse.UpDown.DOWN;
+ UP : OperationalStateInfoResponse.UpDown.DOWN;
OperationalStateInfoResponse.UpDown adminState = sapResult.getAdminState() ?
- OperationalStateInfoResponse.UpDown.UP : OperationalStateInfoResponse.UpDown.DOWN;
+ UP : OperationalStateInfoResponse.UpDown.DOWN;
response.getEndpoints().add(OperationalStateInfoResponse.EndpointOpInfo.builder()
- .device(device)
- .vlanId(sapResult.getVlan())
- .port(sapResult.getPort())
- .operState(operState)
- .adminState(adminState)
- .state(endpointState)
- .build());
+ .device(device)
+ .vlanId(sapResult.getVlan())
+ .port(sapResult.getPort())
+ .operState(operState)
+ .adminState(adminState)
+ .state(endpointState)
+ .build());
}
-
// mapping SDPs is slightly more complicated though
- Map> byFarEnd = new HashMap<>();
-
- // split these by far-end
- for (LiveStatusSdpResult sdpResult : allSdpsForDevice.get(device)) {
- if (!byFarEnd.containsKey(sdpResult.getFarEndAddress())) {
- byFarEnd.put(sdpResult.getFarEndAddress(), new HashSet<>());
+ List deviceSdpIds = nsoSdpIds.stream().filter(sdpId -> sdpId.getDevice().equals(device)).toList();
+ // dumpDebug("deviceSdpIds", deviceSdpIds);
+
+ // first collect our desired SDP ids and group them by remote end
+ Map> byTarget = new HashMap<>();
+ for (NsoSdpId nsoSdpId : deviceSdpIds) {
+ if (!byTarget.containsKey(nsoSdpId.getTarget())) {
+ byTarget.put(nsoSdpId.getTarget(), new HashSet<>());
}
- byFarEnd.get(sdpResult.getFarEndAddress()).add(sdpResult);
+ byTarget.get(nsoSdpId.getTarget()).add(nsoSdpId);
}
-
+ // dumpDebug("byTarget on "+device, byTarget);
// for each far end, make a tunnel, then we have to figure out the health of the tunnel
// each tunnel is composed of a primary SDP and maybe a secondary one as well.
- for (String farEnd : byFarEnd.keySet()) {
- String targetDevice = farEnd;
-
- Set sdpOpInfos = new HashSet<>();
+ for (String target : byTarget.keySet()) {
Map okByPrecedence = new HashMap<>();
-
- for (LiveStatusSdpResult sdpResult : byFarEnd.get(farEnd)) {
-
- OperationalStateInfoResponse.UpDown operState = sdpResult.getOperationalState() ?
- OperationalStateInfoResponse.UpDown.UP : OperationalStateInfoResponse.UpDown.DOWN;
- OperationalStateInfoResponse.UpDown adminState = sdpResult.getAdminState() ?
- OperationalStateInfoResponse.UpDown.UP : OperationalStateInfoResponse.UpDown.DOWN;
-
- NsoVplsSdpPrecedence precedence = PRIMARY;
- boolean foundIt = false;
-
- for (NsoSdpId nsoSdpId : byDevice.get(device)) {
- if (nsoSdpId.getSdpId().equals(sdpResult.getSdpId())) {
- if (nsoSdpId.getPrecedence().equals(PRIMARY.toString())) {
- foundIt = true;
- targetDevice = nsoSdpId.getTarget();
- break;
- } else if (nsoSdpId.getPrecedence().equals(SECONDARY.toString())) {
+ Set sdpOpInfos = new HashSet<>();
+ for (NsoSdpId nsoSdpId : byTarget.get(target)) {
+ log.info("checking status for nsoSdpId "+nsoSdpId.getDevice()+" "+nsoSdpId.getSdpId());
+ for (LiveStatusSdpResult sdpResult : allSdpsForAllDevices) {
+ log.info("examining sdpResult for "+sdpResult.getDevice()+" "+sdpResult.getSdpId());
+ if (sdpResult.getDevice().equals(nsoSdpId.getDevice()) && sdpResult.getSdpId().equals(nsoSdpId.getSdpId())) {
+ log.info("matched! ");
+ OperationalStateInfoResponse.UpDown operState = sdpResult.getOperationalState() ?
+ UP : OperationalStateInfoResponse.UpDown.DOWN;
+ OperationalStateInfoResponse.UpDown adminState = sdpResult.getAdminState() ?
+ UP : OperationalStateInfoResponse.UpDown.DOWN;
+ String precedenceStr = nsoSdpId.getPrecedence();
+ NsoVplsSdpPrecedence precedence = PRIMARY;
+ if (precedenceStr.equals(SECONDARY.toString())) {
precedence = SECONDARY;
- foundIt = true;
- targetDevice = nsoSdpId.getTarget();
- break;
}
+ if (operState.equals(UP) && adminState.equals(UP)) {
+ okByPrecedence.put(precedence, true);
+ } else {
+ okByPrecedence.put(precedence, false);
+ }
+
+ sdpOpInfos.add(OperationalStateInfoResponse.SdpOpInfo.builder()
+ .sdpId(sdpResult.getSdpId())
+ .vcId(sdpResult.getVcId())
+ .operState(operState)
+ .adminState(adminState)
+ .precedence(precedence)
+ .build());
+ break;
}
}
- if (foundIt) {
- boolean sdpOk = sdpResult.getAdminState() && sdpResult.getOperationalState();
- okByPrecedence.put(precedence, sdpOk);
-
- sdpOpInfos.add(OperationalStateInfoResponse.SdpOpInfo.builder()
- .sdpId(sdpResult.getSdpId())
- .vcId(sdpResult.getVcId())
- .operState(operState)
- .adminState(adminState)
- .precedence(precedence)
- .build());
- }
+
}
+ // dumpDebug("sdpOpInfos", sdpOpInfos);
+ // dumpDebug("okByPrecedence", okByPrecedence);
// the rule is...
// - if the primary SDP exists and is UP the tunnel is UP
@@ -291,10 +286,10 @@ public OperationalStateInfoResponse getOperationalStateInfo(@RequestBody NsoLive
}
}
response.getTunnels().add(OperationalStateInfoResponse.TunnelOpInfo.builder()
- .state(tunnelState)
- .sdps(sdpOpInfos.stream().toList())
- .device(device)
- .remote(targetDevice)
+ .state(tunnelState)
+ .sdps(sdpOpInfos.stream().toList())
+ .device(device)
+ .remote(target)
.build());
}
@@ -392,8 +387,8 @@ private RequestData getRequestData(NsoLiveStatusRequest request) {
}
// extract subset
- for(String device : devicesFromRest) {
- if(devicesFromId.contains(device)) {
+ for (String device : devicesFromRest) {
+ if (devicesFromId.contains(device)) {
devices.add(device);
}
}
@@ -404,4 +399,20 @@ private RequestData getRequestData(NsoLiveStatusRequest request) {
.conn(conn)
.build();
}
+
+ private void dumpDebug(String context, Object o) {
+ String pretty = null;
+
+ try {
+ pretty = (new ObjectMapper())
+ .registerModule(new JavaTimeModule())
+ .writerWithDefaultPrettyPrinter()
+ .writeValueAsString(o);
+ } catch (JsonProcessingException ex) {
+ log.error(ex.getMessage());
+ }
+
+ log.info(context + "\n" + pretty);
+ }
+
}