Mercurial > hg > thermostat-ng > agent
changeset 2637:8781afc57d5e
Update NetworkInterfaceInfoDAO to communicate with web gateway
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-May/022961.html
line wrap: on
line diff
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/dao/NetworkInterfaceInfoDAO.java Fri May 05 12:15:44 2017 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/dao/NetworkInterfaceInfoDAO.java Fri May 05 12:16:15 2017 -0400 @@ -36,17 +36,13 @@ package com.redhat.thermostat.storage.dao; -import java.util.List; - import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.Countable; -import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; @Service -public interface NetworkInterfaceInfoDAO extends Countable { +public interface NetworkInterfaceInfoDAO { static Key<String> ifaceKey = new Key<>("interfaceName"); static Key<String> ip4AddrKey = new Key<>("ip4Addr"); @@ -55,8 +51,6 @@ static final Category<NetworkInterfaceInfo> networkInfoCategory = new Category<>("network-info", NetworkInterfaceInfo.class, Key.AGENT_ID, ifaceKey, ip4AddrKey, ip6AddrKey); - public List<NetworkInterfaceInfo> getNetworkInterfaces(HostRef ref); - public void putNetworkInterfaceInfo(NetworkInterfaceInfo info); }
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/dao/NetworkInterfaceInfoDAOImpl.java Fri May 05 12:15:44 2017 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/dao/NetworkInterfaceInfoDAOImpl.java Fri May 05 12:16:15 2017 -0400 @@ -36,98 +36,255 @@ package com.redhat.thermostat.storage.internal.dao; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.Arrays; import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; import java.util.logging.Logger; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpContentResponse; +import org.eclipse.jetty.client.HttpRequest; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; + import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.CategoryAdapter; -import com.redhat.thermostat.storage.core.HostRef; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.PreparedStatement; -import com.redhat.thermostat.storage.core.Storage; -import com.redhat.thermostat.storage.dao.AbstractDaoQuery; -import com.redhat.thermostat.storage.dao.AbstractDaoStatement; -import com.redhat.thermostat.storage.dao.BaseCountable; import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO; -import com.redhat.thermostat.storage.model.AggregateCount; +import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoTypeAdapter.NetworkInterfaceInfoUpdateTypeAdapter; import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; -public class NetworkInterfaceInfoDAOImpl extends BaseCountable implements NetworkInterfaceInfoDAO { +public class NetworkInterfaceInfoDAOImpl implements NetworkInterfaceInfoDAO { private static final Logger logger = LoggingUtils.getLogger(NetworkInterfaceInfoDAOImpl.class); - static final String QUERY_NETWORK_INFO = "QUERY " - + networkInfoCategory.getName() + " WHERE '" - + Key.AGENT_ID.getName() + "' = ?s"; - // REPLACE network-info SET 'agentId' = ?s , \ - // 'interfaceName' = ?s , \ - // 'ip4Addr' = ?s , \ - // 'ip6Addr' = ?s - // WHERE 'agentId' = ?s AND 'interfaceName' = ?s - static final String DESC_REPLACE_NETWORK_INFO = "REPLACE " + - networkInfoCategory.getName() + - " SET " + - "'" + Key.AGENT_ID.getName() + "' = ?s , " + - "'" + ifaceKey.getName() + "' = ?s , " + - "'" + ip4AddrKey.getName() + "' = ?s , " + - "'" + ip6AddrKey.getName() + "' = ?s " + - "WHERE '" + Key.AGENT_ID.getName() + "' = ?s AND " + - "'" + ifaceKey.getName() + "' = ?s"; - static final String AGGREGATE_COUNT_ALL_NETWORK_INTERFACES = "QUERY-COUNT " + - networkInfoCategory.getName(); - - private final Category<AggregateCount> aggregateCategory; - private final Storage storage; + private static final String GATEWAY_URL = "http://localhost:26000/api/v100"; // TODO configurable + private static final String GATEWAY_PATH = "/network-info/systems/*/agents/"; + private static final String CONTENT_TYPE = "application/json"; + private static final String GATEWAY_QUERY = "?q="; + private static final String QUERY_INTERFACE_PARAM = ifaceKey.getName() + "=="; + + private final HttpHelper httpHelper; + private final JsonHelper jsonHelper; - public NetworkInterfaceInfoDAOImpl(Storage storage) { - this.storage = storage; - storage.registerCategory(networkInfoCategory); - CategoryAdapter<NetworkInterfaceInfo, AggregateCount> adapter = - new CategoryAdapter<>(networkInfoCategory); - aggregateCategory = adapter.getAdapted(AggregateCount.class); - storage.registerCategory(aggregateCategory); + public NetworkInterfaceInfoDAOImpl() throws Exception { + this(new HttpHelper(new HttpClient()), new JsonHelper(new NetworkInterfaceInfoTypeAdapter(), + new NetworkInterfaceInfoUpdateTypeAdapter())); } - @Override - public List<NetworkInterfaceInfo> getNetworkInterfaces(final HostRef ref) { - return executeQuery( - new AbstractDaoQuery<NetworkInterfaceInfo>(storage, networkInfoCategory, QUERY_NETWORK_INFO) { - @Override - public PreparedStatement<NetworkInterfaceInfo> customize(PreparedStatement<NetworkInterfaceInfo> preparedStatement) { - preparedStatement.setString(0, ref.getAgentId()); - return preparedStatement; - } - }).asList(); + NetworkInterfaceInfoDAOImpl(HttpHelper httpHelper, JsonHelper jsonHelper) throws Exception { + this.httpHelper = httpHelper; + this.jsonHelper = jsonHelper; + + this.httpHelper.startClient(); } @Override public void putNetworkInterfaceInfo(final NetworkInterfaceInfo info) { - executeStatement( - new AbstractDaoStatement<NetworkInterfaceInfo>(storage, networkInfoCategory, DESC_REPLACE_NETWORK_INFO) { - @Override - public PreparedStatement<NetworkInterfaceInfo> customize(PreparedStatement<NetworkInterfaceInfo> preparedStatement) { - // SET params. - preparedStatement.setString(0, info.getAgentId()); - preparedStatement.setString(1, info.getInterfaceName()); - preparedStatement.setString(2, info.getIp4Addr()); - preparedStatement.setString(3, info.getIp6Addr()); - // WHERE params. - preparedStatement.setString(4, info.getAgentId()); - preparedStatement.setString(5, info.getInterfaceName()); - return preparedStatement; - } - }); + try { + // Check if there is an existing entry for this interface + NetworkInterfaceInfo existing = getExistingInfo(info.getAgentId(), info.getInterfaceName()); + if (existing == null) { + // Add a new network interface info record + addNetworkInterfaceInfo(info); + } else if (!existing.equals(info)) { // Check if update necessary + // Update existing record + updateNetworkInterfaceInfo(info); + } + } catch (IOException | InterruptedException | TimeoutException | ExecutionException e) { + logger.log(Level.WARNING, "Failed to query network interface information from web gateway", e); + } + } + + private NetworkInterfaceInfo getExistingInfo(String agentId, String interfaceName) throws InterruptedException, TimeoutException, ExecutionException, IOException { + NetworkInterfaceInfo result = null; + // Query NetworkInterfaceInfo with matching interface name + String url = getURLWithQueryString(agentId, interfaceName); + Request httpRequest = httpHelper.newRequest(url); + httpRequest.method(HttpMethod.GET); + ContentResponse response = sendRequest(httpRequest); + String json = response.getContentAsString(); + + // Return the first item, or null if there was no match + List<NetworkInterfaceInfo> infos = jsonHelper.fromJson(json); + if (!infos.isEmpty()) { + result = infos.get(0); + } + return result; + } + + private void addNetworkInterfaceInfo(NetworkInterfaceInfo info) { + try { + // Encode as JSON and send as POST request + String json = jsonHelper.toJson(Arrays.asList(info)); + StringContentProvider provider = httpHelper.createContentProvider(json); + + String url = getURL(info.getAgentId()); + Request httpRequest = httpHelper.newRequest(url); + httpRequest.method(HttpMethod.POST); + httpRequest.content(provider, CONTENT_TYPE); + sendRequest(httpRequest); + } catch (IOException | InterruptedException | TimeoutException | ExecutionException e) { + logger.log(Level.WARNING, "Failed to send network interface information to web gateway", e); + } + } + + private void updateNetworkInterfaceInfo(NetworkInterfaceInfo info) { + try { + // Encode as JSON and send as PUT request + NetworkInterfaceInfoUpdate update = new NetworkInterfaceInfoUpdate(info.getIp4Addr(), info.getIp6Addr()); + String json = jsonHelper.toJson(update); + StringContentProvider provider = httpHelper.createContentProvider(json); + + String url = getURLWithQueryString(info.getAgentId(), info.getInterfaceName()); + Request httpRequest = httpHelper.newRequest(url); + httpRequest.method(HttpMethod.PUT); + httpRequest.content(provider, CONTENT_TYPE); + sendRequest(httpRequest); + } catch (IOException | InterruptedException | TimeoutException | ExecutionException e) { + logger.log(Level.WARNING, "Failed to send network interface information update to web gateway", e); + } + } + + private ContentResponse sendRequest(Request httpRequest) + throws InterruptedException, TimeoutException, ExecutionException, IOException { + ContentResponse resp = httpRequest.send(); + int status = resp.getStatus(); + if (status != HttpStatus.OK_200) { + throw new IOException("Gateway returned HTTP status " + String.valueOf(status) + " - " + resp.getReason()); + } + return resp; + } + + private String getURL(String agentId) { + StringBuilder builder = buildURL(agentId); + return builder.toString(); + } + + private String getURLWithQueryString(String agentId, String interfaceName) throws UnsupportedEncodingException { + StringBuilder builder = buildURL(agentId); + builder.append(GATEWAY_QUERY); + String query = QUERY_INTERFACE_PARAM.concat(interfaceName); + String encodedQuery = URLEncoder.encode(query, "UTF-8"); + builder.append(encodedQuery); + return builder.toString(); } - @Override - public long getCount() { - return getCount(storage, aggregateCategory, AGGREGATE_COUNT_ALL_NETWORK_INTERFACES); + private StringBuilder buildURL(String agentId) { + StringBuilder builder = new StringBuilder(); + builder.append(GATEWAY_URL); + builder.append(GATEWAY_PATH); + builder.append(agentId); + return builder; + } + + static class NetworkInterfaceInfoUpdate { + + private final String ipv4Addr; + private final String ipv6Addr; + + public NetworkInterfaceInfoUpdate(String ipv4Addr, String ipv6Addr) { + this.ipv4Addr = ipv4Addr; + this.ipv6Addr = ipv6Addr; + } + + String getIPv4Addr() { + return ipv4Addr; + } + + String getIPv6Addr() { + return ipv6Addr; + } + + } + + // For testing purposes + static class JsonHelper { + + private final NetworkInterfaceInfoTypeAdapter typeAdapter; + private final NetworkInterfaceInfoUpdateTypeAdapter updateTypeAdapter; + + public JsonHelper(NetworkInterfaceInfoTypeAdapter typeAdapter, NetworkInterfaceInfoUpdateTypeAdapter updateTypeAdapter) { + this.typeAdapter = typeAdapter; + this.updateTypeAdapter = updateTypeAdapter; + } + + List<NetworkInterfaceInfo> fromJson(String json) throws IOException { + return typeAdapter.fromJson(json); + } + + String toJson(List<NetworkInterfaceInfo> infos) throws IOException { + return typeAdapter.toJson(infos); + } + + String toJson(NetworkInterfaceInfoUpdate update) throws IOException { + return updateTypeAdapter.toJson(update); + } + } + + // For testing purposes + static class HttpHelper { + + private final HttpClient httpClient; - @Override - protected Logger getLogger() { - return logger; + HttpHelper(HttpClient httpClient) { + this.httpClient = httpClient; + } + + void startClient() throws Exception { + httpClient.start(); + } + + StringContentProvider createContentProvider(String content) { + return new StringContentProvider(content); + } + + Request newRequest(String url) { + return new MockRequest(httpClient, URI.create(url)); + } + + } + + // FIXME This class should be removed when the web gateway has a microservice for this DAO + private static class MockRequest extends HttpRequest { + + MockRequest(HttpClient client, URI uri) { + super(client, uri); + } + + @Override + public ContentResponse send() throws InterruptedException, TimeoutException, ExecutionException { + return new MockResponse(); + } + + } + + // FIXME This class should be removed when the web gateway has a microservice for this DAO + private static class MockResponse extends HttpContentResponse { + + MockResponse() { + super(null, null, null); + } + + @Override + public int getStatus() { + return HttpStatus.OK_200; + } + + @Override + public String getContentAsString() { + // Simulate empty response + return "{\"response\" : [], \"time\" : \"0\"}"; + } + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/dao/NetworkInterfaceInfoTypeAdapter.java Fri May 05 12:16:15 2017 -0400 @@ -0,0 +1,210 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.internal.dao; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl.NetworkInterfaceInfoUpdate; +import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; + +public class NetworkInterfaceInfoTypeAdapter extends TypeAdapter<List<NetworkInterfaceInfo>> { + + private static final String AGENT_ID = "agentId"; + private static final String INTERFACE_NAME = "interfaceName"; + private static final String IP4_ADDR = "ip4Addr"; + private static final String IP6_ADDR = "ip6Addr"; + private static final String RESPONSE_ROOT = "response"; + private static final String SERVER_TIME = "time"; + + @Override + public void write(JsonWriter out, List<NetworkInterfaceInfo> value) throws IOException { + // Request is an array of NetworkInterfaceInfo objects + out.beginArray(); + + for (NetworkInterfaceInfo info : value) { + writeNetworkInterfaceInfo(out, info); + } + + out.endArray(); + } + + private void writeNetworkInterfaceInfo(JsonWriter out, NetworkInterfaceInfo info) throws IOException { + out.beginObject(); + + // Write each field of NetworkInterfaceInfo as part of a JSON object + out.name(AGENT_ID); + out.value(info.getAgentId()); + out.name(INTERFACE_NAME); + out.value(info.getInterfaceName()); + out.name(IP4_ADDR); + out.value(info.getIp4Addr()); + out.name(IP6_ADDR); + out.value(info.getIp6Addr()); + + out.endObject(); + } + + @Override + public List<NetworkInterfaceInfo> read(JsonReader in) throws IOException { + List<NetworkInterfaceInfo> infos = null; + + try { + // Parse root object + in.beginObject(); + while (in.hasNext()) { + String name = in.nextName(); + switch (name) { + case RESPONSE_ROOT: + infos = readResponse(in); + break; + case SERVER_TIME: + in.nextString(); + break; + default: + throw new IOException("Unexpected JSON name in gateway response: '" + name + "'"); + } + } + in.endObject(); + } catch (IllegalStateException e) { + throw new IOException("Reading JSON response from web gateway failed", e); + } + + return infos; + } + + private List<NetworkInterfaceInfo> readResponse(JsonReader in) throws IOException { + List<NetworkInterfaceInfo> infos = new ArrayList<>(); + + // Parse array of NetworkInterfaceInfos + in.beginArray(); + + while (in.hasNext()) { + NetworkInterfaceInfo info = readNetworkInterfaceInfo(in); + infos.add(info); + } + in.endArray(); + + return infos; + } + + private NetworkInterfaceInfo readNetworkInterfaceInfo(JsonReader in) throws IOException { + String agentId = null; + String interfaceName = null; + String ipv4Addr = null; + String ipv6Addr = null; + + // Begin parsing a NetworkInterfaceInfo record + in.beginObject(); + + while (in.hasNext()) { + String name = in.nextName(); + switch (name) { + case AGENT_ID: + agentId = in.nextString(); + break; + case INTERFACE_NAME: + interfaceName = in.nextString(); + break; + case IP4_ADDR: // Addresses may be null + ipv4Addr = readStringOrNull(in); + break; + case IP6_ADDR: + ipv6Addr = readStringOrNull(in); + break; + default: + throw new IOException("Unexpected JSON name in record: '" + name + "'"); + } + } + + in.endObject(); + + // Create NetworkInterfaceInfo if all required fields present + if (agentId == null || interfaceName == null) { + throw new IOException("Network interface information record is incomplete"); + } + NetworkInterfaceInfo result = new NetworkInterfaceInfo(agentId, interfaceName); + result.setIp4Addr(ipv4Addr); + result.setIp6Addr(ipv6Addr); + + return result; + } + + private String readStringOrNull(JsonReader in) throws IOException { + String result = null; + JsonToken token = in.peek(); + if (token == JsonToken.NULL) { + in.nextNull(); + } else { + result = in.nextString(); + } + return result; + } + + static class NetworkInterfaceInfoUpdateTypeAdapter extends TypeAdapter<NetworkInterfaceInfoUpdate> { + + private static final String SET = "set"; + + @Override + public void write(JsonWriter out, NetworkInterfaceInfoUpdate value) throws IOException { + // List fields to update as part of a JSON object with name "set" + out.beginObject(); + out.name(SET); + + out.beginObject(); + out.name(IP4_ADDR); + out.value(value.getIPv4Addr()); + out.name(IP6_ADDR); + out.value(value.getIPv6Addr()); + out.endObject(); + + out.endObject(); + } + + @Override + public NetworkInterfaceInfoUpdate read(JsonReader in) throws IOException { + throw new UnsupportedOperationException(); + } + + } + +}
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/model/NetworkInterfaceInfo.java Fri May 05 12:15:44 2017 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/model/NetworkInterfaceInfo.java Fri May 05 12:16:15 2017 -0400 @@ -36,6 +36,8 @@ package com.redhat.thermostat.storage.model; +import java.util.Objects; + import com.redhat.thermostat.storage.core.Entity; import com.redhat.thermostat.storage.core.Persist; @@ -94,5 +96,23 @@ public void clearIp6Addr() { ip6Addr = null; } + + @Override + public boolean equals(Object obj) { + // Super implementation should check for class and agentId equality and check for null + boolean equal = super.equals(obj); + if (equal) { + NetworkInterfaceInfo other = (NetworkInterfaceInfo) obj; + equal = Objects.equals(iFace, other.iFace) && Objects.equals(ip4Addr, other.ip4Addr) + && Objects.equals(ip6Addr, other.ip6Addr); + } + return equal; + } + + @Override + public int hashCode() { + // Super implementation should hash agentId + return Objects.hash(super.hashCode(), iFace, ip4Addr, ip6Addr); + } }
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/dao/NetworkInterfaceInfoDAOTest.java Fri May 05 12:15:44 2017 -0400 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/dao/NetworkInterfaceInfoDAOTest.java Fri May 05 12:16:15 2017 -0400 @@ -39,36 +39,78 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.NoSuchElementException; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; -import com.redhat.thermostat.storage.core.Cursor; -import com.redhat.thermostat.storage.core.DescriptorParsingException; -import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.PreparedStatement; -import com.redhat.thermostat.storage.core.StatementDescriptor; -import com.redhat.thermostat.storage.core.StatementExecutionException; -import com.redhat.thermostat.storage.core.Storage; import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO; -import com.redhat.thermostat.storage.model.AggregateCount; +import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl.HttpHelper; +import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl.JsonHelper; +import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl.NetworkInterfaceInfoUpdate; import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; public class NetworkInterfaceInfoDAOTest { + private static final String URL = "http://localhost:26000/api/v100/network-info/systems/*/agents/fooAgent"; + private static final String QUERY_URL = URL + "?q=interfaceName%3D%3Dsome+interface.+maybe+eth0"; private static final String INTERFACE_NAME = "some interface. maybe eth0"; private static final String IPV4_ADDR = "256.256.256.256"; private static final String IPV6_ADDR = "100:100:100::::1"; + private static final String SOME_JSON = "{\"some\" : \"json\"}"; + private static final String SOME_OTHER_JSON = "{\"some\" : {\"other\" : \"json\"}}"; + private static final String EMPTY_JSON = "{}"; + private static final String CONTENT_TYPE = "application/json"; + private NetworkInterfaceInfo info; + private JsonHelper jsonHelper; + private HttpHelper httpHelper; + private StringContentProvider contentProvider; + private Request request; + private ContentResponse response; + + @Before + public void setup() throws Exception { + info = new NetworkInterfaceInfo("fooAgent", INTERFACE_NAME); + info.setIp4Addr(IPV4_ADDR); + info.setIp6Addr(IPV6_ADDR); + + httpHelper = mock(HttpHelper.class); + contentProvider = mock(StringContentProvider.class); + when(httpHelper.createContentProvider(anyString())).thenReturn(contentProvider); + request = mock(Request.class); + when(httpHelper.newRequest(anyString())).thenReturn(request); + response = mock(ContentResponse.class); + when(response.getStatus()).thenReturn(HttpStatus.OK_200); + when(request.send()).thenReturn(response); + + jsonHelper = mock(JsonHelper.class); + when(jsonHelper.toJson(anyListOf(NetworkInterfaceInfo.class))).thenReturn(SOME_JSON); + when(jsonHelper.toJson(any(NetworkInterfaceInfoUpdate.class))).thenReturn(SOME_OTHER_JSON); + List<NetworkInterfaceInfo> emptyList = Collections.emptyList(); + when(jsonHelper.fromJson(EMPTY_JSON)).thenReturn(emptyList); + when(jsonHelper.fromJson(SOME_JSON)).thenReturn(Arrays.asList(info)); + } + @Test public void testCategory() { Collection<Key<?>> keys; @@ -83,118 +125,78 @@ } @Test - public void preparedQueryDescriptorsAreSane() { - String expectedNetworkInfo = "QUERY network-info WHERE 'agentId' = ?s"; - assertEquals(expectedNetworkInfo, NetworkInterfaceInfoDAOImpl.QUERY_NETWORK_INFO); - String aggregateCountAllNetworkInterfaces = "QUERY-COUNT network-info"; - assertEquals(aggregateCountAllNetworkInterfaces, - NetworkInterfaceInfoDAOImpl.AGGREGATE_COUNT_ALL_NETWORK_INTERFACES); - String replaceNetworkInfo = "REPLACE network-info SET 'agentId' = ?s , " + - "'interfaceName' = ?s , " + - "'ip4Addr' = ?s , " + - "'ip6Addr' = ?s WHERE " + - "'agentId' = ?s AND 'interfaceName' = ?s"; - assertEquals(replaceNetworkInfo, NetworkInterfaceInfoDAOImpl.DESC_REPLACE_NETWORK_INFO); + public void testPutNetworkInterfaceInfoAdd() throws Exception { + NetworkInterfaceInfoDAO dao = new NetworkInterfaceInfoDAOImpl(httpHelper, jsonHelper); + when(response.getContentAsString()).thenReturn(EMPTY_JSON); + dao.putNetworkInterfaceInfo(info); + + // Check query first + verify(httpHelper).newRequest(QUERY_URL); + verify(request).method(HttpMethod.GET); + verify(jsonHelper).fromJson(EMPTY_JSON); + + // Check data added + verify(httpHelper).newRequest(URL); + verify(request).method(HttpMethod.POST); + verify(jsonHelper).toJson(eq(Arrays.asList(info))); + verify(httpHelper).createContentProvider(SOME_JSON); + verify(request).content(contentProvider, CONTENT_TYPE); + + verify(request, times(2)).send(); + verify(response, times(2)).getStatus(); + } + + @Test + public void testPutNetworkInterfaceInfoUpdate() throws Exception { + NetworkInterfaceInfoDAO dao = new NetworkInterfaceInfoDAOImpl(httpHelper, jsonHelper); + + when(response.getContentAsString()).thenReturn(SOME_JSON); + NetworkInterfaceInfo other = new NetworkInterfaceInfo("fooAgent", INTERFACE_NAME); + other.setIp4Addr("1.2.3.4"); + other.setIp6Addr(IPV6_ADDR); + when(jsonHelper.fromJson(SOME_JSON)).thenReturn(Arrays.asList(other)); + dao.putNetworkInterfaceInfo(info); + + // Check query first + verify(request).method(HttpMethod.GET); + verify(jsonHelper).fromJson(SOME_JSON); + + // Check data updated + verify(httpHelper, times(2)).newRequest(QUERY_URL); + verify(request).method(HttpMethod.PUT); + + ArgumentCaptor<NetworkInterfaceInfoUpdate> updateCaptor = ArgumentCaptor.forClass(NetworkInterfaceInfoUpdate.class); + verify(jsonHelper).toJson(updateCaptor.capture()); + NetworkInterfaceInfoUpdate update = updateCaptor.getValue(); + assertEquals(IPV4_ADDR, update.getIPv4Addr()); + assertEquals(IPV6_ADDR, update.getIPv6Addr()); + + verify(httpHelper).createContentProvider(SOME_OTHER_JSON); + verify(request).content(contentProvider, CONTENT_TYPE); + + verify(request, times(2)).send(); + verify(response, times(2)).getStatus(); } @Test - public void testGetNetworkInterfaces() throws DescriptorParsingException, StatementExecutionException { - - NetworkInterfaceInfo niInfo = new NetworkInterfaceInfo("foo-agent", INTERFACE_NAME); - niInfo.setIp4Addr(IPV4_ADDR); - niInfo.setIp6Addr(IPV6_ADDR); - - @SuppressWarnings("unchecked") - Cursor<NetworkInterfaceInfo> cursor = (Cursor<NetworkInterfaceInfo>) mock(Cursor.class); - when(cursor.hasNext()).thenReturn(true).thenReturn(false); - when(cursor.next()).thenReturn(niInfo); - - Storage storage = mock(Storage.class); - @SuppressWarnings("unchecked") - PreparedStatement<NetworkInterfaceInfo> stmt = (PreparedStatement<NetworkInterfaceInfo>) mock(PreparedStatement.class); - when(storage.prepareStatement(anyDescriptor())).thenReturn(stmt); - when(stmt.executeQuery()).thenReturn(cursor); - - HostRef hostRef = mock(HostRef.class); - when(hostRef.getAgentId()).thenReturn("system"); - - NetworkInterfaceInfoDAO dao = new NetworkInterfaceInfoDAOImpl(storage); - List<NetworkInterfaceInfo> netInfo = dao.getNetworkInterfaces(hostRef); - - verify(storage).prepareStatement(anyDescriptor()); - verify(stmt).setString(0, "system"); - verify(stmt).executeQuery(); - verifyNoMoreInteractions(stmt); - - assertEquals(1, netInfo.size()); - - NetworkInterfaceInfo info = netInfo.get(0); - - assertEquals(INTERFACE_NAME, info.getInterfaceName()); - assertEquals(IPV4_ADDR, info.getIp4Addr()); - assertEquals(IPV6_ADDR, info.getIp6Addr()); - } - - @SuppressWarnings("unchecked") - private StatementDescriptor<NetworkInterfaceInfo> anyDescriptor() { - return (StatementDescriptor<NetworkInterfaceInfo>) any(StatementDescriptor.class); - } + public void testPutNetworkInterfaceInfoNoUpdate() throws Exception { + NetworkInterfaceInfoDAO dao = new NetworkInterfaceInfoDAOImpl(httpHelper, jsonHelper); + + when(response.getContentAsString()).thenReturn(SOME_JSON); + dao.putNetworkInterfaceInfo(info); + + // Check query first + verify(httpHelper).newRequest(QUERY_URL); + verify(request).method(HttpMethod.GET); + verify(jsonHelper).fromJson(SOME_JSON); + verify(request).send(); + verify(response).getStatus(); - @SuppressWarnings("unchecked") - @Test - public void testPutNetworkInterfaceInfo() - throws DescriptorParsingException, StatementExecutionException { - String agentId = "fooAgent"; - Storage storage = mock(Storage.class); - PreparedStatement<NetworkInterfaceInfo> replace = mock(PreparedStatement.class); - when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(replace); - - NetworkInterfaceInfo info = new NetworkInterfaceInfo(agentId, INTERFACE_NAME); - info.setIp4Addr(IPV4_ADDR); - info.setIp6Addr(IPV6_ADDR); - - NetworkInterfaceInfoDAO dao = new NetworkInterfaceInfoDAOImpl(storage); - dao.putNetworkInterfaceInfo(info); - - @SuppressWarnings("rawtypes") - ArgumentCaptor<StatementDescriptor> captor = ArgumentCaptor.forClass(StatementDescriptor.class); - - verify(storage).prepareStatement(captor.capture()); - - StatementDescriptor<?> desc = captor.getValue(); - assertEquals(NetworkInterfaceInfoDAOImpl.DESC_REPLACE_NETWORK_INFO, desc.getDescriptor()); - - verify(replace).setString(0, info.getAgentId()); - verify(replace).setString(1, info.getInterfaceName()); - verify(replace).setString(2, info.getIp4Addr()); - verify(replace).setString(3, info.getIp6Addr()); - verify(replace).setString(4, info.getAgentId()); - verify(replace).setString(5, info.getInterfaceName()); - verify(replace).execute(); - verifyNoMoreInteractions(replace); - } - - @Test - public void testGetCount() - throws DescriptorParsingException, StatementExecutionException { - AggregateCount count = new AggregateCount(); - count.setCount(2); - - @SuppressWarnings("unchecked") - Cursor<AggregateCount> c = (Cursor<AggregateCount>) mock(Cursor.class); - when(c.hasNext()).thenReturn(true).thenReturn(false); - when(c.next()).thenReturn(count).thenThrow(new NoSuchElementException()); - - Storage storage = mock(Storage.class); - @SuppressWarnings("unchecked") - PreparedStatement<AggregateCount> stmt = (PreparedStatement<AggregateCount>) mock(PreparedStatement.class); - @SuppressWarnings("unchecked") - StatementDescriptor<AggregateCount> desc = any(StatementDescriptor.class); - when(storage.prepareStatement(desc)).thenReturn(stmt); - when(stmt.executeQuery()).thenReturn(c); - NetworkInterfaceInfoDAOImpl dao = new NetworkInterfaceInfoDAOImpl(storage); - - assertEquals(2, dao.getCount()); + // Check no update sent + verify(request, never()).method(HttpMethod.PUT); + verify(jsonHelper, never()).toJson(any(NetworkInterfaceInfoUpdate.class)); + verify(httpHelper, never()).createContentProvider(SOME_OTHER_JSON); + verify(request, never()).content(contentProvider, CONTENT_TYPE); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/dao/NetworkInterfaceInfoTypeAdapterTest.java Fri May 05 12:16:15 2017 -0400 @@ -0,0 +1,112 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.internal.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl.NetworkInterfaceInfoUpdate; +import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoTypeAdapter.NetworkInterfaceInfoUpdateTypeAdapter; +import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; + +public class NetworkInterfaceInfoTypeAdapterTest { + + @Test + public void testWrite() throws Exception { + NetworkInterfaceInfoTypeAdapter adapter = new NetworkInterfaceInfoTypeAdapter(); + final String expected = "[{\"agentId\":\"agent1\",\"interfaceName\":\"lo\",\"ip4Addr\":\"127.0.0.1\"," + + "\"ip6Addr\":\"0:0:0:0:0:0:0:1%lo\"},{\"agentId\":\"agent2\",\"interfaceName\":\"if1\"," + + "\"ip4Addr\":\"1.2.3.4\",\"ip6Addr\":\"1:2:3:4:5:6:7:8%if1\"}]"; + + NetworkInterfaceInfo first = createNetworkInterfaceInfo("agent1", "lo", "127.0.0.1", "0:0:0:0:0:0:0:1%lo"); + NetworkInterfaceInfo second = createNetworkInterfaceInfo("agent2", "if1", "1.2.3.4", "1:2:3:4:5:6:7:8%if1"); + List<NetworkInterfaceInfo> infos = Arrays.asList(first, second); + + String json = adapter.toJson(infos); + assertEquals(expected, json); + } + + @Test + public void testRead() throws Exception { + NetworkInterfaceInfoTypeAdapter adapter = new NetworkInterfaceInfoTypeAdapter(); + final String json = "{\"response\" : [{\"agentId\" : \"agent1\", \"interfaceName\" : \"lo\", " + + "\"ip4Addr\" : \"127.0.0.1\", \"ip6Addr\" : \"0:0:0:0:0:0:0:1%lo\"}, " + + "{\"agentId\" : \"agent2\", \"interfaceName\" : \"if1\", " + + "\"ip4Addr\" : \"1.2.3.4\", \"ip6Addr\" : null}], " + + "\"time\" : \"500000000\"}"; + + List<NetworkInterfaceInfo> infos = adapter.fromJson(json); + assertEquals(2, infos.size()); + + NetworkInterfaceInfo first = infos.get(0); + assertEquals("agent1", first.getAgentId()); + assertEquals("lo", first.getInterfaceName()); + assertEquals("127.0.0.1", first.getIp4Addr()); + assertEquals("0:0:0:0:0:0:0:1%lo", first.getIp6Addr()); + + NetworkInterfaceInfo second = infos.get(1); + assertEquals("agent2", second.getAgentId()); + assertEquals("if1", second.getInterfaceName()); + assertEquals("1.2.3.4", second.getIp4Addr()); + assertNull(second.getIp6Addr()); + } + + @Test + public void testUpdate() throws Exception { + NetworkInterfaceInfoUpdateTypeAdapter adapter = new NetworkInterfaceInfoUpdateTypeAdapter(); + final String expected = "{\"set\":{\"ip4Addr\":\"1.2.3.4\",\"ip6Addr\":\"1:2:3:4:5:6:7:8%if0\"}}"; + + NetworkInterfaceInfoUpdate update = new NetworkInterfaceInfoUpdate("1.2.3.4", "1:2:3:4:5:6:7:8%if0"); + + String json = adapter.toJson(update); + assertEquals(expected, json); + } + + private NetworkInterfaceInfo createNetworkInterfaceInfo(String agentId, String iFace, String ip4Addr, + String ip6Addr) { + NetworkInterfaceInfo info = new NetworkInterfaceInfo(agentId, iFace); + info.setIp4Addr(ip4Addr); + info.setIp6Addr(ip6Addr); + return info; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/model/NetworkInterfaceInfoTest.java Fri May 05 12:16:15 2017 -0400 @@ -0,0 +1,121 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +public class NetworkInterfaceInfoTest { + + private NetworkInterfaceInfo info; + + @Before + public void setup() { + info = createInfo("agent1", "if1", "1.2.3.4", "1:2:3:4:5:6:7:8"); + } + + @Test + public void testEquals() { + NetworkInterfaceInfo other = createInfo("agent1", "if1", "1.2.3.4", "1:2:3:4:5:6:7:8"); + assertTrue(info.equals(other)); + } + + @Test + public void testEqualsWrongAgentId() { + NetworkInterfaceInfo other = createInfo("agent2", "if1", "1.2.3.4", "1:2:3:4:5:6:7:8"); + assertFalse(info.equals(other)); + } + + @Test + public void testEqualsWrongInterface() { + NetworkInterfaceInfo other = createInfo("agent1", "if2", "1.2.3.4", "1:2:3:4:5:6:7:8"); + assertFalse(info.equals(other)); + } + + @Test + public void testEqualsWrongIpV4() { + NetworkInterfaceInfo other = createInfo("agent1", "if1", "1.2.3.5", "1:2:3:4:5:6:7:8"); + assertFalse(info.equals(other)); + } + + @Test + public void testEqualsWrongIpV6() { + NetworkInterfaceInfo other = createInfo("agent1", "if1", "1.2.3.4", "1:2:3:4:5:6:7:9"); + assertFalse(info.equals(other)); + } + + @Test + public void testHashCode() { + NetworkInterfaceInfo other = createInfo("agent1", "if1", "1.2.3.4", "1:2:3:4:5:6:7:8"); + assertEquals(info.hashCode(), other.hashCode()); + } + + @Test + public void testHashCodeWrongAgentId() { + NetworkInterfaceInfo other = createInfo("agent2", "if1", "1.2.3.4", "1:2:3:4:5:6:7:8"); + assertFalse(info.hashCode() == other.hashCode()); + } + + @Test + public void testHashCodeWrongInterface() { + NetworkInterfaceInfo other = createInfo("agent1", "if2", "1.2.3.4", "1:2:3:4:5:6:7:8"); + assertFalse(info.hashCode() == other.hashCode()); + } + + @Test + public void testHashCodeWrongIpV4() { + NetworkInterfaceInfo other = createInfo("agent1", "if1", "1.2.3.5", "1:2:3:4:5:6:7:8"); + assertFalse(info.hashCode() == other.hashCode()); + } + + @Test + public void testHashCodeWrongIpV6() { + NetworkInterfaceInfo other = createInfo("agent1", "if1", "1.2.3.4", "1:2:3:4:5:6:7:9"); + assertFalse(info.hashCode() == other.hashCode()); + } + + private NetworkInterfaceInfo createInfo(String agentId, String iFace, String ip4Addr, String ip6Addr) { + NetworkInterfaceInfo info = new NetworkInterfaceInfo(agentId, iFace); + info.setIp4Addr(ip4Addr); + info.setIp6Addr(ip6Addr); + return info; + } +}