2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.netconf.sal.rest.doc.mountpoints;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.DEFAULT_PAGESIZE;
13 import static org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator.BASE_PATH;
14 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.DESCRIPTION_KEY;
15 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.RESPONSES_KEY;
16 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.SUMMARY_KEY;
17 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.SUMMARY_SEPARATOR;
18 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.TAGS_KEY;
19 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.buildTagsValue;
20 import static org.opendaylight.netconf.sal.rest.doc.util.JsonUtil.addFields;
22 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
23 import com.fasterxml.jackson.databind.node.ObjectNode;
24 import com.google.common.collect.Range;
25 import java.util.HashMap;
27 import java.util.Map.Entry;
28 import java.util.Optional;
29 import java.util.TreeMap;
30 import java.util.concurrent.atomic.AtomicLong;
31 import javax.ws.rs.HttpMethod;
32 import javax.ws.rs.core.Response;
33 import javax.ws.rs.core.UriInfo;
34 import org.opendaylight.mdsal.dom.api.DOMMountPointListener;
35 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
36 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
37 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
38 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.URIType;
39 import org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator;
40 import org.opendaylight.netconf.sal.rest.doc.impl.DefinitionNames;
41 import org.opendaylight.netconf.sal.rest.doc.swagger.CommonApiObject;
42 import org.opendaylight.netconf.sal.rest.doc.swagger.SwaggerObject;
43 import org.opendaylight.yangtools.concepts.ListenerRegistration;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
46 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
47 import org.opendaylight.yangtools.yang.model.api.Module;
48 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
52 public class MountPointSwagger implements DOMMountPointListener, AutoCloseable {
54 private static final Logger LOG = LoggerFactory.getLogger(MountPointSwagger.class);
56 private static final String DATASTORES_REVISION = "-";
57 private static final String DATASTORES_LABEL = "Datastores";
59 private final DOMSchemaService globalSchema;
60 private final DOMMountPointService mountService;
61 private final BaseYangSwaggerGenerator swaggerGenerator;
62 private final Map<YangInstanceIdentifier, Long> instanceIdToLongId =
63 new TreeMap<>((o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString()));
64 private final Map<Long, YangInstanceIdentifier> longIdToInstanceId = new HashMap<>();
66 private final Object lock = new Object();
68 private final AtomicLong idKey = new AtomicLong(0);
70 private ListenerRegistration<DOMMountPointListener> registration;
72 public MountPointSwagger(final DOMSchemaService globalSchema, final DOMMountPointService mountService,
73 final BaseYangSwaggerGenerator swaggerGenerator) {
74 this.globalSchema = requireNonNull(globalSchema);
75 this.mountService = requireNonNull(mountService);
76 this.swaggerGenerator = requireNonNull(swaggerGenerator);
80 registration = mountService.registerProvisionListener(this);
85 if (registration != null) {
90 public Map<String, Long> getInstanceIdentifiers() {
91 final Map<String, Long> urlToId = new HashMap<>();
92 synchronized (this.lock) {
93 final SchemaContext context = this.globalSchema.getGlobalContext();
94 for (final Entry<YangInstanceIdentifier, Long> entry : this.instanceIdToLongId.entrySet()) {
95 final String modName = findModuleName(entry.getKey(), context);
96 urlToId.put(swaggerGenerator.generateUrlPrefixFromInstanceID(entry.getKey(), modName),
103 private static String findModuleName(final YangInstanceIdentifier id, final SchemaContext context) {
104 final PathArgument rootQName = id.getPathArguments().iterator().next();
105 for (final Module mod : context.getModules()) {
106 if (mod.findDataChildByName(rootQName.getNodeType()).isPresent()) {
107 return mod.getName();
113 private String getYangMountUrl(final YangInstanceIdentifier key) {
114 final String modName = findModuleName(key, this.globalSchema.getGlobalContext());
115 return swaggerGenerator.generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount";
118 private YangInstanceIdentifier getInstanceId(final Long id) {
119 final YangInstanceIdentifier instanceId;
120 synchronized (this.lock) {
121 instanceId = this.longIdToInstanceId.get(id);
126 private EffectiveModelContext getSchemaContext(final YangInstanceIdentifier id) {
131 checkState(mountService != null);
132 return this.mountService.getMountPoint(id)
133 .flatMap(mountPoint -> mountPoint.getService(DOMSchemaService.class))
134 .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
138 public CommonApiObject getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
139 final String revision, final URIType uriType, final OAversion oaversion) {
140 final YangInstanceIdentifier iid = getInstanceId(id);
141 final EffectiveModelContext context = getSchemaContext(iid);
142 final String urlPrefix = getYangMountUrl(iid);
143 final String deviceName = extractDeviceName(iid);
145 if (context == null) {
149 if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
150 return generateDataStoreApiDoc(uriInfo, urlPrefix, deviceName);
152 final SwaggerObject swaggerObject = swaggerGenerator.getApiDeclaration(module, revision, uriInfo, context,
153 urlPrefix, uriType, oaversion);
154 return BaseYangSwaggerGenerator.getAppropriateDoc(swaggerObject, oaversion);
157 public CommonApiObject getMountPointApi(final UriInfo uriInfo, final Long id, final Optional<Integer> pageNum,
158 final URIType uriType, final OAversion oaversion) {
159 final YangInstanceIdentifier iid = getInstanceId(id);
160 final EffectiveModelContext context = getSchemaContext(iid);
161 final String urlPrefix = getYangMountUrl(iid);
162 final String deviceName = extractDeviceName(iid);
164 if (context == null) {
167 final DefinitionNames definitionNames = new DefinitionNames();
169 boolean includeDataStore = true;
170 Optional<Range<Integer>> range = Optional.empty();
172 if (pageNum.isPresent()) {
173 final int pageNumValue = pageNum.get();
174 final int end = DEFAULT_PAGESIZE * pageNumValue - 1;
175 int start = end - DEFAULT_PAGESIZE;
176 if (pageNumValue == 1) {
179 includeDataStore = false;
181 range = Optional.of(Range.closed(start, end));
184 final SwaggerObject doc;
186 final SwaggerObject swaggerObject = swaggerGenerator.getAllModulesDoc(uriInfo, range, context,
187 Optional.of(deviceName), urlPrefix, definitionNames, uriType, oaversion);
189 if (includeDataStore) {
190 doc = generateDataStoreApiDoc(uriInfo, urlPrefix, deviceName);
191 addFields(doc.getPaths() ,swaggerObject.getPaths().fields());
192 addFields(doc.getDefinitions() ,swaggerObject.getDefinitions().fields());
193 doc.getInfo().setTitle(swaggerObject.getInfo().getTitle());
198 return BaseYangSwaggerGenerator.getAppropriateDoc(doc, oaversion);
201 private static String extractDeviceName(final YangInstanceIdentifier iid) {
202 return ((YangInstanceIdentifier.NodeIdentifierWithPredicates.Singleton)iid.getLastPathArgument())
203 .values().getElement().toString();
206 private SwaggerObject generateDataStoreApiDoc(final UriInfo uriInfo, final String context,
207 final String deviceName) {
208 final SwaggerObject declaration = swaggerGenerator.createSwaggerObject(
209 swaggerGenerator.createSchemaFromUriInfo(uriInfo),
210 swaggerGenerator.createHostFromUriInfo(uriInfo),
214 final ObjectNode pathsObject = JsonNodeFactory.instance.objectNode();
215 createGetPathItem("config", "Queries the config (startup) datastore on the mounted hosted.",
216 context, deviceName, pathsObject);
217 createGetPathItem("operational", "Queries the operational (running) datastore on the mounted hosted.",
218 context, deviceName, pathsObject);
219 createGetPathItem("operations", "Queries the available operations (RPC calls) on the mounted hosted.",
220 context, deviceName, pathsObject);
222 declaration.setPaths(pathsObject);
223 declaration.setDefinitions(JsonNodeFactory.instance.objectNode());
228 private void createGetPathItem(final String resourceType, final String description, final String context,
229 final String deviceName, final ObjectNode pathsObject) {
230 final ObjectNode pathItem = JsonNodeFactory.instance.objectNode();
231 final ObjectNode operationObject = JsonNodeFactory.instance.objectNode();
232 pathItem.set("get", operationObject);
233 operationObject.put(DESCRIPTION_KEY, description);
234 operationObject.put(SUMMARY_KEY, HttpMethod.GET + SUMMARY_SEPARATOR + deviceName + SUMMARY_SEPARATOR
235 + swaggerGenerator.getResourcePathPart(resourceType));
236 operationObject.set(TAGS_KEY, buildTagsValue(Optional.of(deviceName), "GET root"));
237 final ObjectNode okResponse = JsonNodeFactory.instance.objectNode();
238 okResponse.put(DESCRIPTION_KEY, Response.Status.OK.getReasonPhrase());
239 final ObjectNode responses = JsonNodeFactory.instance.objectNode();
240 responses.set(String.valueOf(Response.Status.OK.getStatusCode()), okResponse);
241 operationObject.set(RESPONSES_KEY, responses);
242 pathsObject.set(swaggerGenerator.getResourcePath(resourceType, context), pathItem);
246 public void onMountPointCreated(final YangInstanceIdentifier path) {
247 synchronized (this.lock) {
248 LOG.debug("Mount point {} created", path);
249 final Long idLong = this.idKey.incrementAndGet();
250 this.instanceIdToLongId.put(path, idLong);
251 this.longIdToInstanceId.put(idLong, path);
256 public void onMountPointRemoved(final YangInstanceIdentifier path) {
257 synchronized (this.lock) {
258 LOG.debug("Mount point {} removed", path);
259 final Long id = this.instanceIdToLongId.remove(path);
260 this.longIdToInstanceId.remove(id);