Fix config-netconf-connector needed for interacting with yuma's yangcli.
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / mapping / config / Services.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config;
10
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Function;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.Maps;
15 import com.google.common.collect.Sets;
16 import org.opendaylight.controller.netconf.util.xml.XmlElement;
17 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
18 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21 import org.w3c.dom.Document;
22 import org.w3c.dom.Element;
23
24 import javax.annotation.Nullable;
25 import javax.management.ObjectName;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.Set;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 public final class Services {
35     private static final Logger logger = LoggerFactory.getLogger(Services.class);
36
37     private static final String PROVIDER_KEY = "provider";
38     private static final String NAME_KEY = "name";
39     public static final String TYPE_KEY = "type";
40     public static final String SERVICE_KEY = "service";
41
42     private long suffix = 1;
43
44     private final Map<ServiceInstance, String> instanceToRef = Maps.newHashMap();
45     private final Map<String/* ServiceName */, Map<String/* refName */, ServiceInstance>> serviceNameToRefNameToInstance = Maps
46             .newHashMap();
47
48     public String addServiceEntry(String serviceName, ObjectName on) {
49
50         String moduleName = on.getKeyProperty("moduleFactoryName");
51         String instanceName = on.getKeyProperty("instanceName");
52
53         String refName = addServiceEntry(serviceName, moduleName, instanceName);
54         logger.trace("Added service entry to tracker. Service name {}, ref name {}, module name {}, instance name {}",
55                 serviceName, refName, moduleName, instanceName);
56         return refName;
57     }
58
59     @VisibleForTesting
60     public String addServiceEntry(String serviceName, String moduleName, String instanceName) {
61         ServiceInstance serviceInstance = new ServiceInstance(moduleName, instanceName);
62         serviceInstance.setServiceName(serviceName);
63
64         String refName = instanceToRef.get(serviceInstance);
65
66         Map<String, ServiceInstance> refNameToInstance = serviceNameToRefNameToInstance.get(serviceName);
67         if (refNameToInstance == null) {
68             refNameToInstance = Maps.newHashMap();
69             serviceNameToRefNameToInstance.put(serviceName, refNameToInstance);
70         }
71
72         if (refName != null) {
73             if (serviceNameToRefNameToInstance.get(serviceName).containsKey(moduleName) == false) {
74                 refNameToInstance.put(refName, serviceInstance);
75             }
76             return refName;
77         } else {
78             refName = "ref_" + instanceName;
79
80             final Set<String> refNamesAsSet = toSet(instanceToRef.values());
81             if (refNamesAsSet.contains(refName)) {
82                 refName = findAvailableRefName(refName, refNamesAsSet);
83             }
84
85             instanceToRef.put(serviceInstance, refName);
86             refNameToInstance.put(refName, serviceInstance);
87
88             return refName;
89         }
90     }
91
92     private Set<String> toSet(Collection<String> values) {
93         Set<String> refNamesAsSet = Sets.newHashSet();
94
95         for (String refName : values) {
96             boolean resultAdd = refNamesAsSet.add(refName);
97             Preconditions.checkState(resultAdd,
98                     "Error occurred building services element, reference name {} was present twice", refName);
99         }
100
101         return refNamesAsSet;
102     }
103
104     public ServiceInstance getByServiceAndRefName(String serviceName, String refName) {
105         Map<String, ServiceInstance> refNameToInstance = serviceNameToRefNameToInstance.get(serviceName);
106         Preconditions.checkArgument(refNameToInstance != null, "No serviceInstances mapped to " + serviceName + " , "
107                 + serviceNameToRefNameToInstance.keySet());
108
109         ServiceInstance serviceInstance = refNameToInstance.get(refName);
110         Preconditions.checkArgument(serviceInstance != null, "No serviceInstance mapped to " + refName
111                 + " under service name " + serviceName + " , " + refNameToInstance.keySet());
112         return serviceInstance;
113     }
114
115     // TODO hide getMappedServices, call it explicitly in toXml
116
117     public Map<String, Map<String, String>> getMappedServices() {
118         Map<String, Map<String, String>> retVal = Maps.newHashMap();
119
120         for (String serviceName : serviceNameToRefNameToInstance.keySet()) {
121
122             Map<String, String> innerRetVal = Maps.transformValues(serviceNameToRefNameToInstance.get(serviceName),
123                     new Function<ServiceInstance, String>() {
124                         @Nullable
125                         @Override
126                         public String apply(@Nullable ServiceInstance serviceInstance) {
127                             return serviceInstance.toString();
128                         }
129                     });
130             retVal.put(serviceName, innerRetVal);
131         }
132
133         return retVal;
134     }
135
136     // TODO hide resolveServices, call it explicitly in fromXml
137
138     public static Services resolveServices(Map<String, Map<String, String>> mappedServices) {
139         Services tracker = new Services();
140
141         for (Entry<String, Map<String, String>> serviceEntry : mappedServices.entrySet()) {
142
143             String serviceName = serviceEntry.getKey();
144             for (Entry<String, String> refEntry : serviceEntry.getValue().entrySet()) {
145
146                 Map<String, ServiceInstance> refNameToInstance = tracker.serviceNameToRefNameToInstance
147                         .get(serviceName);
148                 if (refNameToInstance == null) {
149                     refNameToInstance = Maps.newHashMap();
150                     tracker.serviceNameToRefNameToInstance.put(serviceName, refNameToInstance);
151                 }
152
153                 String refName = refEntry.getKey();
154                 Preconditions.checkState(false == refNameToInstance.containsKey(refName),
155                         "Duplicate reference name to service " + refName + " under service " + serviceName);
156                 ServiceInstance serviceInstance = ServiceInstance.fromString(refEntry.getValue());
157                 refNameToInstance.put(refName, serviceInstance);
158
159                 tracker.instanceToRef.put(serviceInstance, refEntry.getKey());
160             }
161         }
162         return tracker;
163     }
164
165     public static Map<String, Map<String, String>> fromXml(XmlElement xml) {
166         Map<String, Map<String, String>> retVal = Maps.newHashMap();
167
168         List<XmlElement> services = xml.getChildElements(SERVICE_KEY);
169         xml.checkUnrecognisedElements(services);
170
171         for (XmlElement service : services) {
172
173             XmlElement typeElement = service.getOnlyChildElement(TYPE_KEY);
174             String serviceName = typeElement.getTextContent();
175
176             Map<String, String> innerMap = Maps.newHashMap();
177             retVal.put(serviceName, innerMap);
178
179             List<XmlElement> instances = service.getChildElements(XmlNetconfConstants.INSTANCE_KEY);
180             service.checkUnrecognisedElements(instances, typeElement);
181
182             for (XmlElement instance : instances) {
183                 XmlElement nameElement = instance.getOnlyChildElement(NAME_KEY);
184                 String refName = nameElement.getTextContent();
185
186                 XmlElement providerElement = instance.getOnlyChildElement(PROVIDER_KEY);
187                 String providerName = providerElement.getTextContent();
188
189                 instance.checkUnrecognisedElements(nameElement, providerElement);
190
191                 innerMap.put(refName, providerName);
192             }
193         }
194
195         return retVal;
196     }
197
198     private String findAvailableRefName(String refName, Set<String> refNamesAsSet) {
199         String intitialRefName = refName;
200
201         while (true) {
202             refName = intitialRefName + "_" + suffix++;
203             if (refNamesAsSet.contains(refName) == false)
204                 return refName;
205         }
206     }
207
208     public Element toXml(Map<String, Map<String, String>> mappedServices, Document document) {
209         Element root = document.createElement(XmlNetconfConstants.SERVICES_KEY);
210         XmlUtil.addNamespaceAttr(root, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
211
212         for (Entry<String, Map<String, String>> serviceEntry : mappedServices.entrySet()) {
213             Element serviceElement = document.createElement(SERVICE_KEY);
214             root.appendChild(serviceElement);
215
216             Element typeElement = XmlUtil.createTextElement(document, TYPE_KEY, serviceEntry.getKey());
217             serviceElement.appendChild(typeElement);
218
219             for (Entry<String, String> instanceEntry : serviceEntry.getValue().entrySet()) {
220                 Element instanceElement = document.createElement(XmlNetconfConstants.INSTANCE_KEY);
221                 serviceElement.appendChild(instanceElement);
222
223                 Element nameElement = XmlUtil.createTextElement(document, NAME_KEY, instanceEntry.getKey());
224                 instanceElement.appendChild(nameElement);
225
226                 Element providerElement = XmlUtil.createTextElement(document, PROVIDER_KEY, instanceEntry.getValue());
227                 instanceElement.appendChild(providerElement);
228             }
229         }
230
231         return root;
232     }
233
234     public static final class ServiceInstance {
235         public ServiceInstance(String moduleName, String instanceName) {
236             this.moduleName = moduleName;
237             this.instanceName = instanceName;
238         }
239
240         public static ServiceInstance fromString(String instanceId) {
241             instanceId = instanceId.trim();
242             Matcher matcher = p.matcher(instanceId);
243             Preconditions.checkArgument(matcher.matches(), "Unexpected format for provider, expected " + p.toString()
244                     + " but was " + instanceId);
245             String factoryName = matcher.group(1);
246             String instanceName = matcher.group(2);
247             return new ServiceInstance(factoryName, instanceName);
248         }
249
250         private final String moduleName, instanceName;
251         private String serviceName;
252
253         public String getServiceName() {
254             return serviceName;
255         }
256
257         public void setServiceName(String serviceName) {
258             this.serviceName = serviceName;
259         }
260
261         public String getModuleName() {
262             return moduleName;
263         }
264
265         public String getInstanceName() {
266             return instanceName;
267         }
268
269         private static final String blueprint = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
270                 + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "["
271                 + XmlNetconfConstants.NAME_KEY + "='%s']/" + XmlNetconfConstants.INSTANCE_KEY + "["
272                 + XmlNetconfConstants.NAME_KEY + "='%s']";
273
274         private static final String blueprintR = "/" + XmlNetconfConstants.CONFIG_KEY + "/"
275                 + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\["
276                 + XmlNetconfConstants.NAME_KEY + "='%s'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\["
277                 + XmlNetconfConstants.NAME_KEY + "='%s'\\]";
278
279         private static final Pattern p = Pattern.compile(String.format(blueprintR, "(.+)", "(.+)"));
280
281         @Override
282         public String toString() {
283             return String.format(blueprint, moduleName, instanceName);
284         }
285
286         @Override
287         public int hashCode() {
288             final int prime = 31;
289             int result = 1;
290             result = prime * result + ((instanceName == null) ? 0 : instanceName.hashCode());
291             result = prime * result + ((moduleName == null) ? 0 : moduleName.hashCode());
292             return result;
293         }
294
295         @Override
296         public boolean equals(Object obj) {
297             if (this == obj)
298                 return true;
299             if (obj == null)
300                 return false;
301             if (getClass() != obj.getClass())
302                 return false;
303             ServiceInstance other = (ServiceInstance) obj;
304             if (instanceName == null) {
305                 if (other.instanceName != null)
306                     return false;
307             } else if (!instanceName.equals(other.instanceName))
308                 return false;
309             if (moduleName == null) {
310                 if (other.moduleName != null)
311                     return false;
312             } else if (!moduleName.equals(other.moduleName))
313                 return false;
314             return true;
315         }
316
317     }
318
319 }