starcluster.exception

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

# Source Code for Module starcluster.exception

      1  #!/usr/bin/env python
      2  """
      3  StarCluster Exception Classes
      4  """
      5
      6  import os
      7
      8  from starcluster import static
      9  from starcluster.logger import log
     10  from starcluster.templates import config, user_msgs
     11
     12
     13 -class BaseException(Exception):
     14 -    def __init__(self, *args):
     15          self.msg = args[0]
     16
     17 -    def __str__(self):
     18          return self.msg
     19
     20 -    def explain(self):
     21          return "%s: %s" % (self.__class__.__name__, self.msg)
     22
     23
     24 -class CommandNotFound(BaseException):
     25      """Raised when command is not found on the system's PATH """
     26 -    def __init__(self, cmd):
     27          self.msg = "command not found: '%s'" % cmd
     28
     29
     30 -class RemoteCommandNotFound(CommandNotFound):
     31      """Raised when command is not found on a *remote* system's PATH """
     32 -    def __init__(self, cmd):
     33          self.msg = "command not found on remote system: '%s'" % cmd
     34
     35
     36 -class SSHError(BaseException):
     37      """Base class for all SSH related errors"""
     38
     39
     40 -class SSHConnectionError(SSHError):
     41      """Raised when ssh fails to to connect to a host (socket error)"""
     42 -    def __init__(self, host, port):
     43          self.msg = "failed to connect to host %s on port %s" % (host, port)
     44
     45
     46 -class SSHAuthException(SSHError):
     47      """Raised when an ssh connection fails to authenticate"""
     48 -    def __init__(self, user, host):
     49          self.msg = "failed to authenticate to host %s as user %s" % (host,
     50                                                                       user)
     51
     52
     53 -class SSHNoCredentialsError(SSHError):
     54 -    def __init__(self):
     55          self.msg = "No password or key specified"
     56
     57
     58 -class AWSError(BaseException):
     59      """Base exception for all AWS related errors"""
     60
     61
     62 -class RegionDoesNotExist(AWSError):
     63 -    def __init__(self, region_name):
     64          self.msg = "region %s does not exist" % region_name
     65
     66
     67 -class AMIDoesNotExist(AWSError):
     68 -    def __init__(self, image_id):
     69          self.msg = "AMI %s does not exist" % image_id
     70
     71
     72 -class InstanceDoesNotExist(AWSError):
     73 -    def __init__(self, instance_id, label='instance'):
     74          self.msg = "%s '%s' does not exist" % (label, instance_id)
     75
     76
     77 -class InstanceNotRunning(AWSError):
     78 -    def __init__(self, instance_id, state, label='instance'):
     79          self.msg = "%s %s is not running (%s)" % (label, instance_id, state)
     80
     81
     82 -class SecurityGroupDoesNotExist(AWSError):
     83 -    def __init__(self, sg_name):
     84          self.msg = "security group %s does not exist" % sg_name
     85
     86
     87 -class KeyPairDoesNotExist(AWSError):
     88 -    def __init__(self, keyname):
     89          self.msg = "keypair %s does not exist" % keyname
     90
     91
     92 -class ZoneDoesNotExist(AWSError):
     93 -    def __init__(self, zone, region):
     94          self.msg = "zone %s does not exist in region %s" % (zone, region)
     95
     96
     97 -class VolumeDoesNotExist(AWSError):
     98 -    def __init__(self, vol_id):
     99          self.msg = "volume %s does not exist" % vol_id
    100
    101
    102 -class SnapshotDoesNotExist(AWSError):
    103 -    def __init__(self, snap_id):
    104          self.msg = "snapshot %s does not exist" % snap_id
    105
    106
    107 -class BucketAlreadyExists(AWSError):
    108 -    def __init__(self, bucket_name):
    109          self.msg = "bucket with name '%s' already exists on S3\n" % bucket_name
    110          self.msg += "(NOTE: S3's bucket namepsace is shared by all AWS users)"
    111
    112
    113 -class BucketDoesNotExist(AWSError):
    114 -    def __init__(self, bucket_name):
    115          self.msg = "bucket '%s' does not exist" % bucket_name
    116
    117
    118 -class InvalidOperation(AWSError):
    119      pass
    120
    121
    122 -class InvalidBucketName(AWSError):
    123 -    def __init__(self, bucket_name):
    124          self.msg = "bucket name %s is not valid" % bucket_name
    125
    126
    127 -class InvalidImageName(AWSError):
    128 -    def __init__(self, image_name):
    129          self.msg = "image name %s is not valid" % image_name
    130
    131
    132 -class AWSUserIdRequired(AWSError):
    133 -    def __init__(self):
    134          self.msg = "No Amazon user id specified in config (AWS_USER_ID)"
    135
    136
    137 -class EC2CertRequired(AWSError):
    138 -    def __init__(self):
    139          self.msg = "No certificate file (pem) specified in config (EC2_CERT)"
    140
    141
    142 -class EC2PrivateKeyRequired(AWSError):
    143 -    def __init__(self):
    144          self.msg = "No private certificate file (pem) file specified in " + \
    145                     "config (EC2_PRIVATE_KEY)"
    146
    147
    148 -class EC2CertDoesNotExist(AWSError):
    149 -    def __init__(self, key):
    150          self.msg = "EC2 certificate file %s does not exist" % key
    151
    152
    153 -class EC2PrivateKeyDoesNotExist(AWSError):
    154 -    def __init__(self, key):
    155          self.msg = "EC2 private key file %s does not exist" % key
    156
    157
    158 -class SpotHistoryError(AWSError):
    159 -    def __init__(self, start, end):
    160          self.msg = "no spot price history for the dates specified: " + \
    161                  "%s - %s" % (start, end)
    162
    163
    164 -class InvalidIsoDate(BaseException):
    165 -    def __init__(self, date):
    166          self.msg = "Invalid date specified: %s" % date
    167
    168
    169 -class ConfigError(BaseException):
    170      """Base class for all config related errors"""
    171
    172
    173 -class ConfigSectionMissing(ConfigError):
    174      pass
    175
    176
    177 -class ConfigHasNoSections(ConfigError):
    178 -    def __init__(self, cfg_file):
    179          self.msg = "No valid sections defined in config file %s" % cfg_file
    180
    181
    182 -class PluginNotFound(ConfigError):
    183 -    def __init__(self, plugin):
    184          self.msg = 'Plugin "%s" not found in config' % plugin
    185
    186
    187 -class NoDefaultTemplateFound(ConfigError):
    188 -    def __init__(self, options=None):
    189          msg = "No default cluster template specified. To set the default "
    190          msg += "cluster template, set DEFAULT_TEMPLATE in the [global] section"
    191          msg += " of the config to the name of one of your cluster templates "
    192          if options:
    193              msg += '(' + ', '.join(options) + ')'
    194          self.msg = msg
    195
    196
    197 -class ConfigNotFound(ConfigError):
    198 -    def __init__(self, *args):
    199          self.msg = args[0]
    200          self.cfg = args[1]
    201          self.template = config.copy_paste_template
    202
    203 -    def create_config(self):
    204          cfg_parent_dir = os.path.dirname(self.cfg)
    205          if not os.path.exists(cfg_parent_dir):
    206              os.makedirs(cfg_parent_dir)
    207          cfg_file = open(self.cfg, 'w')
    208          cfg_file.write(config.config_template)
    209          cfg_file.close()
    210          log.info("Config template written to %s. Please customize this file." %
    211                   self.cfg)
    212
    213 -    def display_options(self):
    214          print 'Options:'
    215          print '--------'
    216          print '[1] Show the StarCluster config template'
    217          print '[2] Write config template to %s' % self.cfg
    218          print '[q] Quit'
    219          resp = raw_input('\nPlease enter your selection: ')
    220          if resp == '1':
    221              print self.template
    222          elif resp == '2':
    223              print
    224              self.create_config()
    225
    226
    227 -class KeyNotFound(ConfigError):
    228 -    def __init__(self, keyname):
    229          self.msg = "key %s not found in config" % keyname
    230
    231
    232 -class InvalidDevice(BaseException):
    233 -    def __init__(self, device):
    234          self.msg = "invalid device specified: %s" % device
    235
    236
    237 -class InvalidPartition(BaseException):
    238 -    def __init__(self, part):
    239          self.msg = "invalid partition specified: %s" % part
    240
    241
    242 -class PluginError(BaseException):
    243      """Base class for plugin errors"""
    244
    245
    246 -class PluginLoadError(PluginError):
    247      """Raised when an error is encountered while loading a plugin"""
    248
    249
    250 -class PluginSyntaxError(PluginError):
    251      """Raised when plugin contains syntax errors"""
    252
    253
    254 -class ValidationError(BaseException):
    255      """Base class for validation related errors"""
    256
    257
    258 -class ClusterReceiptError(BaseException):
    259      """Raised when creating/loading a cluster receipt fails"""
    260
    261
    262 -class ClusterValidationError(ValidationError):
    263      """Cluster validation related errors"""
    264
    265
    266 -class IncompatibleSettings(ClusterValidationError):
    267      """Raised when two or more settings conflict with each other"""
    268
    269
    270 -class InvalidProtocol(ClusterValidationError):
    271      """Raised when user specifies an invalid IP protocol for permission"""
    272 -    def __init__(self, protocol):
    273          self.msg = "protocol %s is not a valid ip protocol. options: %s" % \
    274          (protocol, ', '.join(static.PROTOCOLS))
    275
    276
    277 -class InvalidPortRange(ClusterValidationError):
    278      """Raised when user specifies an invalid port range for permission"""
    279 -    def __init__(self, from_port, to_port, reason=None):
    280          self.msg = ''
    281          if reason:
    282              self.msg += "%s\n" % reason
    283          self.msg += "port range is invalid: from %s to %s" % (from_port,
    284                                                                to_port)
    285
    286
    287 -class InvalidCIDRSpecified(ClusterValidationError):
    288      """Raised when user specifies an invalid CIDR ip for permission"""
    289 -    def __init__(self, cidr):
    290          self.msg = "cidr_ip is invalid: %s" % cidr
    291
    292
    293 -class InvalidZone(ClusterValidationError):
    294      """
    295      Raised when a zone has been specified that does not match the common
    296      zone of the volumes being attached
    297      """
    298 -    def __init__(self, zone, common_vol_zone):
    299          cvz = common_vol_zone
    300          self.msg = ("availability_zone setting '%s' does not " +
    301                      "match the common volume zone '%s'") % (zone, cvz)
    302
    303
    304 -class VolumesZoneError(ClusterValidationError):
    305 -    def __init__(self, volumes):
    306          vlist = ', '.join(volumes)
    307          self.msg = 'Volumes %s are not in the same availability zone' % vlist
    308
    309
    310 -class ClusterTemplateDoesNotExist(BaseException):
    311      """
    312      Exception raised when user requests a cluster template that does not exist
    313      """
    314 -    def __init__(self, cluster_name):
    315          self.msg = "cluster template %s does not exist" % cluster_name
    316
    317
    318 -class ClusterNotRunning(BaseException):
    319      """
    320      Exception raised when user requests a running cluster that does not exist
    321      """
    322 -    def __init__(self, cluster_name):
    323          self.msg = "cluster %s is not running" % cluster_name
    324
    325
    326 -class ClusterDoesNotExist(BaseException):
    327      """
    328      Exception raised when user requests a running cluster that does not exist
    329      """
    330 -    def __init__(self, cluster_name):
    331          self.msg = "cluster %s does not exist" % cluster_name
    332
    333
    334 -class ClusterExists(BaseException):
    335 -    def __init__(self, cluster_name, is_ebs=False, stopped_ebs=False):
    336          ctx = dict(cluster_name=cluster_name)
    337          if stopped_ebs:
    338              self.msg = user_msgs.stopped_ebs_cluster % ctx
    339          elif is_ebs:
    340              self.msg = user_msgs.active_ebs_cluster % ctx
    341          else:
    342              self.msg = user_msgs.cluster_exists % ctx
    343
    344
    345 -class CancelledStartRequest(BaseException):
    346 -    def __init__(self, tag):
    347          self.msg = "Request to start cluster '%s' was cancelled" % tag
    348          self.msg += "\n\nPlease be aware that instances may still be running."
    349          self.msg += "\nYou can check this from the output of:"
    350          self.msg += "\n\n   $ starcluster listclusters"
    351          self.msg += "\n\nIf you wish to destroy these instances please run:"
    352          self.msg += "\n\n   $ starcluster stop %s" % tag
    353          self.msg += "\n\nYou can then use:\n\n   $ starcluster listinstances"
    354          self.msg += "\n\nto verify that the instances have been terminated."
    355          self.msg += "\n\nAnother option is to use the AWS management console"
    356          self.msg += "\nto terminate the instances manually."
    357          self.msg += "\n\nIf you would like to re-use these instances, rerun"
    358          self.msg += "\nthe same start command with the --no-create option"
    359
    360
    361 -class CancelledCreateVolume(BaseException):
    362 -    def __init__(self):
    363          self.msg = "Request to create volume was cancelled"
    364          self.msg += "\n\nPlease be aware that the volume host instance"
    365          self.msg += " may still be running. "
    366          self.msg += "\n\nTo destroy this instance please run:"
    367          self.msg += "\n\n   $ starcluster terminate %s" % \
    368                  static.VOLUME_GROUP_NAME
    369          self.msg += "\n\nand then use\n\n   $ starcluster listinstances"
    370          self.msg += "\n\nto verify that this instance has been terminated."
    371          self.msg += "\n\nAnother option is to use the AWS management console "
    372          self.msg += "to terminate\nthis instance manually."
    373
    374
    375 -class CancelledCreateImage(BaseException):
    376 -    def __init__(self, bucket, image_name):
    377          self.msg = "Request to create an S3 AMI was cancelled"
    378          self.msg += "\n\nDepending on how far along the process was before it "
    379          self.msg += "was cancelled, \nsome intermediate files might still be "
    380          self.msg += "around in /mnt on the instance."
    381          self.msg += "\n\nAlso, some of these intermediate files might "
    382          self.msg += "have been uploaded to \nS3 in the '%(bucket)s' bucket "
    383          self.msg += "you specified. You can check this by running:"
    384          self.msg += "\n\n   $ starcluster showbucket %(bucket)s\n\n"
    385          self.msg += "and looking for files like: "
    386          self.msg += "'%(iname)s.manifest.xml' or '%(iname)s.part.*'"
    387          self.msg += "\nRe-executing the same s3image command "
    388          self.msg += "should take care of these \nintermediate files and "
    389          self.msg += "will also automatically override any\npartially uploaded "
    390          self.msg += "files in S3."
    391          self.msg = self.msg % {'bucket': bucket, 'iname': image_name}
    392
    393
    394  CancelledS3ImageCreation = CancelledCreateImage
    395
    396
    397 -class CancelledEBSImageCreation(BaseException):
    398 -    def __init__(self, is_ebs_backed, image_name):
    399          self.msg = "Request to create EBS image %s was cancelled" % image_name
    400          if is_ebs_backed:
    401              self.msg += "\n\nDepending on how far along the process was "
    402              self.msg += "before it was cancelled, \na snapshot of the image "
    403              self.msg += "host's root volume may have been created.\nPlease "
    404              self.msg += "inspect the output of:\n\n"
    405              self.msg += "   $ starcluster listsnapshots\n\n"
    406              self.msg += "and clean up any unwanted snapshots"
    407          else:
    408              self.msg += "\n\nDepending on how far along the process was "
    409              self.msg += "before it was cancelled, \na new volume and a "
    410              self.msg += "snapshot of that new volume may have been created.\n"
    411              self.msg += "Please inspect the output of:\n\n"
    412              self.msg += "   $ starcluster listvolumes\n\n"
    413              self.msg += "   and\n\n"
    414              self.msg += "   $ starcluster listsnapshots\n\n"
    415              self.msg += "and clean up any unwanted volumes or snapshots"
    416
    417
    418 -class ExperimentalFeature(BaseException):
    419 -    def __init__(self, feature_name):
    420          self.msg = "%s is an experimental feature for this " % feature_name
    421          self.msg += "release. \nIf you wish to test this feature, set "
    422          self.msg += "ENABLE_EXPERIMENTAL=True \nin the [global] section of the"
    423          self.msg += " config. \nYou have officially been warned :D"
    424
    425
    426 -class ThreadPoolException(BaseException):
    427 -    def __init__(self, msg, exceptions):
    428          self.msg = msg
    429          self.exceptions = exceptions
    430
    431 -    def print_excs(self):
    432          print self.format_excs()
    433
    434 -    def format_excs(self):
    435          excs = []
    436          for exception in self.exceptions:
    437              e, tb_msg, jobid = exception
    438              excs.append('error occured in job (id=%s): %s' % (jobid, str(e)))
    439              excs.append(tb_msg)
    440          return '\n'.join(excs)
    441
    442
    443 -class IncompatibleCluster(BaseException):
    444      main_msg = """\
    445  The cluster '%(tag)s' was either created by a previous stable or development \
    446  version of StarCluster or you manually created the '%(group)s' group. In any \
    447  case '%(tag)s' cannot be used with this version of StarCluster (%(version)s).
    448
    449  """
    450
    451      insts_msg = """\
    452  The cluster '%(tag)s' currently has %(num_nodes)d active nodes.
    453
    454  """
    455
    456      no_insts_msg = """\
    457  The cluster '%(tag)s' does not have any nodes and is safe to terminate.
    458
    459  """
    460
    461      terminate_msg = """\
    462  Please terminate the cluster using:
    463
    464      $ starcluster terminate %(tag)s
    465  """
    466
    467 -    def __init__(self, group):
    468          tag = group.name.replace(static.SECURITY_GROUP_PREFIX + '-', '')
    469          self.msg = "Incompatible Cluster: %(tag)s\n\n" % dict(tag=tag)
    470          self.msg += self.main_msg % dict(group=group.name, tag=tag,
    471                                           version=static.VERSION)
    472          states = ['pending', 'running', 'stopping', 'stopped']
    473          insts = filter(lambda x: x.state in states, group.instances())
    474          ctx = dict(tag=tag, num_nodes=len(insts))
    475          if insts:
    476              self.msg += self.insts_msg % ctx
    477          else:
    478              self.msg += self.no_insts_msg % ctx
    479          self.msg += self.terminate_msg % ctx
    480