blob: 20780b30c2ddbf94917f94570ef5983c187a12dd [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Laboratory
*
* 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.onosproject.xran.rest;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.onosproject.rest.AbstractWebResource;
import org.onosproject.xran.XranStore;
import org.onosproject.xran.annotations.Patch;
import org.onosproject.xran.controller.XranController;
import org.onosproject.xran.controller.XranControllerImpl;
import org.onosproject.xran.entities.RnibCell;
import org.onosproject.xran.entities.RnibLink;
import org.onosproject.xran.entities.RnibUe;
import org.onosproject.xran.codecs.ber.types.BerInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* Link web resource.
*/
@Path("links")
public class LinkWebResource extends AbstractWebResource {
private static final Logger log =
LoggerFactory.getLogger(LinkWebResource.class);
public LinkWebResource() {
}
/**
* test.
*
* @param eciHex test
* @param ue test
* @return test
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getLinksBetween(@DefaultValue("") @QueryParam("cell") String eciHex,
@DefaultValue("-1") @QueryParam("ue") long ue) {
List<RnibLink> list = Lists.newArrayList();
if (!eciHex.isEmpty() && ue != -1) {
RnibLink link = get(XranStore.class).getLinkBetweenCellIdUeId(eciHex, ue);
if (link != null) {
list.add(link);
}
} else if (!eciHex.isEmpty()) {
list.addAll(get(XranStore.class).getLinksByCellId(eciHex));
} else if (ue != -1) {
list.addAll(get(XranStore.class).getLinksByUeId(ue));
} else {
list.addAll(get(XranStore.class).getLinks());
}
if (list.size() > 0) {
try {
JsonNode jsonNode = mapper().valueToTree(list);
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.OK,
jsonNode
);
} catch (Exception e) {
String fullStackTrace = ExceptionUtils.getFullStackTrace(e);
log.error(fullStackTrace);
e.printStackTrace();
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.INTERNAL_SERVER_ERROR,
"Exception",
fullStackTrace
);
}
}
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.NOT_FOUND,
"Not Found",
"Specified links not found"
);
}
/**
* test.
*
* @param src test
* @param dst test
* @param stream test
* @return test
*/
@Patch
@Path("{src},{dst}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response patchLinks(@PathParam("src") String src, @PathParam("dst") long dst, InputStream stream) {
RnibLink link = get(XranStore.class).getLinkBetweenCellIdUeId(src, dst);
if (link != null) {
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
JsonNode type = jsonTree.path("type");
if (!type.isMissingNode()) {
RnibLink.Type anEnum = RnibLink.Type.getEnum(type.asText());
return handleTypeChange(link, anEnum);
}
JsonNode trafficpercent = jsonTree.path("trafficpercent");
if (!trafficpercent.isMissingNode()) {
return handleTrafficChange(link, trafficpercent);
}
JsonNode rrmConf = jsonTree.path("RRMConf");
if (!rrmConf.isMissingNode()) {
return handleRRMChange(link, rrmConf);
}
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.NOT_IMPLEMENTED,
"Not Implemented",
"The command you specified is not implemented or doesn't exist. We support " +
"type/RRMConf/traficpercent commands."
);
} catch (Exception e) {
String fullStackTrace = ExceptionUtils.getFullStackTrace(e);
log.error(fullStackTrace);
e.printStackTrace();
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.INTERNAL_SERVER_ERROR,
"Exception",
fullStackTrace
);
}
}
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.NOT_FOUND,
"Not Found",
"Link not found use POST request"
);
}
/**
* test.
*
* @param src test
* @param dst test
* @param stream test
* @return test
*/
@POST
@Path("{src},{dst}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response postLinks(@PathParam("src") String src, @PathParam("dst") long dst, InputStream stream) {
RnibCell cell = get(XranStore.class).getCell(src);
RnibUe ue = get(XranStore.class).getUe(dst);
if (cell == null) {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.NOT_FOUND,
"Not Found",
"Cell " + src + " was not found"
);
}
if (ue == null) {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.NOT_FOUND,
"Not Found",
"Ue with " + dst + " was not found"
);
}
if (get(XranStore.class).getLink(cell.getEcgi(), ue.getId()) != null) {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.BAD_REQUEST,
"Bad Request",
"Link already exists use PATCH to modify"
);
}
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
JsonNode type = jsonTree.path("type");
RnibLink link = new RnibLink(cell, ue);
// store it as non-serving when creating link
get(XranStore.class).storeLink(link);
if (!type.isMissingNode()) {
return handleTypeChange(link, RnibLink.Type.getEnum(type.asText()));
}
JsonNode trafficpercent = jsonTree.path("trafficpercent");
if (!trafficpercent.isMissingNode()) {
return handleTrafficChange(link, trafficpercent);
}
JsonNode rrmConf = jsonTree.path("RRMConf");
if (!rrmConf.isMissingNode()) {
return handleRRMChange(link, rrmConf);
}
} catch (Exception e) {
String fullStackTrace = ExceptionUtils.getFullStackTrace(e);
log.error(fullStackTrace);
e.printStackTrace();
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.INTERNAL_SERVER_ERROR,
"Exception",
fullStackTrace
);
}
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.BAD_REQUEST,
"Bad Request",
"The command you specified is not implemented or doesn't exist. We support " +
"type/RRMConf/traficpercent commands."
);
}
private Response handleTypeChange(RnibLink link, RnibLink.Type newType) throws InterruptedException {
final SynchronousQueue<String>[] queue = new SynchronousQueue[1];
if (newType.equals(RnibLink.Type.SERVING_PRIMARY)) {
switch (link.getType()) {
case SERVING_PRIMARY: {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.BAD_REQUEST,
"Bad Request",
"Link is already a primary link"
);
}
case SERVING_SECONDARY_CA:
case SERVING_SECONDARY_DC:
case NON_SERVING: {
List<RnibLink> linksByUeId = get(XranStore.class).getLinksByUeId(link.getLinkId().getUeId());
Optional<RnibLink> primary = linksByUeId.stream()
.filter(l -> l.getType().equals(RnibLink.Type.SERVING_PRIMARY))
.findFirst();
if (primary.isPresent()) {
queue[0] = get(XranController.class).sendHORequest(link, primary.get());
String poll = queue[0].poll(get(XranController.class).getNorthbound_timeout(), TimeUnit.MILLISECONDS);
if (poll != null) {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.OK,
"Handoff Response",
poll
);
} else {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.REQUEST_TIMEOUT,
"Handoff Timeout",
"eNodeB did not send a HOComplete/HOFailure on time"
);
}
} else {
link.setType(RnibLink.Type.SERVING_PRIMARY);
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.OK,
"OK",
"Link set to primary"
);
}
}
}
} else if (newType.equals(RnibLink.Type.NON_SERVING)) {
switch (link.getType()) {
case NON_SERVING:
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.BAD_REQUEST,
"Bad Request",
"Link is already a primary link"
);
case SERVING_PRIMARY:
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.BAD_REQUEST,
"Bad Request",
"Cannot modify a primary link"
);
case SERVING_SECONDARY_CA:
case SERVING_SECONDARY_DC:
if (get(XranController.class).sendScellDelete(link)) {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.OK,
"OK",
"Link set to non-serving"
);
} else {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.NOT_FOUND,
"Not Found",
"Could not find cell config report to construct Scell Delete"
);
}
}
} else if (newType.equals(RnibLink.Type.SERVING_SECONDARY_CA)) {
switch (link.getType()) {
case SERVING_PRIMARY:
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.BAD_REQUEST,
"Bad Request",
"Cannot modify a primary link"
);
case SERVING_SECONDARY_DC:
case NON_SERVING:
queue[0] = get(XranController.class).sendScellAdd(link);
String poll = queue[0].poll(get(XranController.class).getNorthbound_timeout(), TimeUnit.MILLISECONDS);
if (poll != null) {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.OK,
"ScellAdd Response",
poll
);
} else {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.REQUEST_TIMEOUT,
"ScellAdd Timeout",
"eNodeB did not send a ScellAddStatus on time"
);
}
case SERVING_SECONDARY_CA:
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.BAD_REQUEST,
"Bad Request",
"Link is already a secondary CA link"
);
}
}
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.BAD_REQUEST,
"Bad Request",
"The command you specified is not implemented or doesn't exist."
);
}
private Response handleTrafficChange(RnibLink link, JsonNode trafficpercent) {
JsonNode jsonNode = trafficpercent.path("traffic-percent-dl");
if (!jsonNode.isMissingNode()) {
link.getTrafficPercent().setTrafficPercentDl(new BerInteger(jsonNode.asInt()));
}
jsonNode = trafficpercent.path("traffic-percent-ul");
if (!jsonNode.isMissingNode()) {
link.getTrafficPercent().setTrafficPercentUl(new BerInteger(jsonNode.asInt()));
}
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.OK,
"OK",
"Traffic Percent changed"
);
}
private Response handleRRMChange(RnibLink link, JsonNode rrmConf) throws InterruptedException {
final SynchronousQueue<String>[] queue = new SynchronousQueue[1];
get(XranStore.class).modifyLinkRrmConf(link, rrmConf);
boolean isxICIC = link.getLinkId().getCell().getVersion() <= 3;
queue[0] = get(XranController.class).sendModifiedRRMConf(link.getRrmParameters(),
isxICIC);
if (isxICIC) {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.OK,
"OK",
"xICIC was sent successfully"
);
} else {
String poll = queue[0].poll(get(XranController.class).getNorthbound_timeout(), TimeUnit.MILLISECONDS);
if (poll != null) {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.OK,
"RRMConfig Response",
poll
);
} else {
return ResponseHelper.getResponse(
mapper(),
ResponseHelper.statusCode.REQUEST_TIMEOUT,
"RRMConfig Timeout",
"eNodeB did not send a RRMConfingStatus on time"
);
}
}
}
}