Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISPN-12622 Expose Cross-Site Views via CLI and REST endpoint #9008

Merged
merged 1 commit into from Feb 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
56 changes: 55 additions & 1 deletion cli/src/main/java/org/infinispan/cli/commands/rest/Site.java
@@ -1,15 +1,18 @@
package org.infinispan.cli.commands.rest;

import java.io.IOException;
import java.util.concurrent.CompletionStage;

import org.aesh.command.Command;
import org.aesh.command.CommandDefinition;
import org.aesh.command.CommandException;
import org.aesh.command.CommandResult;
import org.aesh.command.GroupCommandDefinition;
import org.aesh.command.option.Option;
import org.infinispan.cli.activators.ConnectionActivator;
import org.infinispan.cli.commands.CliCommand;
import org.infinispan.cli.completers.CacheCompleter;
import org.infinispan.cli.connection.Connection;
import org.infinispan.cli.impl.ContextAwareCommandInvocation;
import org.infinispan.cli.resources.Resource;
import org.infinispan.client.rest.RestClient;
Expand All @@ -32,6 +35,8 @@
Site.CancelReceiveState.class,
Site.PushSiteStatus.class,
Site.ClearPushStateStatus.class,
Site.View.class,
Site.Name.class,
}
)
public class Site extends CliCommand {
Expand Down Expand Up @@ -139,7 +144,7 @@ protected CompletionStage<RestResponse> exec(ContextAwareCommandInvocation invoc
}
}

@CommandDefinition(name = "cancel-push-state", description = "Cacncels pushing state to a site", activator = ConnectionActivator.class)
@CommandDefinition(name = "cancel-push-state", description = "Cancels pushing state to a site", activator = ConnectionActivator.class)
public static class CancelPushState extends RestCliCommand {
@Option(required = true, completer = CacheCompleter.class)
String cache;
Expand Down Expand Up @@ -220,4 +225,53 @@ protected CompletionStage<RestResponse> exec(ContextAwareCommandInvocation invoc
return client.cache(cache).clearPushStateStatus();
}
}

@CommandDefinition(name = "view", description = "Prints the global sites view", activator = ConnectionActivator.class)
public static class View extends CliCommand {

@Option(shortName = 'h', hasValue = false, overrideRequired = true)
protected boolean help;

@Override
public boolean isHelp() {
return help;
}

@Override
protected CommandResult exec(ContextAwareCommandInvocation invocation) throws CommandException {
try {
Connection connection = invocation.getContext().getConnection();
connection.refreshServerInfo();
invocation.println(connection.getSitesView().toString());
return CommandResult.SUCCESS;
} catch (IOException e) {
throw new CommandException(e);
}
}

}

@CommandDefinition(name = "name", description = "Prints the local site name", activator = ConnectionActivator.class)
public static class Name extends CliCommand {

@Option(shortName = 'h', hasValue = false, overrideRequired = true)
protected boolean help;

@Override
public boolean isHelp() {
return help;
}

@Override
protected CommandResult exec(ContextAwareCommandInvocation invocation) throws CommandException {
try {
Connection connection = invocation.getContext().getConnection();
connection.refreshServerInfo();
invocation.println(connection.getLocalSiteName());
return CommandResult.SUCCESS;
} catch (IOException e) {
throw new CommandException(e);
}
}
}
}
Expand Up @@ -73,6 +73,10 @@ public interface Connection extends Closeable {

Collection<String> getBackupNames(String container) throws IOException;

Collection<String> getSitesView();

String getLocalSiteName();

MediaType getEncoding();

void setEncoding(MediaType encoding);
Expand Down
Expand Up @@ -54,7 +54,9 @@ public class RestConnection implements Connection, Closeable {
private boolean connected;
private String serverVersion;
private String serverInfo;
private Path workingDir;
private List<String> sitesView;
private String localSite;
private final Path workingDir;

public RestConnection(RestClientConfigurationBuilder builder) {
this.builder = builder;
Expand Down Expand Up @@ -270,9 +272,9 @@ public Collection<String> getAvailableTasks(String container) throws IOException
}

@Override
public Collection<String> getAvailableSites(String container, String cache) {
CompletionStage<RestResponse> response = client.cache(cache).xsiteBackups();
return null;
public Collection<String> getAvailableSites(String container, String cache) throws IOException {
Map<String, String> sites = parseBody(fetch(() -> client.cache(cache).xsiteBackups()), Map.class);
return sites == null ? Collections.emptyList() : sites.keySet();
}

@Override
Expand Down Expand Up @@ -355,6 +357,16 @@ public Collection<String> getBackupNames(String container) throws IOException {
return parseBody(fetch(client.cacheManager(container).getBackupNames()), List.class);
}

@Override
public Collection<String> getSitesView() {
return sitesView;
}

@Override
public String getLocalSiteName() {
return localSite;
}

@Override
public void refreshServerInfo() throws IOException {
try {
Expand All @@ -373,6 +385,9 @@ public void refreshServerInfo() throws IOException {

String nodeAddress = (String) cacheManagerInfo.get("node_address");
String clusterName = (String) cacheManagerInfo.get("cluster_name");
localSite = (String) cacheManagerInfo.get("local_site");
sitesView = new ArrayList<>((Collection<String>) cacheManagerInfo.get("sites_view"));
Collections.sort(sitesView);
clusterMembers = (Collection<String>) cacheManagerInfo.get("cluster_members");
if (nodeAddress != null) {
serverInfo = nodeAddress + "@" + clusterName;
Expand Down
10 changes: 10 additions & 0 deletions cli/src/main/resources/help/site.adoc
Expand Up @@ -24,6 +24,10 @@ SYNOPSIS

*site push-site-status* ['OPTIONS']

*site name*

*site view*


OPTIONS
-------
Expand Down Expand Up @@ -62,3 +66,9 @@ Cancels the operation to receive state from `NYC`.

`site clear-push-state-status --cache=myCache` +
Clears the status of the push state operation for `mycache`.

`site name` +
Returns the name of the local site. If cross-site replication is not configured, the name of the local site is always "local".

`site view` +
Returns a list of names for all sites or an empty list ("[]") if cross-site replication is not configured.
12 changes: 8 additions & 4 deletions core/src/main/java/org/infinispan/manager/CacheManagerInfo.java
@@ -1,5 +1,6 @@
package org.infinispan.manager;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -110,12 +111,14 @@ public String getClusterName() {
}

public String getLocalSite() {
if (cacheManager.getTransport() == null) return null;
if (cacheManager.getTransport() == null) return "local";
return cacheManager.getTransport().localSiteName();
}

private String getLogicalAddressString() {
return cacheManager.getAddress() == null ? "local" : cacheManager.getAddress().toString();
public Collection<String> getSites() {
return cacheManager.getTransport() == null ?
Collections.emptyList() :
cacheManager.getTransport().getSitesView();
}

@Override
Expand All @@ -136,7 +139,8 @@ public Json toJson() {
.set("cluster_members_physical_addresses", Json.make(getClusterMembersPhysicalAddresses()))
.set("cluster_size", getClusterSize())
.set("defined_caches", Json.make(getDefinedCaches()))
.set("local_site", getLocalSite());
.set("local_site", getLocalSite())
.set("sites_view", Json.make(getSites()));
}

static class BasicCacheInfo implements JsonSerialization {
Expand Down
Expand Up @@ -41,6 +41,10 @@
"name":"cache2",
"started":true
}
],
"local_site": "LON",
"sites_view": [
"LON",
"NYC"
]

}
Expand Up @@ -30,6 +30,10 @@ For possible values, check the https://docs.jboss.org/infinispan/9.4/apidocs/org
* `cluster_members` and `cluster_members_physical_addresses` an array of logical and physical addresses of the members of the cluster
* `cluster_size` number of members in the cluster
* `defined_caches` A list of all caches defined in the cache manager, excluding private caches but including internal caches that are accessible
* `local_site` The name of the local site. +
If cross-site replication is not configured, {brandname} returns "local".
* `sites_view` The list of sites that participate in cross-site replication. +
If cross-site replication is not configured, {brandname} returns an empty list.


[id='rest_v2_cache_manager_health']
Expand Down Expand Up @@ -431,4 +435,4 @@ DELETE /rest/v2/cache-managers/{cacheManagerName}/restores/{restoreName}

A `204` response indicates that the restore metadata is deleted.
A `202` response indicates that the restore operation is in progress and will be deleted when the operation
completes.
completes.
Expand Up @@ -258,6 +258,8 @@ public void testInfo() {
assertEquals(2, cmInfo.at("cluster_members").asList().size());
assertEquals(2, cmInfo.at("cluster_members_physical_addresses").asList().size());
assertEquals("LON-1", cmInfo.at("local_site").asString());
assertEquals(1, cmInfo.at("sites_view").asList().size());
assertEquals("LON-1", cmInfo.at("sites_view").asList().get(0));
}

@Test
Expand Down
Expand Up @@ -9,10 +9,12 @@
import static org.infinispan.xsite.XSiteAdminOperations.OFFLINE;
import static org.infinispan.xsite.XSiteAdminOperations.ONLINE;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -350,6 +352,21 @@ public void testCancelPushAllCaches() throws Exception {
assertEquals("CANCELED", pushStatusCache2.at(SFO).asString());
}

@Test
public void testXsiteView() {
assertXSiteView(jsonResponseBody(clientPerSite.get(LON).cacheManager(CACHE_MANAGER).info()));
assertXSiteView(jsonResponseBody(clientPerSite.get(NYC).cacheManager(CACHE_MANAGER).info()));
assertXSiteView(jsonResponseBody(clientPerSite.get(SFO).cacheManager(CACHE_MANAGER).info()));
}

private void assertXSiteView(Json rsp) {
List<Object> view = rsp.asJsonMap().get("sites_view").asList();
assertTrue(view.contains(LON));
assertTrue(view.contains(NYC));
assertTrue(view.contains(SFO));
assertEquals(3, view.size());
}

private int getCacheSize(RestCacheClient cacheClient) {
return Integer.parseInt(responseBody(cacheClient.size()));
}
Expand Down
Expand Up @@ -39,9 +39,6 @@ protected EmbeddedInfinispanServerDriver(InfinispanServerTestConfiguration confi
}

protected int clusterPortOffset() {
if (configuration.site() != null)
configuration.sitePortOffset();

return configuration.site() == null ? configuration.getPortOffset() : configuration.sitePortOffset();
}

Expand Down