| /* |
| * Copyright 2017-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.onosproject.drivers.microsemi.yang.impl; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| import com.google.common.base.Charsets; |
| import com.google.common.io.ByteSource; |
| import org.apache.felix.scr.annotations.Activate; |
| import org.apache.felix.scr.annotations.Component; |
| import org.apache.felix.scr.annotations.Deactivate; |
| import org.apache.felix.scr.annotations.Reference; |
| import org.apache.felix.scr.annotations.ReferenceCardinality; |
| import org.apache.felix.scr.annotations.Service; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.CoreService; |
| import org.onosproject.netconf.DatastoreId; |
| import org.onosproject.netconf.NetconfException; |
| import org.onosproject.netconf.NetconfSession; |
| import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.CcmIntervalEnum; |
| import org.onosproject.yang.gen.v1.mseasoampm.rev20160229.mseasoampm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.augmentedmseacfmmaintenanceassociationendpoint.lossmeasurements.lossmeasurement.MessagePeriodEnum; |
| import org.onosproject.yang.model.ModelConverter; |
| import org.onosproject.yang.model.ModelObjectData; |
| import org.onosproject.yang.model.ResourceData; |
| import org.onosproject.yang.model.ResourceId; |
| import org.onosproject.yang.model.SchemaContext; |
| import org.onosproject.yang.model.SchemaContextProvider; |
| import org.onosproject.yang.runtime.AnnotatedNodeInfo; |
| import org.onosproject.yang.runtime.CompositeData; |
| import org.onosproject.yang.runtime.CompositeStream; |
| import org.onosproject.yang.runtime.DefaultCompositeData; |
| import org.onosproject.yang.runtime.DefaultCompositeStream; |
| import org.onosproject.yang.runtime.DefaultYangSerializerContext; |
| import org.onosproject.yang.runtime.YangModelRegistry; |
| import org.onosproject.yang.runtime.YangSerializer; |
| import org.onosproject.yang.runtime.YangSerializerContext; |
| import org.onosproject.yang.runtime.YangSerializerRegistry; |
| import org.onosproject.yang.serializers.xml.XmlSerializer; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Abstract class that implements some of the core functions of a YANG model service. |
| * |
| */ |
| @Component(immediate = true) |
| @Service |
| public abstract class AbstractYangServiceImpl { |
| public static final String NC_OPERATION = "nc:operation"; |
| public static final String OP_DELETE = "delete"; |
| |
| protected final Logger log = LoggerFactory.getLogger(getClass()); |
| protected boolean alreadyLoaded = false; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected CoreService coreService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected YangModelRegistry yangModelRegistry; |
| |
| // @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| // protected SchemaContextProvider schemaContextProvider; |
| |
| protected ApplicationId appId; |
| |
| // xSer is not a service and is a class variable. Can be lost on deactivate. |
| // Must be recreated on activate |
| protected XmlSerializer xSer; |
| protected YangSerializerContext yCtx; |
| |
| protected static final Pattern REGEX_XML_HEADER = |
| Pattern.compile("(<\\?xml).*(\\?>)", Pattern.DOTALL); |
| protected static final Pattern REGEX_RPC_REPLY = |
| Pattern.compile("(<rpc-reply)[ ]*" + |
| "(xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\")[ ]*" + |
| "(message-id=\")[0-9]*(\">)", Pattern.DOTALL); |
| protected static final Pattern REGEX_RPC_REPLY_DATA_NS = |
| Pattern.compile("(<data)[ ]*(xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">)"); |
| protected static final Pattern REGEX_RPC_REPLY_DATA = |
| Pattern.compile("(<data>)"); |
| protected static final Pattern REGEX_RPC_REPLY_DATA_CLOSE = |
| Pattern.compile("(</data>)"); |
| protected static final Pattern REGEX_RPC_REPLY_DATA_EMPTY = |
| Pattern.compile("(<data/>)"); |
| protected static final Pattern REGEX_RPC_REPLY_CLOSE = |
| Pattern.compile("(</rpc-reply>)"); |
| protected static final Pattern REGEX_RPC_OK = |
| Pattern.compile("\\R?\\s*(<ok/>)\\R?"); |
| @Activate |
| public void activate() { |
| Set<YangSerializer> yangSer = ((YangSerializerRegistry) yangModelRegistry).getSerializers(); |
| yangSer.forEach(ser -> { |
| if (ser instanceof XmlSerializer) { |
| xSer = (XmlSerializer) ser; |
| } |
| }); |
| SchemaContext context = ((SchemaContextProvider) yangModelRegistry) |
| .getSchemaContext(ResourceId.builder().addBranchPointSchema("/", null).build()); |
| |
| yCtx = new DefaultYangSerializerContext(context, null); |
| }; |
| |
| @Deactivate |
| public void deactivate() { |
| alreadyLoaded = false; |
| } |
| |
| /** |
| * Internal method to generically make a NETCONF get query from YANG objects. |
| * @param moFilter A YANG object model |
| * @param session A NETCONF session |
| * @return YangObjectModel |
| * @throws NetconfException if the session has any error |
| */ |
| protected final ModelObjectData getNetconfObject( |
| ModelObjectData moFilter, NetconfSession session) |
| throws NetconfException { |
| |
| return getConfigNetconfObject(moFilter, session, null); |
| } |
| |
| /** |
| * Internal method to generically make a NETCONF get-config query from YANG objects. |
| * |
| * @param moFilter A YANG object model |
| * @param session A NETCONF session |
| * @param targetDs - running,candidate or startup |
| * @return YangObjectModel |
| * @throws NetconfException if the session has any error |
| */ |
| protected final ModelObjectData getConfigNetconfObject( |
| ModelObjectData moFilter, NetconfSession session, DatastoreId targetDs) |
| throws NetconfException { |
| if (session == null) { |
| throw new NetconfException("Session is null when calling getConfigNetconfObject()"); |
| } |
| |
| if (moFilter == null) { |
| throw new NetconfException("Query object cannot be null"); |
| } |
| |
| String xmlQueryStr = encodeMoToXmlStr(moFilter, null); |
| |
| log.debug("Sending <get-(config)> query on NETCONF session " + session.getSessionId() + |
| ":\n" + xmlQueryStr); |
| String xmlResult; |
| if (targetDs == null) { |
| xmlResult = session.get(xmlQueryStr, null); |
| } else { |
| xmlResult = session.getConfig(targetDs, xmlQueryStr); |
| } |
| xmlResult = removeRpcReplyData(xmlResult); |
| |
| DefaultCompositeStream resultDcs = new DefaultCompositeStream( |
| null, new ByteArrayInputStream(xmlResult.getBytes())); |
| CompositeData compositeData = xSer.decode(resultDcs, yCtx); |
| |
| return ((ModelConverter) yangModelRegistry).createModel(compositeData.resourceData()); |
| } |
| |
| /** |
| * Internal method to generically make a NETCONF edit-config call from a set of YANG objects. |
| * |
| * @param moConfig A YANG object model |
| * @param session A NETCONF session |
| * @param targetDs - running,candidate or startup |
| * @param annotations A list of AnnotatedNodeInfos to be added to the DataNodes |
| * @return Boolean value indicating success or failure of command |
| * @throws NetconfException if the session has any error |
| */ |
| protected final boolean setNetconfObject( |
| ModelObjectData moConfig, NetconfSession session, DatastoreId targetDs, |
| List<AnnotatedNodeInfo> annotations) throws NetconfException { |
| if (moConfig == null) { |
| throw new NetconfException("Query object cannot be null"); |
| } else if (session == null) { |
| throw new NetconfException("Session is null when calling setNetconfObject()"); |
| } else if (targetDs == null) { |
| throw new NetconfException("TargetDs is null when calling setNetconfObject()"); |
| } |
| |
| String xmlQueryStr = encodeMoToXmlStr(moConfig, annotations); |
| log.debug("Sending <edit-config> query on NETCONF session " + session.getSessionId() + |
| ":\n" + xmlQueryStr); |
| |
| //Some encoded values just have to be replaced |
| xmlQueryStr = xmlQueryStr.replace(MessagePeriodEnum.YANGAUTOPREFIX3MS.toString(), "3ms"); |
| xmlQueryStr = xmlQueryStr.replace(MessagePeriodEnum.YANGAUTOPREFIX10MS.toString(), "10ms"); |
| xmlQueryStr = xmlQueryStr.replace(MessagePeriodEnum.YANGAUTOPREFIX100MS.toString(), "100ms"); |
| xmlQueryStr = xmlQueryStr.replace(MessagePeriodEnum.YANGAUTOPREFIX1000MS.toString(), "1000ms"); |
| |
| xmlQueryStr = xmlQueryStr.replace(CcmIntervalEnum.YANGAUTOPREFIX3_3MS.toString(), "3.3ms"); |
| xmlQueryStr = xmlQueryStr.replace(CcmIntervalEnum.YANGAUTOPREFIX10MS.toString(), "10ms"); |
| xmlQueryStr = xmlQueryStr.replace(CcmIntervalEnum.YANGAUTOPREFIX100MS.toString(), "100ms"); |
| xmlQueryStr = xmlQueryStr.replace(CcmIntervalEnum.YANGAUTOPREFIX1S.toString(), "1s"); |
| |
| return session.editConfig(targetDs, null, xmlQueryStr); |
| } |
| |
| /** |
| * Internal method to generically call a NETCONF custom RPC from a set of YANG objects. |
| * |
| * @param customRpcInput A YANG object model |
| * @param rpcName The name of the RPC - replaces 'input' in the XML payload |
| * @param session A NETCONF session |
| * @return ModelObjectData value indicating success or failure of command |
| * @throws NetconfException if the session has any error |
| */ |
| protected final ModelObjectData customRpcNetconf( |
| ModelObjectData customRpcInput, String rpcName, NetconfSession session) |
| throws NetconfException { |
| if (customRpcInput == null) { |
| throw new NetconfException("Input object cannot be null"); |
| } else if (session == null) { |
| throw new NetconfException("Session is null when calling customRpcNetconf()"); |
| } |
| |
| String xmlQueryStr = encodeMoToXmlStr(customRpcInput, null); |
| xmlQueryStr = xmlQueryStr.replace("input", rpcName); |
| log.debug("Sending <edit-config> query on NETCONF session " + session.getSessionId() + |
| ":\n" + xmlQueryStr); |
| |
| String xmlResult = session.doWrappedRpc(xmlQueryStr); |
| xmlResult = removeRpcReplyData(xmlResult); |
| if (REGEX_RPC_OK.matcher(xmlResult).matches()) { |
| return null; |
| } |
| |
| DefaultCompositeStream resultDcs = new DefaultCompositeStream( |
| null, new ByteArrayInputStream(xmlResult.getBytes())); |
| CompositeData compositeData = xSer.decode(resultDcs, yCtx); |
| |
| return ((ModelConverter) yangModelRegistry).createModel(compositeData.resourceData()); |
| } |
| |
| protected final String encodeMoToXmlStr(ModelObjectData yangObjectOpParamFilter, |
| List<AnnotatedNodeInfo> annotations) |
| throws NetconfException { |
| //Convert the param to XML to use as a filter |
| ResourceData rd = ((ModelConverter) yangModelRegistry).createDataNode(yangObjectOpParamFilter); |
| |
| DefaultCompositeData.Builder cdBuilder = |
| DefaultCompositeData.builder().resourceData(rd); |
| if (annotations != null) { |
| for (AnnotatedNodeInfo ani : annotations) { |
| cdBuilder.addAnnotatedNodeInfo(ani); |
| } |
| } |
| CompositeStream cs = xSer.encode(cdBuilder.build(), yCtx); |
| //Convert the param to XML to use as a filter |
| |
| try { |
| ByteSource byteSource = new ByteSource() { |
| @Override |
| public InputStream openStream() throws IOException { |
| return cs.resourceData(); |
| } |
| }; |
| |
| return byteSource.asCharSource(Charsets.UTF_8).read(); |
| } catch (IOException e) { |
| throw new NetconfException("Error decoding CompositeStream to String", e); |
| } |
| } |
| |
| protected static final String removeRpcReplyData(String rpcReplyXml) throws NetconfException { |
| rpcReplyXml = REGEX_XML_HEADER.matcher(rpcReplyXml).replaceFirst(""); |
| if (rpcReplyXml.contains("<rpc-error")) { |
| throw new NetconfException("NETCONF rpc-error: " + rpcReplyXml); |
| } |
| |
| rpcReplyXml = REGEX_RPC_REPLY.matcher(rpcReplyXml).replaceFirst(""); |
| rpcReplyXml = REGEX_RPC_REPLY_DATA_NS.matcher(rpcReplyXml).replaceFirst(""); |
| rpcReplyXml = REGEX_RPC_REPLY_DATA.matcher(rpcReplyXml).replaceFirst(""); |
| rpcReplyXml = REGEX_RPC_REPLY_DATA_CLOSE.matcher(rpcReplyXml).replaceFirst(""); |
| rpcReplyXml = REGEX_RPC_REPLY_DATA_EMPTY.matcher(rpcReplyXml).replaceFirst(""); |
| rpcReplyXml = REGEX_RPC_REPLY_CLOSE.matcher(rpcReplyXml).replaceFirst(""); |
| rpcReplyXml = rpcReplyXml.replace("\t", ""); |
| return rpcReplyXml; |
| } |
| } |