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. Please customize this file." % 226 self.cfg)
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):
284 self.msg = "No cluster nodes found!"
285 286
287 -class NoClusterSpotRequests(ValidationError):
288 """Raised if no spot requests belonging to a cluster are found"""
289 - def __init__(self):
290 self.msg = "No cluster spot requests found!"
291 292
293 -class MasterDoesNotExist(ClusterValidationError):
294 """Raised when no master node is available"""
295 - def __init__(self):
296 self.msg = "No master node found!"
297 298
299 -class IncompatibleSettings(ClusterValidationError):
300 """Raised when two or more settings conflict with each other"""
301 302
303 -class InvalidProtocol(ClusterValidationError):
304 """Raised when user specifies an invalid IP protocol for permission"""
305 - def __init__(self, protocol):
306 self.msg = "protocol %s is not a valid ip protocol. options: %s" % \ 307 (protocol, ', '.join(static.PROTOCOLS))
308 309
310 -class InvalidPortRange(ClusterValidationError):
311 """Raised when user specifies an invalid port range for permission"""
312 - def __init__(self, from_port, to_port, reason=None):
313 self.msg = '' 314 if reason: 315 self.msg += "%s\n" % reason 316 self.msg += "port range is invalid: from %s to %s" % (from_port, 317 to_port)
318 319
320 -class InvalidCIDRSpecified(ClusterValidationError):
321 """Raised when user specifies an invalid CIDR ip for permission"""
322 - def __init__(self, cidr):
323 self.msg = "cidr_ip is invalid: %s" % cidr
324 325
326 -class InvalidZone(ClusterValidationError):
327 """ 328 Raised when a zone has been specified that does not match the common 329 zone of the volumes being attached 330 """
331 - def __init__(self, zone, common_vol_zone):
332 cvz = common_vol_zone 333 self.msg = ("availability_zone setting '%s' does not " + 334 "match the common volume zone '%s'") % (zone, cvz)
335 336
337 -class VolumesZoneError(ClusterValidationError):
338 - def __init__(self, volumes):
339 vlist = ', '.join(volumes) 340 self.msg = 'Volumes %s are not in the same availability zone' % vlist
341 342
343 -class ClusterTemplateDoesNotExist(BaseException):
344 """ 345 Exception raised when user requests a cluster template that does not exist 346 """
347 - def __init__(self, cluster_name):
348 self.msg = "cluster template %s does not exist" % cluster_name
349 350
351 -class ClusterNotRunning(BaseException):
352 """ 353 Exception raised when user requests a running cluster that does not exist 354 """
355 - def __init__(self, cluster_name):
356 self.msg = "cluster %s is not running" % cluster_name
357 358
359 -class ClusterDoesNotExist(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' does not exist" % cluster_name
365 366
367 -class ClusterExists(BaseException):
368 - def __init__(self, cluster_name, is_ebs=False, stopped_ebs=False):
369 ctx = dict(cluster_name=cluster_name) 370 if stopped_ebs: 371 self.msg = user_msgs.stopped_ebs_cluster % ctx 372 elif is_ebs: 373 self.msg = user_msgs.active_ebs_cluster % ctx 374 else: 375 self.msg = user_msgs.cluster_exists % ctx
376 377
378 -class CancelledStartRequest(BaseException):
379 - def __init__(self, tag):
380 self.msg = "Request to start cluster '%s' was cancelled!!!" % tag 381 self.msg += "\n\nPlease be aware that instances may still be running." 382 self.msg += "\nYou can check this from the output of:" 383 self.msg += "\n\n $ starcluster listclusters" 384 self.msg += "\n\nIf you wish to destroy these instances please run:" 385 self.msg += "\n\n $ starcluster terminate %s" % tag 386 self.msg += "\n\nYou can then use:\n\n $ starcluster listclusters" 387 self.msg += "\n\nto verify that the cluster has been terminated." 388 self.msg += "\n\nIf you would like to re-use these instances, rerun" 389 self.msg += "\nthe same start command with the -x (--no-create) option"
390 391
392 -class CancelledCreateVolume(BaseException):
393 - def __init__(self):
394 self.msg = "Request to create a new volume was cancelled!!!" 395 self.msg += "\n\nPlease be aware that volume host instances" 396 self.msg += " may still be running. " 397 self.msg += "\n\nTo destroy these instances:" 398 self.msg += "\n\n $ starcluster terminate %s" % \ 399 static.VOLUME_GROUP_NAME 400 self.msg += "\n\nYou can then use\n\n $ starcluster listinstances" 401 self.msg += "\n\nto verify that the volume hosts have been terminated."
402 403
404 -class CancelledCreateImage(BaseException):
405 - def __init__(self, bucket, image_name):
406 self.msg = "Request to create an S3 AMI was cancelled" 407 self.msg += "\n\nDepending on how far along the process was before it " 408 self.msg += "was cancelled, \nsome intermediate files might still be " 409 self.msg += "around in /mnt on the instance." 410 self.msg += "\n\nAlso, some of these intermediate files might " 411 self.msg += "have been uploaded to \nS3 in the '%(bucket)s' bucket " 412 self.msg += "you specified. You can check this using:" 413 self.msg += "\n\n $ starcluster showbucket %(bucket)s\n\n" 414 self.msg += "Look for files like: " 415 self.msg += "'%(iname)s.manifest.xml' or '%(iname)s.part.*'" 416 self.msg += "\nRe-executing the same s3image command " 417 self.msg += "should clean up these \nintermediate files and " 418 self.msg += "also automatically override any\npartially uploaded " 419 self.msg += "files in S3." 420 self.msg = self.msg % {'bucket': bucket, 'iname': image_name}
421 422 423 CancelledS3ImageCreation = CancelledCreateImage 424 425
426 -class CancelledEBSImageCreation(BaseException):
427 - def __init__(self, is_ebs_backed, image_name):
428 self.msg = "Request to create EBS image %s was cancelled" % image_name 429 if is_ebs_backed: 430 self.msg += "\n\nDepending on how far along the process was " 431 self.msg += "before it was cancelled, \na snapshot of the image " 432 self.msg += "host's root volume may have been created.\nPlease " 433 self.msg += "inspect the output of:\n\n" 434 self.msg += " $ starcluster listsnapshots\n\n" 435 self.msg += "and clean up any unwanted snapshots" 436 else: 437 self.msg += "\n\nDepending on how far along the process was " 438 self.msg += "before it was cancelled, \na new volume and a " 439 self.msg += "snapshot of that new volume may have been created.\n" 440 self.msg += "Please inspect the output of:\n\n" 441 self.msg += " $ starcluster listvolumes\n\n" 442 self.msg += " and\n\n" 443 self.msg += " $ starcluster listsnapshots\n\n" 444 self.msg += "and clean up any unwanted volumes or snapshots"
445 446
447 -class ExperimentalFeature(BaseException):
448 - def __init__(self, feature_name):
449 self.msg = "%s is an experimental feature for this " % feature_name 450 self.msg += "release. If you wish to test this feature, please set " 451 self.msg += "ENABLE_EXPERIMENTAL=True in the [global] section of the" 452 self.msg += " config. \n\nYou've officially been warned :D"
453 454
455 -class ThreadPoolException(BaseException):
456 - def __init__(self, msg, exceptions):
457 self.msg = msg 458 self.exceptions = exceptions
459
460 - def print_excs(self):
461 print self.format_excs()
462
463 - def format_excs(self):
464 excs = [] 465 for exception in self.exceptions: 466 e, tb_msg, jobid = exception 467 excs.append('error occured in job (id=%s): %s' % (jobid, str(e))) 468 excs.append(tb_msg) 469 return '\n'.join(excs)
470 471
472 -class IncompatibleCluster(BaseException):
473 main_msg = """\ 474 The cluster '%(tag)s' was either created by a previous stable or development \ 475 version of StarCluster or you manually created the '%(group)s' group. In any \ 476 case '%(tag)s' cannot be used with this version of StarCluster (%(version)s). 477 478 """ 479 480 insts_msg = """\ 481 The cluster '%(tag)s' currently has %(num_nodes)d active nodes. 482 483 """ 484 485 no_insts_msg = """\ 486 The cluster '%(tag)s' does not have any nodes and is safe to terminate. 487 488 """ 489 490 terminate_msg = """\ 491 Please terminate the cluster using: 492 493 $ starcluster terminate %(tag)s 494 """ 495
496 - def __init__(self, group):
497 tag = group.name.replace(static.SECURITY_GROUP_PREFIX + '-', '') 498 self.msg = "Incompatible Cluster: %(tag)s\n\n" % dict(tag=tag) 499 self.msg += self.main_msg % dict(group=group.name, tag=tag, 500 version=static.VERSION) 501 states = ['pending', 'running', 'stopping', 'stopped'] 502 insts = filter(lambda x: x.state in states, group.instances()) 503 ctx = dict(tag=tag, num_nodes=len(insts)) 504 if insts: 505 self.msg += self.insts_msg % ctx 506 else: 507 self.msg += self.no_insts_msg % ctx 508 self.msg += self.terminate_msg % ctx
509