blob: 625f1ed3347be5a399f2b82dcd763cccc1a80eee [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.yms.app.ydt;
import com.google.common.collect.ImmutableMap;
import org.onosproject.yangutils.datamodel.YangList;
import org.onosproject.yangutils.datamodel.YangSchemaNode;
import org.onosproject.yangutils.datamodel.YangSchemaNodeContextInfo;
import org.onosproject.yangutils.datamodel.YangSchemaNodeIdentifier;
import org.onosproject.yms.app.ysr.YangSchemaRegistry;
import org.onosproject.yms.ydt.YdtContext;
import org.onosproject.yms.ydt.YdtContextOperationType;
import org.onosproject.yms.ydt.YdtType;
import org.onosproject.yms.ydt.YmsOperationType;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.onosproject.yangutils.datamodel.YangSchemaNodeType.YANG_MULTI_INSTANCE_LEAF_NODE;
import static org.onosproject.yms.app.ydt.AppNodeFactory.getAppContext;
import static org.onosproject.yms.app.ydt.RequestedCallType.LEAF;
import static org.onosproject.yms.app.ydt.RequestedCallType.OTHER;
import static org.onosproject.yms.app.ydt.RequestedCardinality.MULTI_INSTANCE;
import static org.onosproject.yms.app.ydt.RequestedCardinality.MULTI_INSTANCE_LEAF;
import static org.onosproject.yms.app.ydt.RequestedCardinality.SINGLE_INSTANCE;
import static org.onosproject.yms.app.ydt.RequestedCardinality.UNKNOWN;
import static org.onosproject.yms.app.ydt.YdtConstants.errorMsg;
import static org.onosproject.yms.app.ydt.YdtNodeFactory.getAppOpTypeFromYdtOpType;
import static org.onosproject.yms.ydt.YdtContextOperationType.CREATE;
import static org.onosproject.yms.ydt.YdtContextOperationType.DELETE;
import static org.onosproject.yms.ydt.YdtContextOperationType.MERGE;
import static org.onosproject.yms.ydt.YdtContextOperationType.REMOVE;
import static org.onosproject.yms.ydt.YdtType.MULTI_INSTANCE_LEAF_VALUE_NODE;
import static org.onosproject.yms.ydt.YdtType.MULTI_INSTANCE_NODE;
/**
* Represents YANG request work bench which contains all parameters for
* request handling and methods to build and obtain YANG data tree
* which is data (sub)instance representation, abstract of protocol.
*/
public class YangRequestWorkBench implements YdtExtendedBuilder {
// ydt formatted error string
private static final String FMT_NOT_EXIST =
"Application with name \"%s\" doesn't exist.";
private static final String E_USE_ADDLEAF =
"Requested Node should be created using addLeaf interface";
private static final String E_MULTI_INS =
"Adds an instance of type list or leaf-list node only";
private static final String E_CREATE =
"Create request is not allowed under delete operation";
private static final String E_DEL =
"Delete request is not allowed under create operation";
private static final String E_INVOKE_PARENT =
"Can't invoke get parent at logical root node";
private static final String FMT_TOO_FEW =
"Too few key parameters in %s. Expected %d; actual %d.";
private static final String FMT_TOO_MANY =
"Too many key parameters in %s. Expected %d; actual %d.";
/*
* Current node in YANG data tree, kept to maintain the
* current context in YDT.
*/
private YdtNode curNode;
/*
* Root node in YANG data tree, kept to maintain the root context in
* YDT.
*/
private YdtNode rootNode;
/*
* Current node in YANG data tree, kept to maintain the current context
* in ydt application tree.
*/
private YdtAppContext appCurNode;
/*
* Root node in YANG data tree, kept to maintain the root context in ydt
* application tree.
*/
private YdtAppContext appRootNode;
/**
* Root Node Tag attribute in YANG data tree, kept to maintain the root
* tag attributes in YDT.
* <p>
* First key param of map represent tagName name of tag attribute.
* Second param of map represent tagValue value of tag attribute
*/
private Map<String, String> rootTagAttributeMap;
/*
* YANG schema registry reference.
*/
private YangSchemaRegistry registry = null;
/*
* YMS operation type.
*/
private final YmsOperationType ymsOperationType;
/*
* YDT default operation type.
*/
private YdtContextOperationType ydtDefaultOpType;
/*
* Flag to identify data validation need to be done by YDT or not.
*/
private boolean validate = false;
// TODO validate need to be handle later with interaction type basis in
// future when it will be supported
/**
* Creates an instance of YANG request work bench which is use to initialize
* logical rootNode and and schema registry.
*
* @param name name of logical container of a protocol
* which is a holder of the complete tree
* @param namespace namespace of logical container
* @param opType type of operation done by using YANG
* interface
* @param registry Yang schema registry
* @param isValidate Flag to identify data validation need to be
* done by YDT or not
*/
public YangRequestWorkBench(String name, String namespace,
YmsOperationType opType,
YangSchemaRegistry registry,
boolean isValidate) {
YdtNode newNode;
YangSchemaNodeIdentifier nodeIdentifier =
new YangSchemaNodeIdentifier();
nodeIdentifier.setName(name);
nodeIdentifier.setNameSpace(namespace);
newNode = new YdtSingleInstanceNode(nodeIdentifier);
setRootNode(newNode);
this.registry = registry;
ymsOperationType = opType;
validate = isValidate;
// Set the logical root node for yang data app tree.
DefaultYdtAppContext appNode = getAppContext(true);
setAppRootNode(appNode);
}
/**
* Creates an instance of YANG request work bench which is used to build YDT
* tree in YAB.
*
* @param curNode current YDT node
* @param operationType YMS operation type
*/
public YangRequestWorkBench(YdtNode curNode,
YmsOperationType operationType) {
this.curNode = curNode;
ymsOperationType = operationType;
}
/**
* Sets the logical root context information available in YDT node.
*
* @param node logical root node
*/
private void setRootNode(YdtNode node) {
rootNode = node;
curNode = node;
}
/**
* Sets the app context tree logical root node for ydt application tree.
*
* @param node application tree's logical root node
*/
private void setAppRootNode(YdtAppContext node) {
appRootNode = node;
appCurNode = node;
}
/**
* Returns the YANG schema registry of Ydt.
* This method will be used by ytb.
*
* @return YANG schema registry
*/
public YangSchemaRegistry getYangSchemaRegistry() {
return registry;
}
/**
* Returns the app context tree root node for ydt application tree.
* This method will be used by yab.
*
* @return YdtAppContext refers to root node of ydt application tree
*/
public YdtAppContext getAppRootNode() {
return appRootNode;
}
/**
* Returns the data tree for given node identifier.
*
* @param id Represents node identifier of YANG data tree node
* @param namespace namespace of the application requested by user
* @return YANG data tree node
*/
private YdtNode moduleHandler(YangSchemaNodeIdentifier id,
String namespace) {
YangSchemaNode node = registry
.getYangSchemaNodeUsingSchemaName(id.getName());
if (node == null ||
namespace != null && !namespace.equals(node.getNameSpace())) {
curNode.errorHandler(errorMsg(
FMT_NOT_EXIST, id.getName()), rootNode);
}
YdtNode newNode = new YdtSingleInstanceNode(id);
newNode.setYangSchemaNode(node);
id.setNameSpace(node.getNameSpace());
return newNode;
}
@Override
public void setRootTagAttributeMap(Map<String, String> attributeTag) {
rootTagAttributeMap = attributeTag;
}
@Override
public Map<String, String> getRootTagAttributeMap() {
if (rootTagAttributeMap != null) {
return ImmutableMap.copyOf(rootTagAttributeMap);
}
return null;
}
@Override
public void addChild(String name, String namespace) {
addChild(name, namespace, UNKNOWN, null, OTHER);
}
@Override
public void addChild(String name, String namespace, YdtType ydtType) {
addChild(name, namespace, ydtType, null);
}
@Override
public void addChild(String name, String namespace,
YdtContextOperationType opType) {
addChild(name, namespace, UNKNOWN, opType, OTHER);
}
@Override
public void addChild(String name, String namespace, YdtType ydtType,
YdtContextOperationType opType) {
RequestedCardinality cardinality = null;
switch (ydtType) {
case MULTI_INSTANCE_NODE:
cardinality = MULTI_INSTANCE;
break;
case SINGLE_INSTANCE_NODE:
cardinality = SINGLE_INSTANCE;
break;
default:
curNode.errorHandler(E_USE_ADDLEAF, rootNode);
}
addChild(name, namespace, cardinality, opType, OTHER);
}
/**
* Adds a last child to YANG data tree; this method is to be used by all
* protocols internally which are aware or unaware of the nature
* (single/multiple) of node.
*
* @param name name of child to be added
* @param namespace namespace of child to be added
* @param cardinality type of YANG data tree node operation
* @param opType type of requested operation over a node
* @param callType to identify the whether its a leaf or other node
*/
private void addChild(String name, String namespace,
RequestedCardinality cardinality,
YdtContextOperationType opType,
RequestedCallType callType) {
YdtNode childNode;
boolean isContextSwitch = false;
YangSchemaNode schemaNode = null;
YangSchemaNodeContextInfo contextInfo;
YangSchemaNode augmentingSchema = null;
YangSchemaNodeIdentifier id = new YangSchemaNodeIdentifier();
id.setName(name);
// Module/sub-module node handler.
if (curNode.equals(rootNode)) {
childNode = moduleHandler(id, namespace);
} else {
// If namespace given by user null, then take namespace from parent.
if (namespace == null) {
namespace = curNode.getYdtNodeIdentifier().getNameSpace();
}
id.setNameSpace(namespace);
/*
* Get the already exiting YDT node in YDT tree with same
* nodeIdentifier
*/
childNode = curNode.getCollidingChild(id);
/*
* If colliding child doesn't exist ,
* then query yang data model for schema of given node.
*/
if (childNode == null) {
/*
* Get Yang Schema node context info which is having
* YangSchemaNode and ContextSwitchedNode.
*/
contextInfo = curNode.getSchemaNodeContextInfo(id);
if (contextInfo.getContextSwitchedNode() != null) {
augmentingSchema = appCurNode.getAugmentingSchemaNode(
id, contextInfo);
if (augmentingSchema != null) {
/*
* As two tree(YDT and YDT Application Tree) are getting
* prepared in parallel, So setting context switch
* flag it will help ydt to keep the track whether
* ydtApp tree also need to be traversed back to parent
* or not with YDT tree traverse to parent call.
*/
isContextSwitch = true;
}
}
schemaNode = contextInfo.getSchemaNode();
} else {
/*
* If colliding child exist , then will be leaf-list or list
* If its leaf-list then return and add new requested
* value/valueSet in same node else take yang data model
* information from colliding child.
*/
if (childNode.getYdtType() == MULTI_INSTANCE_LEAF_VALUE_NODE) {
curNode = childNode;
return;
}
schemaNode = childNode.getYangSchemaNode();
}
childNode = YdtNodeFactory.getNode(id, schemaNode, cardinality,
callType);
}
opType = getValidOpType(opType, callType, schemaNode);
childNode.setYdtContextOperationType(opType);
curNode.addChild(childNode, true);
// Update parent ydt node map.
curNode.updateYdtMap(id, childNode);
processAppTree(opType, childNode, augmentingSchema, isContextSwitch);
// Updating the curNode.
curNode = childNode;
}
/**
* Processes application tree on the bases of requested ydt node.
*
* @param opType user requested operation type
* @param childNode requested ydt node
* @param augmentingSchema schema of last augmenting node
* @param isContextSwitch true, for module node call; false for modules
* sub-node calls
*/
private void processAppTree(
YdtContextOperationType opType, YdtNode childNode,
YangSchemaNode augmentingSchema, boolean isContextSwitch) {
if (augmentingSchema != null) {
if (!appCurNode.addSchemaToAppSet(augmentingSchema)) {
return;
}
}
if (opType == null) {
opType = curNode.getYdtContextOperationType();
} else {
// Updating operation type for parent nodes
appCurNode.updateAppOperationType(opType);
}
/*
* Create entry of module node in ydt app tree.
* Or if context switch happened then also add entry for same ydt
* node in the ydt application tree.
*/
if (curNode.equals(rootNode) || isContextSwitch) {
addChildInAppTree(childNode, augmentingSchema, opType,
isContextSwitch);
// Setting app tree node operation.
appCurNode.setOperationType(getAppOpTypeFromYdtOpType(opType));
}
// Updating the delete operation list in app tree.
if (opType == DELETE || opType == REMOVE) {
appCurNode.addDeleteNode(childNode);
}
}
/**
* Returns the valid operation type for requested ydt node after performing
* validation.
*
* @param opType user requested operation type
* @param callType to identify the whether its a leaf or other node
* @param schemaNode schema node of user requested ydt node
* @return operation type
*/
private YdtContextOperationType getValidOpType(
YdtContextOperationType opType, RequestedCallType callType,
YangSchemaNode schemaNode) {
// Operation type not supported for leaf node.
if (callType == LEAF || (callType == RequestedCallType.MULTI_INSTANCE &&
schemaNode.getYangSchemaNodeType() ==
YANG_MULTI_INSTANCE_LEAF_NODE)) {
return null;
}
// Reference for parent node operation type.
YdtContextOperationType parentOpType = curNode
.getYdtContextOperationType();
if (opType != null && parentOpType != null) {
validateOperationType(parentOpType, opType);
} else if (opType == null) {
opType = getOperationType(parentOpType);
}
return opType;
}
/**
* Returns the operation type for non leaf node.
* When "operation" attribute for current node is not specified or null,
* then the operation applied to the parent data node of the
* configuration is used. If no parent data node is available,
* then the default-operation'value is used.
* If default operation type is not set, merge will be taken as default
* operation type.
*
* @param parentOpType operation type of parent node
* @return operation type for current non leaf node
*/
private YdtContextOperationType getOperationType(
YdtContextOperationType parentOpType) {
return parentOpType != null ? parentOpType :
(ydtDefaultOpType != null ? ydtDefaultOpType : MERGE);
}
/**
* Adds a last child to YANG app data tree.this method is to be used
* internally by other ydt interfaces.
*
* @param childNode node to be added in tree
* @param schemaNode last augmenting module node
* @param childOpType operation type of node
* @param isContextSwitch true, for module node call; false for modules
* sub-node calls
*/
private void addChildInAppTree(YdtNode childNode,
YangSchemaNode schemaNode,
YdtContextOperationType childOpType,
boolean isContextSwitch) {
YdtAppNodeOperationType opType;
DefaultYdtAppContext appContext = getAppContext(isContextSwitch);
// Add context switched child in ydt App tree.
appCurNode.addChild(appContext);
//Updating the curNode.
appCurNode = appContext;
// Get the app tree operation type from ydt operation type.
opType = getAppOpTypeFromYdtOpType(childOpType);
appCurNode.setAppData(childNode, schemaNode);
appCurNode.setOperationType(opType);
childNode.setAppContextSwitch();
}
/**
* Validates the various combination of operation type.
*
* @param parentOpType Reference for parent node operation type
* @param childOpType type of YANG data tree node operation
*/
private void validateOperationType(YdtContextOperationType parentOpType,
YdtContextOperationType childOpType) {
switch (parentOpType) {
case CREATE:
// Inside the create operation delete operation should not come.
if (childOpType == DELETE) {
curNode.errorHandler(E_CREATE, rootNode);
}
break;
case DELETE:
// Inside the delete operation create operation should not come.
if (childOpType == CREATE) {
curNode.errorHandler(E_DEL, rootNode);
}
break;
default:
//TODO check all possible scenario.
}
}
@Override
public void addLeaf(String name, String namespace, String value) {
addLeaf(name, namespace, value, null, UNKNOWN);
}
@Override
public void addLeaf(String name, String namespace, Set<String> valueSet) {
addLeaf(name, namespace, null, valueSet, MULTI_INSTANCE_LEAF);
}
/**
* Adds a last leaf with list of values/single value to YANG data tree.
* This method is used by all protocols which knows the nature
* (single/multiple) or not.
* Value of leaf can be null which indicates selection node in get
* operation.
*
* @param name name of child to be added
* @param namespace namespace of child to be added, if it's
* null, parent's
* namespace will be applied to child
* @param value value of the child
* @param valueSet list of value of the child
* @param cardinality type of YANG data tree node operation
*/
private void addLeaf(String name, String namespace, String value,
Set<String> valueSet,
RequestedCardinality cardinality) {
addChild(name, namespace, cardinality, null, LEAF);
// After successful addition of child node updating the values in same.
if (value != null) {
curNode.addValue(value);
} else if (valueSet != null) {
curNode.addValueSet(valueSet);
}
}
@Override
public void traverseToParent() {
// If traverse back to parent for logical root node comes
if (curNode.equals(rootNode)) {
curNode.errorHandler(E_INVOKE_PARENT, rootNode);
}
// If node is of multiInstanceNode type then check key uniqueness.
if (curNode.getYdtType() == MULTI_INSTANCE_NODE) {
curNode.createKeyNodeList();
}
/*
* Check application switch for curNode if set,
* then traverseToParent in YDT application tree.
*/
if (curNode.getParent().equals(rootNode) ||
curNode.getAppContextSwitch()) {
traverseToAppTreeParent();
}
/*
* Validate all multi Instance inside current context,
* This is not valid for leaf and leaf-list node.
*/
if (curNode instanceof YdtMultiInstanceNode ||
curNode instanceof YdtSingleInstanceNode) {
curNode.validateMultiInstanceNode();
}
curNode = curNode.getParent();
}
/**
* Traverses up in YANG application tree to the parent node,
* This will be used when Ydt current context switch flag is set.
*/
private void traverseToAppTreeParent() {
appCurNode = appCurNode.getParent();
}
@Override
public YdtContext getCurNode() {
return curNode;
}
@Override
public void setDefaultEditOperationType(
YdtContextOperationType opType) {
ydtDefaultOpType = opType;
}
@Override
public YdtExtendedContext getRootNode() {
return rootNode;
}
@Override
public YmsOperationType getYmsOperationType() {
return ymsOperationType;
}
@Override
public void addMultiInstanceChild(String name, String namespace,
List<String> keysValueList,
YdtContextOperationType opType) {
addChild(name, namespace, UNKNOWN, opType,
RequestedCallType.MULTI_INSTANCE);
int inputCount = keysValueList.size();
int expectedCount;
if (curNode.getYdtType() == MULTI_INSTANCE_LEAF_VALUE_NODE) {
// After successful addition of child node updating
// the values in same.
// inputCount = curNode.getValueSet().size() + inputCount;
// checkElementCount(expectedCount, inputCount);
// TODO check the element count
for (String value : keysValueList) {
curNode.addValue(value);
}
} else if (curNode.getYdtType() == MULTI_INSTANCE_NODE) {
YangList yangListHolder = (YangList) curNode.getYangSchemaNode();
List<String> schemaKeyList = yangListHolder.getKeyList();
expectedCount = schemaKeyList.size();
checkElementCount(name, expectedCount, inputCount);
Iterator<String> sklIter = schemaKeyList.iterator();
Iterator<String> kvlIter = keysValueList.iterator();
String keyEleName;
while (kvlIter.hasNext()) {
String value = kvlIter.next();
keyEleName = sklIter.next();
addLeaf(keyEleName, namespace, value);
if (kvlIter.hasNext()) {
traverseToParentWithoutValidation();
}
}
curNode = curNode.getParent();
} else {
curNode.errorHandler(E_MULTI_INS, rootNode);
}
}
/**
* Checks the user supplied list of argument match's the expected value
* or not.
*
* @param name name of the parent list/leaf-list node
* @param expected count suppose to be
* @param actual user supplied values count
*/
private void checkElementCount(String name, int expected,
int actual) {
if (expected < actual) {
curNode.errorHandler(errorMsg(FMT_TOO_MANY, name, expected, actual),
rootNode);
} else if (expected > actual) {
curNode.errorHandler(errorMsg(FMT_TOO_FEW, name, expected, actual),
rootNode);
}
}
/**
* Adds a last child to YANG data tree, this method is to be used by
* YANG object builder sub-calls internally.
*
* @param opType type of requested operation over a node
* @return returns added ydt node in YDT tree
*/
private YdtNode addExtendedChildNode(YdtContextOperationType opType,
YangSchemaNode schemaNode) {
YdtNode childNode;
YangSchemaNodeIdentifier id =
schemaNode.getYangSchemaNodeIdentifier();
childNode = YdtNodeFactory
.getYangSchemaNodeTypeSpecificContext(
id, schemaNode.getYangSchemaNodeType());
childNode.setId(id);
childNode.setYangSchemaNode(schemaNode);
childNode.setYdtContextOperationType(opType);
curNode.addChild(childNode, true);
curNode = childNode;
return childNode;
}
@Override
public YdtExtendedContext addChild(YdtContextOperationType opType,
YangSchemaNode schemaNode) {
return addExtendedChildNode(opType, schemaNode);
}
@Override
public YdtExtendedContext addLeafList(Set<String> valueSet,
YangSchemaNode schemaNode) {
YdtNode childNode = addExtendedChildNode(null, schemaNode);
// After successful addition of child node updating the values in same.
childNode.addValueSetWithoutValidation(valueSet);
return childNode;
}
@Override
public YdtExtendedContext addLeaf(String value,
YangSchemaNode schemaNode) {
YdtNode childNode = addExtendedChildNode(null, schemaNode);
// After successful addition of child node updating the values in same.
childNode.addValueWithoutValidation(value);
return childNode;
}
@Override
public void traverseToParentWithoutValidation() {
// If traverse back to parent for logical root node comes
if (curNode.equals(rootNode)) {
curNode.errorHandler(E_INVOKE_PARENT, rootNode);
}
curNode = curNode.getParent();
}
}