...
Let's look at some code to see how users can use this service. The below example shows how to create a region using CMS.
Curl (any standard REST client)
Code Block | ||||
---|---|---|---|---|
| ||||
curl [-v] [-u user[:password]] -H "Content-Type: application/json" http://<locator.host>:7070/geode-management/v2/regions -XPOST -d ' { "name": "Foo", "type": "PARTITION", "group": "optional-group-name" }' Sample to copy/paste: curl -H "Content-Type: application/json" http://localhost:7070/geode-management/v2/regions -XPOST -d '{"name": "Foo","type": "PARTITION"}' curl -H "Content-Type: application/json" http://localhost:7070/geode-management/v2/regions -XPOST -d '{"name": "Foo","type": "PARTITION", "group": "optional-group-name"}' |
Java Client
To ease the interaction with the rest end point, we provided a java client version of Cluster Management Service. Here is an example to get an instance of this service and use it in any java client code. You will need to have geode-management.jar in your classpath.
About the definition of Region type , can refer to the following class:
org.apache.geode.cache.configuration.RegionType
Code Block | ||||
---|---|---|---|---|
| ||||
public static void main(String[] args) { String regionName = args[0]; ClusterManagementService cms = ClusterManagementServiceProvider.getServiceClusterManagementServiceBuilder.buildWithHostAddress().setHost("localhost", 7070).build(); BasicRegionConfig config = new BasicRegionConfig(); config.setName(regionName); config.setType(RegionType.PARTITION); config.setGroup("optional-group-name"); ClusterManagementResult result = cms.create(config); if (!result.isSuccessful()) { throw new RuntimeException( "Failure creating region: " + result.getStatusMessage()); } } |
The above example is for interacting with the Cluster Management Service's REST end point which has no ssl nor security turned on. To manage a cluster that has security and SSL enabled, you will need to provide a SSLContext
and credentials when getting the service:
Code Block | ||||
---|---|---|---|---|
| ||||
public static void main(String[] args) { String regionName = args[0]; SSLContext sslContext = SSLContext.getDefault(); HostnameVerifier hostnameVerifier = new NoopHostnameVerifier(); ClusterManagementService cms = ClusterManagementServiceProvider.getServiceClusterManagementServiceBuilder.buildWithHostAddress().setHost("localhost", 7070, sslContext, hostnameVerifier, ).setSslContext(sslContext).setHostnameVerifier(hostnameVerifiler).setCredentials("username", "password").build(); ..... } |
Note: In the context of Geode client, an instance of the ClusterManagementService
can be retrieved be calling ClusterManagementServiceProviderClusterManagementServiceBuilder.getServicebuildWithCache(clientCache)
with providing any parameters. .build(),
This will attempt to use any existing security or SSL configuration to determine the CMS REST endpoint. For this to automagically work If a SecurityManager
is enabled, the Geode properties security-username
and security-password
must be set.
You can use this java client when authoring server side code as well. Here is how one can use CMS on a server,
Code Block | ||||
---|---|---|---|---|
| ||||
public class MyFunction implements Function<String> { @Override public void execute(FunctionContext context) { //1. Get the service instance. You don't need to provide url or port or ssl information since all that information is deduced by the server automatically. // but you will need to provide a username/password if the cluster is secured. ClusterManagementService cms = ClusterManagementServiceProvider.getServiceClusterManagementServiceBuilder.buildWithCache(context.getCache()).build(); //2. Create the config object, these are just JAXB generated POJOs BasicRegionConfig regionConfig = new BasicRegionConfig(); //These are JAXB generated configuration objects regionConfig.setName("ACCOUNTS"); regionConfig.setType(RegionType.REPLICATE); //3. Invoke create, update, delete or get depending on what you want to do. ClusterManagementResult result = cms.create(regionConfig); } } |
ClusterManagementService Interface
The primary ClusterManagementService
interface is as follows:
Code Block | ||||
---|---|---|---|---|
| ||||
public interface ClusterManagementService { <T extends CacheElement> ClusterManagementResult<?, ClusterManagementResult?> create(CacheElementT config); <T ClusterManagementResultextends CacheElement> ClusterManagementResult<?, ?> delete(CacheElementT config); <T extends CacheElement> ClusterManagementResult<?, ClusterManagementResult?> update(CacheElementT config); <T extends CacheElement & CorrespondWith<R>, R extends RuntimeInfo> ClusterManagementResult<T, ClusterManagementResultR> list(T config); <T extends CacheElement & CorrespondWith<R>, R extends RuntimeInfo> ClusterManagementResult<T, R> get(T config); } |
The methods on this interface all interact with simple Java classes that map directly to elements in the Geode cache.xml
file. For example, region actions will use the RegionConfig
class. When creating Geode components, these classes are used as input to the API. When querying Geode, these classes (or subclasses) are returned as the response to those queries. See ClusterManagementResult
below. When creating or updating a Geode component, the method arguments are the state to be created or applied respectively. When deleting or list(ing) a component, the argument will act as a filter for the respective method. For example, to retrieve the confi
ClusterManagementResult
ClusterManagementResult is the result object you get when you invoke a method using cluster management service. Here is an instance of this object in json format:
Code Block | ||||
---|---|---|---|---|
| ||||
{ "memberStatuses" : { "server-1" : { "success" : true, "message" : "success" } }, "statusCode" : "OK", "statusMessage" : "successfully persisted config for cluster", "successful" : true } |
Here is an explanation of each of the fields in the result object:
"successful": a boolean value indicating the overall success/failure status of the service call. it will be true if and only if the "statusCode" value is "OK".
"statusCode": a enum field indicating the result status. Here is a list of possible values in this field:
statusCode | description |
---|---|
OK | operation is successful, configuration is persisted and realized on servers |
FAIL_TO_PERSIST | configuration is realized on servers, but some error occurred when persisting the configuration. the "statusMessage" should tell you exactly why. Normally this means some developer error. when this happened, developer needs to revert the changes or try making the same call again to try to persist it again. |
ERROR | operation is not successful. this includes precondition is not met (either service is not running or no servers available, or the configuration encountered some error when trying to be realized on one member (configuration is not fully realized on all applicable members). |
ENTITY_NOT_FOUND | entity you are trying modify/delete does not exist |
ENTITY_EXISTS | entity you are trying to create already exists |
UNAUTHORIZED | in a secured cluster, you are not authorized to do this operation |
UNAUTHENTICATED | in a secured cluster, you are not authenticated |
ILLEGAL_ARGUMENT | the configuration object you passed in for this operation is not valid. |
"StatusMessage": a detailed message about the result of the operation
"memberStatus": information about the operation status on each server.
Behind the scenes
Gliffy Diagram | ||||||||
---|---|---|---|---|---|---|---|---|
|
Pros and Cons:
Pros:
- A common interface to call either on the locator/server/client side
- A common workflow to enforce behavior consistency
- Modularized implementation. The configuration object needs to implement the additional interfaces in order to be used in this API. This allows us to add functionality gradually and per function groups.
Cons:
- Existing gfsh commands need to be refactored to use this API as well, otherwise we would have duplicate implementations, or have different behaviors between this API and gfsh commands.
- When refactoring gfsh commands, some commands' behaviors will change if they want to strictly follow this workflow, unless we add additional APIs for specific configuration objects.
Migration Strategy:
Our current commands uses numerous options to configure the behavior of the commands. We will have to follow these steps to refactor the commands.
- Combine all the command options into one configuration object inside the command itself.
- Have the command execution call the public API if the command conforms to the new workflow. In this step, the config objects needs to implement the ClusterConfigElement.
- If the command can't use the common workflow, make a special method in the API for that specific configuration object. (We need to evaluate carefully - we don't want to make too many exceptions to the common workflow.)
The above work can be divided into functional groups so that different groups can share the workload.
Once all the commands are converted using the ClusterManagementService API, each command class can be reduced to a facade that collects the options and their values, builds the config object and calls into the API. At this point, the command objects can exist only on the gfsh client.
The end architecture would look like this:
Gliffy Diagram | ||||
---|---|---|---|---|
|
Project Milestones
- API is clearly defined
- All commands are converted using this API
- Command classes exist only on a gfsh client. The GfshHttpInvoker uses the REST API to call this ClusterConfigurationService with the configuration objects directly.