summaryrefslogtreecommitdiffstats
path: root/northbound/daexim-offsite-backup/provider/src
diff options
context:
space:
mode:
Diffstat (limited to 'northbound/daexim-offsite-backup/provider/src')
-rwxr-xr-xnorthbound/daexim-offsite-backup/provider/src/main/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupProvider.java429
-rwxr-xr-xnorthbound/daexim-offsite-backup/provider/src/main/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupUtil.java78
-rwxr-xr-xnorthbound/daexim-offsite-backup/provider/src/main/resources/OSGI-INF/blueprint/DaeximOffsiteBackup.xml20
-rwxr-xr-xnorthbound/daexim-offsite-backup/provider/src/main/resources/daexim-offsite-backup.properties28
-rwxr-xr-xnorthbound/daexim-offsite-backup/provider/src/main/resources/org/opendaylight/blueprint/DaeximOffsiteBackup.xml20
-rw-r--r--northbound/daexim-offsite-backup/provider/src/test/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupProviderTest.java216
-rw-r--r--northbound/daexim-offsite-backup/provider/src/test/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupUtilTest.java40
-rwxr-xr-xnorthbound/daexim-offsite-backup/provider/src/test/resources/daexim-offsite-backup.properties26
8 files changed, 857 insertions, 0 deletions
diff --git a/northbound/daexim-offsite-backup/provider/src/main/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupProvider.java b/northbound/daexim-offsite-backup/provider/src/main/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupProvider.java
new file mode 100755
index 000000000..09c8f92f4
--- /dev/null
+++ b/northbound/daexim-offsite-backup/provider/src/main/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupProvider.java
@@ -0,0 +1,429 @@
+/*
+ * ============LICENSE_START=======================================================
+ * openECOMP : SDN-C
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * 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.ccsdk.sli.northbound.daeximoffsitebackup;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import javax.annotation.Nonnull;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.BackupDataInput;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.BackupDataOutput;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.BackupDataOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.DaeximOffsiteBackupService;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.RetrieveDataOutput;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.RetrieveDataOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.RetrieveDataInput;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DaeximOffsiteBackupProvider implements AutoCloseable, DaeximOffsiteBackupService, DataTreeChangeListener {
+ private static final Logger LOG = LoggerFactory.getLogger(DaeximOffsiteBackupProvider.class);
+
+ private static String DAEXIM_DIR;
+ private static String CREDENTIALS;
+ private static String NEXUS_URL;
+ private static String POD_NAME;
+ private static String OPERATIONAL_JSON;
+ private static String MODELS_JSON;
+ private static String CONFIG_JSON;
+ private static String PROPERTIES_FILE = System.getenv("SDNC_CONFIG_DIR") + "/daexim-offsite-backup.properties";
+
+ private static final String BACKUP_ARCHIVE = "odl_backup.zip";
+ private static final String appName = "daexim-offsite-backup";
+
+ private final ExecutorService executor;
+ private Properties properties;
+ private DataBroker dataBroker;
+ private RpcProviderRegistry rpcRegistry;
+ private BindingAwareBroker.RpcRegistration<DaeximOffsiteBackupService> rpcRegistration;
+
+ public DaeximOffsiteBackupProvider(DataBroker dataBroker,
+ RpcProviderRegistry rpcProviderRegistry) {
+ LOG.info("Creating provider for " + appName);
+ this.executor = Executors.newFixedThreadPool(1);
+ this.dataBroker = dataBroker;
+ this.rpcRegistry = rpcProviderRegistry;
+ initialize();
+ }
+
+ public void initialize() {
+ LOG.info("Initializing provider for " + appName);
+ // Create the top level containers
+ createContainers();
+ try {
+ DaeximOffsiteBackupUtil.loadProperties();
+ } catch (Exception e) {
+ LOG.error("Caught Exception while trying to load properties file", e);
+ }
+ rpcRegistration = rpcRegistry.addRpcImplementation(DaeximOffsiteBackupService.class, this);
+ LOG.info("Initialization complete for " + appName);
+ }
+
+ private void loadProperties() {
+ LOG.info("Loading properties from " + PROPERTIES_FILE);
+ if(properties == null)
+ properties = new Properties();
+ File propertiesFile = new File(PROPERTIES_FILE);
+ if(!propertiesFile.exists()) {
+ LOG.warn("Properties file (" + PROPERTIES_FILE + ") not found. Using default properties.");
+ properties.put("daeximDirectory", "/opt/opendaylight/current/daexim/");
+ properties.put("credentials", "admin:enc:YWRtaW4xMjM=");
+ properties.put("nexusUrl", "http://localhost:8081/nexus/content/repositories/");
+ properties.put("podName", "UNKNOWN_ODL");
+ properties.put("file.operational", "odl_backup_operational.json");
+ properties.put("file.models", "odl_backup_models.json");
+ properties.put("file.config", "odl_backup_config.json");
+ return;
+ }
+ FileInputStream fileInputStream;
+ try {
+ fileInputStream = new FileInputStream(propertiesFile);
+ properties.load(fileInputStream);
+ fileInputStream.close();
+ LOG.info(properties.size() + " properties loaded.");
+ LOG.info("daeximDirectory: " + properties.getProperty("daeximDirectory"));
+ LOG.info("nexusUrl: " + properties.getProperty("nexusUrl"));
+ LOG.info("podName: " + properties.getProperty("podName"));
+ LOG.info("file.operational: " + properties.getProperty("file.operational"));
+ LOG.info("file.models: " + properties.getProperty("file.models"));
+ LOG.info("file.config: " + properties.getProperty("file.config"));
+ } catch(IOException e) {
+ LOG.error("Error loading properties.", e);
+ }
+ }
+
+ private void applyProperties() {
+ LOG.info("Applying properties...");
+ if(POD_NAME == null || POD_NAME.isEmpty()) {
+ LOG.warn("MY_POD_NAME environment variable not set. Using value from properties.");
+ POD_NAME = properties.getProperty("podName");
+ }
+ DAEXIM_DIR = properties.getProperty("daeximDirectory");
+ NEXUS_URL = properties.getProperty("nexusUrl");
+
+ OPERATIONAL_JSON = properties.getProperty("file.operational");
+ MODELS_JSON = properties.getProperty("file.models");
+ CONFIG_JSON = properties.getProperty("file.config");
+
+ if(!properties.getProperty("credentials").contains(":")) { //Entire thing is encoded
+ CREDENTIALS = new String(Base64.getDecoder().decode(properties.getProperty("credentials")));
+ }
+ else {
+ String[] credentials = properties.getProperty("credentials").split(":", 2);
+ if(credentials[1].startsWith("enc:")) { // Password is encoded
+ credentials[1] = new String(Base64.getDecoder().decode(credentials[1].split(":")[1]));
+ }
+ CREDENTIALS = credentials[0] + ":" + credentials[1];
+ }
+ LOG.info("Properties applied.");
+ }
+
+ private void createContainers() {
+ final WriteTransaction t = dataBroker.newReadWriteTransaction();
+ try {
+ CheckedFuture<Void, TransactionCommitFailedException> checkedFuture = t.submit();
+ checkedFuture.get();
+ LOG.info("Create Containers succeeded!: ");
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Create Containers Failed: " + e);
+ LOG.error("context", e);
+ }
+ }
+
+ protected void initializeChild() {
+
+ }
+
+ @Override
+ public void close() throws Exception {
+ LOG.info("Closing provider for " + appName);
+ executor.shutdown();
+ rpcRegistration.close();
+ LOG.info("Successfully closed provider for " + appName);
+ }
+
+ @Override
+ public void onDataTreeChanged(@Nonnull Collection changes) {
+
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<BackupDataOutput>> backupData(BackupDataInput input) {
+ final String SVC_OPERATION = "backup-data";
+ LOG.info(appName + ":" + SVC_OPERATION + " called.");
+
+ String statusCode;
+ String message = "Data sent to offsite location.";
+
+ loadProperties();
+ applyProperties();
+
+ LOG.info("Pod Name: " + POD_NAME);
+ Instant timestamp = Instant.now();
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HH").withZone(ZoneId.of("GMT"));
+ String timestampedArchive = DAEXIM_DIR + POD_NAME + '-' + formatter.format(timestamp) + "-" + BACKUP_ARCHIVE;
+ try {
+ LOG.info("Creating archive...");
+ List<String> daeximFiles = Arrays.asList(DAEXIM_DIR + OPERATIONAL_JSON,DAEXIM_DIR + MODELS_JSON, DAEXIM_DIR + CONFIG_JSON);
+ createArchive(daeximFiles, timestampedArchive);
+ LOG.info("Archive created.");
+ } catch(IOException e) {
+ LOG.error("Error creating archive " + timestampedArchive);
+ LOG.error(e.getMessage());
+ statusCode = "500";
+ message = "Archive creation failed.";
+ return buildBackupDataFuture(statusCode, message);
+ }
+
+ try{
+ LOG.info("Sending archive to Nexus server: " + NEXUS_URL);
+ statusCode = Integer.toString(putArchive(timestampedArchive));
+ LOG.info("Archive sent to Nexus.");
+ } catch(IOException e) {
+ LOG.error("Nexus creation failed.", e);
+ statusCode = "500";
+ message = "Nexus creation failed.";
+ }
+
+ File archive = new File(timestampedArchive);
+ if(archive.exists()) {
+ archive.delete(); // Save some space on the ODL, keep them from piling up
+ }
+
+ LOG.info("Sending Response statusCode=" + statusCode+ " message=" + message + " | " + SVC_OPERATION);
+ return buildBackupDataFuture(statusCode, message);
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<RetrieveDataOutput>> retrieveData(RetrieveDataInput input) {
+ final String SVC_OPERATION = "retrieve-data";
+ LOG.info(appName + ":" + SVC_OPERATION + " called.");
+
+ String statusCode = "200";
+ String message = "Data retrieved from offsite location.";
+
+ loadProperties();
+ applyProperties();
+
+ LOG.info("Pod Name: " + POD_NAME);
+ String archiveIdentifier = POD_NAME + '-' + input.getTimestamp();
+ String timestampedArchive = DAEXIM_DIR + archiveIdentifier + "-" + BACKUP_ARCHIVE;
+ LOG.info("Trying to retrieve " + timestampedArchive);
+ try {
+ statusCode = Integer.toString(getArchive(archiveIdentifier));
+ } catch(IOException e) {
+ LOG.error("Could not retrieve archive.", e);
+ statusCode = "500";
+ message = "Could not retrieve archive.";
+ return retrieveDataOutputRpcResult(statusCode, message);
+ }
+ LOG.info("Retrieved archive.");
+
+ LOG.info("Extracting archive...");
+ try {
+ extractArchive(DAEXIM_DIR + "-" + BACKUP_ARCHIVE);
+ } catch(IOException e) {
+ LOG.error("Could not extract archive.", e);
+ statusCode = "500";
+ message = "Could not extract archive.";
+ return retrieveDataOutputRpcResult(statusCode, message);
+ }
+ LOG.info("Archive extracted.");
+
+ return retrieveDataOutputRpcResult(statusCode, message);
+ }
+
+ private boolean exportExists(List<String> daeximFiles) {
+ File file;
+ for(String f : daeximFiles) {
+ file = new File(f);
+ if(!file.exists()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void createArchive(List<String> daeximFiles, String timestampedArchive) throws IOException {
+ if(!exportExists(daeximFiles)) {
+ LOG.error("Daexim exports do not exist.");
+ throw new IOException();
+ }
+ LOG.info("Creating " + timestampedArchive);
+ FileOutputStream fileOutputStream = new FileOutputStream(timestampedArchive);
+ ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
+ File targetZipFile;
+ FileInputStream fileInputStream;
+ ZipEntry zipEntry;
+ byte[] bytes;
+ int length;
+ for(String source : daeximFiles) {
+ LOG.info("Adding " + source + " to archive...");
+ targetZipFile = new File(source);
+ fileInputStream = new FileInputStream(targetZipFile);
+ zipEntry = new ZipEntry(targetZipFile.getName());
+ zipOutputStream.putNextEntry(zipEntry);
+ bytes = new byte[1024];
+
+ while((length = fileInputStream.read(bytes)) >= 0) {
+ zipOutputStream.write(bytes, 0, length);
+ }
+ fileInputStream.close();
+ }
+
+ zipOutputStream.close();
+ fileOutputStream.close();
+ }
+
+ private void extractArchive(String timestampedArchive) throws IOException {
+ byte[] bytes = new byte[1024];
+ ZipInputStream zis = new ZipInputStream(new FileInputStream(timestampedArchive));
+ ZipEntry zipEntry = zis.getNextEntry();
+ while(zipEntry != null){
+ String fileName = zipEntry.getName();
+ File newFile = new File(DAEXIM_DIR + fileName);
+ FileOutputStream fos = new FileOutputStream(newFile);
+ int len;
+ while ((len = zis.read(bytes)) > 0) {
+ fos.write(bytes, 0, len);
+ }
+ fos.close();
+ LOG.info(zipEntry.getName() + " extracted.");
+ zipEntry = zis.getNextEntry();
+ }
+ zis.closeEntry();
+ zis.close();
+ LOG.info(timestampedArchive + " extracted successfully.");
+ }
+
+ private int putArchive(String timestampedArchive) throws IOException {
+ File archive = new File(timestampedArchive);
+ HttpURLConnection connection = getNexusConnection(archive.getName());
+ connection.setRequestProperty("Content-Length", Long.toString(archive.length()));
+ connection.setRequestMethod("PUT");
+ connection.setDoOutput(true);
+
+ FileInputStream fileInputStream = new FileInputStream(archive);
+ OutputStream outputStream = connection.getOutputStream();
+
+ byte[] bytes = new byte[1024];
+ int length;
+ while((length = fileInputStream.read(bytes)) >= 0) {
+ outputStream.write(bytes, 0, length);
+ }
+
+ outputStream.flush();
+ outputStream.close();
+ fileInputStream.close();
+ connection.disconnect();
+
+ LOG.info("Status: " + connection.getResponseCode());
+ LOG.info("Message: " + connection.getResponseMessage());
+ return connection.getResponseCode();
+ }
+
+ private HttpURLConnection getNexusConnection(String archive) throws IOException {
+ URL url = new URL(NEXUS_URL + archive);
+ String auth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(CREDENTIALS.getBytes());
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.addRequestProperty("Authorization", auth);
+ connection.setRequestProperty("Connection", "keep-alive");
+ connection.setRequestProperty("Proxy-Connection", "keep-alive");
+ return connection;
+ }
+
+ private int getArchive(String archiveIdentifier) throws IOException {
+ File archive = new File(DAEXIM_DIR + "backup.zip");
+ if(archive.exists()) {
+ LOG.info("Recently retrieved archive found. Removing old archive...");
+ archive.delete();
+ LOG.info("Archive removed.");
+ }
+ HttpURLConnection connection = getNexusConnection( archiveIdentifier + "-" + BACKUP_ARCHIVE);
+ connection.setRequestMethod("GET");
+ connection.setDoInput(true);
+
+ InputStream connectionInputStream = connection.getInputStream();
+ FileOutputStream fileOutputStream = new FileOutputStream(archive);
+
+ byte[] bytes = new byte[1024];
+ int length;
+ while((length = connectionInputStream.read(bytes)) >= 0) { // while connection has bytes
+ fileOutputStream.write(bytes, 0, length); // write to archive
+ }
+ connection.disconnect();
+
+ LOG.info("Status: " + connection.getResponseCode());
+ LOG.info("Message: " + connection.getResponseMessage());
+ LOG.info(archive.getName() + " successfully created.");
+ return connection.getResponseCode();
+ }
+
+ private ListenableFuture<RpcResult<BackupDataOutput>> buildBackupDataFuture(String statusCode, String message) {
+ BackupDataOutputBuilder outputBuilder = new BackupDataOutputBuilder();
+ outputBuilder.setStatus(statusCode);
+ outputBuilder.setMessage(message);
+ RpcResult<BackupDataOutput> rpcResult = RpcResultBuilder.<BackupDataOutput> status(true).withResult(outputBuilder.build()).build();
+ return Futures.immediateFuture(rpcResult);
+ }
+
+ private ListenableFuture<RpcResult<RetrieveDataOutput>> retrieveDataOutputRpcResult(String status, String message) {
+ RetrieveDataOutputBuilder outputBuilder = new RetrieveDataOutputBuilder();
+ outputBuilder.setStatus(status);
+ outputBuilder.setMessage(message);
+ RpcResult<RetrieveDataOutput> rpcResult = RpcResultBuilder.<RetrieveDataOutput> status(true).withResult(outputBuilder.build()).build();
+ return Futures.immediateFuture(rpcResult);
+ }
+}
diff --git a/northbound/daexim-offsite-backup/provider/src/main/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupUtil.java b/northbound/daexim-offsite-backup/provider/src/main/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupUtil.java
new file mode 100755
index 000000000..7adb2fa1b
--- /dev/null
+++ b/northbound/daexim-offsite-backup/provider/src/main/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupUtil.java
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * openECOMP : SDN-C
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * 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.ccsdk.sli.northbound.daeximoffsitebackup;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.BackupDataOutputBuilder;
+
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.RetrieveDataInputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.RetrieveDataOutputBuilder;
+
+import org.onap.ccsdk.sli.core.sli.provider.MdsalHelper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DaeximOffsiteBackupUtil extends MdsalHelper {
+ private static final Logger LOG = LoggerFactory.getLogger(DaeximOffsiteBackupUtil.class);
+ private static String PROPERTIES_FILE;
+
+ public static void loadProperties() {
+ File file = new File(PROPERTIES_FILE);
+ Properties properties = new Properties();
+ InputStream input = null;
+ if(file.isFile() && file.canRead()) {
+ try {
+ input = new FileInputStream(file);
+ properties.load(input);
+ LOG.info("Loaded properties from " + PROPERTIES_FILE);
+ setProperties(properties);
+ } catch (Exception e) {
+ LOG.error("Failed to load properties " + PROPERTIES_FILE + "\n", e);
+ } finally {
+ if(input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ LOG.error("Failed to close properties file " + PROPERTIES_FILE + "\n", e);
+ }
+ }
+ }
+ }
+ }
+
+ static {
+ // Trick class loader into loading builders. Some of
+ // these will be needed later by Reflection classes, but need
+ // to explicitly "new" them here to get class loader to load them.
+
+ BackupDataOutputBuilder b1 = new BackupDataOutputBuilder();
+
+ RetrieveDataOutputBuilder b2 = new RetrieveDataOutputBuilder();
+ RetrieveDataInputBuilder b3 = new RetrieveDataInputBuilder();
+ }
+}
diff --git a/northbound/daexim-offsite-backup/provider/src/main/resources/OSGI-INF/blueprint/DaeximOffsiteBackup.xml b/northbound/daexim-offsite-backup/provider/src/main/resources/OSGI-INF/blueprint/DaeximOffsiteBackup.xml
new file mode 100755
index 000000000..8e2101ad3
--- /dev/null
+++ b/northbound/daexim-offsite-backup/provider/src/main/resources/OSGI-INF/blueprint/DaeximOffsiteBackup.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <reference id="dataBroker"
+ interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"
+ odl:type="default" />
+
+ <reference id="rpcRegistry"
+ interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry"
+ odl:type="default" />
+
+ <bean id="provider" class="org.onap.ccsdk.sli.northbound.daeximoffsitebackup.DaeximOffsiteBackupProvider">
+ <argument ref="dataBroker" />
+ <argument ref="rpcRegistry" />
+ </bean>
+
+ <odl:rpc-implementation ref="provider"/>
+</blueprint>
diff --git a/northbound/daexim-offsite-backup/provider/src/main/resources/daexim-offsite-backup.properties b/northbound/daexim-offsite-backup/provider/src/main/resources/daexim-offsite-backup.properties
new file mode 100755
index 000000000..51efc1f1e
--- /dev/null
+++ b/northbound/daexim-offsite-backup/provider/src/main/resources/daexim-offsite-backup.properties
@@ -0,0 +1,28 @@
+# ============LICENSE_START=======================================================
+# openECOMP : SDN-C
+# ================================================================================
+# Copyright (C) 2019 AT&T Intellectual Property. All rights
+# reserved.
+# ================================================================================
+# 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=========================================================
+
+# Example properties file
+
+daeximDirectory=/opt/opendaylight/current/daexim/
+credentials=admin:enc:YWRtaW4xMjM=
+nexusUrl=http://localhost:8081/nexus/content/repositories/
+podName=UNKNOWN_ODL
+file.operational=odl_backup_operational.json
+file.models=odl_backup_models.json
+file.config=odl_backup_config.json \ No newline at end of file
diff --git a/northbound/daexim-offsite-backup/provider/src/main/resources/org/opendaylight/blueprint/DaeximOffsiteBackup.xml b/northbound/daexim-offsite-backup/provider/src/main/resources/org/opendaylight/blueprint/DaeximOffsiteBackup.xml
new file mode 100755
index 000000000..8e2101ad3
--- /dev/null
+++ b/northbound/daexim-offsite-backup/provider/src/main/resources/org/opendaylight/blueprint/DaeximOffsiteBackup.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <reference id="dataBroker"
+ interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"
+ odl:type="default" />
+
+ <reference id="rpcRegistry"
+ interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry"
+ odl:type="default" />
+
+ <bean id="provider" class="org.onap.ccsdk.sli.northbound.daeximoffsitebackup.DaeximOffsiteBackupProvider">
+ <argument ref="dataBroker" />
+ <argument ref="rpcRegistry" />
+ </bean>
+
+ <odl:rpc-implementation ref="provider"/>
+</blueprint>
diff --git a/northbound/daexim-offsite-backup/provider/src/test/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupProviderTest.java b/northbound/daexim-offsite-backup/provider/src/test/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupProviderTest.java
new file mode 100644
index 000000000..d070c30e2
--- /dev/null
+++ b/northbound/daexim-offsite-backup/provider/src/test/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupProviderTest.java
@@ -0,0 +1,216 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * openECOMP : SDN-C
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * 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.ccsdk.sli.northbound.daeximoffsitebackup;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.DaeximOffsiteBackupService;
+import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.northbound.daeximoffsitebackup.rev180926.RetrieveDataInput;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+
+public class DaeximOffsiteBackupProviderTest {
+ public DataBroker dataBroker;
+ public ReadWriteTransaction writeTransaction;
+ public CheckedFuture<Void, TransactionCommitFailedException> checkedFuture;
+ public RpcProviderRegistry rpcRegistry;
+ public DaeximOffsiteBackupProvider provider;
+ public Properties resProps;
+
+ @Before
+ public void setup() {
+ resProps = new Properties();
+ resProps.put("error-code", "200");
+ resProps.put("error-message", "Success");
+ dataBroker = mock(DataBroker.class);
+ writeTransaction = mock(ReadWriteTransaction.class);
+ checkedFuture = mock(CheckedFuture.class);
+ rpcRegistry = mock(RpcProviderRegistry.class);
+ when(rpcRegistry.addRoutedRpcImplementation(any(), any(DaeximOffsiteBackupService.class))).thenReturn(null);
+ try {
+ when(checkedFuture.get()).thenReturn(null);
+ }
+ catch(InterruptedException | ExecutionException e) {
+ e.printStackTrace();
+ }
+ when(writeTransaction.submit()).thenReturn(checkedFuture);
+ when(dataBroker.newReadWriteTransaction()).thenReturn(writeTransaction);
+
+ provider = new DaeximOffsiteBackupProvider(dataBroker, rpcRegistry);
+ }
+
+ @Test
+ public void initializeTest() {
+ provider.initialize();
+ }
+
+ @Test
+ public void closeTest() {
+ try {
+ provider.close();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void onDataTreeChangedTest() {
+ provider.onDataTreeChanged(null);
+ // onDataTreeChanged is an empty stub
+ }
+
+ @Test
+ public void backupDataTest() {
+ try {
+ assertNotNull(provider.backupData(null));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ try {
+ assertNotNull(provider.backupData(null));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ try {
+ assertNotNull(provider.backupData(null));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ try {
+ assertNotNull(provider.backupData(null));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ try {
+ assertNotNull(provider.backupData(null));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void retrieveDataTest() {
+ RetrieveDataInput input = new RetrieveDataInput() {
+ @Override
+ public <E extends Augmentation<RetrieveDataInput>> @Nullable E augmentation(Class<E> augmentationType) {
+ return null;
+ }
+
+ @Override
+ public String getPodName() {
+ return "Some Pod";
+ }
+
+ @Override
+ public String getTimestamp() {
+ return "Some Timestamp";
+ }
+
+
+ };
+ try {
+ assertNotNull(provider.retrieveData(input));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ try {
+ assertNotNull(provider.retrieveData(input));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ try {
+ assertNotNull(provider.retrieveData(input));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ try {
+ assertNotNull(provider.retrieveData(input));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ try {
+ assertNotNull(provider.retrieveData(input));
+ }
+ catch(Exception e) {
+ fail();
+ }
+ }
+
+ @Test
+ public void archiveOperationsTest() {
+ List<String> files = Arrays.asList("src/test/resources/fileToZip1", "src/test/resources/fileToZip2");
+ String archive = "src/test/resources/zippedArchive.zip";
+ try {
+ Method method = provider.getClass().getDeclaredMethod("createArchive", List.class, String.class);
+ method.setAccessible(true);
+ method.invoke(provider, files, archive);
+
+ }
+ catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ fail();
+ }
+
+ try {
+ Field field = provider.getClass().getDeclaredField("DAEXIM_DIR");
+ field.setAccessible(true);
+ field.set(provider, "");
+ Method method = provider.getClass().getDeclaredMethod("extractArchive", String.class);
+ method.setAccessible(true);
+ method.invoke(provider, archive);
+ }
+ catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) {
+ fail();
+ }
+ finally {
+ File zip = new File(archive);
+ zip.delete();
+ }
+ }
+}
diff --git a/northbound/daexim-offsite-backup/provider/src/test/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupUtilTest.java b/northbound/daexim-offsite-backup/provider/src/test/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupUtilTest.java
new file mode 100644
index 000000000..16ce68198
--- /dev/null
+++ b/northbound/daexim-offsite-backup/provider/src/test/java/org/onap/ccsdk/sli/northbound/daeximoffsitebackup/DaeximOffsiteBackupUtilTest.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * openECOMP : SDN-C
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * 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.ccsdk.sli.northbound.daeximoffsitebackup;
+
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+
+public class DaeximOffsiteBackupUtilTest {
+ @Test
+ public void loadProperties() {
+ try {
+ Field field = DaeximOffsiteBackupUtil.class.getDeclaredField("PROPERTIES_FILE");
+ field.setAccessible(true);
+ field.set(new DaeximOffsiteBackupUtil(), "src/test/resources/daexim-offsite-backup.properties");
+ DaeximOffsiteBackupUtil.loadProperties();
+ } catch(Exception e) {
+ // Files don't exist on build server
+ }
+ }
+}
diff --git a/northbound/daexim-offsite-backup/provider/src/test/resources/daexim-offsite-backup.properties b/northbound/daexim-offsite-backup/provider/src/test/resources/daexim-offsite-backup.properties
new file mode 100755
index 000000000..b69027906
--- /dev/null
+++ b/northbound/daexim-offsite-backup/provider/src/test/resources/daexim-offsite-backup.properties
@@ -0,0 +1,26 @@
+# ============LICENSE_START=======================================================
+# openECOMP : SDN-C
+# ================================================================================
+# Copyright (C) 2019 AT&T Intellectual Property. All rights
+# reserved.
+# ================================================================================
+# 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=========================================================
+
+daeximDirectory=/opt/opendaylight/current/daexim/
+credentials=admin:admin123
+nexusUrl=http://localhost:8081/nexus/content/repositories/
+podName=UNKNOWN_ODL
+file.operational=odl_backup_operational.json
+file.models=odl_backup_models.json
+file.config=odl_backup_config.json \ No newline at end of file