Package starcluster :: Module cli
[hide private]
[frames] | no frames]

Source Code for Module starcluster.cli

   1  #!/usr/bin/env python 
   2  """ 
   3  starcluster [global-opts] action [action-opts] [<action-args> ...] 
   4  """ 
   5   
   6  __description__ = """ 
   7  StarCluster - (http://web.mit.edu/starcluster) 
   8  Software Tools for Academics and Researchers (STAR) 
   9  Please submit bug reports to starcluster@mit.edu 
  10  """ 
  11   
  12  __moredoc__ = """ 
  13  Each command consists of a class, which has the following properties: 
  14   
  15  - Must have a class member 'names' which is a list of the names for the command; 
  16   
  17  - Can optionally have a addopts(self, parser) method which adds options to the 
  18    given parser. This defines command options. 
  19  """ 
  20   
  21  from starcluster import __version__ 
  22  __author__ = "Justin Riley <justin.t.riley@gmail.com>" 
  23   
  24  import os 
  25  import sys 
  26  import time 
  27  import socket 
  28  import signal 
  29  from datetime import datetime, timedelta 
  30  from pprint import pprint, pformat 
  31   
  32  # hack for now to ignore pycrypto 2.0.1 using md5 and sha 
  33  # why is pycrypto 2.1.0 not on pypi? 
  34  import warnings 
  35  warnings.filterwarnings("ignore", category=DeprecationWarning) 
  36   
  37  from boto.exception import EC2ResponseError, S3ResponseError 
  38  from starcluster import cluster 
  39  from starcluster import node 
  40  from starcluster import config 
  41  from starcluster import exception 
  42  from starcluster import static 
  43  from starcluster import optcomplete 
  44  from starcluster import image 
  45  from starcluster import volume 
  46  from starcluster import utils 
  47  from starcluster.templates import experimental 
  48  from starcluster.logger import log, console, DEBUG 
49 50 #try: 51 #import optcomplete 52 #CmdComplete = optcomplete.CmdComplete 53 #except ImportError,e: 54 #optcomplete, CmdComplete = None, object 55 56 -class CmdBase(optcomplete.CmdComplete):
57 parser = None 58 opts = None 59 gopts = None 60 61 @property
62 - def goptions_dict(self):
63 return dict(self.gopts.__dict__)
64 65 @property
66 - def options_dict(self):
67 return dict(self.opts.__dict__)
68 69 @property
70 - def specified_options_dict(self):
71 """ only return options with non-None value """ 72 specified = {} 73 options = self.options_dict 74 for opt in options: 75 if options[opt] is not None: 76 specified[opt] = options[opt] 77 return specified
78 79 @property
80 - def cfg(self):
81 return self.goptions_dict.get('CONFIG')
82
83 - def cancel_command(self, signum, frame):
84 print 85 log.info("Exiting...") 86 sys.exit(1)
87
88 - def catch_ctrl_c(self, handler=None):
89 handler = handler or self.cancel_command 90 signal.signal(signal.SIGINT, handler)
91
92 - def warn_experimental(self, msg):
93 for l in msg.splitlines(): 94 log.warn(l) 95 num_secs = 10 96 r = range(1,num_secs+1) 97 r.reverse() 98 print 99 log.warn("Waiting %d seconds before continuing..." % num_secs) 100 log.warn("Press CTRL-C to cancel...") 101 for i in r: 102 sys.stdout.write('%d...' % i) 103 sys.stdout.flush() 104 time.sleep(1) 105 print
106
107 -class CmdStart(CmdBase):
108 """ 109 start [options] <cluster_tag> 110 111 Start a new cluster 112 113 Example: 114 115 $ starcluster start mynewcluster 116 117 This will launch a cluster tagged "mynewcluster" using the 118 settings from the "default" cluster template defined 119 in the configuration file. The default cluster template 120 is the template that has DEFAULT=True in the configuration file. 121 122 $ starcluster start --cluster largecluster mynewcluster 123 124 This will do the same thing only using the "largecluster" 125 cluster template rather than the "default" template assuming 126 "largecluster" has been defined in the configuration file. 127 """ 128 names = ['start'] 129 130 tag = None 131 132 @property
133 - def completer(self):
134 if optcomplete: 135 try: 136 cfg = config.StarClusterConfig() 137 cfg.load() 138 return optcomplete.ListCompleter(cfg.get_cluster_names()) 139 except Exception, e: 140 log.error('something went wrong fix me: %s' % e)
141
142 - def addopts(self, parser):
143 opt = parser.add_option("-x","--no-create", dest="no_create", 144 action="store_true", default=False, help="Do not launch new ec2 " + \ 145 "instances when starting cluster (uses existing instances instead)") 146 opt = parser.add_option("-v","--validate-only", dest="validate_only", 147 action="store_true", default=False, help="Only validate cluster " + \ 148 "settings, do not start a cluster") 149 parser.add_option("-l","--login-master", dest="login_master", 150 action="store_true", default=False, 151 help="ssh to ec2 cluster master node after launch") 152 opt = parser.add_option("-b","--bid", dest="spot_bid", 153 action="store", type="float", default=None, 154 help="Requests spot instances instead of the usual flat rate " + \ 155 "instances. Uses SPOT_BID as max bid for the request. " + \ 156 "(EXPERIMENTAL)") 157 parser.add_option("-c","--cluster-template", dest="cluster_template", 158 action="store", type="string", default=None, 159 help="cluster template to use from the config file") 160 parser.add_option("-d","--description", dest="cluster_description", 161 action="store", type="string", 162 default="Cluster requested at %s" % time.strftime("%Y%m%d%H%M"), 163 help="brief description of cluster") 164 parser.add_option("-s","--cluster-size", dest="cluster_size", 165 action="store", type="int", default=None, 166 help="number of ec2 instances to launch") 167 parser.add_option("-u","--cluster-user", dest="cluster_user", 168 action="store", type="string", default=None, 169 help="name of user to create on cluster (defaults to sgeadmin)") 170 opt = parser.add_option("-S","--cluster-shell", dest="cluster_shell", 171 action="store", choices=static.AVAILABLE_SHELLS.keys(), 172 default=None, help="shell for cluster user (defaults to bash)") 173 if optcomplete: 174 opt.completer = optcomplete.ListCompleter(opt.choices) 175 parser.add_option("-m","--master-image-id", dest="master_image_id", 176 action="store", type="string", default=None, 177 help="AMI to use when launching master") 178 parser.add_option("-n","--node-image-id", dest="node_image_id", 179 action="store", type="string", default=None, 180 help="AMI to use when launching nodes") 181 opt = parser.add_option("-I","--master-instance-type", dest="master_instance_type", 182 action="store", choices=static.INSTANCE_TYPES.keys(), 183 default=None, help="specify machine type for the master instance") 184 opt = parser.add_option("-i","--node-instance-type", dest="node_instance_type", 185 action="store", choices=static.INSTANCE_TYPES.keys(), 186 default=None, help="specify machine type for the node instances") 187 if optcomplete: 188 opt.completer = optcomplete.ListCompleter(opt.choices) 189 parser.add_option("-a","--availability-zone", dest="availability_zone", 190 action="store", type="string", default=None, 191 help="availability zone to launch ec2 instances in") 192 parser.add_option("-k","--keyname", dest="keyname", 193 action="store", type="string", default=None, 194 help="name of the AWS keypair to use when launching the cluster") 195 parser.add_option("-K","--key-location", dest="key_location", 196 action="store", type="string", default=None, metavar="FILE", 197 help="path to ssh private key used for this cluster")
198
199 - def cancel_command(self, signum, frame):
201
202 - def execute(self, args):
203 if len(args) != 1: 204 self.parser.error("please specify a <tag_name> for this cluster") 205 cfg = self.cfg 206 use_experimental = cfg.globals.get('enable_experimental') 207 if self.opts.spot_bid is not None and not use_experimental: 208 raise exception.ExperimentalFeature('Using spot instances') 209 tag = self.tag = args[0] 210 template = self.opts.cluster_template 211 if not template: 212 template = cfg.get_default_cluster_template(tag) 213 log.info("Using default cluster template: %s" % template) 214 scluster = cfg.get_cluster_template(template, tag) 215 scluster.update(self.specified_options_dict) 216 if cluster.cluster_exists(tag,cfg) and not self.opts.no_create: 217 raise exception.ClusterExists(tag) 218 #from starcluster.utils import ipy_shell; ipy_shell(); 219 check_running = self.opts.no_create 220 if check_running: 221 log.info("Validating existing instances...") 222 if scluster.is_running_valid(): 223 log.info('Existing instances are valid') 224 else: 225 log.error('existing instances are not compatible with cluster' + \ 226 ' template settings') 227 sys.exit(1) 228 log.info("Validating cluster template settings...") 229 if scluster.is_valid(): 230 log.info('Cluster template settings are valid') 231 if not self.opts.validate_only: 232 if self.opts.spot_bid is not None: 233 cmd = ' '.join(sys.argv[1:]) + ' --no-create' 234 launch_group = static.SECURITY_GROUP_TEMPLATE % tag 235 msg = experimental.spotmsg % {'cmd':cmd, 236 'launch_group': launch_group} 237 self.warn_experimental(msg) 238 self.catch_ctrl_c() 239 scluster.start(create=not self.opts.no_create) 240 if self.opts.login_master: 241 cluster.ssh_to_master(tag, self.cfg) 242 else: 243 log.error('settings for cluster template "%s" are not valid' % template) 244 sys.exit(1)
245
246 -class CmdStop(CmdBase):
247 """ 248 stop [options] <cluster> 249 250 Shutdown a running cluster 251 252 Example: 253 254 $ starcluster stop mycluster 255 256 This will stop a currently running cluster tagged "mycluster" 257 """ 258 names = ['stop'] 259 260 @property
261 - def completer(self):
262 if optcomplete: 263 try: 264 cfg = config.StarClusterConfig() 265 cfg.load() 266 clusters = cluster.get_cluster_security_groups(cfg) 267 completion_list = [sg.name.replace(static.SECURITY_GROUP_PREFIX+'-','') for sg in clusters] 268 return optcomplete.ListCompleter(completion_list) 269 except Exception, e: 270 log.error('something went wrong fix me: %s' % e)
271
272 - def addopts(self, parser):
273 opt = parser.add_option("-c","--confirm", dest="confirm", 274 action="store_true", default=False, 275 help="Do not prompt for confirmation, " + \ 276 "just shutdown the cluster")
277
278 - def execute(self, args):
279 if not args: 280 self.parser.error("please specify a cluster") 281 cfg = self.cfg 282 for cluster_name in args: 283 cl = cluster.get_cluster(cluster_name,cfg) 284 if not self.opts.confirm: 285 resp = raw_input("Shutdown cluster %s (y/n)? " % cluster_name) 286 if resp not in ['y','Y', 'yes']: 287 log.info("Aborting...") 288 continue 289 cluster.stop_cluster(cluster_name, cfg)
290
291 -class CmdSshMaster(CmdBase):
292 """ 293 sshmaster [options] <cluster> 294 295 SSH to a cluster's master node 296 297 Example: 298 299 $ sshmaster mycluster 300 """ 301 names = ['sshmaster'] 302 303 @property
304 - def completer(self):
305 if optcomplete: 306 try: 307 cfg = config.StarClusterConfig() 308 cfg.load() 309 clusters = cluster.get_cluster_security_groups(cfg) 310 completion_list = [sg.name.replace(static.SECURITY_GROUP_PREFIX+'-','') for sg in clusters] 311 return optcomplete.ListCompleter(completion_list) 312 except Exception, e: 313 log.error('something went wrong fix me: %s' % e)
314
315 - def addopts(self, parser):
316 opt = parser.add_option("-u","--user", dest="USER", action="store", 317 type="string", default='root', 318 help="login as USER (defaults to root)")
319
320 - def execute(self, args):
321 if not args: 322 self.parser.error("please specify a cluster") 323 for arg in args: 324 cluster.ssh_to_master(arg, self.cfg, user=self.opts.USER)
325
326 -class CmdSshNode(CmdBase):
327 """ 328 sshnode <cluster> <node> 329 330 SSH to a cluster node 331 332 Examples: 333 334 $ starcluster sshnode mycluster master 335 $ starcluster sshnode mycluster node001 336 ... 337 338 or same thing in shorthand: 339 340 $ starcluster sshnode mycluster 0 341 $ starcluster sshnode mycluster 1 342 ... 343 """ 344 names = ['sshnode'] 345 346 @property
347 - def completer(self):
348 if optcomplete: 349 try: 350 cfg = config.StarClusterConfig() 351 cfg.load() 352 clusters = cluster.get_cluster_security_groups(cfg) 353 completion_list = [sg.name.replace(static.SECURITY_GROUP_PREFIX+'-','') for sg in clusters] 354 max_num_nodes = 0 355 for scluster in clusters: 356 num_instances = len(scluster.instances()) 357 if num_instances > max_num_nodes: 358 max_num_nodes = num_instances 359 completion_list.extend(['master']) 360 completion_list.extend([str(i) for i in range(0,num_instances)]) 361 completion_list.extend(["node%03d" % i for i in range(1,num_instances)]) 362 return optcomplete.ListCompleter(completion_list) 363 except Exception, e: 364 print e 365 log.error('something went wrong fix me: %s' % e)
366
367 - def addopts(self, parser):
368 opt = parser.add_option("-u","--user", dest="USER", action="store", 369 type="string", default='root', 370 help="login as USER (defaults to root)")
371
372 - def execute(self, args):
373 if len(args) != 2: 374 self.parser.error("please specify a <cluster> and <node> to connect to") 375 scluster = args[0] 376 ids = args[1:] 377 for id in ids: 378 cluster.ssh_to_cluster_node(scluster, id, self.cfg, 379 user=self.opts.USER)
380
381 -class CmdSshInstance(CmdBase):
382 """ 383 sshintance [options] <instance-id> 384 385 SSH to an EC2 instance 386 387 Examples: 388 389 $ starcluster sshinstance i-14e9157c 390 $ starcluster sshinstance ec2-123-123-123-12.compute-1.amazonaws.com 391 392 """ 393 names = ['sshinstance'] 394 395 @property
396 - def completer(self):
397 if optcomplete: 398 try: 399 cfg = config.StarClusterConfig() 400 cfg.load() 401 ec2 = cfg.get_easy_ec2() 402 instances = ec2.get_all_instances() 403 completion_list = [i.id for i in instances] 404 completion_list.extend([i.dns_name for i in instances]) 405 return optcomplete.ListCompleter(completion_list) 406 except Exception, e: 407 log.error('something went wrong fix me: %s' % e)
408
409 - def addopts(self, parser):
410 opt = parser.add_option("-u","--user", dest="USER", action="store", 411 type="string", default='root', 412 help="login as USER (defaults to root)")
413
414 - def execute(self, args):
415 if not args: 416 self.parser.error( 417 "please specify an instance id or dns name to connect to") 418 for arg in args: 419 # user specified dns name or instance id 420 instance = args[0] 421 node.ssh_to_node(instance, self.cfg, user=self.opts.USER)
422
423 -class CmdListClusters(CmdBase):
424 """ 425 listclusters 426 427 List all active clusters 428 """ 429 names = ['listclusters']
430 - def execute(self, args):
431 cfg = self.cfg 432 cluster.list_clusters(cfg)
433
434 -class CmdCreateImage(CmdBase):
435 """ 436 createimage [options] <instance-id> <image_name> <bucket> 437 438 Create a new image (AMI) from a currently running EC2 instance 439 440 Example: 441 442 $ starcluster createimage i-999999 my-new-image mybucket 443 444 NOTE: It is recommended not to create a new StarCluster AMI from 445 an instance launched by StarCluster. Rather, launch a single 446 StarCluster instance using ElasticFox or the EC2 API tools, modify 447 it to your liking, and then use this command to create a new AMI from 448 the running instance. 449 """ 450 names = ['createimage'] 451 452 bucket = None 453 image_name = None 454 455 @property
456 - def completer(self):
457 if optcomplete: 458 try: 459 cfg = config.StarClusterConfig() 460 cfg.load() 461 ec2 = cfg.get_easy_ec2() 462 instances = ec2.get_all_instances() 463 completion_list = [i.id for i in instances] 464 completion_list.extend([i.dns_name for i in instances]) 465 return optcomplete.ListCompleter(completion_list) 466 except Exception, e: 467 log.error('something went wrong fix me: %s' % e)
468
469 - def addopts(self, parser):
470 opt = parser.add_option( 471 "-c","--confirm", dest="confirm", 472 action="store_true", default=False, 473 help="Do not warn about re-imaging StarCluster instances") 474 opt = parser.add_option( 475 "-r","--remove-image-files", dest="remove_image_files", 476 action="store_true", default=False, 477 help="Remove generated image files on the instance after registering") 478 opt = parser.add_option( 479 "-d","--description", dest="description", action="store", 480 type="string", default=time.strftime("%Y%m%d%H%M"), 481 help="short description of this AMI") 482 opt = parser.add_option( 483 "-k","--kernel-id", dest="kernel_id", action="store", 484 type="string", default=None, 485 help="kernel id for the new AMI") 486 opt = parser.add_option( 487 "-R","--ramdisk-id", dest="ramdisk_id", action="store", 488 type="string", default=None, 489 help="ramdisk id for the new AMI")
490
491 - def cancel_command(self, signum, frame):
493
494 - def execute(self, args):
495 if len(args) != 3: 496 self.parser.error('you must specify an instance-id, image name, and bucket') 497 instanceid, image_name, bucket = args 498 self.bucket = bucket 499 self.image_name = image_name 500 cfg = self.cfg 501 ec2 = cfg.get_easy_ec2() 502 i = ec2.get_instance(instanceid) 503 if not self.opts.confirm: 504 for group in i.groups: 505 if group.id.startswith(static.SECURITY_GROUP_PREFIX): 506 log.warn("Instance %s is a StarCluster instance" % i.id) 507 print 508 log.warn("Creating an image from a StarCluster instance " + \ 509 "can lead to problems when attempting to use the resulting " + \ 510 "image with StarCluster later on") 511 print 512 log.warn( 513 "The recommended way to re-image a StarCluster AMI is " + \ 514 "to launch a single instance using either ElasticFox, the " +\ 515 "EC2 command line tools, or the AWS management console. " +\ 516 "Then login to the instance, modify it, and use this " + \ 517 "command to create a new AMI from it.") 518 print 519 resp = raw_input("Continue anyway (y/n)? ") 520 if resp not in ['y','Y','yes']: 521 log.info("Aborting...") 522 sys.exit(1) 523 break 524 self.catch_ctrl_c() 525 ami_id = image.create_image(instanceid, image_name, bucket, cfg, 526 **self.specified_options_dict) 527 log.info("Your new AMI id is: %s" % ami_id)
528
529 -class CmdCreateVolume(CmdBase):
530 """ 531 createvolume [options] <volume_size> <volume_zone> 532 533 Create a new EBS volume for use with StarCluster 534 """ 535 536 names = ['createvolume'] 537
538 - def addopts(self, parser):
539 opt = parser.add_option( 540 "-i","--image-id", dest="image_id", 541 action="store", type="string", default=None, 542 help="Specifies the AMI to use when launching volume host instance") 543 opt = parser.add_option( 544 "-n","--no-shutdown", dest="shutdown_instance", 545 action="store_false", default=True, 546 help="Do not shutdown volume host instance after creating volume")
547 #opt = parser.add_option( 548 #"-a","--add-to-config", dest="add_to_cfg", 549 #action="store_true", default=False, 550 #help="Add a new volume section to the config after creating volume") 551
552 - def cancel_command(self, signum, frame):
554
555 - def execute(self, args):
556 if len(args) != 2: 557 self.parser.error("you must specify a size (in GB) and an availability zone") 558 size, zone = args 559 vc = volume.VolumeCreator(self.cfg, **self.specified_options_dict) 560 self.catch_ctrl_c() 561 volid = vc.create(size, zone) 562 if volid: 563 log.info("Your new %sGB volume %s has been created successfully" % \ 564 (size,volid)) 565 else: 566 log.error("failed to create new volume")
567
568 -class CmdListZones(CmdBase):
569 """ 570 listzones 571 572 List all EC2 availability zones 573 """ 574 names = ['listzones']
575 - def execute(self, args):
576 ec2 = self.cfg.get_easy_ec2() 577 ec2.list_zones()
578
579 -class CmdListImages(CmdBase):
580 """ 581 listimages [options] 582 583 List all registered EC2 images (AMIs) 584 """ 585 names = ['listimages'] 586
587 - def addopts(self, parser):
588 opt = parser.add_option( 589 "-x","--executable-by-me", dest="executable", 590 action="store_true", default=False, 591 help="Show images that you have permission to execute")
592
593 - def execute(self, args):
594 ec2 = self.cfg.get_easy_ec2() 595 if self.opts.executable: 596 ec2.list_executable_images() 597 else: 598 ec2.list_registered_images()
599
600 -class CmdListBuckets(CmdBase):
601 """ 602 listbuckets 603 604 List all S3 buckets 605 """ 606 names = ['listbuckets']
607 - def execute(self, args):
608 s3 = self.cfg.get_easy_s3() 609 buckets = s3.list_buckets()
610
611 -class CmdShowImage(CmdBase):
612 """ 613 showimage <image_id> 614 615 Show all AMI parts and manifest files on S3 for an EC2 image (AMI) 616 617 Example: 618 619 $ starcluster showimage ami-999999 620 """ 621 names = ['showimage']
622 - def execute(self, args):
623 if not args: 624 self.parser.error('please specify an AMI id') 625 ec2 = self.cfg.get_easy_ec2() 626 for arg in args: 627 ec2.list_image_files(arg)
628
629 -class CmdShowBucket(CmdBase):
630 """ 631 showbucket <bucket> 632 633 Show all files in an S3 bucket 634 635 Example: 636 637 $ starcluster showbucket mybucket 638 """ 639 names = ['showbucket']
640 - def execute(self, args):
641 if not args: 642 self.parser.error('please specify an S3 bucket') 643 for arg in args: 644 s3 = self.cfg.get_easy_s3() 645 bucket = s3.list_bucket(arg)
646
647 -class CmdRemoveVolume(CmdBase):
648 """ 649 removevolume [options] <volume_id> 650 651 Delete one or more EBS volumes 652 653 WARNING: This command *permanently* removes an EBS volume. 654 Be careful! 655 656 Example: 657 658 $ starcluster removevolume vol-999999 659 """ 660 names = ['removevolume'] 661
662 - def addopts(self, parser):
663 parser.add_option("-c","--confirm", dest="confirm", action="store_true", 664 default=False, 665 help="do not prompt for confirmation, just remove the volume")
666
667 - def execute(self, args):
668 if not args: 669 self.parser.error("no volumes specified. exiting...") 670 for arg in args: 671 volid = arg 672 ec2 = self.cfg.get_easy_ec2() 673 vol = ec2.get_volume(volid) 674 if vol.status in ['attaching', 'in-use']: 675 log.error("volume is currently in use. aborting...") 676 return 677 if vol.status == 'detaching': 678 log.error("volume is currently detaching. " + \ 679 "please wait a few moments and try again...") 680 return 681 if not self.opts.confirm: 682 resp = raw_input("**PERMANENTLY** delete %s (y/n)? " % volid) 683 if resp not in ['y','Y', 'yes']: 684 log.info("Aborting...") 685 return 686 if vol.delete(): 687 log.info("Volume %s deleted successfully" % vol.id) 688 else: 689 log.error("Error deleting volume %s" % vol.id)
690
691 -class CmdRemoveImage(CmdBase):
692 """ 693 removeami [options] <imageid> 694 695 Deregister an EC2 image (AMI) and remove it from S3 696 697 WARNING: This command *permanently* removes an AMI from 698 EC2/S3 including all AMI parts and manifest. Be careful! 699 700 Example: 701 702 $ starcluster removeami ami-999999 703 """ 704 names = ['removeimage'] 705
706 - def addopts(self, parser):
707 parser.add_option("-p","--pretend", dest="pretend", action="store_true", 708 default=False, 709 help="pretend run, dont actually remove anything") 710 parser.add_option("-c","--confirm", dest="confirm", action="store_true", 711 default=False, 712 help="do not prompt for confirmation, just remove the image")
713
714 - def execute(self, args):
715 if not args: 716 self.parser.error("no images specified. exiting...") 717 for arg in args: 718 imageid = arg 719 ec2 = self.cfg.get_easy_ec2() 720 image = ec2.get_image(imageid) 721 confirmed = self.opts.confirm 722 pretend = self.opts.pretend 723 if not confirmed: 724 if not pretend: 725 resp = raw_input("**PERMANENTLY** delete %s (y/n)? " % imageid) 726 if resp not in ['y','Y', 'yes']: 727 log.info("Aborting...") 728 return 729 ec2.remove_image(imageid, pretend=pretend)
730
731 -class CmdListInstances(CmdBase):
732 """ 733 listinstances [options] 734 735 List all running EC2 instances 736 """ 737 names = ['listinstances'] 738
739 - def addopts(self, parser):
740 parser.add_option("-t","--show-terminated", dest="show_terminated", action="store_true", 741 default=False, 742 help="show terminated instances")
743
744 - def execute(self, args):
745 ec2 = self.cfg.get_easy_ec2() 746 ec2.list_all_instances(self.opts.show_terminated)
747
748 -class CmdListSpots(CmdBase):
749 """ 750 listspots 751 752 List all EC2 spot instance requests 753 """ 754 names = ['listspots']
755 - def addopts(self, parser):
756 parser.add_option("-c", "--show-closed", dest="show_closed", 757 action="store_true", default=False, 758 help="show closed spot instance requests")
759 - def execute(self, args):
760 ec2 = self.cfg.get_easy_ec2() 761 ec2.list_all_spot_instances(self.opts.show_closed)
762
763 -class CmdShowConsole(CmdBase):
764 """ 765 showconsole <instance-id> 766 767 Show console output for an EC2 instance 768 769 Example: 770 771 $ starcluster showconsole i-999999 772 773 This will display the startup logs for instance i-999999 774 """ 775 names = ['showconsole'] 776 777 @property
778 - def completer(self):
779 if optcomplete: 780 try: 781 cfg = config.StarClusterConfig() 782 cfg.load() 783 ec2 = cfg.get_easy_ec2() 784 instances = ec2.get_all_instances() 785 completion_list = [i.id for i in instances] 786 return optcomplete.ListCompleter(completion_list) 787 except Exception, e: 788 log.error('something went wrong fix me: %s' % e)
789
790 - def execute(self, args):
791 if not len(args) == 1: 792 self.parser.error('please provide an instance id') 793 ec2 = self.cfg.get_easy_ec2() 794 ec2.show_console_output(args[0])
795
796 -class CmdListVolumes(CmdBase):
797 """ 798 listvolumes 799 800 List all EBS volumes 801 """ 802 names = ['listvolumes']
803 - def execute(self, args):
804 ec2 = self.cfg.get_easy_ec2() 805 ec2.list_volumes()
806
807 -class CmdListPublic(CmdBase):
808 """ 809 listpublic 810 811 List all public StarCluster images on EC2 812 """ 813 names = ['listpublic']
814 - def execute(self, args):
815 ec2 = self.cfg.get_easy_ec2() 816 ec2.list_starcluster_public_images()
817
818 -class CmdRunPlugin(CmdBase):
819 """ 820 runplugin <plugin_name> <cluster_tag> 821 822 Run a StarCluster plugin on a runnning cluster 823 824 plugin_name - name of plugin section defined in the config 825 cluster_tag - tag name of a running StarCluster 826 827 Example: 828 829 $ starcluster runplugin myplugin mycluster 830 """ 831 names = ['runplugin']
832 - def execute(self,args):
833 if len(args) != 2: 834 self.parser.error("Please provide a plugin_name and <cluster_tag>") 835 plugin_name, cluster_tag = args 836 cluster.run_plugin(plugin_name, cluster_tag, self.cfg)
837
838 -class CmdSpotHistory(CmdBase):
839 """ 840 spothistory [options] <instance_type> 841 842 Show spot instance pricing history stats (last 30 days by default) 843 844 Examples: 845 846 To show the current, max, and average spot price for m1.small instance type: 847 848 $ starcluster spothistory m1.small 849 850 Do the same thing but also plot the spot history over time using matplotlib: 851 852 $ starcluster spothistory -p m1.small 853 """ 854 names = ['spothistory'] 855
856 - def addopts(self, parser):
857 now_tup = datetime.now() 858 now = utils.datetime_tuple_to_iso(now_tup) 859 thirty_days_ago = utils.datetime_tuple_to_iso(now_tup - timedelta(days=30)) 860 parser.add_option("-d","--days", dest="days_ago", 861 action="store", type="float", 862 help="provide history in the last DAYS_AGO days " + \ 863 "(overrides -s and -e options)") 864 parser.add_option("-s","--start-time", dest="start_time", 865 action="store", type="string", 866 default=thirty_days_ago, 867 help="show price history after START_TIME" + \ 868 "(e.g. 2010-01-15T22:22:22)") 869 parser.add_option("-e","--end-time", dest="end_time", 870 action="store", type="string", 871 default=now, 872 help="show price history up until END_TIME" + \ 873 "(e.g. 2010-02-15T22:22:22)") 874 parser.add_option("-p","--plot", dest="plot", 875 action="store_true", default=False, 876 help="plot spot history using matplotlib")
877
878 - def execute(self,args):
879 instance_types = ', '.join(static.INSTANCE_TYPES.keys()) 880 if len(args) != 1: 881 self.parser.error('please provide an instance type (options: %s)' % \ 882 instance_types) 883 instance_type = args[0] 884 if not static.INSTANCE_TYPES.has_key(instance_type): 885 self.parser.error('invalid instance type. possible options: %s' % \ 886 instance_types) 887 start = self.opts.start_time 888 end = self.opts.end_time 889 if self.opts.days_ago: 890 now = datetime.now() 891 end = utils.datetime_tuple_to_iso(now) 892 start = utils.datetime_tuple_to_iso( 893 now - timedelta(days=self.opts.days_ago)) 894 ec2 = self.cfg.get_easy_ec2() 895 ec2.get_spot_history(instance_type, start, end, self.opts.plot)
896
897 -class CmdShell(CmdBase):
898 """ 899 shell 900 901 Load interactive IPython shell for starcluster development 902 903 The following objects are automatically available at the prompt: 904 905 cfg - starcluster.config.StarClusterConfig instance 906 ec2 - starcluster.awsutils.EasyEC2 instance 907 s3 - starcluster.awsutils.EasyS3 instance 908 909 All starcluster modules are automatically imported in the IPython session 910 """ 911 names = ['shell']
912 - def execute(self,args):
913 cfg = self.cfg 914 ec2 = cfg.get_easy_ec2() 915 s3 = ec2.s3 916 import starcluster 917 for modname in starcluster.__all__: 918 log.info('Importing module %s' % modname) 919 fullname = starcluster.__name__ + '.' + modname 920 try: 921 __import__(fullname) 922 locals()[modname] = sys.modules[fullname] 923 except ImportError,e: 924 log.error("Error loading module %s: %s" % (modname, e)) 925 from starcluster.utils import ipy_shell; ipy_shell();
926
927 -class CmdHelp:
928 """ 929 help 930 931 Show StarCluster usage 932 """ 933 names =['help']
934 - def execute(self, args):
935 import optparse 936 if args: 937 cmdname = args[0] 938 try: 939 sc = subcmds_map[cmdname] 940 lparser = optparse.OptionParser(sc.__doc__.strip()) 941 if hasattr(sc, 'addopts'): 942 sc.addopts(lparser) 943 lparser.print_help() 944 except KeyError: 945 raise SystemExit("Error: invalid command '%s'" % cmdname) 946 else: 947 gparser.parse_args(['--help'])
948
949 -def get_description():
950 return __description__.replace('\n','',1)
951
952 -def parse_subcommands(gparser, subcmds):
953 954 """Parse given global arguments, find subcommand from given list of 955 subcommand objects, parse local arguments and return a tuple of global 956 options, selected command object, command options, and command arguments. 957 Call execute() on the command object to run. The command object has members 958 'gopts' and 'opts' set for global and command options respectively, you 959 don't need to call execute with those but you could if you wanted to.""" 960 961 import optparse 962 global subcmds_map # needed for help command only. 963 964 print get_description() 965 966 # Build map of name -> command and docstring. 967 subcmds_map = {} 968 gparser.usage += '\n\nAvailable Actions\n' 969 for sc in subcmds: 970 helptxt = sc.__doc__.splitlines()[3].strip() 971 gparser.usage += '- %s: %s\n' % (', '.join(sc.names), 972 helptxt) 973 for n in sc.names: 974 assert n not in subcmds_map 975 subcmds_map[n] = sc 976 977 # Declare and parse global options. 978 gparser.disable_interspersed_args() 979 980 gopts, args = gparser.parse_args() 981 if not args: 982 gparser.print_help() 983 raise SystemExit("\nError: you must specify an action.") 984 subcmdname, subargs = args[0], args[1:] 985 986 # set debug level if specified 987 if gopts.DEBUG: 988 console.setLevel(DEBUG) 989 # load StarClusterConfig into global options 990 try: 991 cfg = config.StarClusterConfig(gopts.CONFIG) 992 cfg.load() 993 except exception.ConfigNotFound,e: 994 log.error(e.msg) 995 e.display_options() 996 sys.exit(1) 997 except exception.ConfigError,e: 998 log.error(e.msg) 999 sys.exit(1) 1000 gopts.CONFIG = cfg 1001 1002 # Parse command arguments and invoke command. 1003 try: 1004 sc = subcmds_map[subcmdname] 1005 lparser = optparse.OptionParser(sc.__doc__.strip()) 1006 if hasattr(sc, 'addopts'): 1007 sc.addopts(lparser) 1008 sc.parser = lparser 1009 sc.gopts = gopts 1010 sc.opts, subsubargs = lparser.parse_args(subargs) 1011 except KeyError: 1012 raise SystemExit("Error: invalid command '%s'" % subcmdname) 1013 1014 return gopts, sc, sc.opts, subsubargs
1015
1016 -def main():
1017 # Create global options parser. 1018 global gparser # only need for 'help' command (optional) 1019 import optparse 1020 gparser = optparse.OptionParser(__doc__.strip(), version=__version__) 1021 gparser.add_option("-d","--debug", dest="DEBUG", action="store_true", 1022 default=False, 1023 help="print debug messages (useful for diagnosing problems)") 1024 gparser.add_option("-c","--config", dest="CONFIG", action="store", 1025 metavar="FILE", 1026 help="use alternate config file (default: %s)" % \ 1027 static.STARCLUSTER_CFG_FILE) 1028 1029 # Declare subcommands. 1030 subcmds = [ 1031 CmdStart(), 1032 CmdStop(), 1033 CmdListClusters(), 1034 CmdSshMaster(), 1035 CmdSshNode(), 1036 CmdSshInstance(), 1037 CmdListInstances(), 1038 CmdListImages(), 1039 CmdListPublic(), 1040 CmdCreateImage(), 1041 CmdRemoveImage(), 1042 CmdListVolumes(), 1043 CmdCreateVolume(), 1044 CmdRemoveVolume(), 1045 CmdListSpots(), 1046 CmdSpotHistory(), 1047 CmdShowConsole(), 1048 CmdListZones(), 1049 CmdListBuckets(), 1050 CmdShowBucket(), 1051 CmdShowImage(), 1052 CmdShell(), 1053 CmdHelp(), 1054 ] 1055 1056 # subcommand completions 1057 scmap = {} 1058 for sc in subcmds: 1059 for n in sc.names: 1060 scmap[n] = sc 1061 1062 if optcomplete: 1063 listcter = optcomplete.ListCompleter(scmap.keys()) 1064 subcter = optcomplete.NoneCompleter() 1065 optcomplete.autocomplete( 1066 gparser, listcter, None, subcter, subcommands=scmap) 1067 elif 'COMP_LINE' in os.environ: 1068 return -1 1069 1070 gopts, sc, opts, args = parse_subcommands(gparser, subcmds) 1071 if args and args[0] =='help': 1072 sc.parser.print_help() 1073 sys.exit(0) 1074 try: 1075 sc.execute(args) 1076 except exception.BaseException,e: 1077 lines = e.msg.splitlines() 1078 for l in lines: 1079 log.error(l) 1080 #log.error(e.msg) 1081 sys.exit(1) 1082 except EC2ResponseError,e: 1083 log.error("%s: %s" % (e.error_code, e.error_message)) 1084 sys.exit(1) 1085 except S3ResponseError,e: 1086 log.error("%s: %s" % (e.error_code, e.error_message)) 1087 sys.exit(1) 1088 except socket.gaierror,e: 1089 log.error("Unable to connect: %s" % e) 1090 log.error("Check your internet connection?") 1091 sys.exit(1) 1092 except Exception,e: 1093 import traceback 1094 if not gopts.DEBUG: 1095 traceback.print_exc() 1096 log.debug(traceback.format_exc()) 1097 print 1098 log.error("Oops! Looks like you've found a bug in StarCluster") 1099 log.error("Debug file written to: %s" % static.DEBUG_FILE) 1100 log.error("Please submit this file, minus any private information,") 1101 log.error("to starcluster@mit.edu") 1102 sys.exit(1)
1103
1104 -def test():
1105 pass
1106 1107 if os.environ.has_key('starcluster_commands_test'): 1108 test() 1109 elif __name__ == '__main__': 1110 try: 1111 main() 1112 except KeyboardInterrupt: 1113 print "Interrupted, exiting." 1114