Add DB update support for IPAM interaction
For assign and unassign scenario, we need to interact
with the SDNC DB in order to cache the IP Address
information used for a given ServiceInstance / VfModule.
Change-Id: Id349338216d12d2dc9efd76cd672b5cc9fdc6192
Issue-ID: CCSDK-462
Signed-off-by: Alexis de Talhouët <adetalhouet89@gmail.com>
diff --git a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/api/NetboxClient.java b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/api/NetboxClient.java
index 57d727a..e11fe8b 100644
--- a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/api/NetboxClient.java
+++ b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/api/NetboxClient.java
@@ -15,26 +15,37 @@
*/
package org.onap.ccsdk.sli.adaptors.netbox.api;
+import java.sql.SQLException;
import org.onap.ccsdk.sli.adaptors.netbox.model.IPAddress;
import org.onap.ccsdk.sli.adaptors.netbox.model.Prefix;
+/**
+ * This client is meant to interact both with the IPAM system, and the SDNC DB, in order to provide, at any time,
+ * an up to date status of the assigned resources.
+ */
public interface NetboxClient {
/**
- * Assign next available IP in prefix.
+ * Assign next available IP in prefix and store it in the SDNC database, table IPAM_IP_ASSIGNEMENT.
*
* @param prefix The prefix from which to get next available IP.
+ * @param serviceInstanceId The service instance ID uniquely identifying the service.
+ * @param vfModuleId The VF module ID uniquely identifying the VF.
* @return The IPAddress
- * @throws IpamException If something goes wrong.
+ * @throws IpamException If something goes wrong while communicating with the IPAM system.
+ * @throws SQLException If something goes wrong while communicating with the SDNC DB.
*/
- IPAddress assign(Prefix prefix) throws IpamException;
+ IPAddress assign(Prefix prefix, String serviceInstanceId, String vfModuleId) throws IpamException, SQLException;
/**
- * Free the IP.
+ * Release the IP and remove the entry in the SDNC database, table IPAM_IP_ASSIGNEMENT.
*
* @param ip The IP to release.
- * @throws IpamException If something goes wrong.
+ * @param serviceInstanceId The service instance ID uniquely identifying the service.
+ * @param vfModuleId The VF module ID uniquely identifying the VF.
+ * @throws IpamException If something goes wrong while communicating with the IPAM system.
+ * @throws SQLException If something goes wrong while communicating with the SDNC DB.
*/
- void unassign(IPAddress ip) throws IpamException;
+ void unassign(IPAddress ip, String serviceInstanceId, String vfModuleId) throws IpamException, SQLException;
}
diff --git a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxClientImpl.java b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxClientImpl.java
index 0520ad5..036ff44 100644
--- a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxClientImpl.java
+++ b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxClientImpl.java
@@ -16,12 +16,15 @@
package org.onap.ccsdk.sli.adaptors.netbox.impl;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSerializer;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.concurrent.CompletionException;
import org.apache.http.HttpResponse;
import org.onap.ccsdk.sli.adaptors.netbox.api.IpamException;
@@ -29,19 +32,29 @@
import org.onap.ccsdk.sli.adaptors.netbox.model.IPAddress;
import org.onap.ccsdk.sli.adaptors.netbox.model.Prefix;
import org.onap.ccsdk.sli.adaptors.netbox.model.Status;
+import org.onap.ccsdk.sli.core.dblib.DbLibService;
+import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin;
-public class NetboxClientImpl implements NetboxClient {
+public class NetboxClientImpl implements NetboxClient, SvcLogicJavaPlugin {
private static final String NEXT_AVAILABLE_IP_IN_PREFIX_PATH = "/api/ipam/prefixes/%s/available-ips/";
private static final String IP_ADDRESS_PATH = "/api/ipam/ip-addresses/%s/";
private static final String EMPTY_STRING = "";
private static final String ID_MISSING_MSG = "Id must be set";
+ private static final String ASSIGN_IP_SQL_STATEMENT =
+ "INSERT INTO IPAM_IP_ASSIGNEMENT (service_instance_id, vf_module_id, prefix_id, ip_address_id, ip_adress, ip_status) \n"
+ + "VALUES (?, ?, ?, ?, ?, ?)";
+ private static final String UNASSIGN_IP_SQL_STATEMENT =
+ "DELETE FROM IPAM_IP_ASSIGNEMENT WHERE service_instance_id = ? AND vf_module_id = ? AND ip_address_id = ?";
+
private final NetboxHttpClient client;
private final Gson gson;
+ private final DbLibService dbLibService;
- public NetboxClientImpl(final NetboxHttpClient client) {
+ public NetboxClientImpl(final NetboxHttpClient client, final DbLibService dbLibService) {
this.client = client;
+ this.dbLibService = dbLibService;
final JsonSerializer<Status> vlanStatusDeserializer = (val, type, context) -> val.toJson();
gson = new GsonBuilder()
.registerTypeAdapter(Status.class, vlanStatusDeserializer)
@@ -49,31 +62,54 @@
}
@Override
- public IPAddress assign(final Prefix prefix) throws IpamException {
+ public IPAddress assign(final Prefix prefix, final String serviceInstanceId, final String vfModuleId)
+ throws IpamException, SQLException {
+
checkArgument(prefix.getId() != null);
try {
- return client.post(String.format(NEXT_AVAILABLE_IP_IN_PREFIX_PATH, prefix.getId()), EMPTY_STRING)
+ IPAddress ipAddress = client
+ .post(String.format(NEXT_AVAILABLE_IP_IN_PREFIX_PATH, prefix.getId()), EMPTY_STRING)
.thenApply(this::getIpAddress)
.toCompletableFuture()
.join();
+
+ ArrayList<String> args = Lists.newArrayList(serviceInstanceId,
+ vfModuleId,
+ String.valueOf(prefix.getId()),
+ String.valueOf(ipAddress.getId()),
+ ipAddress.getAddress(),
+ ipAddress.getStatus().getLabel());
+ dbLibService.writeData(ASSIGN_IP_SQL_STATEMENT, args, null);
+
+ return ipAddress;
} catch (CompletionException e) {
- // Unwrap the ComplettionException and wrap in IpamException
+ // Unwrap the CompletionException and wrap in IpamException
throw new IpamException("Fail to assign IP for Prefix(id= " + prefix.getId() + "). " + e.getMessage(),
e.getCause());
}
}
@Override
- public void unassign(final IPAddress ipAddress) throws IpamException {
+ public void unassign(final IPAddress ipAddress, final String serviceInstanceId, final String vfModuleId)
+ throws IpamException, SQLException {
+
checkArgument(ipAddress.getId() != null);
try {
client.delete(String.format(IP_ADDRESS_PATH, ipAddress.getId()))
.thenAccept(this::checkResult)
.toCompletableFuture()
.join();
+
+ ArrayList<String> args = Lists.newArrayList(
+ serviceInstanceId,
+ vfModuleId,
+ String.valueOf(ipAddress.getId()));
+ dbLibService.writeData(UNASSIGN_IP_SQL_STATEMENT, args, null);
+
} catch (CompletionException e) {
- // Unwrap the ComplettionException and wrap in IpamException
- throw new IpamException("Fail to unassign IP for IPAddress(id= " + ipAddress.getId() + "). " + e.getMessage(),
+ // Unwrap the CompletionException and wrap in IpamException
+ throw new IpamException(
+ "Fail to unassign IP for IPAddress(id= " + ipAddress.getId() + "). " + e.getMessage(),
e.getCause());
}
}
diff --git a/netbox-client/provider/src/main/resources/org/opendaylight/blueprint/netbox-client.xml b/netbox-client/provider/src/main/resources/org/opendaylight/blueprint/netbox-client.xml
index cf8a1af..950fd97 100644
--- a/netbox-client/provider/src/main/resources/org/opendaylight/blueprint/netbox-client.xml
+++ b/netbox-client/provider/src/main/resources/org/opendaylight/blueprint/netbox-client.xml
@@ -17,6 +17,9 @@
<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="dbLibService"
+ interface="org.onap.ccsdk.sli.core.dblib.DbLibService"/>
+
<bean id="netboxProperty" class="org.onap.ccsdk.sli.adaptors.netbox.property.NetboxProperties"/>
<bean id="httpClient" class="org.onap.ccsdk.sli.adaptors.netbox.impl.NetboxHttpClient" init-method="init"
destroy-method="close">
@@ -25,6 +28,7 @@
<bean id="netboxClient" class="org.onap.ccsdk.sli.adaptors.netbox.impl.NetboxClientImpl">
<argument ref="httpClient"/>
+ <argument ref="dbLibService"/>
</bean>
<service ref="netboxClient"
diff --git a/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxClientImplTest.java b/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxClientImplTest.java
index 19b178c..f1eda73 100644
--- a/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxClientImplTest.java
+++ b/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxClientImplTest.java
@@ -30,6 +30,9 @@
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpHeaders.AUTHORIZATION;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -41,6 +44,9 @@
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.UUID;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
@@ -50,11 +56,14 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.onap.ccsdk.sli.adaptors.netbox.api.IpamException;
import org.onap.ccsdk.sli.adaptors.netbox.model.IPAddress;
import org.onap.ccsdk.sli.adaptors.netbox.model.Prefix;
import org.onap.ccsdk.sli.adaptors.netbox.model.Status.Values;
+import org.onap.ccsdk.sli.core.dblib.DbLibService;
@RunWith(MockitoJUnitRunner.class)
public class NetboxClientImplTest {
@@ -64,7 +73,12 @@
@Rule
public WireMockRule wm = new WireMockRule(wireMockConfig().dynamicPort());
+ @Mock
+ private DbLibService dbLib;
+
private String token = "token";
+ private String serviceInstanceId = UUID.randomUUID().toString();
+ private String vfModuleId = UUID.randomUUID().toString();
private NetboxHttpClient httpClient;
private NetboxClientImpl netboxClient;
@@ -76,7 +90,7 @@
httpClient = new NetboxHttpClient(baseUrl, token);
httpClient.init();
- netboxClient = new NetboxClientImpl(httpClient);
+ netboxClient = new NetboxClientImpl(httpClient, dbLib);
wm.addMockServiceRequestListener(
(request, response) -> {
@@ -93,11 +107,11 @@
}
@Test
- public void nextAvailableIpInPrefixTestNoId() {
+ public void nextAvailableIpInPrefixTestNoId() throws SQLException {
Prefix prefix = mock(Prefix.class);
doReturn(null).when(prefix).getId();
try {
- netboxClient.assign(prefix);
+ netboxClient.assign(prefix, serviceInstanceId, vfModuleId);
} catch (IpamException e) {
Assert.assertEquals("Id must be set", e.getMessage());
return;
@@ -106,7 +120,7 @@
}
@Test
- public void nextAvailableIpInPrefixTest() throws IOException, IpamException {
+ public void nextAvailableIpInPrefixTest() throws IOException, IpamException, SQLException {
Integer id = 3;
Prefix prefix = mock(Prefix.class);
doReturn(id).when(prefix).getId();
@@ -117,16 +131,17 @@
String expectedUrl = "/api/ipam/prefixes/" + id + "/available-ips/";
givenThat(post(urlEqualTo(expectedUrl)).willReturn(created().withBody(response)));
- netboxClient.assign(prefix);
+ netboxClient.assign(prefix, serviceInstanceId, vfModuleId);
verify(postRequestedFor(urlEqualTo(expectedUrl))
.withHeader(ACCEPT, equalTo(APPLICATION_JSON))
.withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON))
.withHeader(AUTHORIZATION, equalTo("Token " + token)));
+ Mockito.verify(dbLib).writeData(anyString(), any(ArrayList.class), eq((null)));
}
@Test
- public void deleteIpTestError500() {
+ public void deleteIpTestError500() throws SQLException {
Integer id = 3;
IPAddress ipAddress = mock(IPAddress.class);
doReturn(id).when(ipAddress).getId();
@@ -134,7 +149,7 @@
String expectedUrl = "/api/ipam/ip-addresses/" + id + "/";
givenThat(delete(urlEqualTo(expectedUrl)).willReturn(serverError()));
try {
- netboxClient.unassign(ipAddress);
+ netboxClient.unassign(ipAddress, serviceInstanceId, vfModuleId);
} catch (IpamException e) {
Assert.assertEquals(IllegalStateException.class, e.getCause().getClass());
Assert.assertTrue(e.getMessage().contains(
@@ -145,18 +160,19 @@
}
@Test
- public void deleteIpTest() throws IpamException {
+ public void deleteIpTest() throws IpamException, SQLException {
Integer id = 3;
IPAddress ipAddress = mock(IPAddress.class);
doReturn(id).when(ipAddress).getId();
String expectedUrl = "/api/ipam/ip-addresses/" + id + "/";
givenThat(delete(urlEqualTo(expectedUrl)).willReturn(ok()));
- netboxClient.unassign(ipAddress);
+ netboxClient.unassign(ipAddress, serviceInstanceId, vfModuleId);
verify(deleteRequestedFor(urlEqualTo(expectedUrl))
.withHeader(ACCEPT, equalTo(APPLICATION_JSON))
.withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON))
.withHeader(AUTHORIZATION, equalTo("Token " + token)));
+ Mockito.verify(dbLib).writeData(anyString(), any(ArrayList.class), eq((null)));
}