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