diff options
14 files changed, 1381 insertions, 0 deletions
diff --git a/actn-interface-tools/global-control/pom.xml b/actn-interface-tools/global-control/pom.xml new file mode 100644 index 0000000..1c01ad8 --- /dev/null +++ b/actn-interface-tools/global-control/pom.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ ============LICENSE_START======================================================= + ~ Actn Interface Tools + ~ ================================================================================ + ~ Copyright (C) 2022 Huawei Canada Limited. + ~ ================================================================================ + ~ 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. + ~ ============LICENSE_END========================================================= + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.integration.ietf-actn-tools</groupId> + <artifactId>actn-interface-tools</artifactId> + <version>1.0-SNAPSHOT</version> + </parent> + <artifactId>global-control</artifactId> +</project> + diff --git a/actn-interface-tools/pom.xml b/actn-interface-tools/pom.xml index baa2400..b92341b 100644 --- a/actn-interface-tools/pom.xml +++ b/actn-interface-tools/pom.xml @@ -31,10 +31,14 @@ <modules> <module>actn-model</module> <module>actn-client</module> + <module>global-control</module> + <module>yang-utils</module> + <module>restconf-client</module> </modules> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> + <slf4j.version>1.7.21</slf4j.version> </properties> <dependencies> <dependency> @@ -49,12 +53,32 @@ <version>2.0.1</version> <scope>compile</scope> </dependency> + <dependency> <groupId>org.apache.felix</groupId> <artifactId>org.apache.felix.scr.annotations</artifactId> <scope>provided</scope> <version>1.9.12</version> </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>com.google.auto.service</groupId> + <artifactId>auto-service-annotations</artifactId> + <version>1.0</version> + <scope>compile</scope> + </dependency> + + + </dependencies> </project>
\ No newline at end of file diff --git a/actn-interface-tools/restconf-client/api/pom.xml b/actn-interface-tools/restconf-client/api/pom.xml new file mode 100644 index 0000000..49998cb --- /dev/null +++ b/actn-interface-tools/restconf-client/api/pom.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ ============LICENSE_START======================================================= + ~ Actn Interface Tools + ~ ================================================================================ + ~ Copyright (C) 2023 Huawei Canada Limited. + ~ ================================================================================ + ~ 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. + ~ ============LICENSE_END========================================================= + --> + +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.integration.ietf-actn-tools</groupId> + <artifactId>restconf-client</artifactId> + <version>1.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + <groupId>org.onap.integration.ietf-actn-tools</groupId> + <artifactId>restconf-client-api</artifactId> + <version>1.0-SNAPSHOT</version> + <!-- <packaging>bundle</packaging>--> + <packaging>jar</packaging> + <description>ONOS RESTCONF southbound plugin API</description> + <dependencies> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.5</version> + <scope>compile</scope> + </dependency> + </dependencies> +</project> diff --git a/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/DefaultPncInstance.java b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/DefaultPncInstance.java new file mode 100644 index 0000000..4bb8b66 --- /dev/null +++ b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/DefaultPncInstance.java @@ -0,0 +1,127 @@ +/* + * ============LICENSE_START======================================================= + * Actn Interface Tools + * ================================================================================ + * Copyright (C) 2023 Huawei Canada Limited. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.integration.actninterfacetools.protocol.restconf; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import org.apache.commons.lang3.StringUtils; +import java.net.InetAddress; +import java.util.Objects; +import java.util.UUID; + +/** + * Default implementation for Rest devices. + */ +public class DefaultPncInstance implements PncInstance { + private static final String REST = "rest"; + private static final String COLON = ":"; + + private final UUID pncId; + private final InetAddress ip; + private final int port; + private final String username; + private final String password; + private String protocol; + private String url; + + public DefaultPncInstance(InetAddress ip, int port, String name, String password, + String protocol, String url) { + this.pncId = UUID.randomUUID(); + Preconditions.checkNotNull(ip, "IP address cannot be null"); + Preconditions.checkArgument(port > 0, "Port address cannot be negative"); + Preconditions.checkNotNull(protocol, "protocol address cannot be null"); + this.ip = ip; + this.port = port; + this.username = name; + this.password = StringUtils.isEmpty(password) ? null : password; + this.protocol = protocol; + this.url = StringUtils.isEmpty(url) ? null : url; + } + + @Override + public InetAddress ip() { + return this.ip; + } + + @Override + public int port() { + return this.port; + } + + @Override + public String username() { + return this.username; + } + + @Override + public String password() { + return this.password; + } + + @Override + public UUID pncId() { + return this.pncId; + } + + @Override + public String protocol() { + return protocol; + } + + @Override + public String url() { + return url; + } + + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .omitNullValues() + .add("url", url) + .add("protocol", protocol) + .add("username", username) + .add("port", port) + .add("ip", ip) + .toString(); + + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof PncInstance)) { + return false; + } + PncInstance pncInstance = (PncInstance) obj; + return this.username.equals(pncInstance.username()) && this.ip.equals(pncInstance.ip()) && + this.port == pncInstance.port(); + + } + + @Override + public int hashCode() { + return Objects.hash(ip, port); + } + +} diff --git a/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/PncInstance.java b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/PncInstance.java new file mode 100644 index 0000000..a6726e8 --- /dev/null +++ b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/PncInstance.java @@ -0,0 +1,81 @@ +/* + * ============LICENSE_START======================================================= + * Actn Interface Tools + * ================================================================================ + * Copyright (C) 2023 Huawei Canada Limited. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.integration.actninterfacetools.protocol.restconf; + +import java.net.InetAddress; +import java.util.Optional; +import java.util.UUID; + +/** + * Represents an abstraction of a Pnc. + */ +public interface PncInstance { + /** + * Returns the ip of the Pnc Service. + * + * @return ip + */ + InetAddress ip(); + + /** + * Returns the port of the Pnc Service. + * + * @return port + */ + int port(); + + /** + * Returns the username of the Pnc Service. + * + * @return username + */ + String username(); + + /** + * Returns the password of the Pnc Service. + * + * @return password + */ + String password(); + + /** + * Returns the pncId. + * + * @return pncId + */ + UUID pncId(); + + + /** + * Returns the protocol for the REST request, usually HTTP o HTTPS. + * + * @return protocol + */ + String protocol(); + + /** + * Returns the url for the REST requests, to be used instead of IP and PORT. + * + * @return url + */ + String url(); + +} diff --git a/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/RestConfSBController.java b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/RestConfSBController.java new file mode 100644 index 0000000..86cfbc6 --- /dev/null +++ b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/RestConfSBController.java @@ -0,0 +1,216 @@ +/* + * ============LICENSE_START======================================================= + * Actn Interface Tools + * ================================================================================ + * Copyright (C) 2023 Huawei Canada Limited. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.integration.actninterfacetools.protocol.restconf; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +import javax.ws.rs.core.MediaType; +import java.net.InetAddress; +import java.util.Map; +import java.util.UUID; + +/** + * Abstraction of a RESTCONF controller. Serves as a one-stop shop for obtaining + * RESTCONF southbound devices and (un)register listeners. + */ +public interface RestConfSBController { + /** + * Returns all the devices known to this controller. + * + * @return map of devices + */ + Map<UUID, PncInstance> getPncInstances(); + + /** + * Returns a device by node identifier. + * + * @param deviceInfo node identifier + * @return RestSBDevice rest device + */ + PncInstance getPncInstance(UUID deviceInfo); + + /** + * Returns a device by Ip and Port. + * + * @param ip device ip + * @param port device port + * @return RestSBDevice rest device + */ + PncInstance getPncInstance(InetAddress ip, int port); + + /** + * Adds a device to the device map. + * + * @param pncInstance to be added + */ + void addPncInstance(PncInstance pncInstance); + + /** + * Removes the device from the devices map. + * + * @param pncId to be removed + */ + void removeDevice(UUID pncId); + /** + * This method is to be called by whoever is interested to receive + * Notifications from a specific device. It does a REST GET request + * with specified parameters to the device, and calls the provided + * callBackListener upon receiving notifications to notify the requester + * about notifications. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param mediaType format to retrieve the content in + * @param callBackListener method to call when notifications arrives + */ + void enableNotifications(UUID pncId, String request, String mediaType, + RestconfNotificationEventListener callBackListener); + + /** + * Registers a listener for notification events that occur to restconf + * devices. + * + * @param pncId identifier of the pnc to which the listener is attached + * @param listener the listener to notify + */ + void addNotificationListener(UUID pncId, + RestconfNotificationEventListener listener); + + /** + * Unregisters the listener for the device. + * + * @param pncId identifier of the pnc for which the listener + * is to be removed + * @param listener listener to be removed + */ + void removeNotificationListener(UUID pncId, + RestconfNotificationEventListener listener); + + /** + * Returns true if a listener has been installed to listen to RESTCONF + * notifications sent from a particular device. + * + * @param pncId identifier of the pnc from which the notifications + * are generated + * @return true if listener is installed; false otherwise + */ + boolean isNotificationEnabled(UUID pncId); + + /** + * HTTP POST request with specified parameters to the pnc. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param payload payload of the request as an ObjectNode + * @param mediaType type of content in the payload i.e. application/json + * @return status Commonly used status codes defined by HTTP + */ + int post(UUID pncId, String request, ObjectNode payload, MediaType mediaType); + /** + * HTTP POST request with specified parameters to the pnc. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param payload payload of the request as an ObjectNode + * @param mediaType type of content in the payload in the form of string i.e. "application/json" + * @return true if status defined by HTTP is OK, CREATED, ACCEPTED or PARTIAL-CONTEND, otherwise return false + */ + boolean post(UUID pncId, String request, ObjectNode payload, String mediaType); + + <T> T post(UUID pncId, String request, ObjectNode payload, String mediaType, Class<T> responseClass); + + <T> T post(UUID pncId, String request, ObjectNode payload, MediaType mediaType, Class<T> responseClass); + + /** + * HTTP PUT request with specified parameters to the device. + * + * @param pncId pnc to make the request to + * @param request resource path of the request + * @param payload payload of the request as an ObjectNode + * @param mediaType type of content in the payload i.e. application/json + * @return status Commonly used status codes defined by HTTP + */ + int put(UUID pncId, String request, ObjectNode payload, MediaType mediaType); + + /** + * HTTP PUT request with specified parameters to the pnc. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param payload payload of the request as an ObjectNode + * @param mediaType type of content in the payload in the form of string i.e. "application/json" + * @return true if status defined by HTTP is OK, CREATED, ACCEPTED or PARTIAL-CONTEND, otherwise return false + */ + boolean put(UUID pncId, String request, ObjectNode payload, String mediaType); + + /** + * HTTP PATCH request with specified parameters to the device. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param payload payload of the request as an ObjectNode + * @param mediaType format to retrieve the content in + * @return status Commonly used status codes defined by HTTP + */ + int patch(UUID pncId, String request, ObjectNode payload, MediaType mediaType); + + /** + * HTTP PATCH request with specified parameters to the pnc. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param payload payload of the request as an ObjectNode + * @param mediaType type of content in the payload i.e. application/json + * @return status Commonly used status codes defined by HTTP + */ + boolean patch(UUID pncId, String request, ObjectNode payload, String mediaType); + + /** + * HTTP DELETE request with specified parameters to the device. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param mediaType type of content in the payload i.e. application/json + * @return status Commonly used status codes defined by HTTP + */ + int delete(UUID pncId, String request, MediaType mediaType); + + /** + * HTTP DELETE request with specified parameters to the pnc. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param mediaType type of content in the payload i.e. application/json + * @return status Commonly used status codes defined by HTTP + */ + boolean delete(UUID pncId, String request, String mediaType); + + /** + * + * HTTP GET request with specified parameters to the device. + * + * @param pncId pnc to make the request to + * @param request url of the request + * @param mediaType format to retrieve the content in + * @return an ObjectNode of data from the reply. + */ + ObjectNode get(UUID pncId, String request, String mediaType); + +} diff --git a/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/RestconfNotificationEventListener.java b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/RestconfNotificationEventListener.java new file mode 100644 index 0000000..70eb2d6 --- /dev/null +++ b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/RestconfNotificationEventListener.java @@ -0,0 +1,36 @@ +/* + * ============LICENSE_START======================================================= + * Actn Interface Tools + * ================================================================================ + * Copyright (C) 2023 Huawei Canada Limited. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.integration.actninterfacetools.protocol.restconf; + +import java.util.UUID; + +/** + * Notifies providers about incoming RESTCONF notification events. + */ +public interface RestconfNotificationEventListener<T> { + + /** + * Handles the notification event. + * + * @param pncId restconf device identifier + * @param event event payload + */ + void handleNotificationEvent(UUID pncId, T event); +} diff --git a/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/WebClientUtil.java b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/WebClientUtil.java new file mode 100644 index 0000000..066d4bd --- /dev/null +++ b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/WebClientUtil.java @@ -0,0 +1,61 @@ +/* + * ============LICENSE_START======================================================= + * Actn Interface Tools + * ================================================================================ + * Copyright (C) 2023 Huawei Canada Limited. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.integration.actninterfacetools.protocol.restconf; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class WebClientUtil { + + protected static final Logger LOGGER = LoggerFactory.getLogger(WebClientUtil.class); + + private WebClientUtil() { + } + + public static String resetContentRange(String range) { + String rangeResult = ""; + if (range != null) { + final Pattern pattern = Pattern.compile("\\d+\\-\\d+/\\S+"); + final Matcher matcher = pattern.matcher(range); + if (matcher.find()) { + final String[] result = matcher.group(0).split("/"); + if (result.length >= 2) { + final String[] ranges = result[0].split("\\D"); + // 如果返回截止长度大于等于总长度-1,则认为结束 + if (isContentEnd(ranges[1], result[1])) { + return ""; + } + rangeResult = "bytes=" + (Integer.parseInt(ranges[1]) + 1) + "-" + + (result[1].equals("*") ? "" : result[1]); + } + } + } + return rangeResult; + } + + private static boolean isContentEnd(String end, String total) { + return total.matches("[0-9]*") && Integer.parseInt(end) >= Integer.parseInt(total) - 1; + } + +} diff --git a/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/package-info.java b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/package-info.java new file mode 100644 index 0000000..6551498 --- /dev/null +++ b/actn-interface-tools/restconf-client/api/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/package-info.java @@ -0,0 +1,24 @@ +/* + * ============LICENSE_START======================================================= + * Actn Interface Tools + * ================================================================================ + * Copyright (C) 2023 Huawei Canada Limited. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/** + * RESTCONF southbound protocols libraries. + */ +package org.onap.integration.actninterfacetools.protocol.restconf; diff --git a/actn-interface-tools/restconf-client/ctl/pom.xml b/actn-interface-tools/restconf-client/ctl/pom.xml new file mode 100644 index 0000000..6326889 --- /dev/null +++ b/actn-interface-tools/restconf-client/ctl/pom.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ ============LICENSE_START======================================================= + ~ Actn Interface Tools + ~ ================================================================================ + ~ Copyright (C) 2023 Huawei Canada Limited. + ~ ================================================================================ + ~ 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. + ~ ============LICENSE_END========================================================= + --> + +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.integration.ietf-actn-tools</groupId> + <artifactId>restconf-client</artifactId> + <version>1.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + <artifactId>restconf-client-ctl</artifactId> + + <packaging>jar</packaging> + + <dependencies> + <dependency> + <groupId>org.onap.integration.ietf-actn-tools</groupId> + <artifactId>restconf-client-api</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>javax.xml.bind</groupId> + <artifactId>jaxb-api</artifactId> + <version>2.3.1</version> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + <scope>provided</scope> + <version>1.9.12</version> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <scope>provided</scope> + <version>5.0.0</version> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-client</artifactId> + <version>2.9</version> + </dependency> + + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient-osgi</artifactId> + <version>4.5.1</version> + </dependency> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpcore-osgi</artifactId> + <version>4.4.4</version> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + </dependency> + </dependencies> + +</project> diff --git a/actn-interface-tools/restconf-client/ctl/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/ctl/RestConfSBControllerImpl.java b/actn-interface-tools/restconf-client/ctl/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/ctl/RestConfSBControllerImpl.java new file mode 100644 index 0000000..71f8797 --- /dev/null +++ b/actn-interface-tools/restconf-client/ctl/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/ctl/RestConfSBControllerImpl.java @@ -0,0 +1,512 @@ +/* + * ============LICENSE_START======================================================= + * Actn Interface Tools + * ================================================================================ + * Copyright (C) 2023 Huawei Canada Limited. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.integration.actninterfacetools.protocol.restconf.ctl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableMap; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.conn.ssl.AllowAllHostnameVerifier; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.glassfish.jersey.client.ChunkedInput; +import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; +import org.onap.integration.actninterfacetools.protocol.restconf.PncInstance; +import org.onap.integration.actninterfacetools.protocol.restconf.RestConfSBController; +import org.onap.integration.actninterfacetools.protocol.restconf.RestconfNotificationEventListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.net.InetAddress; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * The implementation of RestConfSBController. + */ + +public class RestConfSBControllerImpl implements RestConfSBController { + private static volatile RestConfSBControllerImpl restConfClientInstance = null; + private static final int STATUS_OK = Response.Status.OK.getStatusCode(); + private static final int STATUS_CREATED = Response.Status.CREATED.getStatusCode(); + private static final int STATUS_ACCEPTED = Response.Status.ACCEPTED.getStatusCode(); + private static final int PARTIAL_CONTENT = Response.Status.PARTIAL_CONTENT.getStatusCode(); + protected static final String DOUBLESLASH = "/"; + protected static final String COLON = ":"; + private static final String XML = "xml"; + private static final String JSON = "json"; + private static final String HTTPS = "https"; + private static final String AUTHORIZATION_PROPERTY = "authorization"; + private static final String BASIC_AUTH_PREFIX = "Basic "; + private static final Logger log = LoggerFactory + .getLogger(RestConfSBControllerImpl.class); + + // TODO: for the Ibis release when both RESTCONF server and RESTCONF client + // fully support root resource discovery, ROOT_RESOURCE constant will be + // removed and rather the value would get discovered dynamically. + private static final String ROOT_RESOURCE = "/restconf"; + + private static final String RESOURCE_PATH_PREFIX = "/data/"; + private static final String NOTIFICATION_PATH_PREFIX = "/streams/"; + + private Map<UUID, Set<RestconfNotificationEventListener>> + restconfNotificationListenerMap = new ConcurrentHashMap<>(); + private Map<UUID, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>(); + private final Map<UUID, PncInstance> pncMap = new ConcurrentHashMap<>(); + private final Map<UUID, Client> clientMap = new ConcurrentHashMap<>(); + + ExecutorService executor = Executors.newCachedThreadPool(); + + private RestConfSBControllerImpl(){ + + } + public static RestConfSBControllerImpl getRestConfClient(){ + if(restConfClientInstance != null){ + return restConfClientInstance; + } + synchronized (RestConfSBControllerImpl.class) { + if(restConfClientInstance == null){ + restConfClientInstance = new RestConfSBControllerImpl(); + + } + return restConfClientInstance; + } + } + + public void activate() { + log.info("RESTCONF SBI Started"); + } + + public void deactivate() { + log.info("RESTCONF SBI Stopped"); + executor.shutdown(); + this.clientMap.clear(); + this.pncMap.clear(); + } + + public Map<UUID, PncInstance> getPncInstances() { + log.trace("RESTCONF SBI::getDevices"); + return ImmutableMap.copyOf(pncMap); + } + + public PncInstance getPncInstance(UUID pncInfo) { + log.trace("RESTCONF SBI::getDevice with deviceId"); + return pncMap.get(pncInfo); + } + + public PncInstance getPncInstance(InetAddress ip, int port) { + log.trace("RESTCONF SBI::getDevice with ip and port"); + return pncMap.values().stream().filter(v -> v.ip().equals(ip) && v.port() == port).findFirst().get(); + } + + public void addPncInstance(PncInstance pncInstance) { + log.trace("RESTCONF SBI::addDevice"); + if (!pncMap.containsKey(pncInstance.pncId())) { + Client client = ignoreSslClient(); + if (pncInstance.username() != null) { + String username = pncInstance.username(); + String password = pncInstance.password() == null ? "" : pncInstance.password(); + client.register(HttpAuthenticationFeature.basic(username, password)); + } + clientMap.put(pncInstance.pncId(), client); + pncMap.put(pncInstance.pncId(), pncInstance); + } else { + log.warn("Trying to add a device that is already existing {}", pncInstance.pncId()); + } + } + private Client ignoreSslClient() { + SSLContext sslcontext = null; + + try { + sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init(null, new TrustManager[]{new X509TrustManager() { + public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + }}, new java.security.SecureRandom()); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + e.printStackTrace(); + } + + return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((s1, s2) -> true).build(); + } + + public void removeDevice(UUID pncId) { + log.trace("RESTCONF SBI::removeDevice"); + clientMap.remove(pncId); + pncMap.remove(pncId); + } + @Override + public boolean post(UUID pncId, String request, ObjectNode payload, + String mediaType) { + request = discoverRootResource(pncId) + RESOURCE_PATH_PREFIX + + request; + return checkStatusCode(post(pncId, request, payload, typeOfMediaType(mediaType))); + } + private MediaType typeOfMediaType(String type) { + switch (type) { + case XML: + return MediaType.APPLICATION_XML_TYPE; + case JSON: + return MediaType.APPLICATION_JSON_TYPE; + case MediaType.WILDCARD: + return MediaType.WILDCARD_TYPE; + default: + throw new IllegalArgumentException("Unsupported media type " + type); + + } + } + private boolean checkStatusCode(int statusCode) { + if (statusCode == STATUS_OK || statusCode == STATUS_CREATED || statusCode == STATUS_ACCEPTED) { + return true; + } else { + log.error("Failed request, HTTP error code : " + statusCode); + return false; + } + } + @Override + public int post(UUID pndId, String request, ObjectNode payload, MediaType mediaType) { + Response response = getResponse(pndId, request, payload, mediaType); + if (response == null) { + return Response.Status.NO_CONTENT.getStatusCode(); + } + return response.getStatus(); + } + private Response getResponse(UUID pncId, String request, ObjectNode payload, MediaType mediaType) { + + WebTarget wt = getWebTarget(pncId, request); + + Response response = null; + if (payload != null) { + try { + response = wt.request(mediaType) + .post(Entity.entity(payload.toString(), mediaType)); + } catch (Exception e) { + log.error("Cannot do POST {} request on device {} because can't read payload", request, pncId); + } + } else { + response = wt.request(mediaType).post(Entity.entity(null, mediaType)); + } + return response; + } + protected WebTarget getWebTarget(UUID pncId, String request) { + log.info("Sending request to URL {} ", getUrlString(pncId, request)); + return clientMap.get(pncId).target(getUrlString(pncId, request)); + } + protected String getUrlString(UUID pncId, String request) { + PncInstance pncInstance = pncMap.get(pncId); + if (pncInstance == null) { + log.warn("restSbDevice cannot be NULL!"); + return ""; + } + if (pncInstance.url() != null) { + return pncInstance.protocol() + COLON + DOUBLESLASH + pncInstance.url() + request; + } else { + return pncInstance.protocol() + COLON + DOUBLESLASH + pncInstance.ip().toString() + + COLON + pncInstance.port() + request; + } + } + @Override + public <T> T post(UUID pncId, String request, ObjectNode payload, + String mediaType, Class<T> responseClass) { + request = discoverRootResource(pncId) + RESOURCE_PATH_PREFIX + + request; + return post(pncId, request, payload, typeOfMediaType(mediaType), responseClass); + } + @Override + public <T> T post(UUID device, String request, ObjectNode payload, MediaType mediaType, + Class<T> responseClass) { + Response response = getResponse(device, request, payload, mediaType); + if (response != null && response.hasEntity()) { + return responseClass == Response.class ? (T) response : response.readEntity(responseClass); + } + log.error("Response from device {} for request {} contains no entity", device, request); + return null; + } + @Override + public boolean put(UUID pncId, String request, ObjectNode payload, + String mediaType) { + request = discoverRootResource(pncId) + RESOURCE_PATH_PREFIX + + request; + return checkStatusCode(put(pncId, request, payload, typeOfMediaType(mediaType))); + } + @Override + public int put(UUID pncId, String request, ObjectNode payload, MediaType mediaType) { + + WebTarget wt = getWebTarget(pncId, request); + + Response response = null; + if (payload != null) { + try { + response = wt.request(mediaType).put(Entity.entity(payload.toString(), mediaType)); + } catch (Exception e) { + log.error("Cannot do PUT {} request on device {} because can't read payload", request, pncId); + } + } else { + response = wt.request(mediaType).put(Entity.entity(null, mediaType)); + } + + if (response == null) { + return Response.Status.NO_CONTENT.getStatusCode(); + } + return response.getStatus(); + } + @Override + public ObjectNode get(UUID pncId, String request, String mediaType) { + request = discoverRootResource(pncId) + RESOURCE_PATH_PREFIX + + request; + return get(pncId, request, typeOfMediaType(mediaType)); + } + public ObjectNode get(UUID pncId, String request, MediaType mediaType) { + WebTarget wt = getWebTarget(pncId, request); + + Response s = wt.request(mediaType).get(); + + if (checkReply(s)) { + try { + String json = s.readEntity((String.class)); + return new ObjectMapper().readTree(json).deepCopy(); + } catch (Exception ex) { + log.error("ERROR: ", ex); + } + } + + return null; + } + private boolean checkReply(Response response) { + if (response != null) { + boolean statusCode = checkStatusCode(response.getStatus()); + if (!statusCode && response.hasEntity()) { + log.error("Failed request, HTTP error msg : " + response.readEntity(String.class)); + } + return statusCode; + } + log.error("Null reply from device"); + return false; + } + @Override + public boolean patch(UUID pncId, String request, ObjectNode payload, + String mediaType) { + request = discoverRootResource(pncId) + RESOURCE_PATH_PREFIX + + request; + return checkStatusCode(patch(pncId, request, payload, typeOfMediaType(mediaType))); + } + @Override + public int patch(UUID pncId, String request, ObjectNode payload, MediaType mediaType) { + + try { + log.debug("Url request {} ", getUrlString(pncId, request)); + HttpPatch httprequest = new HttpPatch(getUrlString(pncId, request)); + if (pncMap.get(pncId).username() != null) { + String pwd = pncMap.get(pncId).password() == null ? "" : COLON + pncMap.get(pncId).password(); + String userPassword = pncMap.get(pncId).username() + pwd; + String base64string = Base64.getEncoder().encodeToString(userPassword.getBytes(StandardCharsets.UTF_8)); + httprequest.addHeader(AUTHORIZATION_PROPERTY, BASIC_AUTH_PREFIX + base64string); + } + if (payload != null) { + StringEntity input = new StringEntity(payload.toString()); + input.setContentType(mediaType.toString()); + httprequest.setEntity(input); + } + CloseableHttpClient httpClient; + if (pncMap.containsKey(pncId) && pncMap.get(pncId).protocol().equals(HTTPS)) { + httpClient = getApacheSslBypassClient(); + } else { + httpClient = HttpClients.createDefault(); + } + return httpClient.execute(httprequest).getStatusLine().getStatusCode(); + } catch (IOException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) { + log.error("Cannot do PATCH {} request on device {}", request, pncId, e); + } + return Response.Status.BAD_REQUEST.getStatusCode(); + } + private CloseableHttpClient getApacheSslBypassClient() throws NoSuchAlgorithmException, + KeyManagementException, KeyStoreException { + return HttpClients.custom(). + setHostnameVerifier(new AllowAllHostnameVerifier()). + setSslcontext(new SSLContextBuilder() + .loadTrustMaterial(null, (arg0, arg1) -> true) + .build()).build(); + } + @Override + public boolean delete(UUID pncId, String request, + String mediaType) { + request = discoverRootResource(pncId) + RESOURCE_PATH_PREFIX + + request; + return checkStatusCode(delete(pncId, request, typeOfMediaType(mediaType))); + } + @Override + public int delete(UUID pncId, String request, MediaType mediaType) { + + WebTarget wt = getWebTarget(pncId, request); + + Response response = wt.request(mediaType).delete(); + + return response.getStatus(); + } + @Override + public void enableNotifications(UUID pncId, String request, + String mediaType, + RestconfNotificationEventListener listener) { + + if (isNotificationEnabled(pncId)) { + log.warn("enableNotifications: already enabled on device: {}", pncId); + return; + } + + request = discoverRootResource(pncId) + NOTIFICATION_PATH_PREFIX + + request; + + addNotificationListener(pncId, listener); + + GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType, + pncId); + runnableTable.put(pncId, runnable); + executor.execute(runnable); + } + + public void stopNotifications(UUID pncId) { + runnableTable.get(pncId).terminate(); + runnableTable.remove(pncId); + restconfNotificationListenerMap.remove(pncId); + log.debug("Stop sending notifications for device URI: " + pncId.toString()); + + } + + public class GetChunksRunnable implements Runnable { + private String request; + private String mediaType; + private UUID pnc; + + private volatile boolean running = true; + + public void terminate() { + running = false; + } + + /** + * @param request request + * @param mediaType media type + * @param pncId PNC identifier + */ + public GetChunksRunnable(String request, String mediaType, + UUID pncId) { + this.request = request; + this.mediaType = mediaType; + this.pnc = pncId; + } + + @Override + public void run() { + WebTarget wt = getWebTarget(pnc, request); + Response clientResp = wt.request(mediaType).get(); + Set<RestconfNotificationEventListener> listeners = + restconfNotificationListenerMap.get(pnc); + final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp + .readEntity(new GenericType<ChunkedInput<String>>() { + }); + + String chunk; + // Note that the read() is a blocking operation and the invoking + // thread is blocked until a new chunk comes. Jersey implementation + // of this IO operation is in a way that it does not respond to + // interrupts. + while (running) { + chunk = chunkedInput.read(); + if (chunk != null) { + if (running) { + for (RestconfNotificationEventListener listener : listeners) { + listener.handleNotificationEvent(pnc, chunk); + } + } else { + log.trace("the requesting client is no more interested " + + "to receive such notifications."); + } + } else { + log.trace("The received notification chunk is null. do not continue any more."); + break; + } + } + log.trace("out of while loop -- end of run"); + } + } + + public String discoverRootResource(UUID pncId) { + // FIXME: send a GET command to the device to discover the root resource. + // The plan to fix this is for the Ibis release when the RESTCONF server and + // the RESTCONF client both support root resource discovery. + return ROOT_RESOURCE; + } + + @Override + public void addNotificationListener(UUID pncId, + RestconfNotificationEventListener listener) { + Set<RestconfNotificationEventListener> listeners = + restconfNotificationListenerMap.get(pncId); + if (listeners == null) { + listeners = new HashSet<>(); + } + + listeners.add(listener); + + this.restconfNotificationListenerMap.put(pncId, listeners); + } + + @Override + public void removeNotificationListener(UUID pncId, + RestconfNotificationEventListener listener) { + Set<RestconfNotificationEventListener> listeners = + restconfNotificationListenerMap.get(pncId); + if (listeners != null) { + listeners.remove(listener); + } + } + + public boolean isNotificationEnabled(UUID pncId) { + return runnableTable.containsKey(pncId); + } +} diff --git a/actn-interface-tools/restconf-client/ctl/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/ctl/package-info.java b/actn-interface-tools/restconf-client/ctl/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/ctl/package-info.java new file mode 100644 index 0000000..a672161 --- /dev/null +++ b/actn-interface-tools/restconf-client/ctl/src/main/java/org/onap/integration/actninterfacetools/protocol/restconf/ctl/package-info.java @@ -0,0 +1,24 @@ +/* + * ============LICENSE_START======================================================= + * Actn Interface Tools + * ================================================================================ + * Copyright (C) 2023 Huawei Canada Limited. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +/** + * RESTCONF southbound protocol implementation. + */ +package org.onap.integration.actninterfacetools.protocol.restconf.ctl; diff --git a/actn-interface-tools/restconf-client/pom.xml b/actn-interface-tools/restconf-client/pom.xml new file mode 100644 index 0000000..f115180 --- /dev/null +++ b/actn-interface-tools/restconf-client/pom.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ ============LICENSE_START======================================================= + ~ Actn Interface Tools + ~ ================================================================================ + ~ Copyright (C) 2023 Huawei Canada Limited. + ~ ================================================================================ + ~ 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. + ~ ============LICENSE_END========================================================= + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>actn-interface-tools</artifactId> + <groupId>org.onap.integration.ietf-actn-tools</groupId> + <version>1.0-SNAPSHOT</version> + </parent> + <groupId>org.onap.integration.ietf-actn-tools</groupId> + <artifactId>restconf-client</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>pom</packaging> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + <scope>provided</scope> + <version>1.9.12</version> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <scope>provided</scope> + <version>5.0.0</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>2.8.8</version> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-client</artifactId> + <version>2.9</version> + </dependency> + <dependency> + <groupId>javax.xml.bind</groupId> + <artifactId>jaxb-api</artifactId> + <version>2.3.1</version> + </dependency> + + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient-osgi</artifactId> + <version>4.5.1</version> + </dependency> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpcore-osgi</artifactId> + <version>4.4.4</version> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + </dependency> + </dependencies> + <modules> + <module>api</module> + <module>ctl</module> + </modules> + <description>RESTCONF Client Module</description> +</project> diff --git a/actn-interface-tools/yang-utils/pom.xml b/actn-interface-tools/yang-utils/pom.xml new file mode 100644 index 0000000..6fc3a7c --- /dev/null +++ b/actn-interface-tools/yang-utils/pom.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ ============LICENSE_START======================================================= + ~ Actn Interface Tools + ~ ================================================================================ + ~ Copyright (C) 2022 Huawei Canada Limited. + ~ ================================================================================ + ~ 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. + ~ ============LICENSE_END========================================================= + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.integration.ietf-actn-tools</groupId> + <artifactId>actn-interface-tools</artifactId> + <version>1.0-SNAPSHOT</version> + </parent> + <artifactId>yang-utils</artifactId> +</project> |