Merge "Bug 5708: Schemaless netconf mount point"
[netconf.git] / restconf / sal-rest-docgen / src / main / java / org / opendaylight / netconf / sal / rest / doc / mountpoints / MountPointSwagger.java
1 /*
2  * Copyright (c) 2014 Brocade Communications 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 package org.opendaylight.netconf.sal.rest.doc.mountpoints;
9
10 import com.google.common.base.Optional;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.LinkedList;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.TreeMap;
18 import java.util.concurrent.atomic.AtomicLong;
19 import java.util.concurrent.atomic.AtomicReference;
20 import javax.ws.rs.core.UriInfo;
21 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
22 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
23 import org.opendaylight.controller.sal.core.api.model.SchemaService;
24 import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
25 import org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator;
26 import org.opendaylight.netconf.sal.rest.doc.swagger.Api;
27 import org.opendaylight.netconf.sal.rest.doc.swagger.ApiDeclaration;
28 import org.opendaylight.netconf.sal.rest.doc.swagger.Operation;
29 import org.opendaylight.netconf.sal.rest.doc.swagger.Resource;
30 import org.opendaylight.netconf.sal.rest.doc.swagger.ResourceList;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
35 import org.opendaylight.yangtools.yang.model.api.Module;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37
38 public class MountPointSwagger extends BaseYangSwaggerGenerator implements MountProvisionListener {
39
40     private static final String DATASTORES_REVISION = "-";
41     private static final String DATASTORES_LABEL = "Datastores";
42
43     private DOMMountPointService mountService;
44     private final Map<YangInstanceIdentifier, Long> instanceIdToLongId = new TreeMap<>(
45             (o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString()));
46     private final Map<Long, YangInstanceIdentifier> longIdToInstanceId = new HashMap<>();
47     private final Object lock = new Object();
48
49     private final AtomicLong idKey = new AtomicLong(0);
50
51     private static AtomicReference<MountPointSwagger> selfRef = new AtomicReference<>();
52     private SchemaService globalSchema;
53
54     public Map<String, Long> getInstanceIdentifiers() {
55         Map<String, Long> urlToId = new HashMap<>();
56         synchronized (lock) {
57             SchemaContext context = globalSchema.getGlobalContext();
58             for (Entry<YangInstanceIdentifier, Long> entry : instanceIdToLongId.entrySet()) {
59                 String modName = findModuleName(entry.getKey(), context);
60                 urlToId.put(generateUrlPrefixFromInstanceID(entry.getKey(), modName),
61                         entry.getValue());
62             }
63         }
64         return urlToId;
65     }
66
67     public void setGlobalSchema(final SchemaService globalSchema) {
68         this.globalSchema = globalSchema;
69     }
70
71     private String findModuleName(final YangInstanceIdentifier id, final SchemaContext context) {
72         PathArgument rootQName = id.getPathArguments().iterator().next();
73         for (Module mod : context.getModules()) {
74             if (mod.getDataChildByName(rootQName.getNodeType()) != null) {
75                 return mod.getName();
76             }
77         }
78         return null;
79     }
80
81     private String generateUrlPrefixFromInstanceID(final YangInstanceIdentifier key, final String moduleName) {
82         StringBuilder builder = new StringBuilder();
83         if (moduleName != null) {
84             builder.append(moduleName);
85             builder.append(':');
86         }
87         for (PathArgument arg : key.getPathArguments()) {
88             String name = arg.getNodeType().getLocalName();
89             if (arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
90                 NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg;
91                 for (Entry<QName, Object> entry : nodeId.getKeyValues().entrySet()) {
92                     builder.append(entry.getValue()).append('/');
93                 }
94             } else {
95                 builder.append(name);
96                 builder.append('/');
97             }
98         }
99         return builder.toString();
100     }
101
102     private String getYangMountUrl(final YangInstanceIdentifier key) {
103         String modName = findModuleName(key, globalSchema.getGlobalContext());
104         return generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount/";
105     }
106
107     public ResourceList getResourceList(final UriInfo uriInfo, final Long id) {
108         YangInstanceIdentifier iid = getInstanceId(id);
109         if (iid == null) {
110             return null; // indicating not found.
111         }
112         SchemaContext context = getSchemaContext(iid);
113         if (context == null) {
114             return createResourceList();
115         }
116         List<Resource> resources = new LinkedList<>();
117         Resource dataStores = new Resource();
118         dataStores.setDescription("Provides methods for accessing the data stores.");
119         dataStores.setPath(generatePath(uriInfo, DATASTORES_LABEL, DATASTORES_REVISION));
120         resources.add(dataStores);
121         String urlPrefix = getYangMountUrl(iid);
122         ResourceList list = super.getResourceListing(uriInfo, context, urlPrefix);
123         resources.addAll(list.getApis());
124         list.setApis(resources);
125         return list;
126     }
127
128     private YangInstanceIdentifier getInstanceId(final Long id) {
129         YangInstanceIdentifier instanceId;
130         synchronized (lock) {
131             instanceId = longIdToInstanceId.get(id);
132         }
133         return instanceId;
134     }
135
136     private SchemaContext getSchemaContext(final YangInstanceIdentifier id) {
137
138         if (id == null) {
139             return null;
140         }
141
142         Optional<DOMMountPoint> mountPoint = mountService.getMountPoint(id);
143         if (!mountPoint.isPresent()) {
144             return null;
145         }
146
147         SchemaContext context = mountPoint.get().getSchemaContext();
148         if (context == null) {
149             return null;
150         }
151         return context;
152     }
153
154     public ApiDeclaration getMountPointApi(final UriInfo uriInfo, final Long id, final String module, final String revision) {
155         YangInstanceIdentifier iid = getInstanceId(id);
156         SchemaContext context = getSchemaContext(iid);
157         String urlPrefix = getYangMountUrl(iid);
158         if (context == null) {
159             return null;
160         }
161
162         if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
163             return generateDataStoreApiDoc(uriInfo, urlPrefix);
164         }
165         return super.getApiDeclaration(module, revision, uriInfo, context, urlPrefix);
166     }
167
168     private ApiDeclaration generateDataStoreApiDoc(final UriInfo uriInfo, final String context) {
169         List<Api> apis = new LinkedList<>();
170         apis.add(createGetApi("config",
171                 "Queries the config (startup) datastore on the mounted hosted.", context));
172         apis.add(createGetApi("operational",
173                 "Queries the operational (running) datastore on the mounted hosted.", context));
174         apis.add(createGetApi("operations",
175                 "Queries the available operations (RPC calls) on the mounted hosted.", context));
176
177         ApiDeclaration declaration = super.createApiDeclaration(createBasePathFromUriInfo(uriInfo));
178         declaration.setApis(apis);
179         return declaration;
180
181     }
182
183     private Api createGetApi(final String datastore, final String note, final String context) {
184         Operation getConfig = new Operation();
185         getConfig.setMethod("GET");
186         getConfig.setNickname("GET " + datastore);
187         getConfig.setNotes(note);
188
189         Api api = new Api();
190         api.setPath(getDataStorePath("/" + datastore + "/", context));
191         api.setOperations(Collections.singletonList(getConfig));
192
193         return api;
194     }
195
196     public void setMountService(final DOMMountPointService mountService) {
197         this.mountService = mountService;
198     }
199
200     @Override
201     public void onMountPointCreated(final YangInstanceIdentifier path) {
202         synchronized (lock) {
203             Long idLong = idKey.incrementAndGet();
204             instanceIdToLongId.put(path, idLong);
205             longIdToInstanceId.put(idLong, path);
206         }
207     }
208
209     @Override
210     public void onMountPointRemoved(final YangInstanceIdentifier path) {
211         synchronized (lock) {
212             Long id = instanceIdToLongId.remove(path);
213             longIdToInstanceId.remove(id);
214         }
215     }
216
217     public static MountPointSwagger getInstance() {
218         MountPointSwagger swagger = selfRef.get();
219         if (swagger == null) {
220             selfRef.compareAndSet(null, new MountPointSwagger());
221             swagger = selfRef.get();
222         }
223         return swagger;
224     }
225
226 }