1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import sys
19 import getpass
20 import argparse
21 import readline
22 import os
23 import cmd
24 from prettytable import PrettyTable
25 from cm_api.api_client import ApiResource, ApiException
26 from urllib2 import URLError
27
28
29 CONFIG = {'cluster': None, 'output_type': 'table', 'seperator': None}
30
31
32 INIT_PROMPT = "cloudera> "
33
34
35 BANNER = "Welcome to the Cloudera Manager Console\nSelect a cluster using 'show clusters' and 'use'"
36
37
38 EXECUTE = False
39
40
41 readline.set_completer_delims(readline.get_completer_delims().replace('-', ''))
42
43
44 api = None
48 """
49 Interactive shell for communicating with your
50 Cloudera Cluster making use of the cm_api
51 """
52
53
54 prompt = INIT_PROMPT
55
56
57 intro = BANNER
58
59
60 doc_header = "Cloudera Manager Commands"
61 undoc_header = "Other Commands"
62
63
64
65
66 CACHED_ROLES = {}
67 CACHED_SERVICES = None
68 CACHED_CLUSTERS = None
69
71 "Checks if the cluster was pre-defined"
72 if CONFIG['cluster']:
73 self.set_cluster(CONFIG['cluster'])
74 else:
75 self.cluster_object = None
76
78 if CONFIG['output_type'] == "table":
79 table = PrettyTable(headers)
80 if align:
81 for h in align:
82 table.align[h] = 'l'
83
84 for r in rows:
85 table.add_row(r)
86 print(table)
87
88 if CONFIG['output_type'] == "csv":
89 print(','.join(headers))
90 for r in rows:
91 print(','.join(r))
92
93 if CONFIG['output_type'] == "custom":
94 SEP = CONFIG['seperator']
95 print(SEP.join(headers))
96 for r in rows:
97 print(SEP.join(r))
98
100 """Called each time a user hits enter, by
101 default it will redo the last command, this
102 is an extension so it does nothing."""
103 pass
104
117
118 @property
120 if EXECUTE:
121 if not self.set_cluster(CONFIG['cluster']):
122 sys.exit(1)
123 return self.cluster_object.name
124
125 if self.cluster_object:
126 return self.cluster_object.name
127 else:
128 return None
129
131 if not self.cluster:
132 print("Error: No cluster currently selected")
133 return None
134 else:
135 return True
136
137 - def get_log(self, role, log_type=None):
138 if not role:
139 return None
140
141 if not self.has_cluster():
142 return None
143
144 if '-' not in role:
145 print("Please enter a valid role name")
146 return None
147
148 try:
149 service = api.get_cluster(self.cluster).get_service(role.split('-')[0])
150 role = service.get_role(role)
151 try:
152 if EXECUTE:
153 output = sys.stdout
154 else:
155 output = os.popen("less", "w")
156 if log_type == "full":
157 output.write(role.get_full_log())
158 if log_type == "stdout":
159 output.write(role.get_stdout())
160 if log_type == "stderr":
161 output.write(role.get_stderr())
162
163 if not EXECUTE:
164 output.close()
165 except IOError:
166 pass
167 except ApiException:
168 print("Error: Role or Service Not Found")
169
171 """
172 List all services on the cluster
173 Usage:
174 > status
175 """
176 if service:
177 self.do_show("services", single=service)
178 else:
179 self.do_show("services")
180
182 """
183 Download log file for role
184 Usage:
185 > log <role> Download log
186 """
187 self.get_log(role, log_type="full")
188
190 """
191 Download stdout file for role
192 Usage:
193 > stdout <role> Download stdout
194 """
195 self.get_log(role, log_type="stdout")
196
198 """
199 Download stderr file for role
200 Usage:
201 > stderr <role> Download stderr
202 """
203 self.get_log(role, log_type="stderr")
204
205 - def do_show(self, option, single=None):
206 """
207 General System Information
208 Usage:
209 > show clusters list of clusters this CM manages
210 > show hosts list of all hosts CM manages
211 > show services list of all services on this cluster
212 including their health.
213 """
214 headers = []
215 rows = []
216 align = None
217
218 if option == "clusters":
219 "Display list of clusters on system"
220 headers = ["CLUSTER NAME"]
221 clusters = api.get_all_clusters()
222 for cluster in clusters:
223 rows.append([cluster.name])
224
225
226 if option == "hosts":
227 "Display a list of hosts avaiable on the system"
228 headers = ["HOSTNAME", "IP ADDRESS", "RACK"]
229 align = ["HOSTNAME", "IP ADDRESS", "RACK"]
230 for host in api.get_all_hosts():
231 rows.append([host.hostname, host.ipAddress, host.rackId])
232
233
234 if option == "services":
235 "Show list of services on the cluster"
236 headers = ["NAME", "SERVICE", "STATUS", "HEALTH", "CONFIG"]
237 align = ["NAME", "SERVICE"]
238
239
240 if not self.has_cluster():
241 print("Error: Please select a cluster first")
242 return None
243
244 if not single:
245 for s in api.get_cluster(self.cluster).get_all_services():
246 if s.configStale:
247 config = "STALE"
248 else:
249 config = "UP TO DATE"
250 rows.append([s.name, s.type, s.serviceState, s.healthSummary, config])
251 else:
252 s = api.get_cluster(self.cluster).get_service(single)
253 if s.configStale:
254 config = "STALE"
255 else:
256 config = "UP TO DATE"
257 rows.append([s.name, s.type, s.serviceState, s.healthSummary, config])
258
259 self.generate_output(headers, rows, align=align)
260
261 - def complete_log(self, text, line, start_index, end_index):
263
266
269
271 show_commands = ["clusters", "hosts", "services"]
272 if text:
273 return [c for c in show_commands if c.startswith(text)]
274 else:
275 return show_commands
276
278 "Perform given action on service for the selected cluster"
279 try:
280 service = api.get_cluster(self.cluster).get_service(service)
281 except ApiException:
282 print("Service not found")
283 return None
284
285 if action == "start":
286 service.start()
287 if action == "restart":
288 service.restart()
289 if action == "stop":
290 service.stop()
291
292 return True
293
306
308 """
309 Start a service
310 Usage:
311 > start_service <service>
312 """
313 if not self.has_cluster():
314 return None
315
316 if self.service_action(service=service, action="start"):
317 print("%s is being started" % (service))
318 else:
319 print("Error starting service")
320 return None
321
324
326 """
327 Restart a service
328 Usage:
329 > restart_service <service>
330 """
331 if not self.has_cluster():
332 return None
333
334 if self.service_action(service=service, action="restart"):
335 print("%s is being restarted" % (service))
336 else:
337 print("Error restarting service")
338 return None
339
342
344 """
345 Stop a service
346 Usage:
347 > stop_service <service>
348 """
349 if not self.has_cluster():
350 return None
351
352 if self.service_action(service=service, action="stop"):
353 print("%s is being stopped" % (service))
354 else:
355 print("Error stopping service")
356 return None
357
360
362 """
363 Connect to Cluster
364 Usage:
365 > use <cluster>
366 """
367 if not self.set_cluster(cluster):
368 print("Error setting cluster")
369
380
381 - def complete_use(self, text, line, start_index, end_index):
383
385 """
386 Role information
387 Usage:
388 > roles <servicename> Display role information for service
389 > roles all Display all role information for cluster
390 """
391 if not self.has_cluster():
392 return None
393
394 if not service:
395 return None
396
397 if service == "all":
398 if not self.CACHED_SERVICES:
399 self.services_autocomplete('', service, 0, 0)
400
401 for s in self.CACHED_SERVICES:
402 print("= " + s.upper() + " =")
403 self.do_roles(s)
404 return None
405 try:
406 service = api.get_cluster(self.cluster).get_service(service)
407 headers = ["ROLE TYPE", "HOST", "ROLE NAME", "STATE", "HEALTH", "CONFIG"]
408 align = ["ROLE TYPE", "ROLE NAME", "HOST"]
409 rows = []
410 for roletype in service.get_role_types():
411 for role in service.get_roles_by_type(roletype):
412 if role.configStale:
413 config = "STALE"
414 else:
415 config = "UP TO DATE"
416 rows.append([role.type, role.hostRef.hostId, role.name, role.roleState, role.healthSummary, config])
417 self.generate_output(headers, rows, align=align)
418 except ApiException:
419 print("Service not found")
420
423
445
447 """
448 Start a role
449 Usage:
450 > start_role <role> Restarts this role
451 """
452 if not role:
453 return None
454
455 if not self.has_cluster():
456 return None
457
458 if '-' not in role:
459 print("Please enter a valid role name")
460 return None
461
462 try:
463 service = api.get_cluster(self.cluster).get_service(role.split('-')[0])
464 service.start_roles(role)
465 print("Starting Role")
466 except ApiException:
467 print("Error: Role or Service Not Found")
468
471
473 """
474 Restart a role
475 Usage:
476 > restart_role <role> Restarts this role
477 """
478 if not role:
479 return None
480
481 if not self.has_cluster():
482 return None
483
484 if '-' not in role:
485 print("Please enter a valid role name")
486 return None
487
488 try:
489 service = api.get_cluster(self.cluster).get_service(role.split('-')[0])
490 service.restart_roles(role)
491 print("Restarting Role")
492 except ApiException:
493 print("Error: Role or Service Not Found")
494
497
499 """
500 Stop a role
501 Usage:
502 > stop_role <role> Stops this role
503 """
504 if not role:
505 return None
506
507 if not self.has_cluster():
508 return None
509
510 if '-' not in role:
511 print("Please enter a valid role name")
512 return None
513
514 try:
515 service = api.get_cluster(self.cluster).get_service(role.split('-')[0])
516 service.stop_roles(role)
517 print("Stopping Role")
518 except ApiException:
519 print("Error: Role or Service Not Found")
520
523
525 """
526 Completely stop the cluster
527 Usage:
528 > stop_cluster <cluster>
529 """
530 try:
531 cluster = api.get_cluster(cluster)
532 cluster.stop()
533 print("Stopping Cluster")
534 except ApiException:
535 print("Cluster not found")
536 return None
537
540
554
557
579
582
585
588 parser = argparse.ArgumentParser(description='Cloudera Manager Shell')
589 parser.add_argument('-H', '--host', '--hostname', action='store', dest='hostname', required=True)
590 parser.add_argument('-p', '--port', action='store', dest='port', type=int, default=7180)
591 parser.add_argument('-u', '--user', '--username', action='store', dest='username')
592 parser.add_argument('-c', '--cluster', action='store', dest='cluster')
593 parser.add_argument('--password', action='store', dest='password')
594 parser.add_argument('-e', '--execute', action='store', dest='execute')
595 parser.add_argument('-s', '--seperator', action='store', dest='seperator')
596 args = parser.parse_args()
597
598
599 if not args.username:
600 args.username = raw_input("Enter Username: ")
601
602
603 if not args.password:
604 args.password = getpass.getpass("Enter Password: ")
605
606
607 global api
608 api = ApiResource(args.hostname, args.port, args.username, args.password)
609 try:
610 api.echo("ping")
611 except ApiException:
612 try:
613 api = ApiResource(args.hostname, args.port, args.username, args.password, version=1)
614 api.echo("ping")
615 except ApiException:
616 print("Unable to Authenticate")
617 sys.exit(1)
618 except URLError:
619 print("Error: Could not connect to %s" % (args.hostname))
620 sys.exit(1)
621
622 CONFIG['cluster'] = args.cluster
623
624
625 if args.seperator:
626 CONFIG['output_type'] = 'custom'
627 CONFIG['seperator'] = args.seperator
628
629
630 if args.execute:
631 EXECUTE = True
632 shell = ClouderaShell()
633 for command in args.execute.split(';'):
634 shell.onecmd(command)
635 sys.exit(0)
636
637 try:
638 ClouderaShell().cmdloop()
639 except KeyboardInterrupt:
640 sys.stdout.write("\n")
641 sys.exit(0)
642
643 if __name__ == "__main__":
644 main()
645