Package cm_shell :: Module cmps
[hide private]
[frames] | no frames]

Source Code for Module cm_shell.cmps

  1  #!/usr/bin/env python 
  2  # Licensed to Cloudera, Inc. under one 
  3  # or more contributor license agreements.  See the NOTICE file 
  4  # distributed with this work for additional information 
  5  # regarding copyright ownership.  Cloudera, Inc. licenses this file 
  6  # to you under the Apache License, Version 2.0 (the 
  7  # "License"); you may not use this file except in compliance 
  8  # with the License.  You may obtain a copy of the License at 
  9  # 
 10  #     http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, software 
 13  # distributed under the License is distributed on an "AS IS" BASIS, 
 14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 15  # See the License for the specific language governing permissions and 
 16  # limitations under the License. 
 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  # Config 
 29  CONFIG = {'cluster': None, 'output_type': 'table', 'seperator': None} 
 30   
 31  # Initial Prompt 
 32  INIT_PROMPT = "cloudera> " 
 33   
 34  # Banner shown at interactive shell login 
 35  BANNER = "Welcome to the Cloudera Manager Console\nSelect a cluster using 'show clusters' and 'use'" 
 36   
 37  # If true, than the user is running a non-interactive shell (ie: scripting) 
 38  EXECUTE = False 
 39   
 40  # Readline fix for hyphens 
 41  readline.set_completer_delims(readline.get_completer_delims().replace('-', '')) 
 42   
 43  # Global API object 
 44  api = None 
45 46 47 -class ClouderaShell(cmd.Cmd):
48 """ 49 Interactive shell for communicating with your 50 Cloudera Cluster making use of the cm_api 51 """ 52 53 # Set initial cloudera prompt 54 prompt = INIT_PROMPT 55 56 # Set login banner 57 intro = BANNER 58 59 # Help headers 60 doc_header = "Cloudera Manager Commands" 61 undoc_header = "Other Commands" 62 63 # Initial cache is blank 64 # when autocomplete for one of these components 65 # is triggered, it will automatically cache them 66 CACHED_ROLES = {} 67 CACHED_SERVICES = None 68 CACHED_CLUSTERS = None 69
70 - def preloop(self):
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
77 - def generate_output(self, headers, rows, align=None):
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
99 - def emptyline(self):
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
105 - def set_cluster(self, cluster):
106 try: 107 cluster = api.get_cluster(cluster) 108 except ApiException: 109 print("Cluster Not Found!") 110 return None 111 112 self.cluster_object = cluster 113 if not EXECUTE: 114 print("Connected to %s" % (cluster.name)) 115 self.prompt = cluster.name + "> " 116 return True
117 118 @property
119 - def cluster(self):
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
130 - def has_cluster(self):
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
170 - def do_status(self, service):
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
181 - def do_log(self, role):
182 """ 183 Download log file for role 184 Usage: 185 > log <role> Download log 186 """ 187 self.get_log(role, log_type="full")
188
189 - def do_stdout(self, role):
190 """ 191 Download stdout file for role 192 Usage: 193 > stdout <role> Download stdout 194 """ 195 self.get_log(role, log_type="stdout")
196
197 - def do_stderr(self, role):
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 # show clusters 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 # show hosts 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 # show services 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 # Check if the user has selected a cluster 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):
262 return self.roles_autocomplete(text, line, start_index, end_index)
263
264 - def complete_stdout(self, text, line, start_index, end_index):
265 return self.roles_autocomplete(text, line, start_index, end_index)
266
267 - def complete_stderr(self, text, line, start_index, end_index):
268 return self.roles_autocomplete(text, line, start_index, end_index)
269
270 - def complete_show(self, text, line, start_index, end_index):
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
277 - def service_action(self, service, action):
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
294 - def services_autocomplete(self, text, line, start_index, end_index, append=[]):
295 if not self.cluster: 296 return None 297 else: 298 if not self.CACHED_SERVICES: 299 services = [s.name for s in api.get_cluster(self.cluster).get_all_services()] 300 self.CACHED_SERVICES = services 301 302 if text: 303 return [s for s in self.CACHED_SERVICES + append if s.startswith(text)] 304 else: 305 return self.CACHED_SERVICES + append
306
307 - def do_start_service(self, service):
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
322 - def complete_start_service(self, text, line, start_index, end_index):
323 return self.services_autocomplete(text, line, start_index, end_index)
324
325 - def do_restart_service(self, service):
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
340 - def complete_restart_service(self, text, line, start_index, end_index):
341 return self.services_autocomplete(text, line, start_index, end_index)
342
343 - def do_stop_service(self, service):
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
358 - def complete_stop_service(self, text, line, start_index, end_index):
359 return self.services_autocomplete(text, line, start_index, end_index)
360
361 - def do_use(self, cluster):
362 """ 363 Connect to Cluster 364 Usage: 365 > use <cluster> 366 """ 367 if not self.set_cluster(cluster): 368 print("Error setting cluster")
369
370 - def cluster_autocomplete(self, text, line, start_index, end_index):
371 "autocomplete for the use command, obtain list of clusters first" 372 if not self.CACHED_CLUSTERS: 373 clusters = [cluster.name for cluster in api.get_all_clusters()] 374 self.CACHED_CLUSTERS = clusters 375 376 if text: 377 return [cluster for cluster in self.CACHED_CLUSTERS if cluster.startswith(text)] 378 else: 379 return self.CACHED_CLUSTERS
380
381 - def complete_use(self, text, line, start_index, end_index):
382 return self.cluster_autocomplete(text, line, start_index, end_index)
383
384 - def do_roles(self, service):
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
421 - def complete_roles(self, text, line, start_index, end_index):
422 return self.services_autocomplete(text, line, start_index, end_index, append=["all"])
423
424 - def roles_autocomplete(self, text, line, start_index, end_index):
425 "Return full list of roles" 426 if '-' not in line: 427 # Append a dash to each service, makes for faster autocompletion of 428 # roles 429 return [s + '-' for s in self.services_autocomplete(text, line, start_index, end_index)] 430 else: 431 key, role = line.split()[1].split('-', 1) 432 if key not in self.CACHED_ROLES: 433 service = api.get_cluster(self.cluster).get_service(key) 434 roles = [] 435 for t in service.get_role_types(): 436 for r in service.get_roles_by_type(t): 437 roles.append(r.name) 438 439 self.CACHED_ROLES[key] = roles 440 441 if not role: 442 return self.CACHED_ROLES[key] 443 else: 444 return [r for r in self.CACHED_ROLES[key] if r.startswith(line.split()[1])]
445
446 - def do_start_role(self, role):
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
469 - def complete_start_role(self, text, line, start_index, end_index):
470 return self.roles_autocomplete(text, line, start_index, end_index)
471
472 - def do_restart_role(self, role):
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
495 - def complete_restart_role(self, text, line, start_index, end_index):
496 return self.roles_autocomplete(text, line, start_index, end_index)
497
498 - def do_stop_role(self, role):
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
521 - def complete_stop_role(self, text, line, start_index, end_index):
522 return self.roles_autocomplete(text, line, start_index, end_index)
523
524 - def do_stop_cluster(self, cluster):
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
538 - def complete_stop_cluster(self, text, line, start_index, end_index):
539 return self.cluster_autocomplete(text, line, start_index, end_index)
540
541 - def do_start_cluster(self, cluster):
542 """ 543 Start the cluster 544 Usage: 545 > start_cluster <cluster> 546 """ 547 try: 548 cluster = api.get_cluster(cluster) 549 cluster.start() 550 print("Starting Cluster") 551 except ApiException: 552 print("Cluster not found") 553 return None
554
555 - def complete_start_cluster(self, text, line, start_index, end_index):
556 return self.cluster_autocomplete(text, line, start_index, end_index)
557
558 - def do_version(self, cluster=None):
559 """ 560 Obtain cluster CDH version 561 Usage: 562 > version 563 or 564 > version <cluster> 565 """ 566 if not cluster: 567 if not self.has_cluster(): 568 return None 569 else: 570 cluster = api.get_cluster(self.cluster) 571 else: 572 try: 573 cluster = api.get_cluster(cluster) 574 except ApiException: 575 print("Error: Cluster not found") 576 return None 577 578 print("Version: %s" % (cluster.version))
579
580 - def complete_version(self, text, line, start_index, end_index):
581 return self.cluster_autocomplete(text, line, start_index, end_index)
582
583 - def complete_status(self, text, line, start_index, end_index):
584 return self.services_autocomplete(text, line, start_index, end_index)
585
586 587 -def main():
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 # Check if a username was suplied, if not, prompt the user 599 if not args.username: 600 args.username = raw_input("Enter Username: ") 601 602 # Check if the password was supplied, if not, prompt the user 603 if not args.password: 604 args.password = getpass.getpass("Enter Password: ") 605 606 # Attempt to authenticate using the API 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 # Check if a custom seperator was supplied for the output 625 if args.seperator: 626 CONFIG['output_type'] = 'custom' 627 CONFIG['seperator'] = args.seperator 628 629 # Check if user is attempting non-interactive shell 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