blob: e61d5e5f89cc84eac5e2d33ea29e2934396a47e6 [file] [log] [blame]
Sean Condon06613e92017-06-09 15:14:01 +01001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Sean Condon06613e92017-06-09 15:14:01 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.drivers.microsemi.yang.impl;
17
Sean Condon06613e92017-06-09 15:14:01 +010018import com.google.common.base.Charsets;
19import com.google.common.io.ByteSource;
Sean Condon06613e92017-06-09 15:14:01 +010020import org.onosproject.core.ApplicationId;
21import org.onosproject.core.CoreService;
22import org.onosproject.netconf.DatastoreId;
23import org.onosproject.netconf.NetconfException;
24import org.onosproject.netconf.NetconfSession;
Sean Condon0e89bda2017-03-21 14:23:19 +000025import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.CcmIntervalEnum;
26import org.onosproject.yang.gen.v1.mseasoampm.rev20160229.mseasoampm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.augmentedmseacfmmaintenanceassociationendpoint.lossmeasurements.lossmeasurement.MessagePeriodEnum;
Sean Condon06613e92017-06-09 15:14:01 +010027import org.onosproject.yang.model.ModelConverter;
28import org.onosproject.yang.model.ModelObjectData;
29import org.onosproject.yang.model.ResourceData;
30import org.onosproject.yang.model.ResourceId;
31import org.onosproject.yang.model.SchemaContext;
32import org.onosproject.yang.model.SchemaContextProvider;
33import org.onosproject.yang.runtime.AnnotatedNodeInfo;
34import org.onosproject.yang.runtime.CompositeData;
35import org.onosproject.yang.runtime.CompositeStream;
36import org.onosproject.yang.runtime.DefaultCompositeData;
37import org.onosproject.yang.runtime.DefaultCompositeStream;
38import org.onosproject.yang.runtime.DefaultYangSerializerContext;
39import org.onosproject.yang.runtime.YangModelRegistry;
40import org.onosproject.yang.runtime.YangSerializer;
41import org.onosproject.yang.runtime.YangSerializerContext;
42import org.onosproject.yang.runtime.YangSerializerRegistry;
43import org.onosproject.yang.serializers.xml.XmlSerializer;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070044import org.osgi.service.component.annotations.Activate;
45import org.osgi.service.component.annotations.Component;
46import org.osgi.service.component.annotations.Deactivate;
47import org.osgi.service.component.annotations.Reference;
48import org.osgi.service.component.annotations.ReferenceCardinality;
Sean Condon06613e92017-06-09 15:14:01 +010049import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
Ray Milkeyd84f89b2018-08-17 14:54:17 -070052import java.io.ByteArrayInputStream;
53import java.io.IOException;
54import java.io.InputStream;
55import java.util.List;
56import java.util.Set;
57import java.util.regex.Pattern;
58
Sean Condon06613e92017-06-09 15:14:01 +010059/**
60 * Abstract class that implements some of the core functions of a YANG model service.
61 *
62 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070063@Component(immediate = true, service = AbstractYangServiceImpl.class)
Sean Condon06613e92017-06-09 15:14:01 +010064public abstract class AbstractYangServiceImpl {
65 public static final String NC_OPERATION = "nc:operation";
66 public static final String OP_DELETE = "delete";
67
68 protected final Logger log = LoggerFactory.getLogger(getClass());
69 protected boolean alreadyLoaded = false;
70
Ray Milkeyd84f89b2018-08-17 14:54:17 -070071 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sean Condon06613e92017-06-09 15:14:01 +010072 protected CoreService coreService;
73
Ray Milkeyd84f89b2018-08-17 14:54:17 -070074 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sean Condon06613e92017-06-09 15:14:01 +010075 protected YangModelRegistry yangModelRegistry;
76
Sean Condon06613e92017-06-09 15:14:01 +010077 protected ApplicationId appId;
78
79 // xSer is not a service and is a class variable. Can be lost on deactivate.
80 // Must be recreated on activate
Sean Condon1dbcd712017-10-19 12:09:21 +010081 protected XmlSerializer xSer = null;
82 protected YangSerializerContext yCtx = null;
Sean Condon06613e92017-06-09 15:14:01 +010083
84 protected static final Pattern REGEX_XML_HEADER =
85 Pattern.compile("(<\\?xml).*(\\?>)", Pattern.DOTALL);
86 protected static final Pattern REGEX_RPC_REPLY =
87 Pattern.compile("(<rpc-reply)[ ]*" +
88 "(xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\")[ ]*" +
89 "(message-id=\")[0-9]*(\">)", Pattern.DOTALL);
90 protected static final Pattern REGEX_RPC_REPLY_DATA_NS =
91 Pattern.compile("(<data)[ ]*(xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">)");
92 protected static final Pattern REGEX_RPC_REPLY_DATA =
93 Pattern.compile("(<data>)");
94 protected static final Pattern REGEX_RPC_REPLY_DATA_CLOSE =
95 Pattern.compile("(</data>)");
96 protected static final Pattern REGEX_RPC_REPLY_DATA_EMPTY =
97 Pattern.compile("(<data/>)");
98 protected static final Pattern REGEX_RPC_REPLY_CLOSE =
99 Pattern.compile("(</rpc-reply>)");
Sean Condon0e89bda2017-03-21 14:23:19 +0000100 protected static final Pattern REGEX_RPC_OK =
101 Pattern.compile("\\R?\\s*(<ok/>)\\R?");
Sean Condon06613e92017-06-09 15:14:01 +0100102 @Activate
103 public void activate() {
104 Set<YangSerializer> yangSer = ((YangSerializerRegistry) yangModelRegistry).getSerializers();
Sean Condon1dbcd712017-10-19 12:09:21 +0100105 xSer = (XmlSerializer) yangSer.stream()
106 .filter(ser -> (ser instanceof XmlSerializer)).findFirst().get();
Sean Condon06613e92017-06-09 15:14:01 +0100107 SchemaContext context = ((SchemaContextProvider) yangModelRegistry)
108 .getSchemaContext(ResourceId.builder().addBranchPointSchema("/", null).build());
109
110 yCtx = new DefaultYangSerializerContext(context, null);
111 };
112
113 @Deactivate
114 public void deactivate() {
115 alreadyLoaded = false;
116 }
117
118 /**
119 * Internal method to generically make a NETCONF get query from YANG objects.
120 * @param moFilter A YANG object model
121 * @param session A NETCONF session
122 * @return YangObjectModel
123 * @throws NetconfException if the session has any error
124 */
125 protected final ModelObjectData getNetconfObject(
126 ModelObjectData moFilter, NetconfSession session)
127 throws NetconfException {
128
129 return getConfigNetconfObject(moFilter, session, null);
130 }
131
132 /**
133 * Internal method to generically make a NETCONF get-config query from YANG objects.
134 *
135 * @param moFilter A YANG object model
136 * @param session A NETCONF session
137 * @param targetDs - running,candidate or startup
138 * @return YangObjectModel
139 * @throws NetconfException if the session has any error
140 */
141 protected final ModelObjectData getConfigNetconfObject(
142 ModelObjectData moFilter, NetconfSession session, DatastoreId targetDs)
143 throws NetconfException {
144 if (session == null) {
145 throw new NetconfException("Session is null when calling getConfigNetconfObject()");
146 }
147
148 if (moFilter == null) {
149 throw new NetconfException("Query object cannot be null");
150 }
151
152 String xmlQueryStr = encodeMoToXmlStr(moFilter, null);
153
154 log.debug("Sending <get-(config)> query on NETCONF session " + session.getSessionId() +
155 ":\n" + xmlQueryStr);
156 String xmlResult;
157 if (targetDs == null) {
158 xmlResult = session.get(xmlQueryStr, null);
159 } else {
160 xmlResult = session.getConfig(targetDs, xmlQueryStr);
161 }
162 xmlResult = removeRpcReplyData(xmlResult);
163
164 DefaultCompositeStream resultDcs = new DefaultCompositeStream(
165 null, new ByteArrayInputStream(xmlResult.getBytes()));
166 CompositeData compositeData = xSer.decode(resultDcs, yCtx);
167
168 return ((ModelConverter) yangModelRegistry).createModel(compositeData.resourceData());
169 }
170
171 /**
172 * Internal method to generically make a NETCONF edit-config call from a set of YANG objects.
173 *
174 * @param moConfig A YANG object model
175 * @param session A NETCONF session
176 * @param targetDs - running,candidate or startup
177 * @param annotations A list of AnnotatedNodeInfos to be added to the DataNodes
178 * @return Boolean value indicating success or failure of command
179 * @throws NetconfException if the session has any error
180 */
181 protected final boolean setNetconfObject(
182 ModelObjectData moConfig, NetconfSession session, DatastoreId targetDs,
183 List<AnnotatedNodeInfo> annotations) throws NetconfException {
184 if (moConfig == null) {
185 throw new NetconfException("Query object cannot be null");
186 } else if (session == null) {
Sean Condon0e89bda2017-03-21 14:23:19 +0000187 throw new NetconfException("Session is null when calling setNetconfObject()");
Sean Condon06613e92017-06-09 15:14:01 +0100188 } else if (targetDs == null) {
Sean Condon0e89bda2017-03-21 14:23:19 +0000189 throw new NetconfException("TargetDs is null when calling setNetconfObject()");
Sean Condon06613e92017-06-09 15:14:01 +0100190 }
191
192 String xmlQueryStr = encodeMoToXmlStr(moConfig, annotations);
193 log.debug("Sending <edit-config> query on NETCONF session " + session.getSessionId() +
194 ":\n" + xmlQueryStr);
195
Sean Condon0e89bda2017-03-21 14:23:19 +0000196 //Some encoded values just have to be replaced
197 xmlQueryStr = xmlQueryStr.replace(MessagePeriodEnum.YANGAUTOPREFIX3MS.toString(), "3ms");
198 xmlQueryStr = xmlQueryStr.replace(MessagePeriodEnum.YANGAUTOPREFIX10MS.toString(), "10ms");
199 xmlQueryStr = xmlQueryStr.replace(MessagePeriodEnum.YANGAUTOPREFIX100MS.toString(), "100ms");
200 xmlQueryStr = xmlQueryStr.replace(MessagePeriodEnum.YANGAUTOPREFIX1000MS.toString(), "1000ms");
201
202 xmlQueryStr = xmlQueryStr.replace(CcmIntervalEnum.YANGAUTOPREFIX3_3MS.toString(), "3.3ms");
203 xmlQueryStr = xmlQueryStr.replace(CcmIntervalEnum.YANGAUTOPREFIX10MS.toString(), "10ms");
204 xmlQueryStr = xmlQueryStr.replace(CcmIntervalEnum.YANGAUTOPREFIX100MS.toString(), "100ms");
205 xmlQueryStr = xmlQueryStr.replace(CcmIntervalEnum.YANGAUTOPREFIX1S.toString(), "1s");
206
Sean Condon06613e92017-06-09 15:14:01 +0100207 return session.editConfig(targetDs, null, xmlQueryStr);
208 }
209
Sean Condon0e89bda2017-03-21 14:23:19 +0000210 /**
211 * Internal method to generically call a NETCONF custom RPC from a set of YANG objects.
212 *
213 * @param customRpcInput A YANG object model
214 * @param rpcName The name of the RPC - replaces 'input' in the XML payload
215 * @param session A NETCONF session
216 * @return ModelObjectData value indicating success or failure of command
217 * @throws NetconfException if the session has any error
218 */
219 protected final ModelObjectData customRpcNetconf(
220 ModelObjectData customRpcInput, String rpcName, NetconfSession session)
221 throws NetconfException {
222 if (customRpcInput == null) {
223 throw new NetconfException("Input object cannot be null");
224 } else if (session == null) {
225 throw new NetconfException("Session is null when calling customRpcNetconf()");
226 }
227
228 String xmlQueryStr = encodeMoToXmlStr(customRpcInput, null);
229 xmlQueryStr = xmlQueryStr.replace("input", rpcName);
230 log.debug("Sending <edit-config> query on NETCONF session " + session.getSessionId() +
231 ":\n" + xmlQueryStr);
232
233 String xmlResult = session.doWrappedRpc(xmlQueryStr);
234 xmlResult = removeRpcReplyData(xmlResult);
235 if (REGEX_RPC_OK.matcher(xmlResult).matches()) {
236 return null;
237 }
238
239 DefaultCompositeStream resultDcs = new DefaultCompositeStream(
240 null, new ByteArrayInputStream(xmlResult.getBytes()));
241 CompositeData compositeData = xSer.decode(resultDcs, yCtx);
242
243 return ((ModelConverter) yangModelRegistry).createModel(compositeData.resourceData());
244 }
245
Sean Condon06613e92017-06-09 15:14:01 +0100246 protected final String encodeMoToXmlStr(ModelObjectData yangObjectOpParamFilter,
247 List<AnnotatedNodeInfo> annotations)
248 throws NetconfException {
249 //Convert the param to XML to use as a filter
250 ResourceData rd = ((ModelConverter) yangModelRegistry).createDataNode(yangObjectOpParamFilter);
251
252 DefaultCompositeData.Builder cdBuilder =
253 DefaultCompositeData.builder().resourceData(rd);
254 if (annotations != null) {
255 for (AnnotatedNodeInfo ani : annotations) {
256 cdBuilder.addAnnotatedNodeInfo(ani);
257 }
258 }
259 CompositeStream cs = xSer.encode(cdBuilder.build(), yCtx);
260 //Convert the param to XML to use as a filter
261
262 try {
263 ByteSource byteSource = new ByteSource() {
264 @Override
265 public InputStream openStream() throws IOException {
266 return cs.resourceData();
267 }
268 };
269
270 return byteSource.asCharSource(Charsets.UTF_8).read();
271 } catch (IOException e) {
272 throw new NetconfException("Error decoding CompositeStream to String", e);
273 }
274 }
275
Sean Condon0e89bda2017-03-21 14:23:19 +0000276 protected static final String removeRpcReplyData(String rpcReplyXml) throws NetconfException {
Sean Condon06613e92017-06-09 15:14:01 +0100277 rpcReplyXml = REGEX_XML_HEADER.matcher(rpcReplyXml).replaceFirst("");
Sean Condon0e89bda2017-03-21 14:23:19 +0000278 if (rpcReplyXml.contains("<rpc-error")) {
279 throw new NetconfException("NETCONF rpc-error: " + rpcReplyXml);
280 }
281
Sean Condon06613e92017-06-09 15:14:01 +0100282 rpcReplyXml = REGEX_RPC_REPLY.matcher(rpcReplyXml).replaceFirst("");
283 rpcReplyXml = REGEX_RPC_REPLY_DATA_NS.matcher(rpcReplyXml).replaceFirst("");
284 rpcReplyXml = REGEX_RPC_REPLY_DATA.matcher(rpcReplyXml).replaceFirst("");
285 rpcReplyXml = REGEX_RPC_REPLY_DATA_CLOSE.matcher(rpcReplyXml).replaceFirst("");
286 rpcReplyXml = REGEX_RPC_REPLY_DATA_EMPTY.matcher(rpcReplyXml).replaceFirst("");
287 rpcReplyXml = REGEX_RPC_REPLY_CLOSE.matcher(rpcReplyXml).replaceFirst("");
288 rpcReplyXml = rpcReplyXml.replace("\t", "");
289 return rpcReplyXml;
290 }
291}