From 00ea51d999daaa26ea62b6fd0a3a1c911bc26cba Mon Sep 17 00:00:00 2001 From: Alexis de Talhouët Date: Fri, 24 Aug 2018 08:37:11 -0400 Subject: Add SvcLogicContext interaction with netbox-client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And return proper QueryStatus as exepected by the DG. Change-Id: I6af3971a2c6a8b6eda949f7b63cd30fe361dfbc4 Issue-ID: CCSDK-462 Signed-off-by: Alexis de Talhouët --- .../sli/adaptors/netbox/api/NetboxClient.java | 44 ++-- .../sli/adaptors/netbox/impl/NetboxClientImpl.java | 194 ++++++++++------- .../sli/adaptors/netbox/impl/NetboxHttpClient.java | 87 ++------ .../ccsdk/sli/adaptors/netbox/model/IPAddress.java | 22 +- .../ccsdk/sli/adaptors/netbox/model/IPStatus.java | 5 + .../ccsdk/sli/adaptors/netbox/model/Status.java | 77 ------- .../org/opendaylight/blueprint/netbox-client.xml | 3 +- .../adaptors/netbox/impl/NetboxClientImplTest.java | 237 ++++++++++++++------- .../adaptors/netbox/impl/NetboxHttpClientTest.java | 46 +--- .../netbox/property/NetboxPropertiesTest.java | 1 - .../provider/src/test/resources/badResponse.json | 4 + 11 files changed, 341 insertions(+), 379 deletions(-) create mode 100644 netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/IPStatus.java delete mode 100644 netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/Status.java create mode 100644 netbox-client/provider/src/test/resources/badResponse.json (limited to 'netbox-client/provider/src') 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 e11fe8bc..f770d154 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,37 +15,45 @@ */ 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; +import java.util.Map; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.ccsdk.sli.core.sli.SvcLogicResource.QueryStatus; /** * 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 { +public interface NetboxClient extends SvcLogicJavaPlugin { /** * 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 while communicating with the IPAM system. - * @throws SQLException If something goes wrong while communicating with the SDNC DB. + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
service_instance_idMandatoryThe service instance ID uniquely identifying the service.
vf_module_idMandatoryThe VF module ID uniquely identifying the VF.
prefix_idMandatoryThe prefix from which to get next available IP.
*/ - IPAddress assign(Prefix prefix, String serviceInstanceId, String vfModuleId) throws IpamException, SQLException; + QueryStatus assignIpAddress(Map parameters, SvcLogicContext ctx); /** - * Release the IP and remove the entry in the SDNC database, table IPAM_IP_ASSIGNEMENT. + * Release the IP and update the entry in the SDNC database, table IPAM_IP_ASSIGNEMENT. * - * @param ip The IP to release. - * @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. + * @param parameters HashMap of parameters passed by the DG to this function + * + * + * + * + * + * + * + *
parameterMandatory/Optionaldescription
service_instance_idMandatoryThe service instance ID uniquely identifying the service.
vf_module_idMandatoryThe VF module ID uniquely identifying the VF.
ip_address_idMandatoryThe IP to release.
*/ - void unassign(IPAddress ip, String serviceInstanceId, String vfModuleId) throws IpamException, SQLException; + QueryStatus unassignIpAddress(Map parameters, SvcLogicContext ctx); } 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 036ff44d..54700f6c 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 @@ -15,127 +15,163 @@ */ 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 com.google.gson.JsonSyntaxException; 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 java.util.Map; import org.apache.http.HttpResponse; -import org.onap.ccsdk.sli.adaptors.netbox.api.IpamException; +import org.apache.http.util.EntityUtils; import org.onap.ccsdk.sli.adaptors.netbox.api.NetboxClient; 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.adaptors.netbox.model.IPStatus; import org.onap.ccsdk.sli.core.dblib.DbLibService; -import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicResource.QueryStatus; +import org.onap.ccsdk.sli.core.slipluginutils.SliPluginUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class NetboxClientImpl implements NetboxClient, SvcLogicJavaPlugin { +public class NetboxClientImpl implements NetboxClient { + + private static final Logger LOG = LoggerFactory.getLogger(NetboxClientImpl.class); 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 SERVICE_INSTANCE_ID_PROP = "service_instance_id"; + private static final String VF_MODULE_ID_PROP = "vf_module_id"; 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 (?, ?, ?, ?, ?, ?)"; + "INSERT INTO IPAM_IP_ASSIGNEMENT (service_instance_id, vf_module_id, prefix_id, ip_address_id, ip_address, ip_status, ip_response_json) \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 = ?"; + "UPDATE IPAM_IP_ASSIGNEMENT SET ip_status = ? 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, final DbLibService dbLibService) { this.client = client; this.dbLibService = dbLibService; - final JsonSerializer vlanStatusDeserializer = (val, type, context) -> val.toJson(); - gson = new GsonBuilder() - .registerTypeAdapter(Status.class, vlanStatusDeserializer) - .create(); } @Override - public IPAddress assign(final Prefix prefix, final String serviceInstanceId, final String vfModuleId) - throws IpamException, SQLException { + public QueryStatus assignIpAddress(final Map parameters, final SvcLogicContext ctx) { - checkArgument(prefix.getId() != null); try { - IPAddress ipAddress = client - .post(String.format(NEXT_AVAILABLE_IP_IN_PREFIX_PATH, prefix.getId()), EMPTY_STRING) - .thenApply(this::getIpAddress) - .toCompletableFuture() - .join(); - - ArrayList 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); + SliPluginUtils + .checkParameters(parameters, new String[]{SERVICE_INSTANCE_ID_PROP, VF_MODULE_ID_PROP, "prefix_id"}, + LOG); + } catch (SvcLogicException e) { + return QueryStatus.FAILURE; + } - return ipAddress; - } catch (CompletionException e) { - // Unwrap the CompletionException and wrap in IpamException - throw new IpamException("Fail to assign IP for Prefix(id= " + prefix.getId() + "). " + e.getMessage(), - e.getCause()); + final String serviceInstanceId = parameters.get(SERVICE_INSTANCE_ID_PROP); + LOG.trace("assignIpAddress: service_instance_id = {}", serviceInstanceId); + final String vfModuleId = parameters.get(VF_MODULE_ID_PROP); + LOG.trace("assignIpAddress: vf_module_id = {}", vfModuleId); + final String prefixId = parameters.get("prefix_id"); + LOG.trace("assignIpAddress: prefix_id = {}", prefixId); + + HttpResponse httpResp; + try { + httpResp = client + .post(String.format(NEXT_AVAILABLE_IP_IN_PREFIX_PATH, prefixId), EMPTY_STRING); + } catch (IOException e) { + LOG.error("Fail to assign IP for Prefix(id={}). {}", prefixId, e.getMessage(), e.getCause()); + return QueryStatus.FAILURE; } - } - @Override - public void unassign(final IPAddress ipAddress, final String serviceInstanceId, final String vfModuleId) - throws IpamException, SQLException { + String ipamRespJson; + try { + ipamRespJson = EntityUtils.toString(httpResp.getEntity(), "UTF-8"); + } catch (IOException e) { + LOG.error("Fail to parse IPAM response for assign in Prefix(id={}). Response={}", prefixId, + httpResp.getEntity(), e); + return QueryStatus.FAILURE; + } + + if (httpResp.getStatusLine().getStatusCode() != 201) { + LOG.error("Fail to assign IP for Prefix(id={}). HTTP code 201!={}. Response={}", prefixId, + httpResp.getStatusLine().getStatusCode(), ipamRespJson); + return QueryStatus.FAILURE; + } - checkArgument(ipAddress.getId() != null); + IPAddress ipAddress; try { - client.delete(String.format(IP_ADDRESS_PATH, ipAddress.getId())) - .thenAccept(this::checkResult) - .toCompletableFuture() - .join(); - - ArrayList args = Lists.newArrayList( - serviceInstanceId, - vfModuleId, - String.valueOf(ipAddress.getId())); - dbLibService.writeData(UNASSIGN_IP_SQL_STATEMENT, args, null); + ipAddress = IPAddress.fromJson(ipamRespJson); + } catch (JsonSyntaxException e) { + LOG.error("Fail to parse IPAM JSON reponse to IPAddress POJO. IPAM JSON Response={}", ipamRespJson, e); + return QueryStatus.FAILURE; + } - } catch (CompletionException e) { - // Unwrap the CompletionException and wrap in IpamException - throw new IpamException( - "Fail to unassign IP for IPAddress(id= " + ipAddress.getId() + "). " + e.getMessage(), - e.getCause()); + ArrayList args = Lists.newArrayList(serviceInstanceId, + vfModuleId, + String.valueOf(prefixId), + String.valueOf(ipAddress.getId()), + ipAddress.getAddress(), + IPStatus.ASSIGNED.name(), + ipamRespJson); + + try { + dbLibService.writeData(ASSIGN_IP_SQL_STATEMENT, args, null); + } catch (SQLException e) { + LOG.error("Caught SQL exception", e); + return QueryStatus.FAILURE; } + + ctx.setAttribute("self_serve_netbox_ip_assignement.ip-address", ipAddress.getAddress()); + + return QueryStatus.SUCCESS; } - @VisibleForTesting - IPAddress getIpAddress(final HttpResponse response) { - if (response.getStatusLine().getStatusCode() != 201) { - throw new IllegalStateException(NetboxHttpClient.getBodyAsString(response)); + @Override + public QueryStatus unassignIpAddress(final Map parameters, final SvcLogicContext ctx) { + try { + SliPluginUtils + .checkParameters(parameters, new String[]{SERVICE_INSTANCE_ID_PROP, VF_MODULE_ID_PROP, "ip_address_id"}, + LOG); + } catch (SvcLogicException e) { + return QueryStatus.FAILURE; } - try (final Reader reader = new InputStreamReader(response.getEntity().getContent())) { - return gson.fromJson(reader, IPAddress.class); - } catch (final IOException e) { - throw new IllegalStateException(e); + + final String serviceInstanceId = parameters.get(SERVICE_INSTANCE_ID_PROP); + LOG.trace("assignIpAddress: service_instance_id = {}", serviceInstanceId); + final String vfModuleId = parameters.get(VF_MODULE_ID_PROP); + LOG.trace("assignIpAddress: vf_module_id = {}", vfModuleId); + final String ipAddressId = parameters.get("ip_address_id"); + LOG.trace("assignIpAddress: ip_address_id = {}", ipAddressId); + HttpResponse httpResp; + try { + httpResp = client.delete(String.format(IP_ADDRESS_PATH, ipAddressId)); + } catch (IOException e) { + LOG.error("Fail to unassign IP for IPAddress(id= " + ipAddressId + "). " + e.getMessage(), + e.getCause()); + return QueryStatus.FAILURE; } - } - private static void checkArgument(final boolean argument) throws IpamException { - if (!argument) { - throw new IpamException(ID_MISSING_MSG); + if (httpResp.getStatusLine().getStatusCode() - 200 >= 100) { + LOG.error("Fail to unassign IP for IPAddress(id={}). HTTP code={}.", ipAddressId, + httpResp.getStatusLine().getStatusCode()); + return QueryStatus.FAILURE; } - } - private void checkResult(final HttpResponse response) { - if (response.getStatusLine().getStatusCode() - 200 >= 100) { - throw new IllegalStateException( - "Netbox request failed with status: " + NetboxHttpClient.getBodyAsString(response)); + ArrayList args = Lists.newArrayList( + IPStatus.UNASSIGNED.name(), + serviceInstanceId, + vfModuleId, + String.valueOf(ipAddressId)); + try { + dbLibService.writeData(UNASSIGN_IP_SQL_STATEMENT, args, null); + } catch (SQLException e) { + LOG.error("Caught SQL exception", e); + return QueryStatus.FAILURE; } + + return QueryStatus.SUCCESS; } } diff --git a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxHttpClient.java b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxHttpClient.java index a77b4d3e..505c5a77 100644 --- a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxHttpClient.java +++ b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxHttpClient.java @@ -24,31 +24,24 @@ import java.nio.charset.Charset; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.util.Scanner; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; import javax.net.ssl.SSLContext; -import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.concurrent.FutureCallback; +import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.ssl.SSLContexts; import org.apache.http.ssl.TrustStrategy; -import org.onap.ccsdk.sli.adaptors.netbox.api.IpamException; import org.onap.ccsdk.sli.adaptors.netbox.property.NetboxProperties; public class NetboxHttpClient implements AutoCloseable { private static final String APPLICATION_JSON = "application/json"; - private final CloseableHttpAsyncClient client; + private final CloseableHttpClient client; private final String url; private final String token; @@ -69,16 +62,10 @@ public class NetboxHttpClient implements AutoCloseable { } catch (final NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) { throw new IllegalStateException("Can't create http client", e); } - client = HttpAsyncClientBuilder.create() + client = HttpClientBuilder.create() .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .setSSLContext(sslContext) .build(); - - } - - // Has to be public for blueprint container to access it - public void init() { - client.start(); } @Override @@ -86,66 +73,22 @@ public class NetboxHttpClient implements AutoCloseable { client.close(); } - CompletionStage post(final String uri, final String requestBody) { - return sendRequest(uri, requestBody, HttpPost::new); - } - - CompletionStage delete(final String uri) { - return sendRequest(uri, HttpDelete::new); + HttpResponse post(final String uri, final String requestBody) throws IOException { + final HttpPost request = new HttpPost(url + uri); + setHeaders(request); + request.setEntity(new StringEntity(requestBody, Charset.forName("UTF-8"))); + return client.execute(request); } - static String getBodyAsString(final HttpResponse response) { - final String body; - if (response.getEntity() != null) { - try (final Scanner s = new java.util.Scanner(response.getEntity().getContent()).useDelimiter("\\A")) { - body = s.hasNext() ? s.next() : ""; - } catch (final IOException e) { - throw new IllegalStateException(e); - } - } else { - body = ""; - } - return response.toString() + "\n" + body; + HttpResponse delete(final String uri) throws IOException { + final HttpDelete request = new HttpDelete(url + uri); + setHeaders(request); + return client.execute(request); } - private CompletionStage sendRequest(final String uri, - final Function supplier) { - final T request = supplier.apply(url + uri); + private void setHeaders(final HttpRequestBase request) { request.addHeader(ACCEPT, APPLICATION_JSON); request.addHeader(CONTENT_TYPE, APPLICATION_JSON); request.addHeader(AUTHORIZATION, "Token " + token); - return sendRequest(request); - } - - private - CompletionStage sendRequest(final String uri, final String body, - final Function supplier) { - final T request = supplier.apply(url + uri); - request.addHeader(ACCEPT, APPLICATION_JSON); - request.addHeader(CONTENT_TYPE, APPLICATION_JSON); - request.addHeader(AUTHORIZATION, "Token " + token); - request.setEntity(new StringEntity(body, Charset.forName("UTF-8"))); - return sendRequest(request); - } - - private CompletionStage sendRequest(final HttpUriRequest request) { - final CompletableFuture future = new CompletableFuture<>(); - client.execute(request, new FutureCallback() { - @Override - public void completed(final HttpResponse httpResponse) { - future.complete(httpResponse); - } - - @Override - public void failed(final Exception e) { - future.completeExceptionally(new IpamException("Netbox request failed", e)); - } - - @Override - public void cancelled() { - future.cancel(false); - } - }); - return future; } } \ No newline at end of file diff --git a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/IPAddress.java b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/IPAddress.java index 6d62fff9..4c2880bc 100644 --- a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/IPAddress.java +++ b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/IPAddress.java @@ -15,25 +15,20 @@ */ package org.onap.ccsdk.sli.adaptors.netbox.model; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.util.Objects; public class IPAddress extends Identifiable { - private Status.Values status; - private String address; + private static final Gson gson = new GsonBuilder().create(); - public void setStatus(Status.Values status) { - this.status = status; - } + private String address; public void setAddress(String address) { this.address = address; } - public Status.Values getStatus() { - return status; - } - public String getAddress() { return address; } @@ -47,12 +42,15 @@ public class IPAddress extends Identifiable { return false; } IPAddress ipAddress = (IPAddress) o; - return Objects.equals(status, ipAddress.status) && - Objects.equals(address, ipAddress.address); + return Objects.equals(address, ipAddress.address); } @Override public int hashCode() { - return Objects.hash(status, address); + return Objects.hash(address); + } + + public static IPAddress fromJson(final String json) { + return gson.fromJson(json, IPAddress.class); } } diff --git a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/IPStatus.java b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/IPStatus.java new file mode 100644 index 00000000..05cc1ea2 --- /dev/null +++ b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/IPStatus.java @@ -0,0 +1,5 @@ +package org.onap.ccsdk.sli.adaptors.netbox.model; + +public enum IPStatus { + ASSIGNED, UNASSIGNED, PENDING_ASSIGN, PENDING_UNASSIGN +} diff --git a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/Status.java b/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/Status.java deleted file mode 100644 index c56828a8..00000000 --- a/netbox-client/provider/src/main/java/org/onap/ccsdk/sli/adaptors/netbox/model/Status.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2018 Bell Canada. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onap.ccsdk.sli.adaptors.netbox.model; - -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; -import com.google.gson.annotations.SerializedName; - -public class Status { - - private Integer value; - private String label; - - public Integer getValue() { - return value; - } - - public void setValue(final Integer value) { - this.value = value; - } - - public String getLabel() { - return label; - } - - public void setLabel(final String label) { - this.label = label; - } - - public JsonElement toJson() { - return new JsonPrimitive(value); - } - - public enum Values { - @SerializedName("1") - ACTIVE(1, "Active"), - @SerializedName("2") - RESERVED(2, "Reserved"); - - private final int value; - private final String label; - - Values(final int value, final String label) { - this.value = value; - this.label = label; - } - - public int getValue() { - return value; - } - - public String getLabel() { - return label; - } - - public Status getStatus() { - final Status status = new Status(); - status.setValue(value); - status.setLabel(label); - return status; - } - - } -} 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 950fd97b..b667dcba 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 @@ -21,8 +21,7 @@ interface="org.onap.ccsdk.sli.core.dblib.DbLibService"/> - + 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 f1eda736..0f887a63 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 @@ -20,7 +20,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.serverError; @@ -33,37 +32,37 @@ 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; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableMap; import com.google.common.io.Resources; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.net.URL; -import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.UUID; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; 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; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicResource.QueryStatus; +import org.slf4j.LoggerFactory; @RunWith(MockitoJUnitRunner.class) public class NetboxClientImplTest { @@ -72,26 +71,50 @@ public class NetboxClientImplTest { @Rule public WireMockRule wm = new WireMockRule(wireMockConfig().dynamicPort()); - @Mock private DbLibService dbLib; + @Mock + private SvcLogicContext svcLogicContext; + @Mock + private Appender appender; + @Captor + private ArgumentCaptor captor; + private String token = "token"; private String serviceInstanceId = UUID.randomUUID().toString(); private String vfModuleId = UUID.randomUUID().toString(); + private Map params = ImmutableMap + .of("service_instance_id", serviceInstanceId, + "vf_module_id", vfModuleId, + "prefix_id", "3", + "ip_address_id", "3" + ); + private NetboxHttpClient httpClient; private NetboxClientImpl netboxClient; + @Mock + private NetboxHttpClient httpClientMock; + @Mock + private NetboxClientImpl netboxClientMock; + + @Before public void setup() { + ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory + .getLogger(NetboxClientImpl.class); + logger.addAppender(appender); + String baseUrl = "http://localhost:" + wm.port(); httpClient = new NetboxHttpClient(baseUrl, token); - httpClient.init(); netboxClient = new NetboxClientImpl(httpClient, dbLib); + netboxClientMock = new NetboxClientImpl(httpClientMock, dbLib); + wm.addMockServiceRequestListener( (request, response) -> { System.out.println("Request URL :" + request.getAbsoluteUrl()); @@ -107,95 +130,157 @@ public class NetboxClientImplTest { } @Test - public void nextAvailableIpInPrefixTestNoId() throws SQLException { - Prefix prefix = mock(Prefix.class); - doReturn(null).when(prefix).getId(); - try { - netboxClient.assign(prefix, serviceInstanceId, vfModuleId); - } catch (IpamException e) { - Assert.assertEquals("Id must be set", e.getMessage()); - return; - } - Assert.fail(); + public void unassignIpAddressTestNoParams() { + QueryStatus status = netboxClient.unassignIpAddress(ImmutableMap.of(), svcLogicContext); + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("This method requires the parameters [service_instance_id,vf_module_id,ip_address_id], but no parameters were passed in"); } @Test - public void nextAvailableIpInPrefixTest() throws IOException, IpamException, SQLException { - Integer id = 3; - Prefix prefix = mock(Prefix.class); - doReturn(id).when(prefix).getId(); + public void unassignIpAddressFailedRequest() throws IOException { + doThrow(new IOException("Failed request")).when(httpClientMock).delete(anyString()); + QueryStatus status = netboxClientMock + .unassignIpAddress(params, svcLogicContext); - URL url = Resources.getResource("nextAvailableIpResponse.json"); - String response = Resources.toString(url, Charsets.UTF_8); + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("Fail to unassign IP for IPAddress(id= 3). Failed request"); + } - String expectedUrl = "/api/ipam/prefixes/" + id + "/available-ips/"; - givenThat(post(urlEqualTo(expectedUrl)).willReturn(created().withBody(response))); + @Test + public void unassignIpAddressServerError() { - netboxClient.assign(prefix, serviceInstanceId, vfModuleId); + String expectedUrl = "/api/ipam/ip-addresses/3/"; + givenThat(delete(urlEqualTo(expectedUrl)).willReturn(serverError())); - 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))); + QueryStatus status = netboxClient.unassignIpAddress(params, svcLogicContext); + + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("Fail to unassign IP for IPAddress(id=3). HTTP code=500."); } @Test - public void deleteIpTestError500() throws SQLException { - Integer id = 3; - IPAddress ipAddress = mock(IPAddress.class); - doReturn(id).when(ipAddress).getId(); + public void unassignIpAddressFailSQL() throws IOException, SQLException { - String expectedUrl = "/api/ipam/ip-addresses/" + id + "/"; - givenThat(delete(urlEqualTo(expectedUrl)).willReturn(serverError())); - try { - netboxClient.unassign(ipAddress, serviceInstanceId, vfModuleId); - } catch (IpamException e) { - Assert.assertEquals(IllegalStateException.class, e.getCause().getClass()); - Assert.assertTrue(e.getMessage().contains( - "Fail to unassign IP for IPAddress(id= 3). java.lang.IllegalStateException: Netbox request failed with status: HTTP/1.1 500 Server Error")); - return; - } - Assert.fail(); + String response = "{}"; + + String expectedUrl = "/api/ipam/ip-addresses/3/"; + givenThat(delete(urlEqualTo(expectedUrl)).willReturn(created().withBody(response))); + + doThrow(new SQLException("Failed")).when(dbLib).writeData(anyString(), any(ArrayList.class), eq(null)); + + QueryStatus status = netboxClient.unassignIpAddress(params, svcLogicContext); + + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("Caught SQL exception"); } @Test - 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, serviceInstanceId, vfModuleId); + public void unassignIpAddressSuccess() throws IOException, SQLException { + String response = "{}"; + + String expectedUrl = "/api/ipam/ip-addresses/3/"; + givenThat(delete(urlEqualTo(expectedUrl)).willReturn(created().withBody(response))); + + QueryStatus status = netboxClient.unassignIpAddress(params, svcLogicContext); + 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))); + + Mockito.verify(dbLib).writeData(anyString(), any(ArrayList.class), eq(null)); + Assert.assertEquals(QueryStatus.SUCCESS, status); + } + + + @Test + public void nextAvailableIpInPrefixTestNoId() { + QueryStatus status = netboxClient.assignIpAddress(ImmutableMap.of(), svcLogicContext); + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("This method requires the parameters [service_instance_id,vf_module_id,prefix_id], but no parameters were passed in"); } + @Test + public void nextAvailableIpInPrefixFailedRequest() throws IOException { + doThrow(new IOException("Failed request")).when(httpClientMock).post(anyString(), anyString()); + QueryStatus status = netboxClientMock.assignIpAddress(params, svcLogicContext); + + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("Fail to assign IP for Prefix(id=3). Failed request"); + } @Test - public void getIpAddressTest() throws IOException { - StatusLine statusLine = mock(StatusLine.class); - doReturn(201).when(statusLine).getStatusCode(); + public void nextAvailableIpInPrefixBadRespPayload() throws IOException { + URL url = Resources.getResource("badResponse.json"); + String response = Resources.toString(url, Charsets.UTF_8); + + String expectedUrl = "/api/ipam/prefixes/3/available-ips/"; + givenThat(post(urlEqualTo(expectedUrl)).willReturn(created().withBody(response))); + + QueryStatus status = netboxClient.assignIpAddress(params, svcLogicContext); + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("Fail to parse IPAM JSON reponse to IPAddress POJO. IPAM JSON Response={\n" + + " \"id\": 8\n" + + " \"address\": \"192.168.20.7/32\"\n" + + "}"); + } + + @Test + public void nextAvailableIpInPrefixFailSQL() throws IOException, SQLException { URL url = Resources.getResource("nextAvailableIpResponse.json"); String response = Resources.toString(url, Charsets.UTF_8); - InputStream stream = new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8)); - HttpEntity entity = mock(HttpEntity.class); - doReturn(stream).when(entity).getContent(); + String expectedUrl = "/api/ipam/prefixes/3/available-ips/"; + givenThat(post(urlEqualTo(expectedUrl)).willReturn(created().withBody(response))); + + doThrow(new SQLException("Failed")).when(dbLib).writeData(anyString(), any(ArrayList.class), eq(null)); + + QueryStatus status = netboxClient.assignIpAddress(params, svcLogicContext); + + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("Caught SQL exception"); + } + + @Test + public void nextAvailableIpInPrefixError500() throws IOException { + URL url = Resources.getResource("nextAvailableIpResponse.json"); + String response = Resources.toString(url, Charsets.UTF_8); - HttpResponse httpResponse = mock(HttpResponse.class); - doReturn(statusLine).when(httpResponse).getStatusLine(); - doReturn(entity).when(httpResponse).getEntity(); + String expectedUrl = "/api/ipam/prefixes/3/available-ips/"; + givenThat(post(urlEqualTo(expectedUrl)).willReturn(serverError().withBody(response))); - IPAddress ipAddress = netboxClient.getIpAddress(httpResponse); + QueryStatus status = netboxClient.assignIpAddress(params, svcLogicContext); - Assert.assertEquals("192.168.20.7/32", ipAddress.getAddress()); - Assert.assertEquals(Integer.valueOf(8), ipAddress.getId()); - Assert.assertEquals(Values.ACTIVE, ipAddress.getStatus()); + Assert.assertEquals(QueryStatus.FAILURE, status); + verifyLogEntry("Fail to assign IP for Prefix(id=3). HTTP code 201!=500."); } + + @Test + public void nextAvailableIpInPrefixSuccess() throws IOException, SQLException { + URL url = Resources.getResource("nextAvailableIpResponse.json"); + String response = Resources.toString(url, Charsets.UTF_8); + + String expectedUrl = "/api/ipam/prefixes/3/available-ips/"; + givenThat(post(urlEqualTo(expectedUrl)).willReturn(created().withBody(response))); + + QueryStatus status = netboxClient.assignIpAddress(params, svcLogicContext); + + 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)); + Assert.assertEquals(QueryStatus.SUCCESS, status); + } + + private void verifyLogEntry(String message) { + Mockito.verify(appender, times(1)).doAppend(captor.capture()); + List allValues = captor.getAllValues(); + for (ILoggingEvent loggingEvent : allValues) { + Assert.assertTrue(loggingEvent.getFormattedMessage().contains(message)); + } + } + } \ No newline at end of file diff --git a/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxHttpClientTest.java b/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxHttpClientTest.java index ee2861c0..8634742a 100644 --- a/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxHttpClientTest.java +++ b/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/impl/NetboxHttpClientTest.java @@ -15,7 +15,6 @@ */ package org.onap.ccsdk.sli.adaptors.netbox.impl; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; @@ -29,16 +28,12 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMoc import static org.apache.http.HttpHeaders.ACCEPT; import static org.apache.http.HttpHeaders.CONTENT_TYPE; -import com.github.tomakehurst.wiremock.http.Fault; import com.github.tomakehurst.wiremock.junit.WireMockRule; import java.io.IOException; -import java.util.concurrent.CompletionException; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.onap.ccsdk.sli.adaptors.netbox.api.IpamException; public class NetboxHttpClientTest { @@ -55,7 +50,6 @@ public class NetboxHttpClientTest { String token = "token"; httpClient = new NetboxHttpClient(baseUrl, token); - httpClient.init(); wm.addMockServiceRequestListener( (request, response) -> { @@ -72,11 +66,11 @@ public class NetboxHttpClientTest { } @Test - public void postTest() { + public void postTest() throws IOException { String expectedUrl = "/testPost"; givenThat(post(urlEqualTo(expectedUrl)).willReturn(ok())); - httpClient.post(expectedUrl, "").toCompletableFuture().join(); + httpClient.post(expectedUrl, ""); verify(postRequestedFor(urlEqualTo(expectedUrl)) .withHeader(ACCEPT, equalTo(APPLICATION_JSON)) @@ -84,44 +78,12 @@ public class NetboxHttpClientTest { } @Test - public void postTestException() { - String expectedUrl = "/testPost"; - givenThat(post(urlEqualTo(expectedUrl)).willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK))); - - try { - httpClient.post(expectedUrl, "").toCompletableFuture().join(); - } catch (CompletionException e) { - Assert.assertEquals(IpamException.class, e.getCause().getClass()); - Assert.assertEquals("Netbox request failed", e.getCause().getMessage()); - return; - } - Assert.fail(); - } - - @Test - public void deleteTest() { + public void deleteTest() throws IOException { String expectedUrl = "/testDelete"; givenThat(delete(urlEqualTo(expectedUrl)).willReturn(ok())); - - httpClient.delete(expectedUrl).toCompletableFuture().join(); - + httpClient.delete(expectedUrl); verify(deleteRequestedFor(urlEqualTo(expectedUrl)) .withHeader(ACCEPT, equalTo(APPLICATION_JSON)) .withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON))); } - - @Test - public void deleteTestException() { - String expectedUrl = "/testDelete"; - givenThat(delete(urlEqualTo(expectedUrl)).willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK))); - - try { - httpClient.delete(expectedUrl).toCompletableFuture().join(); - } catch (CompletionException e) { - Assert.assertEquals(IpamException.class, e.getCause().getClass()); - Assert.assertEquals("Netbox request failed", e.getCause().getMessage()); - return; - } - Assert.fail(); - } } diff --git a/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/property/NetboxPropertiesTest.java b/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/property/NetboxPropertiesTest.java index d9916707..be5aabfc 100644 --- a/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/property/NetboxPropertiesTest.java +++ b/netbox-client/provider/src/test/java/org/onap/ccsdk/sli/adaptors/netbox/property/NetboxPropertiesTest.java @@ -56,7 +56,6 @@ public class NetboxPropertiesTest { "Missing configuration properties resource for Netbox: netbox.properties"); } - private void verifyLogEntry(String message) { verify(appender, times(1)).doAppend(captor.capture()); List allValues = captor.getAllValues(); diff --git a/netbox-client/provider/src/test/resources/badResponse.json b/netbox-client/provider/src/test/resources/badResponse.json new file mode 100644 index 00000000..713109fd --- /dev/null +++ b/netbox-client/provider/src/test/resources/badResponse.json @@ -0,0 +1,4 @@ +{ + "id": 8 + "address": "192.168.20.7/32" +} \ No newline at end of file -- cgit 1.2.3-korg