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