From 69f669f5d16768258f854ce4139033f94ab2afb9 Mon Sep 17 00:00:00 2001 From: Jan Srnicek Date: Fri, 21 Jul 2017 15:10:46 +0200 Subject: HC2VPP-180 - Doc coverage generator TODO - links to specific vpp api section(now points just to section with apis) TODO - links to specific java binding code(now points to class thats doing binding) TODO - operational coverage(ASM does not support lambda processing) TODO - generate coverage adoc links Change-Id: I44c85012da3bd2e7cdd41930753e5aae6955cd7b Signed-off-by: Jan Srnicek Signed-off-by: Marek Gradzki --- .../src/main/java/io/fd/hc2vpp/acl/AclModule.java | 2 +- .../io/fd/hc2vpp/vppioam/impl/VppIoamModule.java | 4 +- .../src/main/java/io/fd/hc2vpp/nat/NatModule.java | 4 +- .../io/fd/hc2vpp/vppnsh/impl/VppNshModule.java | 4 +- release-notes/pom.xml | 45 +++++ .../src/main/asciidoc/api_docs/api_docs.adoc | 3 + .../src/main/asciidoc/api_docs/api_docs_index.adoc | 19 ++ release-notes/src/main/asciidoc/release_notes.adoc | 4 +- vpp-integration/api-docs/api/asciidoc/Readme.adoc | 13 ++ vpp-integration/api-docs/api/pom.xml | 33 ++++ .../java/io/fd/hc2vpp/docs/api/CoverageUnit.java | 124 +++++++++++++ .../java/io/fd/hc2vpp/docs/api/JavaApiMessage.java | 56 ++++++ .../main/java/io/fd/hc2vpp/docs/api/Operation.java | 93 ++++++++++ .../java/io/fd/hc2vpp/docs/api/PluginCoverage.java | 85 +++++++++ .../java/io/fd/hc2vpp/docs/api/VppApiMessage.java | 69 ++++++++ .../main/java/io/fd/hc2vpp/docs/api/YangType.java | 66 +++++++ vpp-integration/api-docs/asciidoc/Readme.adoc | 3 + vpp-integration/api-docs/core/asciidoc/Readme.adoc | 3 + vpp-integration/api-docs/core/pom.xml | 112 ++++++++++++ .../io/fd/hc2vpp/docs/core/ClassPathTypeIndex.java | 74 ++++++++ .../hc2vpp/docs/core/CollectingWriterBuilder.java | 153 ++++++++++++++++ .../io/fd/hc2vpp/docs/core/CoverageGenerator.java | 184 ++++++++++++++++++++ .../io/fd/hc2vpp/docs/core/CoverageScanner.java | 74 ++++++++ .../java/io/fd/hc2vpp/docs/core/LinkGenerator.java | 33 ++++ .../docs/core/MethodDelegatingClassVisitor.java | 54 ++++++ .../docs/core/MethodPluginCoverageVisitor.java | 90 ++++++++++ .../io/fd/hc2vpp/docs/core/ModelLinkIndex.java | 71 ++++++++ .../io/fd/hc2vpp/docs/core/ModelTypeIndex.java | 88 ++++++++++ .../fd/hc2vpp/docs/core/PluginMethodReference.java | 56 ++++++ .../java/io/fd/hc2vpp/docs/core/VppApiUtils.java | 43 +++++ .../java/io/fd/hc2vpp/docs/core/YangModelKey.java | 73 ++++++++ .../io/fd/hc2vpp/docs/core/YangTypeLinkIndex.java | 56 ++++++ vpp-integration/api-docs/docs/pom.xml | 92 ++++++++++ vpp-integration/api-docs/pom.xml | 40 +++++ vpp-integration/api-docs/scripts/pom.xml | 192 +++++++++++++++++++++ .../docs/scripts/ApiDocsIndexGenerator.groovy | 156 +++++++++++++++++ .../docs/core/mock/binding/MockAclModule.java | 48 ++++++ .../docs/core/mock/binding/MockBindingModule.java | 49 ++++++ .../docs/core/mock/binding/MockIoamModule.java | 73 ++++++++ .../docs/core/mock/binding/MockNatModule.java | 48 ++++++ .../docs/core/mock/binding/MockNshModule.java | 48 ++++++ .../scripts/src/main/resources/routing.json | 4 + .../scripts/src/main/resources/vpp-management.json | 3 + .../src/main/resources/yang_to_jvpp_template | 6 + vpp-integration/pom.xml | 1 + 45 files changed, 2543 insertions(+), 8 deletions(-) create mode 100644 release-notes/src/main/asciidoc/api_docs/api_docs.adoc create mode 100644 release-notes/src/main/asciidoc/api_docs/api_docs_index.adoc create mode 100644 vpp-integration/api-docs/api/asciidoc/Readme.adoc create mode 100644 vpp-integration/api-docs/api/pom.xml create mode 100644 vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/CoverageUnit.java create mode 100644 vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/JavaApiMessage.java create mode 100644 vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/Operation.java create mode 100644 vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/PluginCoverage.java create mode 100644 vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/VppApiMessage.java create mode 100644 vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/YangType.java create mode 100644 vpp-integration/api-docs/asciidoc/Readme.adoc create mode 100644 vpp-integration/api-docs/core/asciidoc/Readme.adoc create mode 100644 vpp-integration/api-docs/core/pom.xml create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ClassPathTypeIndex.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CollectingWriterBuilder.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageGenerator.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageScanner.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/LinkGenerator.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodDelegatingClassVisitor.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodPluginCoverageVisitor.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelLinkIndex.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelTypeIndex.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/PluginMethodReference.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/VppApiUtils.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangModelKey.java create mode 100644 vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangTypeLinkIndex.java create mode 100644 vpp-integration/api-docs/docs/pom.xml create mode 100644 vpp-integration/api-docs/pom.xml create mode 100644 vpp-integration/api-docs/scripts/pom.xml create mode 100644 vpp-integration/api-docs/scripts/src/main/groovy/io/fd/hc2vpp/docs/scripts/ApiDocsIndexGenerator.groovy create mode 100644 vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockAclModule.java create mode 100644 vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockBindingModule.java create mode 100644 vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockIoamModule.java create mode 100644 vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNatModule.java create mode 100644 vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNshModule.java create mode 100644 vpp-integration/api-docs/scripts/src/main/resources/routing.json create mode 100644 vpp-integration/api-docs/scripts/src/main/resources/vpp-management.json create mode 100644 vpp-integration/api-docs/scripts/src/main/resources/yang_to_jvpp_template diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/AclModule.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/AclModule.java index a97f57ec1..afb7da669 100644 --- a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/AclModule.java +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/AclModule.java @@ -52,7 +52,7 @@ public class AclModule extends AbstractModule { } @VisibleForTesting - AclModule(@Nonnull final Class> jvppAclProviderClass) { + protected AclModule(@Nonnull final Class> jvppAclProviderClass) { this.jvppAclProviderClass = jvppAclProviderClass; } diff --git a/ioam/impl/src/main/java/io/fd/hc2vpp/vppioam/impl/VppIoamModule.java b/ioam/impl/src/main/java/io/fd/hc2vpp/vppioam/impl/VppIoamModule.java index 4c143131c..1feaf9a68 100755 --- a/ioam/impl/src/main/java/io/fd/hc2vpp/vppioam/impl/VppIoamModule.java +++ b/ioam/impl/src/main/java/io/fd/hc2vpp/vppioam/impl/VppIoamModule.java @@ -40,7 +40,7 @@ import org.slf4j.LoggerFactory; /* * Glue code necessary for Honeycomb distribution to pick up the plugin classes */ -public final class VppIoamModule extends AbstractModule { +public class VppIoamModule extends AbstractModule { private static final Logger LOG = LoggerFactory.getLogger(VppIoamModule.class); private final Class> jvppIoamTraceProviderClass; @@ -52,7 +52,7 @@ public final class VppIoamModule extends AbstractModule { } @VisibleForTesting - VppIoamModule(Class> jvppIoamTraceProvider, + protected VppIoamModule(Class> jvppIoamTraceProvider, Class> jvppIoamPotProviderClass, Class> jvppIoamExportProviderClass) { this.jvppIoamTraceProviderClass = jvppIoamTraceProvider; diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/NatModule.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/NatModule.java index cf6c3bc22..fece7b011 100644 --- a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/NatModule.java +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/NatModule.java @@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory; /** * Module class instantiating nat plugin components. */ -public final class NatModule extends AbstractModule { +public class NatModule extends AbstractModule { private static final Logger LOG = LoggerFactory.getLogger(NatModule.class); private final Class> jvppSnatProviderClass; @@ -48,7 +48,7 @@ public final class NatModule extends AbstractModule { } @VisibleForTesting - NatModule(Class> jvppSnatProvider) { + protected NatModule(Class> jvppSnatProvider) { this.jvppSnatProviderClass = jvppSnatProvider; } diff --git a/nsh/impl/src/main/java/io/fd/hc2vpp/vppnsh/impl/VppNshModule.java b/nsh/impl/src/main/java/io/fd/hc2vpp/vppnsh/impl/VppNshModule.java index 1964fafe0..aaa1d6c4e 100755 --- a/nsh/impl/src/main/java/io/fd/hc2vpp/vppnsh/impl/VppNshModule.java +++ b/nsh/impl/src/main/java/io/fd/hc2vpp/vppnsh/impl/VppNshModule.java @@ -36,7 +36,7 @@ import org.slf4j.LoggerFactory; /** * This is some glue code necessary for Honeycomb distribution to pick up the plugin classes */ -public final class VppNshModule extends AbstractModule { +public class VppNshModule extends AbstractModule { private static final Logger LOG = LoggerFactory.getLogger(VppNshModule.class); private final Class> jvppNshProviderClass; @@ -45,7 +45,7 @@ public final class VppNshModule extends AbstractModule { this(JVppNshProvider.class); } @VisibleForTesting - VppNshModule(Class> jvppNshProvider) { + protected VppNshModule(Class> jvppNshProvider) { this.jvppNshProviderClass = jvppNshProvider; } diff --git a/release-notes/pom.xml b/release-notes/pom.xml index c06c22cf9..51a0bc359 100644 --- a/release-notes/pom.xml +++ b/release-notes/pom.xml @@ -31,6 +31,14 @@ 4.0.0 Hc2vpp release notes + + + io.fd.hc2vpp.docs + docs + ${project.version} + + + @@ -50,6 +58,25 @@ + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + unpack-configuration + generate-sources + + unpack-dependencies + + + *.adoc + ${project.build.directory}/raw-adoc/api_docs + + + + + + org.asciidoctor asciidoctor-maven-plugin ${asciidoctor.maven.plugin.version} @@ -63,6 +90,9 @@ + + ${project.build.directory}/raw-adoc html5 docbook coderay @@ -96,6 +126,21 @@ maven-resources-plugin + + copy-raw-adoc + generate-sources + + copy-resources + + + ${project.build.directory}/raw-adoc + + + ${project.basedir}/src/main/asciidoc/ + + + + copy-release-notes-to-site site diff --git a/release-notes/src/main/asciidoc/api_docs/api_docs.adoc b/release-notes/src/main/asciidoc/api_docs/api_docs.adoc new file mode 100644 index 000000000..5d519c68d --- /dev/null +++ b/release-notes/src/main/asciidoc/api_docs/api_docs.adoc @@ -0,0 +1,3 @@ +== API documentation + +link:api_docs_index.html[VPP API to Yang index] diff --git a/release-notes/src/main/asciidoc/api_docs/api_docs_index.adoc b/release-notes/src/main/asciidoc/api_docs/api_docs_index.adoc new file mode 100644 index 000000000..08dfeb28f --- /dev/null +++ b/release-notes/src/main/asciidoc/api_docs/api_docs_index.adoc @@ -0,0 +1,19 @@ +== VPP api to Yang index + +// TODO - generate this file based on list of JVPP plugins used + +Mapping between VPP binary apis and JVpp binding in hc2vpp + +include::api_docs/futurejvppcore-yang-config-index.adoc[JVpp Core plugin docs] + +include::api_docs/futurejvppacl-yang-config-index.adoc[JVpp Acl plugin docs] + +include::api_docs/futurejvppsnat-yang-config-index.adoc[JVpp Snat plugin docs] + +include::api_docs/futurejvppnsh-yang-config-index.adoc[JVpp Nsh plugin docs] + +include::api_docs/futurejvppioamtrace-yang-config-index.adoc[JVpp Ioam Trace plugin docs] + +include::api_docs/futurejvppioampot-yang-config-index.adoc[JVpp Ioam Pot plugin docs] + +include::api_docs/futurejvppioamexport-yang-config-index.adoc[JVpp Ioam Export plugin docs] diff --git a/release-notes/src/main/asciidoc/release_notes.adoc b/release-notes/src/main/asciidoc/release_notes.adoc index e4e1588d5..605eda69d 100644 --- a/release-notes/src/main/asciidoc/release_notes.adoc +++ b/release-notes/src/main/asciidoc/release_notes.adoc @@ -26,4 +26,6 @@ include::install_guide/install_guide.adoc[] include::user_guide/user_guide.adoc[] -include::devel_guide/devel_guide.adoc[] \ No newline at end of file +include::devel_guide/devel_guide.adoc[] + +include::api_docs/api_docs.adoc[] \ No newline at end of file diff --git a/vpp-integration/api-docs/api/asciidoc/Readme.adoc b/vpp-integration/api-docs/api/asciidoc/Readme.adoc new file mode 100644 index 000000000..145404972 --- /dev/null +++ b/vpp-integration/api-docs/api/asciidoc/Readme.adoc @@ -0,0 +1,13 @@ += docs-api + +Defines general api for VPP binary api coverage + +== Elements + +* PluginCoverage - Contains coverage data for single JVPP plugin, including supported + VPP binary equivalents in JVpp, what Yang nodes are bind to such api and what operations are + supported +* VppApiMessage - Reference to single VPP binary api with link to its documentation +* JavaApiMessage - Reference to JVpp equivalent of VPP binary api +* YangType - Reference to single Yang type with link to its model +* Operation - reference to single CRUD operation with link to binding class \ No newline at end of file diff --git a/vpp-integration/api-docs/api/pom.xml b/vpp-integration/api-docs/api/pom.xml new file mode 100644 index 000000000..44e906f01 --- /dev/null +++ b/vpp-integration/api-docs/api/pom.xml @@ -0,0 +1,33 @@ + + + + + + hc2vpp-parent + io.fd.hc2vpp.common + 1.17.07-SNAPSHOT + ../../../common/hc2vpp-parent + + 4.0.0 + + docs-api + io.fd.hc2vpp.docs + 1.17.07-SNAPSHOT + + \ No newline at end of file diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/CoverageUnit.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/CoverageUnit.java new file mode 100644 index 000000000..06cb59f69 --- /dev/null +++ b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/CoverageUnit.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.api; + +import java.util.List; +import java.util.Objects; + +/** + * Represents mapping between single supported VPP binary api and its binding + */ +public class CoverageUnit { + + /** + * VPP binary api reference + */ + private final VppApiMessage vppApi; + + /** + * Java equivalent of VPP binary api + */ + private final JavaApiMessage javaApi; + + /** + * Yang types used to bind this request + */ + private final List yangTypes; + + /** + * Operations supported for this api + */ + private final List supportedOperations; + + private CoverageUnit(final VppApiMessage vppApi, final JavaApiMessage javaApi, + final List yangTypes, + final List supportedOperations) { + this.vppApi = vppApi; + this.javaApi = javaApi; + this.yangTypes = yangTypes; + this.supportedOperations = supportedOperations; + } + + public VppApiMessage getVppApi() { + return vppApi; + } + + public JavaApiMessage getJavaApi() { + return javaApi; + } + + public List getYangTypes() { + return yangTypes; + } + + public List getSupportedOperations() { + return supportedOperations; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final CoverageUnit that = (CoverageUnit) o; + + return Objects.equals(this.vppApi, that.vppApi) && + Objects.equals(this.javaApi, that.javaApi) && + Objects.equals(this.yangTypes, that.yangTypes) && + Objects.equals(this.supportedOperations, that.supportedOperations); + } + + @Override + public int hashCode() { + return Objects.hash(vppApi, javaApi, yangTypes, supportedOperations); + } + + public static class CoverageUnitBuilder { + private VppApiMessage vppApi; + private JavaApiMessage javaApi; + private List yangTypes; + private List supportedOperations; + + public CoverageUnitBuilder setVppApi(final VppApiMessage vppApi) { + this.vppApi = vppApi; + return this; + } + + public CoverageUnitBuilder setJavaApi(final JavaApiMessage javaApi) { + this.javaApi = javaApi; + return this; + } + + public CoverageUnitBuilder setYangTypes(final List yangTypes) { + this.yangTypes = yangTypes; + return this; + } + + public CoverageUnitBuilder setSupportedOperations(final List supportedOperations) { + this.supportedOperations = supportedOperations; + return this; + } + + public CoverageUnit build() { + return new CoverageUnit(vppApi, javaApi, yangTypes, supportedOperations); + } + } +} diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/JavaApiMessage.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/JavaApiMessage.java new file mode 100644 index 000000000..db4575c5a --- /dev/null +++ b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/JavaApiMessage.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.api; + +import java.util.Objects; + +/** + * Reference of Java equivalent of VPP binary api + */ +public class JavaApiMessage { + + /** + * Name of the call + */ + private final String api; + + public JavaApiMessage(final String api) { + this.api = api; + } + + public String getApi() { + return api; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final JavaApiMessage that = (JavaApiMessage) o; + return Objects.equals(this.api, that.api); + } + + @Override + public int hashCode() { + return Objects.hash(api); + } +} diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/Operation.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/Operation.java new file mode 100644 index 000000000..e471fb6bf --- /dev/null +++ b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/Operation.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.api; + +import java.util.Objects; + +/** + * Reference to single crud operation + */ +public class Operation { + + /** + * Git link to class that performs referenced operation + */ + private final String link; + //TODO - investigate option to link directly to line number + + /** + * Type of crud operation + */ + private final CrudOperation operation; + + public Operation(final String link, final CrudOperation operation) { + this.link = link; + this.operation = operation; + } + + public String getLink() { + return link; + } + + public CrudOperation getOperation() { + return operation; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final Operation that = (Operation) o; + + return Objects.equals(this.link, that.link) && + Objects.equals(this.operation, that.operation); + } + + @Override + public int hashCode() { + return Objects.hash(link, operation); + } + + public enum CrudOperation { + WRITE("Write", "writeCurrentAttributes"), + UPDATE("Update", "updateCurrentAttributes"), + DELETE("Delete", "deleteCurrentAttributes"), + READ_ALL("Read all", "getAllIds"), + READ_ONE("Read details", "readCurrentAttributes"); + + private final String displayName; + private final String methodReference; + + CrudOperation(final String displayName, final String methodReference) { + this.displayName = displayName; + this.methodReference = methodReference; + } + + public String getMethodReference() { + return methodReference; + } + + public String getDisplayName() { + return displayName; + } + } +} diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/PluginCoverage.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/PluginCoverage.java new file mode 100644 index 000000000..8e62e9ca3 --- /dev/null +++ b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/PluginCoverage.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.api; + +import java.util.Objects; +import java.util.Set; + +/** + * Represents coverage data for single VPP plugin + */ +public class PluginCoverage { + + /** + * Name of the covered plugin + */ + private final String pluginName; + + /** + * Coverage data + */ + private final Set coverage; + + /** + * Whether this is config or operational coverage + */ + private final boolean isConfig; + + public PluginCoverage(final String pluginName, final Set coverage, final boolean isConfig) { + this.pluginName = pluginName; + this.coverage = coverage; + this.isConfig = isConfig; + } + + public String getPluginName() { + return pluginName; + } + + public Set getCoverage() { + return coverage; + } + + public boolean isConfig() { + return isConfig; + } + + public boolean hasCoverage() { + return !coverage.isEmpty(); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final PluginCoverage that = (PluginCoverage) o; + + return Objects.equals(this.isConfig, that.isConfig) && + Objects.equals(this.pluginName, that.pluginName) && + Objects.equals(this.coverage, that.coverage); + } + + @Override + public int hashCode() { + return Objects.hash(this.pluginName, this.coverage, this.isConfig); + } +} + diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/VppApiMessage.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/VppApiMessage.java new file mode 100644 index 000000000..78010471a --- /dev/null +++ b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/VppApiMessage.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.api; + +import java.util.Objects; + +/** + * Represents reference to VPP binary api + */ +public class VppApiMessage { + + /** + * Name of the api + */ + private final String name; + + /** + * fd.io doc link + */ + // TODO - check if possible to add direct link for specific api + private final String link; + + public VppApiMessage(final String name, final String link) { + this.name = name; + this.link = link; + } + + public String getName() { + return name; + } + + public String getLink() { + return link; + } + + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final VppApiMessage that = (VppApiMessage) o; + + return Objects.equals(this.name, that.name) && Objects.equals(this.link, that.link); + } + + @Override + public int hashCode() { + return Objects.hash(name, link); + } +} diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/YangType.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/YangType.java new file mode 100644 index 000000000..a2585acf6 --- /dev/null +++ b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/YangType.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.api; + +import java.util.Objects; + +/** + * Represents Yang type used to bind VPP api's + */ +public class YangType { + + /** + * Fully qualified name of Yang type + */ + private final String type; + + /** + * Git link to its model origin + */ + private final String link; + + public YangType(final String type, final String link) { + this.type = type; + this.link = link; + } + + public String getType() { + return type; + } + + public String getLink() { + return link; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final YangType that = (YangType) o; + return Objects.equals(this.type, that.type) && !Objects.equals(this.link, that.link); + } + + @Override + public int hashCode() { + return Objects.hash(type, link); + } +} diff --git a/vpp-integration/api-docs/asciidoc/Readme.adoc b/vpp-integration/api-docs/asciidoc/Readme.adoc new file mode 100644 index 000000000..b0964c29a --- /dev/null +++ b/vpp-integration/api-docs/asciidoc/Readme.adoc @@ -0,0 +1,3 @@ += api-docs + +Overview of api-docs \ No newline at end of file diff --git a/vpp-integration/api-docs/core/asciidoc/Readme.adoc b/vpp-integration/api-docs/core/asciidoc/Readme.adoc new file mode 100644 index 000000000..919d9c4dc --- /dev/null +++ b/vpp-integration/api-docs/core/asciidoc/Readme.adoc @@ -0,0 +1,3 @@ += docs-core + +Overview of docs-core \ No newline at end of file diff --git a/vpp-integration/api-docs/core/pom.xml b/vpp-integration/api-docs/core/pom.xml new file mode 100644 index 000000000..65bc5dc4f --- /dev/null +++ b/vpp-integration/api-docs/core/pom.xml @@ -0,0 +1,112 @@ + + + + + + hc2vpp-parent + io.fd.hc2vpp.common + 1.17.07-SNAPSHOT + ../../../common/hc2vpp-parent + + 4.0.0 + + io.fd.hc2vpp.docs + docs-core + 1.17.07-SNAPSHOT + + + 1.10.19 + 4.12 + 4.1.0 + 5.2 + 1.5.4 + 1.7.25 + 17.10-SNAPSHOT + 19.0 + 0.9.11 + + + + ${project.basedir}/../.. + + + + + com.google.guava + guava + ${guava.version} + + + io.fd.hc2vpp.docs + docs-api + ${project.version} + + + + io.fd.vpp + jvpp-core + ${jvpp.version} + + + io.fd.vpp + jvpp-registry + ${jvpp.version} + + + + org.ow2.asm + asm + ${asm.version} + + + + com.google.inject + guice + ${guice.version} + + + io.fd.honeycomb + translate-utils + ${project.version} + + + io.fd.honeycomb + translate-api + ${project.version} + + + junit + junit + ${junit.version} + test + + + org.mockito + mockito-core + ${mockito-core.version} + test + + + org.reflections + reflections + ${reflections.version} + + + + \ No newline at end of file diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ClassPathTypeIndex.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ClassPathTypeIndex.java new file mode 100644 index 000000000..e204d6321 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ClassPathTypeIndex.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + +import static java.lang.String.format; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * Index of java classes to relative absolute paths within repository. Used to generate Git links for binding classes of + * VPP apis + */ +public class ClassPathTypeIndex implements LinkGenerator { + + private static final String JAVA_SOURCE_FOLDER = "src/main/java"; + private static final int JAVA_SOURCE_FOLDER_NAME_LENGTH = JAVA_SOURCE_FOLDER.length() + 1; + + /** + *
  • key - fully qualified class name value
  • value - path within codebase/repository
  • + */ + private final Map index; + + public ClassPathTypeIndex(final String projectRoot, final String version) { + index = buildIndex(projectRoot, version); + } + + /** + *
  • input format - LOCAL_ROOT/hc2vpp/module/src/main/java/fully/qualified/class/name/Class.java
  • output + * format - fully.qualified.class.name.Class
  • + */ + private static String key(String raw) { + return raw.substring(raw.indexOf(JAVA_SOURCE_FOLDER) + JAVA_SOURCE_FOLDER_NAME_LENGTH) + .replace("/", ".") + .replace(".java", ""); + } + + public String linkForClass(final String clazz) { + return index.get(clazz.replace("/", ".")); + } + + private Map buildIndex(final String projectRoot, final String version) { + try { + return Files.walk(Paths.get(projectRoot)) + .filter(path -> path.toString().contains("src/main/java")) + .filter(path -> path.toString().endsWith(".java")) + .map(Path::toString) + .map(s -> s.replace(projectRoot, "")) + .distinct() + .collect(Collectors.toMap(ClassPathTypeIndex::key, o -> generateLink(o, version))); + } catch (IOException e) { + throw new IllegalStateException(format("%s not found", projectRoot), e); + } + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CollectingWriterBuilder.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CollectingWriterBuilder.java new file mode 100644 index 000000000..b8fcc8b03 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CollectingWriterBuilder.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + + +import com.google.common.collect.ImmutableSet; +import io.fd.honeycomb.translate.ModifiableSubtreeManagerRegistryBuilder; +import io.fd.honeycomb.translate.write.Writer; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Implementations of builder that collects handlers as they are bind + */ +public class CollectingWriterBuilder implements ModifiableWriterRegistryBuilder { + + private final List> singleNodeHandlers; + private final List multiNodeWriteHandlers; + + public CollectingWriterBuilder() { + singleNodeHandlers = new LinkedList<>(); + multiNodeWriteHandlers = new LinkedList<>(); + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> add( + @Nonnull Writer handler) { + singleNodeHandlers.add(handler); + return this; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> subtreeAdd( + @Nonnull Set> handledChildren, @Nonnull Writer handler) { + multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, handledChildren)); + return this; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> addBefore( + @Nonnull Writer handler, @Nonnull InstanceIdentifier relatedType) { + multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, Collections.singleton(relatedType))); + return this; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> addBefore( + @Nonnull Writer handler, @Nonnull Collection> relatedTypes) { + singleNodeHandlers.add(handler); + return this; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> subtreeAddBefore( + @Nonnull Set> handledChildren, @Nonnull Writer handler, + @Nonnull InstanceIdentifier relatedType) { + multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, handledChildren)); + return null; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> subtreeAddBefore( + @Nonnull Set> handledChildren, @Nonnull Writer handler, + @Nonnull Collection> relatedTypes) { + multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, handledChildren)); + return this; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> addAfter( + @Nonnull Writer handler, @Nonnull InstanceIdentifier relatedType) { + singleNodeHandlers.add(handler); + return this; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> addAfter( + @Nonnull Writer handler, @Nonnull Collection> relatedTypes) { + singleNodeHandlers.add(handler); + return this; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> subtreeAddAfter( + @Nonnull Set> handledChildren, @Nonnull Writer handler, + @Nonnull InstanceIdentifier relatedType) { + multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, Collections.singleton(relatedType))); + return this; + } + + @Override + public ModifiableSubtreeManagerRegistryBuilder> subtreeAddAfter( + @Nonnull Set> handledChildren, @Nonnull Writer handler, + @Nonnull Collection> relatedTypes) { + multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, handledChildren)); + return this; + } + + public List> getSingleNodeHandlers() { + return singleNodeHandlers; + } + + public List getMultiNodeWriteHandlers() { + return multiNodeWriteHandlers; + } + + public static class MultiNodeWriteHandler { + private final Writer writer; + private final Set handledChildren; + + + public MultiNodeWriteHandler(Writer writer, Set> handledChildren) { + this.writer = writer; + this.handledChildren = ImmutableSet.builder() + .add(writer.getManagedDataObjectType().getTargetType().getName()) + .addAll(handledChildren.stream() + .map(InstanceIdentifier::getTargetType) + .map(Class::getName) + .collect(Collectors.toSet())) + .build(); + } + + public Writer getWriter() { + return writer; + } + + public Set getHandledChildren() { + return handledChildren; + } + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageGenerator.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageGenerator.java new file mode 100644 index 000000000..4b6ab776c --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageGenerator.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + + +import static io.fd.hc2vpp.docs.api.Operation.CrudOperation.DELETE; +import static io.fd.hc2vpp.docs.api.Operation.CrudOperation.UPDATE; +import static io.fd.hc2vpp.docs.api.Operation.CrudOperation.WRITE; +import static java.lang.String.format; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.TypeLiteral; +import io.fd.hc2vpp.docs.api.CoverageUnit; +import io.fd.hc2vpp.docs.api.JavaApiMessage; +import io.fd.hc2vpp.docs.api.Operation; +import io.fd.hc2vpp.docs.api.PluginCoverage; +import io.fd.hc2vpp.docs.api.YangType; +import io.fd.honeycomb.translate.write.WriterFactory; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.reflections.ReflectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CoverageGenerator implements VppApiUtils { + + private static final Logger LOG = LoggerFactory.getLogger(CoverageGenerator.class); + + private final CollectingWriterBuilder writerBuilder; + + public CoverageGenerator() { + writerBuilder = new CollectingWriterBuilder(); + } + + public PluginCoverage generateConfigCoverage(final Class pluginClass, + final String version, + final List scannedModules, + final YangTypeLinkIndex yangTypeIndex, + final ClassPathTypeIndex classPathIndex) { + LOG.info("Generating config coverage for plugin {}", pluginClass); + getInjectedWriterFactories(scannedModules).forEach(writerFactory -> writerFactory.init(writerBuilder)); + + LOG.info("Processing single node handlers"); + final Set singleNodeCoverageUnits = writerBuilder.getSingleNodeHandlers().stream() + .flatMap(writer -> { + // extracts customizer class from handler + final Class customizerClass = getCustomizerClass(writer); + + // scans within write method + final Set writeReferences = + new CoverageScanner(customizerClass, WRITE, pluginClass).scan(); + + // scans within update method + final Set updateReferences = + new CoverageScanner(customizerClass, UPDATE, pluginClass).scan(); + + // scans within delete method + final Set deleteReferences = + new CoverageScanner(customizerClass, DELETE, pluginClass).scan(); + + return Stream.of(writeReferences.stream(), updateReferences.stream(), deleteReferences.stream()) + .flatMap(pluginMethodReferences -> pluginMethodReferences) + .map(reference -> { + final CoverageUnit.CoverageUnitBuilder builder = new CoverageUnit.CoverageUnitBuilder(); + + // binds vpp api name and generateLink bind with version + builder.setVppApi(fromJvppApi(version, reference)); + + //binds java api reference + builder.setJavaApi(new JavaApiMessage(reference.getName())); + + //binds Yang types with links from pre-build index + // TODO - use deserialized yii + final String typeName = writer.getManagedDataObjectType().getTargetType().getTypeName(); + builder.setYangTypes(Collections.singletonList(new YangType( + typeName, + yangTypeIndex.getLinkForType(typeName)))); + + final List supportedOperations = new LinkedList<>(); + + final String callerClassLink = classPathIndex.linkForClass(reference.getCaller()); + if (writeReferences.contains(reference)) { + supportedOperations.add(new Operation(callerClassLink, WRITE)); + } + + if (updateReferences.contains(reference)) { + supportedOperations.add(new Operation(callerClassLink, UPDATE)); + } + + if (deleteReferences.contains(reference)) { + supportedOperations.add(new Operation(callerClassLink, DELETE)); + } + return builder.setSupportedOperations(supportedOperations).build(); + }); + }).collect(Collectors.toSet()); + + LOG.info("Processing multi node handlers"); + final Set multiNodeCoverageUnits = writerBuilder.getMultiNodeWriteHandlers().stream() + .flatMap(handler -> { + final Class customizerClass = getCustomizerClass(handler.getWriter()); + final Set writeReferences = + new CoverageScanner(customizerClass, WRITE, pluginClass).scan(); + + final Set updateReferences = + new CoverageScanner(customizerClass, UPDATE, pluginClass).scan(); + + final Set deleteReferences = + new CoverageScanner(customizerClass, DELETE, pluginClass).scan(); + + return Stream.of(writeReferences.stream(), updateReferences.stream(), deleteReferences.stream()) + .flatMap(pluginMethodReferenceStream -> pluginMethodReferenceStream) + .map(reference -> { + final CoverageUnit.CoverageUnitBuilder builder = new CoverageUnit.CoverageUnitBuilder(); + builder.setVppApi(fromJvppApi(version, reference)); + builder.setJavaApi(new JavaApiMessage(reference.getName())); + + builder.setYangTypes(handler.getHandledChildren().stream() + .map(type -> new YangType(type, yangTypeIndex.getLinkForType(type))) + .collect(Collectors.toList())); + + final String callerClassLink = classPathIndex.linkForClass(reference.getCaller()); + final List supportedOperations = new LinkedList<>(); + if (writeReferences.contains(reference)) { + supportedOperations.add(new Operation(callerClassLink, WRITE)); + } + + if (updateReferences.contains(reference)) { + supportedOperations.add(new Operation(callerClassLink, UPDATE)); + } + + if (deleteReferences.contains(reference)) { + supportedOperations.add(new Operation(callerClassLink, DELETE)); + } + return builder.setSupportedOperations(supportedOperations).build(); + }); + }).collect(Collectors.toSet()); + + return new PluginCoverage(pluginClass.getSimpleName(), + Stream.of(singleNodeCoverageUnits.stream(), multiNodeCoverageUnits.stream()) + .flatMap(coverageUnitStream -> coverageUnitStream) + .collect(Collectors.toSet()), true); + } + + private static Class getCustomizerClass(final Object handler) { + try { + final Set customizerFields = + ReflectionUtils.getAllFields(handler.getClass(), field -> "customizer".equals(field.getName())); + final Field customizerField = customizerFields.iterator().next(); + customizerField.setAccessible(true); + return customizerField.get(handler).getClass(); + } catch (IllegalAccessException e) { + throw new IllegalStateException(format("Unable to get customizer from %s ", handler), e); + } + } + + private static Set getInjectedWriterFactories(final List scannedModules) { + Injector injector = Guice.createInjector(scannedModules); + TypeLiteral> writerFactoryType = new TypeLiteral>() { + }; + return injector.getInstance(Key.get(writerFactoryType)); + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageScanner.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageScanner.java new file mode 100644 index 000000000..c1c67d77b --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageScanner.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + + +import static java.lang.String.format; + +import io.fd.hc2vpp.docs.api.Operation; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.objectweb.asm.ClassReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Scans provided class for reference under specified crud method + */ +public class CoverageScanner { + + private static final Logger LOG = LoggerFactory.getLogger(CoverageScanner.class); + + private final Class classToScan; + private final Operation.CrudOperation crudOperation; + private final Class referenceClass; + + public CoverageScanner(final Class classToScan, + final Operation.CrudOperation crudOperation, + final Class referenceClass) { + this.classToScan = classToScan; + this.crudOperation = crudOperation; + this.referenceClass = referenceClass; + } + + static ClassReader loadClass(String className) { + try (InputStream classStream = + CoverageScanner.class.getClassLoader().getResourceAsStream(className + ".class")) { + return new ClassReader(classStream); + } catch (IOException e) { + throw new IllegalStateException(format("Unable to load bytecode for class %s", className), e); + } + } + + public Set scan() { + LOG.debug("Scanning class {}", classToScan.getName()); + final ClassReader classReader = loadClass(byteCodeStyleReference(classToScan.getName())); + final Set foundReferences = Collections.synchronizedSet(new HashSet<>()); + classReader.accept(new MethodDelegatingClassVisitor(byteCodeStyleReference(classToScan.getName()), + crudOperation.getMethodReference(), byteCodeStyleReference(referenceClass.getPackage().getName()), + foundReferences, null), ClassReader.SKIP_DEBUG); + return foundReferences; + } + + // converts java style reference to bytecode-style name(with slashes instead of dots) + private static String byteCodeStyleReference(final String javaStyleName) { + return javaStyleName.replace(".", "/"); + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/LinkGenerator.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/LinkGenerator.java new file mode 100644 index 000000000..3226639b1 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/LinkGenerator.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + +public interface LinkGenerator { + + static String resolveBranch(final String version) { + if (version.contains("SNAPSHOT")) { + return "master"; + } else { + return "stable%2F" + version.replace(".", ""); + } + } + + default String generateLink(final String raw, final String version) { + //https://git.fd.io/hc2vpp/tree/interface-role/api/src/main/yang/interface-role@2017-06-15.yang?h=stable%2F1707 + return "https://git.fd.io/hc2vpp/tree" + raw + "?h=" + resolveBranch(version); + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodDelegatingClassVisitor.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodDelegatingClassVisitor.java new file mode 100644 index 000000000..39a06e626 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodDelegatingClassVisitor.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + + +import java.util.Set; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class MethodDelegatingClassVisitor extends ClassVisitor { + + private final String currentClass; + private final String methodName; + private final String reference; + private final Set foundReferences; + private final Set allreadyProcessedLocalMethods; + + public MethodDelegatingClassVisitor(String currentClass, + String methodName, + String reference, + Set foundReferences, + Set allreadyProcessedLocalMethods) { + super(Opcodes.ASM5); + this.currentClass = currentClass; + this.methodName = methodName; + this.reference = reference; + this.foundReferences = foundReferences; + this.allreadyProcessedLocalMethods = allreadyProcessedLocalMethods; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.equals(methodName)) { + return new MethodPluginCoverageVisitor(currentClass, foundReferences, reference, + allreadyProcessedLocalMethods); + } + return super.visitMethod(access, name, desc, signature, exceptions); + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodPluginCoverageVisitor.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodPluginCoverageVisitor.java new file mode 100644 index 000000000..fe15f5e79 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodPluginCoverageVisitor.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + +import static java.lang.String.format; + +import java.util.HashSet; +import java.util.Set; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MethodPluginCoverageVisitor extends MethodVisitor { + + private static final Logger LOG = LoggerFactory.getLogger(MethodPluginCoverageVisitor.class); + + private final String currentClass; + private final Set foundReferences; + private final String reference; + private final Set allreadyProcessedLocal; + + public MethodPluginCoverageVisitor(String currentClass, Set foundReferences, + String reference, + Set allreadyProcessedLocal) { + super(Opcodes.ASM5); + this.currentClass = currentClass; + this.foundReferences = foundReferences; + this.reference = reference; + // if nonnull then reuse + this.allreadyProcessedLocal = allreadyProcessedLocal == null + ? new HashSet<>() + : allreadyProcessedLocal; + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean isInterface) { + final String normalizedOwner = owner.replace(";", "").replace("[L", ""); + if (normalizedOwner.contains(reference)) { + // reference was found directly in method code + foundReferences.add(new PluginMethodReference(currentClass, owner, name)); + } else { + if (normalizedOwner.contains("io/fd")) { + // filter just our method to reduce scope + if (!normalizedOwner.equals(currentClass)) { + LOG.debug("Processing non-current {}.{}()", normalizedOwner, name); + // method call is from different class than currently processed one + ClassReader classReader = CoverageScanner.loadClass(normalizedOwner); + classReader.accept(new MethodDelegatingClassVisitor(normalizedOwner, name, reference, + foundReferences, + allreadyProcessedLocal), ClassReader.SKIP_DEBUG); + } else { + LOG.debug("Processing current {}.{}()", normalizedOwner, name); + // other methods in same class that are used in visited method + String fullyQualifiedMethodName = fullyQualifiedMethodName(normalizedOwner, name, desc); + if (allreadyProcessedLocal.contains(fullyQualifiedMethodName)) { + //skip already processed local methods to prevent stack overflow + return; + } + allreadyProcessedLocal.add(fullyQualifiedMethodName); + + ClassReader classReader = CoverageScanner.loadClass(normalizedOwner); + classReader.accept(new MethodDelegatingClassVisitor(normalizedOwner, name, reference, + foundReferences, + allreadyProcessedLocal), ClassReader.SKIP_DEBUG); + } + } + } + super.visitMethodInsn(opcode, owner, name, desc, isInterface); + } + + private String fullyQualifiedMethodName(String owner, String name, String desc) { + return format("%s_%s_%s", owner, name, desc); + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelLinkIndex.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelLinkIndex.java new file mode 100644 index 000000000..c1b299826 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelLinkIndex.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + +import static java.lang.String.format; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Index from module file name to git generateLink + */ +class ModelLinkIndex implements LinkGenerator { + + private final Map modelLinkIndex; + + /** + * @param projectRoot for ex.: /home/jsrnicek/Projects/hc2vpp + * @param version for ex.: 17.07 to get generateLink for correct branch + */ + ModelLinkIndex(final String projectRoot, final String version) { + modelLinkIndex = buildIndex(projectRoot, version); + } + + private static String key(String raw) { + return raw.substring(raw.lastIndexOf("/")) + .replace("/", "") + .replace(".yang", ""); + } + + String linkForModel(final String model, final String revision) { + // TODO - figure out how to get revision for model in src,to use YangModelKey + // if not local model,add generateLink to ietf datatracker + return Optional.ofNullable(modelLinkIndex.get(model + "@" + revision)) + .orElse(Optional.ofNullable(modelLinkIndex.get(model)) + .orElse("https://datatracker.ietf.org/")); + } + + private Map buildIndex(final String projectRoot, final String version) { + try { + return Files.walk(Paths.get(projectRoot)) + .filter(path -> path.toString().contains("src/main/yang")) + .filter(path -> path.toString().endsWith(".yang")) + .map(Path::toString) + .map(s -> s.replace(projectRoot, "")) + .distinct() + .collect(Collectors.toMap(ModelLinkIndex::key, o -> generateLink(o, version))); + } catch (IOException e) { + throw new IllegalStateException(format("%s not found", projectRoot), e); + } + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelTypeIndex.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelTypeIndex.java new file mode 100644 index 000000000..9fd5976b7 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelTypeIndex.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + +import static java.util.stream.Collectors.toMap; + +import com.google.common.io.Resources; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider; +import org.opendaylight.yangtools.yang.binding.YangModuleInfo; + +/** + * Maps namespaces to models + */ +class ModelTypeIndex { + + private final Map namespaceToModuleIndex; + + ModelTypeIndex() throws IOException { + namespaceToModuleIndex = collectAllModules(this.getClass().getClassLoader()) + .stream() + .collect(toMap(YangModelKey::new, YangModuleInfo::getName)); + } + + private static YangModelBindingProvider getModuleBindingProviderInstance(final Class aClass) { + try { + return (YangModelBindingProvider) aClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + private static Class loadClass(final ClassLoader classLoader, final String name) { + try { + return classLoader.loadClass(name); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + + } + } + + private static List loadResource(final URL url) { + try { + return Resources.readLines(url, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + String namespaceToModule(final String namespace, + final String revision) { + return namespaceToModuleIndex.get(new YangModelKey(namespace, revision)); + } + + private Set collectAllModules(final ClassLoader classLoader) throws IOException { + return Collections.list(classLoader.getResources("META-INF/services/" + + YangModelBindingProvider.class.getName())) + .stream() + .map(ModelTypeIndex::loadResource) + .flatMap(Collection::stream) + .map(name -> loadClass(classLoader, name)) + .map(ModelTypeIndex::getModuleBindingProviderInstance) + .map(YangModelBindingProvider::getModuleInfo) + .collect(Collectors.toSet()); + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/PluginMethodReference.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/PluginMethodReference.java new file mode 100644 index 000000000..5a98639ae --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/PluginMethodReference.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + +/** + * Represent found reference of plugin method + */ +public class PluginMethodReference { + + /** + * Name of the class that uses such reference + */ + private final String caller; + + /** + * Class of the reference + */ + private final String owner; + + /** + * Name of the reference + */ + private final String name; + + public PluginMethodReference(final String caller, final String owner, final String name) { + this.caller = caller; + this.owner = owner; + this.name = name; + } + + public String getCaller() { + return caller; + } + + public String getOwner() { + return owner; + } + + public String getName() { + return name; + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/VppApiUtils.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/VppApiUtils.java new file mode 100644 index 000000000..f21cc2660 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/VppApiUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + +import com.google.common.base.CaseFormat; +import io.fd.hc2vpp.docs.api.VppApiMessage; + + +public interface VppApiUtils { + + static String vppApiFromJavaApi(final String jvppApi) { + return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, jvppApi); + } + + static String generateVppApiDocLink(final String version, final String vppApi) { + //https://docs.fd.io/vpp/17.07/d9/d1d/structvl__api__create__subif__t.html + // links are using double underscore + //final String doubleUnderscoreApiName = vppApi.replace("_", "__"); + //return format("https://docs.fd.io/vpp/%s/d9/d1d/structvl__api__%s__t.html", version, doubleUnderscoreApiName); + + // FIXME - generateLink has dynamic part that can be resolved from api name + return "https://docs.fd.io/vpp/17.07/annotated.html"; + } + + default VppApiMessage fromJvppApi(final String version, final PluginMethodReference jvppApi) { + final String vppApi = vppApiFromJavaApi(jvppApi.getName()); + return new VppApiMessage(vppApi, generateVppApiDocLink(version, vppApi)); + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangModelKey.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangModelKey.java new file mode 100644 index 000000000..2bc5bc5da --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangModelKey.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core;import org.opendaylight.yangtools.yang.binding.YangModuleInfo; + +final class YangModelKey { + private final String namespace; + private final String revision; + + YangModelKey(final YangModuleInfo moduleInfo) { + this.namespace = moduleInfo.getNamespace(); + this.revision = moduleInfo.getRevision(); + } + + YangModelKey(final String namespace, final String revision) { + this.namespace = namespace; + this.revision = revision; + } + + + public String getNamespace() { + return namespace; + } + + public String getRevision() { + return revision; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final YangModelKey that = (YangModelKey) o; + + if (namespace != null + ? !namespace.equals(that.namespace) + : that.namespace != null) { + return false; + } + return revision != null + ? revision.equals(that.revision) + : that.revision == null; + } + + @Override + public int hashCode() { + int result = namespace != null + ? namespace.hashCode() + : 0; + result = 31 * result + (revision != null + ? revision.hashCode() + : 0); + return result; + } +} diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangTypeLinkIndex.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangTypeLinkIndex.java new file mode 100644 index 000000000..8220d7f78 --- /dev/null +++ b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangTypeLinkIndex.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core; + +import static java.lang.String.format; + +import java.io.IOException; +import java.lang.reflect.Field; +import org.opendaylight.yangtools.yang.common.QName; + +public class YangTypeLinkIndex { + + private final ModelLinkIndex modelLinkIndex; + private final ModelTypeIndex modelTypeIndex; + + public YangTypeLinkIndex(final String projectRoot, final String version) { + modelLinkIndex = new ModelLinkIndex(projectRoot, version); + try { + modelTypeIndex = new ModelTypeIndex(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public String getLinkForType(final String classname) { + final Class loadedClass; + final QName qname; + try { + loadedClass = this.getClass().getClassLoader().loadClass(classname); + final Field qnameField = loadedClass.getField("QNAME"); + qname = QName.class.cast(qnameField.get(null)); + } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) { + throw new IllegalStateException(format("Unable to extract QNAME from %s", classname), e); + } + + + final String namespace = qname.getNamespace().toString(); + final String formattedRevision = qname.getFormattedRevision(); + final String model = modelTypeIndex.namespaceToModule(namespace, formattedRevision); + return modelLinkIndex.linkForModel(model, formattedRevision); + } +} diff --git a/vpp-integration/api-docs/docs/pom.xml b/vpp-integration/api-docs/docs/pom.xml new file mode 100644 index 000000000..c240ff1e4 --- /dev/null +++ b/vpp-integration/api-docs/docs/pom.xml @@ -0,0 +1,92 @@ + + + + + 4.0.0 + + io.fd.hc2vpp.docs + docs + 1.17.07-SNAPSHOT + + + + + io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule, + io.fd.hc2vpp.docs.core.mock.binding.MockAclModule, + io.fd.hc2vpp.docs.core.mock.binding.MockNshModule, + io.fd.hc2vpp.docs.core.mock.binding.MockIoamModule, + io.fd.hc2vpp.docs.core.mock.binding.MockNatModule, + io.fd.hc2vpp.iface.role.InterfaceRoleModule, + io.fd.hc2vpp.l3.InterfaceL3Module, + io.fd.hc2vpp.l3.ProxyArpModule, + io.fd.hc2vpp.l3.SubInterfaceL3Module, + io.fd.hc2vpp.lisp.LispModule, + io.fd.hc2vpp.lisp.gpe.GpeModule, + io.fd.hc2vpp.management.VppManagementModule, + io.fd.hc2vpp.policer.PolicerModule, + io.fd.hc2vpp.routing.RoutingModule, + io.fd.hc2vpp.v3po.V3poModule, + io.fd.hc2vpp.vpp.classifier.InterfaceClassifierAclModule, + io.fd.hc2vpp.vpp.classifier.SubInterfaceClassifierAclModule, + io.fd.hc2vpp.vpp.classifier.VppClassifierModule, + io.fd.hc2vpp.dhcp.DhcpModule, + io.fd.hc2vpp.bgp.inet.BgpInetModule + + ${project.basedir}/../../.. + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + 1.8 + 1.8 + + + + org.codehaus.gmaven + groovy-maven-plugin + + + generate-coverage-doc + prepare-package + + execute + + + + io.fd.hc2vpp.docs.scripts.ApiDocsIndexGenerator.generate(project, log) + + + + + + + io.fd.hc2vpp.docs + scripts + ${project.version} + + + + + + \ No newline at end of file diff --git a/vpp-integration/api-docs/pom.xml b/vpp-integration/api-docs/pom.xml new file mode 100644 index 000000000..d4cc070c0 --- /dev/null +++ b/vpp-integration/api-docs/pom.xml @@ -0,0 +1,40 @@ + + + + + + hc2vpp-parent + io.fd.hc2vpp.common + 1.17.07-SNAPSHOT + ../../common/hc2vpp-parent + + 4.0.0 + + io.fd.hc2vpp.docs + api-docs + pom + 1.17.07-SNAPSHOT + + + core + api + docs + scripts + + \ No newline at end of file diff --git a/vpp-integration/api-docs/scripts/pom.xml b/vpp-integration/api-docs/scripts/pom.xml new file mode 100644 index 000000000..c71889408 --- /dev/null +++ b/vpp-integration/api-docs/scripts/pom.xml @@ -0,0 +1,192 @@ + + + + + 4.0.0 + + io.fd.hc2vpp.docs + scripts + 1.17.07-SNAPSHOT + + + 2.4.7 + 2.9.2-01 + 2.4.3-01 + + 17.10-SNAPSHOT + 4.12 + 0.9.11 + + + + + io.fd.hc2vpp.docs + docs-core + ${project.version} + + + org.codehaus.groovy + groovy-all + ${groovy.version} + + + io.fd.vpp + jvpp-registry + ${jvpp.version} + + + io.fd.vpp + jvpp-core + ${jvpp.version} + + + org.reflections + reflections + ${reflections.version} + + + + junit + junit + ${junit.version} + test + + + + io.fd.hc2vpp.acl + acl-impl + ${project.version} + + + io.fd.hc2vpp.nat + nat2vpp + ${project.version} + + + io.fd.hc2vpp.ioam + vppioam-impl + ${project.version} + + + io.fd.hc2vpp.nsh + vppnsh-impl + ${project.version} + + + io.fd.hc2vpp.v3po + v3po2vpp + ${project.version} + + + io.fd.hc2vpp.lisp + lisp2vpp + ${project.version} + + + io.fd.hc2vpp.management + vpp-management-impl + ${project.version} + + + io.fd.hc2vpp.iface.role + impl + ${project.version} + + + io.fd.hc2vpp.l3 + l3-impl + ${project.version} + + + io.fd.hc2vpp.vpp.classifier + vpp-classifier-impl + ${project.version} + + + io.fd.hc2vpp.routing + routing-impl + ${project.version} + + + io.fd.hc2vpp.dhcp + dhcp-impl + ${project.version} + + + io.fd.hc2vpp.bgp + bgp-inet + ${project.version} + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + + unpack-configuration + prepare-package + + unpack-dependencies + + + **/honeycomb-minimal-resources/ + ${project.build.outputDirectory}/ + + + + + + + org.codehaus.groovy + groovy-eclipse-compiler + ${groovy.eclipse.compiler.version} + true + + + maven-compiler-plugin + + + groovy-eclipse-compiler + 1.8 + 1.8 + + + + org.codehaus.groovy + groovy-eclipse-compiler + ${groovy.eclipse.compiler.version} + + + + org.codehaus.groovy + groovy-eclipse-batch + ${groovy.eclipse.batch.version} + + + + + + + \ No newline at end of file diff --git a/vpp-integration/api-docs/scripts/src/main/groovy/io/fd/hc2vpp/docs/scripts/ApiDocsIndexGenerator.groovy b/vpp-integration/api-docs/scripts/src/main/groovy/io/fd/hc2vpp/docs/scripts/ApiDocsIndexGenerator.groovy new file mode 100644 index 000000000..238f4a25b --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/groovy/io/fd/hc2vpp/docs/scripts/ApiDocsIndexGenerator.groovy @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.scripts + +import groovy.text.SimpleTemplateEngine +import io.fd.hc2vpp.docs.api.* +import io.fd.hc2vpp.docs.core.ClassPathTypeIndex +import io.fd.hc2vpp.docs.core.CoverageGenerator +import io.fd.hc2vpp.docs.core.YangTypeLinkIndex +import io.fd.vpp.jvpp.acl.future.FutureJVppAcl +import io.fd.vpp.jvpp.core.future.FutureJVppCore +import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexport +import io.fd.vpp.jvpp.ioampot.future.FutureJVppIoampot +import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtrace +import io.fd.vpp.jvpp.nsh.future.FutureJVppNsh +import io.fd.vpp.jvpp.snat.future.FutureJVppSnat + +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardOpenOption +import java.util.stream.Collectors + +import static java.util.stream.Collectors.toList + +/** + * Generates VPP api to Yang node index for hc2vpp guice modules listed in api.docs.modules maven property. + */ +class ApiDocsIndexGenerator { + + private static def NL = System.lineSeparator() + // TODO - check if list of plugin classes can be generated based on list of modules enabled for doc generation + private static + def PLUGIN_CLASSES = [FutureJVppCore.class, FutureJVppAcl.class, FutureJVppSnat.class, FutureJVppNsh.class, + FutureJVppIoamexport.class, FutureJVppIoampot.class, FutureJVppIoamtrace.class] + private static def TABLE_PART_MARK = "|" + + /** + * Generate coverage data for all configured coverage.modules and JVpp plugins + * */ + public static void generate(final project, final log) { + def loader = this.getClassLoader() + + String moduleNames = project.properties.get("api.docs.modules") + String projectRoot = project.properties.get("project.root.folder") + + if (moduleNames.trim().isEmpty()) { + log.info "No modules defined for ${project.name}. Skipping api-docs generation." + return + } + + final List moduleNamesList = moduleNames.split(",") + + log.info "Reading module list for ${project.name}" + def modules = moduleNamesList.stream() + .map { moduleName -> moduleName.trim() } + .map { moduleName -> + log.info "Loading class $moduleName" + loader.loadClass(moduleName).newInstance() + } + .collect(toList()) + + String outPath = project.build.outputDirectory + + log.info "Generating yang type generateLink index" + YangTypeLinkIndex yangTypeIndex = new YangTypeLinkIndex(projectRoot, project.version) + log.info "Classpath type generateLink index" + ClassPathTypeIndex classPathIndex = new ClassPathTypeIndex(projectRoot, project.version) + + log.info "Generating VPP API to YANG mapping" + PLUGIN_CLASSES.stream() + .forEach { pluginClass -> + log.info "Generating mapping for ${pluginClass}" + final PluginCoverage configCoverage = new CoverageGenerator() + .generateConfigCoverage(pluginClass, project.version, modules, yangTypeIndex, classPathIndex) + generateJvppCoverageDoc(configCoverage, outPath, log) + + //TODO operational coverage + } + } + + static void generateJvppCoverageDoc( + final PluginCoverage pluginCoverage, final String outPath, final log) { + if (!pluginCoverage.hasCoverage()) { + log.info "Plugin ${pluginCoverage.getPluginName()} does not have coverage data, skipping config docs generation" + return + } + log.info "Generating config api docs for plugin ${pluginCoverage.getPluginName()}" + def template = this.getClassLoader().getResource("yang_to_jvpp_template") + def result = new SimpleTemplateEngine() + .createTemplate(template) + .make(["pluginName": pluginCoverage.getPluginName(), "tableContent": generateConfigTableContent(pluginCoverage.getCoverage())]).toString() + + log.debug "Docs result for ${pluginCoverage.getPluginName()}$NL$result" + + Paths.get(outPath).toFile().mkdirs() + + def outFileName + if (pluginCoverage.isConfig()) { + outFileName = "${normalizePluginName(pluginCoverage.getPluginName())}-yang-config-index.adoc" + } else { + outFileName = "${normalizePluginName(pluginCoverage.getPluginName())}-yang-operational-index.adoc" + } + + def outFilePath = Paths.get(outPath, outFileName) + + Files.write(outFilePath, result.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE) + log.info "Plugin ${pluginCoverage.getPluginName()} config api docs sucessfully writen to ${outFilePath.toString()}" + } + + private static String generateConfigTableContent(final Set coverage) { + coverage.stream() + .map { unit -> + "$NL" + + "${vppApiWithLink(unit.vppApi)}" + + "${javaApi(unit.javaApi)}" + + "${yangTypes(unit.yangTypes)}" + + "${supportedOperations(unit.supportedOperations)}" + } + .collect(Collectors.joining(NL)) + } + + private static String vppApiWithLink(final VppApiMessage vppApi) { + "$TABLE_PART_MARK${vppApi.link}[${vppApi.name}]$NL" + } + + private static String javaApi(final JavaApiMessage javaApi) { + "$TABLE_PART_MARK${javaApi.api}$NL" + } + + private static String yangTypes(final List yangTypes) { + "$NL$TABLE_PART_MARK$NL ${yangTypes.stream().map { yangType -> " ${yangType.link}[${yangType.type}]" }.collect(Collectors.joining(NL))}" + } + + private static String supportedOperations(final List operations) { + "$NL$TABLE_PART_MARK${operations.stream().map { reference -> " ${reference.link}[${reference.operation}]" }.collect(Collectors.joining(NL))}" + } + + private static String normalizePluginName(final String name) { + name.toLowerCase().replace(" ", "-") + } +} diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockAclModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockAclModule.java new file mode 100644 index 000000000..436061850 --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockAclModule.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core.mock.binding; + +import static io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule.noOpProxy; + +import com.google.inject.Provider; +import io.fd.hc2vpp.acl.AclModule; +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.io.IOException; + +/** + * Use to bypass jvpp registration + */ +public class MockAclModule extends AclModule { + + public MockAclModule() { + super(MockFutureAclProvider.class); + } + + static class MockFutureAclProvider implements Provider { + + @Override + public FutureJVppAclFacade get() { + try { + return new FutureJVppAclFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } +} diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockBindingModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockBindingModule.java new file mode 100644 index 000000000..e6500ea4c --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockBindingModule.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core.mock.binding; + +import static com.google.inject.name.Names.named; + +import com.google.inject.AbstractModule; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; + +/** + * Use to bypass jvpp registration, and infra modules + */ +public class MockBindingModule extends AbstractModule { + + private static final InvocationHandler NOOP_INVOCATION_HANDLER = (proxy, method, args) -> null; + + @Override + protected void configure() { + bind(FutureJVppCore.class).toInstance(noOpProxy(FutureJVppCore.class)); + bind(MappingContext.class).annotatedWith(named("honeycomb-context")) + .toInstance(noOpProxy(MappingContext.class)); + bind(DataBroker.class).annotatedWith(named("honeycomb-context")).toInstance(noOpProxy(DataBroker.class)); + bind(JVppRegistry.class).toInstance(noOpProxy(JVppRegistry.class)); + } + + static T noOpProxy(Class clazz) { + return (T) Proxy.newProxyInstance(MockBindingModule.class.getClassLoader(), + new Class[]{clazz}, NOOP_INVOCATION_HANDLER); + } +} diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockIoamModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockIoamModule.java new file mode 100644 index 000000000..f3f06195d --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockIoamModule.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core.mock.binding; + +import static io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule.noOpProxy; + +import com.google.inject.Provider; +import io.fd.hc2vpp.vppioam.impl.VppIoamModule; +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexportFacade; +import io.fd.vpp.jvpp.ioampot.future.FutureJVppIoampotFacade; +import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtraceFacade; +import java.io.IOException; + +/** + * Use to bypass jvpp registration + */ +public class MockIoamModule extends VppIoamModule { + + public MockIoamModule() { + super(MockTraceProvider.class, MockPotProvider.class, MockExportProvider.class); + } + + private static class MockTraceProvider implements Provider { + @Override + public FutureJVppIoamtraceFacade get() { + try { + return new FutureJVppIoamtraceFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + + private static class MockPotProvider implements Provider { + + @Override + public FutureJVppIoampotFacade get() { + try { + return new FutureJVppIoampotFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + + private static class MockExportProvider implements Provider { + + @Override + public FutureJVppIoamexportFacade get() { + try { + return new FutureJVppIoamexportFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } +} diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNatModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNatModule.java new file mode 100644 index 000000000..d74f7f2b9 --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNatModule.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core.mock.binding; + +import static io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule.noOpProxy; + +import com.google.inject.Provider; +import io.fd.hc2vpp.nat.NatModule; +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade; +import java.io.IOException; + +/** + * Use to bypass jvpp registration + */ +public class MockNatModule extends NatModule { + + public MockNatModule() { + super(MockJVppSnatProvider.class); + } + + private static class MockJVppSnatProvider implements Provider { + + @Override + public FutureJVppSnatFacade get() { + try { + return new FutureJVppSnatFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } +} diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNshModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNshModule.java new file mode 100644 index 000000000..dace214e8 --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNshModule.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.docs.core.mock.binding; + +import static io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule.noOpProxy; + +import com.google.inject.Provider; +import io.fd.hc2vpp.vppnsh.impl.VppNshModule; +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.nsh.future.FutureJVppNshFacade; +import java.io.IOException; + +/** + * Use to bypass jvpp registration + */ +public class MockNshModule extends VppNshModule { + + public MockNshModule() { + super(MockJVppNshProvider.class); + } + + private static class MockJVppNshProvider implements Provider { + + @Override + public FutureJVppNshFacade get() { + try { + return new FutureJVppNshFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } +} diff --git a/vpp-integration/api-docs/scripts/src/main/resources/routing.json b/vpp-integration/api-docs/scripts/src/main/resources/routing.json new file mode 100644 index 000000000..95f85414f --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/resources/routing.json @@ -0,0 +1,4 @@ +{ + "default-routing-instance-name": "vpp-routing-instance", + "learned-route-name-prefix": "learned-route" +} \ No newline at end of file diff --git a/vpp-integration/api-docs/scripts/src/main/resources/vpp-management.json b/vpp-integration/api-docs/scripts/src/main/resources/vpp-management.json new file mode 100644 index 000000000..28b25b263 --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/resources/vpp-management.json @@ -0,0 +1,3 @@ +{ + "keepalive-delay":30 +} \ No newline at end of file diff --git a/vpp-integration/api-docs/scripts/src/main/resources/yang_to_jvpp_template b/vpp-integration/api-docs/scripts/src/main/resources/yang_to_jvpp_template new file mode 100644 index 000000000..cf9502cd5 --- /dev/null +++ b/vpp-integration/api-docs/scripts/src/main/resources/yang_to_jvpp_template @@ -0,0 +1,6 @@ +.${pluginName} to Yang index +[width="80%",options="header",cols="1,1,m,1"] +|=== +|*VPP api* |*Java api* |*Yang nodes* |*Supported Operations* +${tableContent} +|=== \ No newline at end of file diff --git a/vpp-integration/pom.xml b/vpp-integration/pom.xml index 39ebdd10a..0e57711f6 100644 --- a/vpp-integration/pom.xml +++ b/vpp-integration/pom.xml @@ -33,6 +33,7 @@ minimal-distribution + api-docs -- cgit 1.2.3-korg