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.restconf.openapi.mountpoints;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.BASE_PATH;
13 import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.SECURITY;
15 import java.io.IOException;
16 import java.util.HashMap;
18 import java.util.Map.Entry;
19 import java.util.TreeMap;
20 import java.util.concurrent.atomic.AtomicLong;
21 import javax.ws.rs.core.UriInfo;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.mdsal.dom.api.DOMMountPointListener;
24 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
25 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
26 import org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator;
27 import org.opendaylight.restconf.openapi.impl.OpenApiInputStream;
28 import org.opendaylight.yangtools.concepts.ListenerRegistration;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
32 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
33 import org.opendaylight.yangtools.yang.model.api.Module;
34 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 public class MountPointOpenApi implements DOMMountPointListener, AutoCloseable {
40 private static final Logger LOG = LoggerFactory.getLogger(MountPointOpenApi.class);
42 private static final String DATASTORES_REVISION = "-";
43 private static final String DATASTORES_LABEL = "Datastores";
45 private final DOMSchemaService globalSchema;
46 private final DOMMountPointService mountService;
47 private final BaseYangOpenApiGenerator openApiGenerator;
48 private final Map<YangInstanceIdentifier, Long> instanceIdToLongId =
49 new TreeMap<>((o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString()));
50 private final Map<Long, YangInstanceIdentifier> longIdToInstanceId = new HashMap<>();
52 private final Object lock = new Object();
54 private final AtomicLong idKey = new AtomicLong(0);
56 private ListenerRegistration<DOMMountPointListener> registration;
58 public MountPointOpenApi(final DOMSchemaService globalSchema, final DOMMountPointService mountService,
59 final BaseYangOpenApiGenerator openApiGenerator) {
60 this.globalSchema = requireNonNull(globalSchema);
61 this.mountService = requireNonNull(mountService);
62 this.openApiGenerator = requireNonNull(openApiGenerator);
66 registration = mountService.registerProvisionListener(this);
71 if (registration != null) {
76 public Map<String, Long> getInstanceIdentifiers() {
77 final Map<String, Long> urlToId = new HashMap<>();
79 final SchemaContext context = globalSchema.getGlobalContext();
80 for (final Entry<YangInstanceIdentifier, Long> entry : instanceIdToLongId.entrySet()) {
81 final String modName = findModuleName(entry.getKey(), context);
82 urlToId.put(generateUrlPrefixFromInstanceID(entry.getKey(), modName), entry.getValue());
88 private static String findModuleName(final YangInstanceIdentifier id, final SchemaContext context) {
89 final PathArgument rootQName = id.getPathArguments().iterator().next();
90 for (final Module mod : context.getModules()) {
91 if (mod.findDataChildByName(rootQName.getNodeType()).isPresent()) {
98 private String getYangMountUrl(final YangInstanceIdentifier key) {
99 final String modName = findModuleName(key, globalSchema.getGlobalContext());
100 return generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount";
103 private static String generateUrlPrefixFromInstanceID(final YangInstanceIdentifier key, final String moduleName) {
104 final StringBuilder builder = new StringBuilder();
106 if (moduleName != null) {
107 builder.append(moduleName).append(':');
109 for (final PathArgument arg : key.getPathArguments()) {
110 final String name = arg.getNodeType().getLocalName();
111 if (arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates nodeId) {
112 for (final Entry<QName, Object> entry : nodeId.entrySet()) {
113 builder.deleteCharAt(builder.length() - 1).append("=").append(entry.getValue()).append('/');
116 builder.append(name).append('/');
119 return builder.toString();
122 private YangInstanceIdentifier getInstanceId(final Long id) {
123 final YangInstanceIdentifier instanceId;
124 synchronized (lock) {
125 instanceId = longIdToInstanceId.get(id);
130 private EffectiveModelContext getSchemaContext(final YangInstanceIdentifier id) {
135 checkState(mountService != null);
136 return mountService.getMountPoint(id)
137 .flatMap(mountPoint -> mountPoint.getService(DOMSchemaService.class))
138 .map(DOMSchemaService::getGlobalContext)
142 public OpenApiInputStream getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
143 final String revision) throws IOException {
144 final YangInstanceIdentifier iid = getInstanceId(id);
145 final EffectiveModelContext context = getSchemaContext(iid);
146 final String urlPrefix = getYangMountUrl(iid);
147 final String deviceName = extractDeviceName(iid);
149 if (context == null) {
153 if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
154 return generateDataStoreOpenApi(context, uriInfo, urlPrefix, deviceName);
156 return openApiGenerator.getApiDeclaration(module, revision, uriInfo, context, urlPrefix, deviceName);
159 public OpenApiInputStream getMountPointApi(final UriInfo uriInfo, final Long id, final @Nullable String strPageNum)
161 final var iid = getInstanceId(id);
162 final var context = getSchemaContext(iid);
163 final var urlPrefix = getYangMountUrl(iid);
164 final var deviceName = extractDeviceName(iid);
166 if (context == null) {
170 boolean includeDataStore = true;
171 if (strPageNum != null) {
172 final var pageNum = Integer.parseInt(strPageNum);
174 includeDataStore = false;
178 final var schema = openApiGenerator.createSchemaFromUriInfo(uriInfo);
179 final var host = openApiGenerator.createHostFromUriInfo(uriInfo);
180 final var title = deviceName + " modules of RESTCONF";
181 final var url = schema + "://" + host + BASE_PATH;
182 final var modules = context.getModules();
183 return new OpenApiInputStream(context, title, url, SECURITY, deviceName, urlPrefix, false, includeDataStore,
187 private static String extractDeviceName(final YangInstanceIdentifier iid) {
188 return ((YangInstanceIdentifier.NodeIdentifierWithPredicates.Singleton)iid.getLastPathArgument())
189 .values().getElement().toString();
192 private OpenApiInputStream generateDataStoreOpenApi(EffectiveModelContext modelContext,
193 final UriInfo uriInfo, final String urlPrefix, final String deviceName) throws IOException {
194 final var schema = openApiGenerator.createSchemaFromUriInfo(uriInfo);
195 final var host = openApiGenerator.createHostFromUriInfo(uriInfo);
196 final var url = schema + "://" + host + BASE_PATH;
197 final var modules = modelContext.getModules();
198 return new OpenApiInputStream(modelContext, urlPrefix, url, SECURITY, deviceName, urlPrefix, true, false,
203 public void onMountPointCreated(final YangInstanceIdentifier path) {
204 synchronized (lock) {
205 LOG.debug("Mount point {} created", path);
206 final Long idLong = idKey.incrementAndGet();
207 instanceIdToLongId.put(path, idLong);
208 longIdToInstanceId.put(idLong, path);
213 public void onMountPointRemoved(final YangInstanceIdentifier path) {
214 synchronized (lock) {
215 LOG.debug("Mount point {} removed", path);
216 final Long id = instanceIdToLongId.remove(path);
217 longIdToInstanceId.remove(id);