1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase;
20
21 import com.fasterxml.jackson.databind.JsonNode;
22 import com.fasterxml.jackson.databind.ObjectMapper;
23 import com.sun.jersey.api.client.Client;
24 import com.sun.jersey.api.client.ClientResponse;
25 import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.conf.Configured;
31 import org.apache.hadoop.util.ReflectionUtils;
32
33 import javax.ws.rs.core.MediaType;
34 import javax.ws.rs.core.Response;
35 import javax.ws.rs.core.UriBuilder;
36 import javax.xml.ws.http.HTTPException;
37 import java.io.IOException;
38 import java.net.URI;
39 import java.util.HashMap;
40 import java.util.Locale;
41 import java.util.Map;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public class RESTApiClusterManager extends Configured implements ClusterManager {
65
66
67 private static final String REST_API_CLUSTER_MANAGER_HOSTNAME =
68 "hbase.it.clustermanager.restapi.hostname";
69 private static final String REST_API_CLUSTER_MANAGER_USERNAME =
70 "hbase.it.clustermanager.restapi.username";
71 private static final String REST_API_CLUSTER_MANAGER_PASSWORD =
72 "hbase.it.clustermanager.restapi.password";
73 private static final String REST_API_CLUSTER_MANAGER_CLUSTER_NAME =
74 "hbase.it.clustermanager.restapi.clustername";
75 private static final String REST_API_DELEGATE_CLUSTER_MANAGER =
76 "hbase.it.clustermanager.restapi.delegate";
77
78
79 private static final String DEFAULT_SERVER_HOSTNAME = "http://localhost:7180";
80 private static final String DEFAULT_SERVER_USERNAME = "admin";
81 private static final String DEFAULT_SERVER_PASSWORD = "admin";
82 private static final String DEFAULT_CLUSTER_NAME = "Cluster 1";
83
84
85
86 private String serverHostname;
87 private String clusterName;
88
89
90
91 private static final String API_VERSION = "v6";
92
93
94 private Client client = Client.create();
95
96
97
98 private ClusterManager hBaseClusterManager;
99
100 private static final Log LOG = LogFactory.getLog(RESTApiClusterManager.class);
101
102 RESTApiClusterManager() { }
103
104 @Override
105 public void setConf(Configuration conf) {
106 super.setConf(conf);
107 if (conf == null) {
108
109 return;
110 }
111
112 final Class<? extends ClusterManager> clazz = conf.getClass(REST_API_DELEGATE_CLUSTER_MANAGER,
113 HBaseClusterManager.class, ClusterManager.class);
114 hBaseClusterManager = ReflectionUtils.newInstance(clazz, conf);
115
116 serverHostname = conf.get(REST_API_CLUSTER_MANAGER_HOSTNAME, DEFAULT_SERVER_HOSTNAME);
117 clusterName = conf.get(REST_API_CLUSTER_MANAGER_CLUSTER_NAME, DEFAULT_CLUSTER_NAME);
118 String serverUsername = conf.get(REST_API_CLUSTER_MANAGER_USERNAME, DEFAULT_SERVER_USERNAME);
119 String serverPassword = conf.get(REST_API_CLUSTER_MANAGER_PASSWORD, DEFAULT_SERVER_PASSWORD);
120
121
122 client.addFilter(new HTTPBasicAuthFilter(serverUsername, serverPassword));
123 }
124
125 @Override
126 public void start(ServiceType service, String hostname, int port) throws IOException {
127 performClusterManagerCommand(service, hostname, RoleCommand.START);
128 }
129
130 @Override
131 public void stop(ServiceType service, String hostname, int port) throws IOException {
132 performClusterManagerCommand(service, hostname, RoleCommand.STOP);
133 }
134
135 @Override
136 public void restart(ServiceType service, String hostname, int port) throws IOException {
137 performClusterManagerCommand(service, hostname, RoleCommand.RESTART);
138 }
139
140 @Override
141 public boolean isRunning(ServiceType service, String hostname, int port) throws IOException {
142 String serviceName = getServiceName(roleServiceType.get(service));
143 String hostId = getHostId(hostname);
144 String roleState = getRoleState(serviceName, service.toString(), hostId);
145 String healthSummary = getHealthSummary(serviceName, service.toString(), hostId);
146 boolean isRunning = false;
147
148
149
150 if ("STARTED".equals(roleState) && "GOOD".equals(healthSummary)) {
151 isRunning = true;
152 }
153
154 return isRunning;
155 }
156
157 @Override
158 public void kill(ServiceType service, String hostname, int port) throws IOException {
159 hBaseClusterManager.kill(service, hostname, port);
160 }
161
162 @Override
163 public void suspend(ServiceType service, String hostname, int port) throws IOException {
164 hBaseClusterManager.suspend(service, hostname, port);
165 }
166
167 @Override
168 public void resume(ServiceType service, String hostname, int port) throws IOException {
169 hBaseClusterManager.resume(service, hostname, port);
170 }
171
172
173
174
175 private void performClusterManagerCommand(ServiceType role, String hostname, RoleCommand command)
176 throws IOException {
177 LOG.info("Performing " + command + " command against " + role + " on " + hostname + "...");
178 String serviceName = getServiceName(roleServiceType.get(role));
179 String hostId = getHostId(hostname);
180 String roleName = getRoleName(serviceName, role.toString(), hostId);
181 doRoleCommand(serviceName, roleName, command);
182 }
183
184
185 private void doRoleCommand(String serviceName, String roleName, RoleCommand roleCommand) {
186 URI uri = UriBuilder.fromUri(serverHostname)
187 .path("api")
188 .path(API_VERSION)
189 .path("clusters")
190 .path(clusterName)
191 .path("services")
192 .path(serviceName)
193 .path("roleCommands")
194 .path(roleCommand.toString())
195 .build();
196 String body = "{ \"items\": [ \"" + roleName + "\" ] }";
197 LOG.info("Executing POST against " + uri + " with body " + body + "...");
198 ClientResponse response = client.resource(uri)
199 .type(MediaType.APPLICATION_JSON)
200 .post(ClientResponse.class, body);
201
202 int statusCode = response.getStatus();
203 if (statusCode != Response.Status.OK.getStatusCode()) {
204 throw new HTTPException(statusCode);
205 }
206 }
207
208
209 private String getHealthSummary(String serviceName, String roleType, String hostId)
210 throws IOException {
211 return getRolePropertyValue(serviceName, roleType, hostId, "healthSummary");
212 }
213
214
215 private String getHostId(String hostname) throws IOException {
216 String hostId = null;
217
218 URI uri = UriBuilder.fromUri(serverHostname)
219 .path("api")
220 .path(API_VERSION)
221 .path("hosts")
222 .build();
223 JsonNode hosts = getJsonNodeFromURIGet(uri);
224 if (hosts != null) {
225
226 for (JsonNode host : hosts) {
227 if (host.get("hostname").textValue().equals(hostname)) {
228 hostId = host.get("hostId").textValue();
229 break;
230 }
231 }
232 } else {
233 hostId = null;
234 }
235
236 return hostId;
237 }
238
239
240 private JsonNode getJsonNodeFromURIGet(URI uri) throws IOException {
241 LOG.info("Executing GET against " + uri + "...");
242 ClientResponse response = client.resource(uri)
243 .accept(MediaType.APPLICATION_JSON_TYPE)
244 .get(ClientResponse.class);
245
246 int statusCode = response.getStatus();
247 if (statusCode != Response.Status.OK.getStatusCode()) {
248 throw new HTTPException(statusCode);
249 }
250
251 return new ObjectMapper().readTree(response.getEntity(String.class)).get("items");
252 }
253
254
255 private String getRoleName(String serviceName, String roleType, String hostId)
256 throws IOException {
257 return getRolePropertyValue(serviceName, roleType, hostId, "name");
258 }
259
260
261 private String getRolePropertyValue(String serviceName, String roleType, String hostId,
262 String property) throws IOException {
263 String roleValue = null;
264 URI uri = UriBuilder.fromUri(serverHostname)
265 .path("api")
266 .path(API_VERSION)
267 .path("clusters")
268 .path(clusterName)
269 .path("services")
270 .path(serviceName)
271 .path("roles")
272 .build();
273 JsonNode roles = getJsonNodeFromURIGet(uri);
274 if (roles != null) {
275
276 for (JsonNode role : roles) {
277 if (role.get("hostRef").get("hostId").textValue().equals(hostId) &&
278 role.get("type")
279 .textValue()
280 .toLowerCase(Locale.ROOT)
281 .equals(roleType.toLowerCase(Locale.ROOT))) {
282 roleValue = role.get(property).textValue();
283 break;
284 }
285 }
286 }
287
288 return roleValue;
289 }
290
291
292 private String getRoleState(String serviceName, String roleType, String hostId)
293 throws IOException {
294 return getRolePropertyValue(serviceName, roleType, hostId, "roleState");
295 }
296
297
298 private String getServiceName(Service service) throws IOException {
299 String serviceName = null;
300 URI uri = UriBuilder.fromUri(serverHostname)
301 .path("api")
302 .path(API_VERSION)
303 .path("clusters")
304 .path(clusterName)
305 .path("services")
306 .build();
307 JsonNode services = getJsonNodeFromURIGet(uri);
308 if (services != null) {
309
310 for (JsonNode serviceEntry : services) {
311 if (serviceEntry.get("type").textValue().equals(service.toString())) {
312 serviceName = serviceEntry.get("name").textValue();
313 break;
314 }
315 }
316 }
317
318 return serviceName;
319 }
320
321
322
323
324
325
326
327
328 private enum RoleCommand {
329 START, STOP, RESTART;
330
331
332 @Override
333 public String toString() {
334 return name().toLowerCase(Locale.ROOT);
335 }
336 }
337
338
339
340
341 private static Map<ServiceType, Service> roleServiceType = new HashMap<ServiceType, Service>();
342 static {
343 roleServiceType.put(ServiceType.HADOOP_NAMENODE, Service.HDFS);
344 roleServiceType.put(ServiceType.HADOOP_DATANODE, Service.HDFS);
345 roleServiceType.put(ServiceType.HADOOP_JOBTRACKER, Service.MAPREDUCE);
346 roleServiceType.put(ServiceType.HADOOP_TASKTRACKER, Service.MAPREDUCE);
347 roleServiceType.put(ServiceType.HBASE_MASTER, Service.HBASE);
348 roleServiceType.put(ServiceType.HBASE_REGIONSERVER, Service.HBASE);
349 }
350
351 private enum Service {
352 HBASE, HDFS, MAPREDUCE
353 }
354 }