blob: bd969db2c4344e0a2c22e1d63b27a4002bcac197 [file] [log] [blame]
David Bainbridge7526c452018-04-20 14:14:37 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
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.netconf;
17
18import static org.slf4j.LoggerFactory.getLogger;
19
20import java.io.IOException;
21import java.io.InputStream;
22import java.nio.charset.StandardCharsets;
23import java.util.HashMap;
24import java.util.Map;
25
26import javax.xml.namespace.QName;
27import javax.xml.parsers.DocumentBuilder;
28import javax.xml.parsers.DocumentBuilderFactory;
29import javax.xml.xpath.XPath;
30import javax.xml.xpath.XPathConstants;
31import javax.xml.xpath.XPathFactory;
32
33import org.apache.commons.io.IOUtils;
34import org.onosproject.netconf.NetconfException;
35import org.onosproject.netconf.NetconfSession;
36import org.slf4j.Logger;
37import org.w3c.dom.Document;
38import org.w3c.dom.Node;
39
40import com.google.common.io.Resources;
41
42/**
43 * Manages templates and provides utilities to execute these templates against a
44 * NETCONF session.
45 */
46public final class TemplateManager {
47 private static final Logger log = getLogger(TemplateManager.class);
48 private final Map<String, String> templates = new HashMap<String, String>();
49 private TemplateRequestDriver requestDriver;
Ray Milkeyaab4ccf2018-05-03 09:11:25 -070050 private static final Map<String, Object> EMPTY_TEMPLATE_CONTEXT = new HashMap<String, Object>();
David Bainbridge7526c452018-04-20 14:14:37 -070051
52 /**
53 * Internal implementation of the request driver that implements a NETCONF
54 * driver.
55 */
56 private class InternalRequestDriver implements TemplateRequestDriver {
57 @Override
58 public Object doRequest(NetconfSession session, String templateName, Map<String, Object> templateContext,
59 String baseXPath, QName returnType) throws NetconfException {
60 try {
61 String data = session.rpc(render(templates.get(templateName), templateContext)).get();
62 InputStream resp = IOUtils.toInputStream(data, StandardCharsets.UTF_8);
63 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
64 DocumentBuilder builder = builderFactory.newDocumentBuilder();
65 Document document = builder.parse(resp);
66 XPath xp = XPathFactory.newInstance().newXPath();
67 return xp.evaluate(baseXPath, document, returnType);
68 } catch (Exception e) {
69 NetconfException ne = new NetconfException(e.getMessage(), e);
70 throw ne;
71 }
72 }
73 }
74
75 /**
76 * Constructs a new template manager and loads the default templates.
77 */
78 public TemplateManager() {
79 requestDriver = new InternalRequestDriver();
80 }
81
82 /**
83 * Sets the request driver for the template manager.
84 *
85 * @param driver
86 * the driver to use
87 */
88 public void setRequestDriver(TemplateRequestDriver driver) {
89 requestDriver = driver;
90 }
91
92 /**
93 * Loads the named templates into the template manager.
94 *
95 * @param reference
96 * the class reference from which to load resources
97 * @param pattern
98 * pattern to convert template name to resource path
99 * @param templateNames
100 * list of template to load
101 */
102 public void load(Class<? extends Object> reference, String pattern, String... templateNames) {
103 for (String name : templateNames) {
104 String key = name;
105 String resource;
106
107 // If the template name begins with a '/', then assume it is a full path
108 // specification
109 if (name.charAt(0) == '/') {
110 int start = name.lastIndexOf('/') + 1;
111 int end = name.lastIndexOf('.');
112 if (end == -1) {
113 key = name.substring(start);
114 } else {
115 key = name.substring(start, end);
116 }
117 resource = name;
118 } else {
119 resource = String.format(pattern, name);
120 }
121
122 log.error("LOAD TEMPLATE: '{}' as '{}' from '{}", name, key, resource);
123
124 try {
125 templates.put(name,
126 Resources.toString(Resources.getResource(reference, resource), StandardCharsets.UTF_8));
127
128 } catch (IOException ioe) {
129 log.error("Unable to load NETCONF request template '{}' from '{}'", key, resource, ioe);
130 }
131 }
132 }
133
134 /**
135 * Loads the named templates into the template manager using the default
136 * reference class and resource path pattern.
137 *
138 * @param templateMNames
139 * list of template to load
140 */
141 public void load(String... templateMNames) {
142 load(this.getClass(), "/templates/requests/%s.j2", templateMNames);
143 }
144
145 /**
146 * Loads the named templates into the template manager using the default
147 * reference class.
148 *
149 * @param pattern
150 * pattern to convert template name to resource path
151 * @param templateMNames
152 * list of template to load
153 */
154 public void load(String pattern, String... templateMNames) {
155 load(this.getClass(), pattern, templateMNames);
156 }
157
158 /**
159 * Returns the named template.
160 *
161 * @param templateName
162 * name of template to return
163 * @return template
164 */
165 public String get(String templateName) {
166 return templates.get(templateName);
167 }
168
169 /**
170 * Performs simple variable substitution into a string in likely the most
171 * inefficient way possible.
172 *
173 * @param template
174 * template into which to substitute variables
175 * @param context
176 * variable substitution map
177 * @return template rendered with variable substitution
178 */
179 public String render(String template, Map<String, Object> context) {
180 if (context == null) {
181 return template;
182 }
183
184 String temp = template;
185 for (Map.Entry<String, Object> e : context.entrySet()) {
186 temp = temp.replaceAll("\\{\\{ *" + e.getKey() + " *\\}\\}", e.getValue().toString());
187 }
188 return temp;
189 }
190
191 /**
192 * Executes the named NETCONF template against the specified session, returning
193 * the referenced XML node as the specified type.
194 *
195 * @param session
196 * NETCONF serssion
197 * @param templateName
198 * name of NETCONF request template to execute
199 * @param templateContext
200 * variable to values substitutions to be used against templates
201 * @param baseXPath
202 * XPath expression to specify the returned document node
203 * @param returnType
204 * expected return type of the referenced node
205 * @return XML document node referenced by the {@code baseXPath}
206 * @throws NetconfException
207 * if any IO, XPath, or NETCONF exception occurs
208 */
209 public Object doRequest(NetconfSession session, String templateName, Map<String, Object> templateContext,
210 String baseXPath, QName returnType) throws NetconfException {
211 return requestDriver.doRequest(session, templateName, templateContext, baseXPath, returnType);
212 }
213
214 /**
215 * Execute the named NETCONF template against the specified session returning
216 * the {@code /rpc-reply/data} section of the response document as a
217 * {@code Node}.
218 *
219 * @param session
220 * NETCONF session
221 * @param templateName
222 * name of NETCONF request template to execute
223 * @return XML document node that represents the NETCONF response data
224 * @throws NetconfException
225 * if any IO, XPath, or NETCONF exception occurs
226 */
227 public Node doRequest(NetconfSession session, String templateName) throws NetconfException {
228 return (Node) doRequest(session, templateName, EMPTY_TEMPLATE_CONTEXT, "/rpc-reply/data", XPathConstants.NODE);
229 }
230
231 /**
232 * Execute the named NETCONF template with the given template context against
233 * the specified session returning the {@code /rpc-reply/data} section of the
234 * response document as a {@code Node}.
235 *
236 * @param session
237 * NETCONF session
238 * @param templateName
239 * name of NETCONF request template to execute
240 * @param templateContext
241 * variables to substitute into the template
242 * @return XML document node that represents the NETCONF response data
243 * @throws NetconfException
244 * if any IO, XPath, or NETCONF exception occurs
245 */
246 public Node doRequest(NetconfSession session, String templateName, Map<String, Object> templateContext)
247 throws NetconfException {
248 return (Node) doRequest(session, templateName, templateContext, "/rpc-reply/data", XPathConstants.NODE);
249 }
250}