galaxy-dev
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
November 2009
- 26 participants
- 233 discussions
details: http://www.bx.psu.edu/hg/galaxy/rev/70ae74578254
changeset: 3082:70ae74578254
user: Enis Afgan <afgane(a)gmail.com>
date: Wed Nov 04 17:19:18 2009 -0500
description:
Bug fixes and minor updates.
diffstat:
lib/galaxy/cloud/providers/ec2.py | 20 ++++-
lib/galaxy/cloud/providers/eucalyptus.py | 36 ++++++---
lib/galaxy/web/controllers/cloud.py | 122 ++++++++++--------------------
templates/cloud/configure_cloud.mako | 20 +++-
4 files changed, 96 insertions(+), 102 deletions(-)
diffs (503 lines):
diff -r 89276f68513a -r 70ae74578254 lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Tue Nov 03 13:20:26 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Wed Nov 04 17:19:18 2009 -0500
@@ -45,7 +45,8 @@
store_states = Bunch(
IN_USE = "in-use",
- CREATING = "creating"
+ CREATING = "creating",
+ ERROR = "error"
)
class EC2CloudProvider( object ):
@@ -94,7 +95,7 @@
"""
Establishes EC2 cloud connection using user's credentials associated with given UCI
"""
- log.debug( '##### Establishing EC2 cloud connection' )
+ log.debug( 'Establishing %s cloud connection' % self.type )
provider = uci_wrapper.get_provider()
try:
region = RegionInfo( None, provider.region_name, provider.region_endpoint )
@@ -276,7 +277,7 @@
if uci_wrapper.get_state() != uci_states.ERROR:
# Start an instance
- log.debug( "***** Starting instance for UCI '%s'" % uci_wrapper.get_name() )
+ log.debug( "Starting instance for UCI '%s'" % uci_wrapper.get_name() )
#TODO: Once multiple volumes can be attached to a single instance, update 'userdata' composition
userdata = uci_wrapper.get_store_volume_id()+"|"+uci_wrapper.get_access_key()+"|"+uci_wrapper.get_secret_key()
log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s, security_groups=[%s], user_data=[OMITTED], instance_type=%s, placement=%s )'
@@ -401,9 +402,19 @@
model.CloudStore.c.status==store_states.CREATING,
model.CloudStore.c.status==None ) ).all()
for store in stores:
- if self.type == store.uci.credentials.provider.type:
+ if self.type == store.uci.credentials.provider.type and store.volume_id != None:
log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider.type, store.volume_id ) )
self.updateStore( store )
+ else:
+ log.error( "[%s] There exists an entry for UCI (%s) storage volume without an ID. Storage volume might have been created with "
+ "cloud provider though. Manual check is recommended." % ( store.uci.credentials.provider.type, store.uci.name ) )
+ store.uci.error = "There exists an entry in local database for a storage volume without an ID. Storage volume might have been created " \
+ "with cloud provider though. Manual check is recommended. After understanding what happened, local database etry for given " \
+ "storage volume should be updated."
+ store.status = store_states.ERROR
+ store.uci.state = uci_states.ERROR
+ store.uci.flush()
+ store.flush()
# Attempt at updating any zombie UCIs (i.e., instances that have been in SUBMITTED state for longer than expected - see below for exact time)
zombies = model.UCI.filter_by( state=uci_states.SUBMITTED ).all()
@@ -605,6 +616,7 @@
Establishes and returns connection to cloud provider. Information needed to do so is obtained
directly from uci database object.
"""
+ log.debug( 'Establishing %s cloud connection' % self.type )
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
diff -r 89276f68513a -r 70ae74578254 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Tue Nov 03 13:20:26 2009 -0500
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Wed Nov 04 17:19:18 2009 -0500
@@ -46,7 +46,8 @@
store_states = Bunch(
IN_USE = "in-use",
- CREATING = "creating"
+ CREATING = "creating",
+ ERROR = "error"
)
class EucalyptusCloudProvider( object ):
@@ -96,7 +97,7 @@
"""
Establishes eucalyptus cloud connection using user's credentials associated with given UCI
"""
- log.debug( '##### Establishing eucalyptus cloud connection' )
+ log.debug( 'Establishing %s cloud connection.' % self.type )
provider = uci_wrapper.get_provider()
try:
euca_region = RegionInfo( None, provider.region_name, provider.region_endpoint )
@@ -170,14 +171,14 @@
and registers relevant information in Galaxy database.
"""
conn = self.get_connection( uci_wrapper )
+
+ # Because only 1 storage volume may be created at UCI config time, index of this storage volume in local Galaxy DB w.r.t
+ # current UCI is 0; therefore, it can be referenced in following code
+ log.info( "Creating volume in zone '%s'..." % uci_wrapper.get_uci_availability_zone() )
if uci_wrapper.get_uci_availability_zone()=='':
log.info( "Availability zone for UCI (i.e., storage volume) was not selected, using default zone: %s" % self.zone )
uci_wrapper.set_store_availability_zone( self.zone )
- #TODO: check if volume associated with UCI already exists (if server crashed for example) and don't recreate it
- log.info( "Creating volume in zone '%s'..." % uci_wrapper.get_uci_availability_zone() )
- # Because only 1 storage volume may be created at UCI config time, index of this storage volume in local Galaxy DB w.r.t
- # current UCI is 0, so reference it in following methods
vol = conn.create_volume( uci_wrapper.get_store_size( 0 ), uci_wrapper.get_uci_availability_zone(), snapshot=None )
uci_wrapper.set_store_volume_id( 0, vol.id )
@@ -209,15 +210,14 @@
failedList.append( v.volume_id )
# Delete UCI if all of associated
- log.debug( "count=%s, len(vl)=%s" % (count, len( vl ) ) )
if count == len( vl ):
uci_wrapper.delete()
else:
log.error( "Deleting following volume(s) failed: %s. However, these volumes were successfully deleted: %s. \
- MANUAL intervention and processing needed." % ( failedList, deletedList ) )
+ Manual intervention and processing needed." % ( str( failedList ), str( deletedList ) ) )
uci_wrapper.change_state( uci_state=uci_states.ERROR )
- uci_wrapper.set_error( "Deleting following volume(s) failed: "+failedList+". However, these volumes were successfully deleted: "+deletedList+". \
- MANUAL intervention and processing needed." )
+ uci_wrapper.set_error( "Deleting following volume(s) failed: "+str(failedList)+". However, these volumes were \
+ successfully deleted: "+str(deletedList)+". Manual intervention and processing needed." )
def addStorageToUCI( self, name ):
""" Adds more storage to specified UCI """
@@ -250,7 +250,7 @@
uci_wrapper.set_mi( i_index, mi_id )
if uci_wrapper.get_state() != uci_states.ERROR:
- log.debug( "***** Starting UCI instance '%s'" % uci_wrapper.get_name() )
+ log.debug( "Starting UCI instance '%s'" % uci_wrapper.get_name() )
log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
try:
reservation = conn.run_instances( image_id=mi_id, key_name=uci_wrapper.get_key_pair_name( i_index ) )
@@ -368,9 +368,19 @@
model.CloudStore.c.status==store_states.CREATING,
model.CloudStore.c.status==None ) ).all()
for store in stores:
- if self.type == store.uci.credentials.provider.type:
+ if self.type == store.uci.credentials.provider.type and store.volume_id != None:
log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider.type, store.volume_id ) )
self.updateStore( store )
+ else:
+ log.error( "[%s] There exists an entry for UCI (%s) storage volume without an ID. Storage volume might have been created with "
+ "cloud provider though. Manual check is recommended." % ( store.uci.credentials.provider.type, store.uci.name ) )
+ store.uci.error = "There exists an entry in local database for a storage volume without an ID. Storage volume might have been created " \
+ "with cloud provider though. Manual check is recommended. After understanding what happened, local database etry for given " \
+ "storage volume should be updated."
+ store.status = store_states.ERROR
+ store.uci.state = uci_states.ERROR
+ store.uci.flush()
+ store.flush()
# Attempt at updating any zombie UCIs (i.e., instances that have been in SUBMITTED state for longer than expected - see below for exact time)
zombies = model.UCI.filter_by( state=uci_states.SUBMITTED ).all()
@@ -455,6 +465,7 @@
conn = self.get_connection_from_uci( uci )
try:
+ log.debug( "vol id: " % store.volume_id )
vl = conn.get_all_volumes( [store.volume_id] )
except boto.exception.EC2ResponseError, e:
log.error( "Retrieving volume(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
@@ -570,6 +581,7 @@
Establishes and returns connection to cloud provider. Information needed to do so is obtained
directly from uci database object.
"""
+ log.debug( 'Establishing %s cloud connection.' % self.type )
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
diff -r 89276f68513a -r 70ae74578254 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Tue Nov 03 13:20:26 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Wed Nov 04 17:19:18 2009 -0500
@@ -52,11 +52,14 @@
SHUTTING_DOWN = "shutting-down"
)
+store_states = Bunch(
+ IN_USE = "in-use",
+ CREATING = "creating",
+ ERROR = "error"
+)
+
class CloudController( BaseController ):
-
-# def __init__( self ):
-# self.cloudManager = CloudManager()
-
+
@web.expose
def index( self, trans ):
return trans.fill_template( "cloud/index.mako" )
@@ -76,22 +79,22 @@
liveInstances = trans.sa_session.query( model.UCI ) \
.filter_by( user=user ) \
- .filter( or_( model.UCI.c.state==uci_states.RUNNING, #"running",
- model.UCI.c.state==uci_states.PENDING, #"pending",
- model.UCI.c.state==uci_states.SUBMITTED, #"submitted",
- model.UCI.c.state==uci_states.SUBMITTED_UCI, #"submittedUCI",
- model.UCI.c.state==uci_states.SHUTTING_DOWN, #"shutting-down",
+ .filter( or_( model.UCI.c.state==uci_states.RUNNING,
+ model.UCI.c.state==uci_states.PENDING,
+ model.UCI.c.state==uci_states.SUBMITTED,
+ model.UCI.c.state==uci_states.SUBMITTED_UCI,
+ model.UCI.c.state==uci_states.SHUTTING_DOWN,
model.UCI.c.state==uci_states.SHUTTING_DOWN_UCI ) ) \
.order_by( desc( model.UCI.c.update_time ) ) \
.all()
prevInstances = trans.sa_session.query( model.UCI ) \
.filter_by( user=user ) \
- .filter( or_( model.UCI.c.state==uci_states.AVAILABLE, #"available",
- model.UCI.c.state==uci_states.NEW, #"new",
- model.UCI.c.state==uci_states.NEW_UCI, #"newUCI",
- model.UCI.c.state==uci_states.ERROR, #"error",
- model.UCI.c.state==uci_states.DELETING, #"deleting",
+ .filter( or_( model.UCI.c.state==uci_states.AVAILABLE,
+ model.UCI.c.state==uci_states.NEW,
+ model.UCI.c.state==uci_states.NEW_UCI,
+ model.UCI.c.state==uci_states.ERROR,
+ model.UCI.c.state==uci_states.DELETING,
model.UCI.c.state==uci_states.DELETING_UCI ) ) \
.order_by( desc( model.UCI.c.update_time ) ) \
.all()
@@ -99,8 +102,8 @@
# Check after update there are instances in pending state; if so, display message
pendingInstances = trans.sa_session.query( model.UCI ) \
.filter_by( user=user ) \
- .filter( or_( model.UCI.c.state==uci_states.PENDING, #"pending" , \
- model.UCI.c.state==uci_states.SUBMITTED, #"submitted" , \
+ .filter( or_( model.UCI.c.state==uci_states.PENDING,
+ model.UCI.c.state==uci_states.SUBMITTED,
model.UCI.c.state==uci_states.SUBMITTED_UCI ) ) \
.all()
if pendingInstances:
@@ -124,6 +127,7 @@
def makeDefault( self, trans, id=None ):
"""
Set current credentials as default.
+ *NOT USED*
"""
currentDefault = get_default_credentials (trans)
if currentDefault:
@@ -137,8 +141,7 @@
# TODO: Fix bug that when this function returns, top Galaxy tab bar is missing from the webpage
return self.list( trans ) #trans.fill_template( "cloud/configure_cloud.mako",
#awsCredentials = awsCredentials )
-
-
+
@web.expose
@web.require_login( "start Galaxy cloud instance" )
def start( self, trans, id, type='m1.small' ):
@@ -149,19 +152,9 @@
uci = get_uci( trans, id )
mi = get_mi( trans, uci, type )
stores = get_stores( trans, uci )
- # Ensure instance is not already running (or related state) and store relevant data
+ # Ensure instance is available and then store relevant data
# into DB to initiate instance startup by cloud manager
- if ( len(stores) is not 0 ) and \
- ( uci.state != uci_states.SUBMITTED ) and \
- ( uci.state != uci_states.SUBMITTED_UCI ) and \
- ( uci.state != uci_states.PENDING ) and \
- ( uci.state != uci_states.DELETING ) and \
- ( uci.state != uci_states.DELETING_UCI ) and \
- ( uci.state != uci_states.DELETED ) and \
- ( uci.state != uci_states.RUNNING ) and \
- ( uci.state != uci_states.NEW_UCI ) and \
- ( uci.state != uci_states.NEW ) and \
- ( uci.state != uci_states.ERROR ):
+ if ( len(stores) is not 0 ) and ( uci.state == uci_states.AVAILABLE ):
instance = model.CloudInstance()
instance.user = user
instance.image = mi
@@ -181,7 +174,10 @@
"instance description." )
return self.list( trans )
- error( "Cannot start instance that is in state '%s'." % uci.state )
+ if len(stores) == 0:
+ error( "This instance does not have any storage volumes associated it and thus cannot be started." )
+ else:
+ error( "Cannot start instance that is in state '%s'." % uci.state )
return self.list( trans )
@web.expose
@@ -228,7 +224,10 @@
trans.set_message( "Galaxy instance '%s' marked for deletion." % name )
return self.list( trans )
- trans.set_message( "Instance '%s' is already marked for deletion." % uci.name )
+ if uci.state != uci_states.ERROR:
+ trans.set_message( "Cannot delete instance in state ERROR." )
+ else:
+ trans.set_message( "Instance '%s' is already marked for deletion." % uci.name )
return self.list( trans )
@web.expose
@@ -251,8 +250,6 @@
.filter_by( user=user, state=instance_states.TERMINATED, uci_id=id ) \
.order_by( desc( model.CloudInstance.c.update_time ) ) \
.all()
-
- log.debug( "id: %s" % id )
return trans.fill_template( "cloud/view_usage.mako", prevInstances = prevInstances )
@@ -284,6 +281,12 @@
providersToZones[storedCred.name] = ['Unknown provider zone']
if instanceName:
+ # Check if volume size is entered as an integer
+ try:
+ volSize = int( volSize )
+ except ValueError:
+ error['vol_error'] = "Volume size must be integer value between 1 and 1000."
+
# Create new user configured instance
try:
if trans.app.model.UCI \
@@ -299,9 +302,6 @@
error['vol_error'] = "You must specify volume size as an integer value between 1 and 1000."
elif ( int( volSize ) < 1 ) or ( int( volSize ) > 1000 ):
error['vol_error'] = "Volume size must be integer value between 1 and 1000."
-# elif type( volSize ) != type( 1 ): # Check if volSize is int
-# log.debug( "volSize='%s'" % volSize )
-# error['vol_error'] = "Volume size must be integer value between 1 and 1000."
elif zone=='':
error['zone_error'] = "You must select zone where this UCI will be registered."
else:
@@ -311,14 +311,14 @@
uci.credentials = trans.app.model.CloudUserCredentials.filter(
trans.app.model.CloudUserCredentials.table.c.name==credName ).first()
uci.user= user
- uci.total_size = volSize # This is OK now because new instance is being created.
+ uci.total_size = volSize # This is OK now because new instance is being created and only one storage volume can be created at UCI creation time
uci.state = uci_states.NEW_UCI
storage = model.CloudStore()
storage.user = user
storage.uci = uci
storage.size = volSize
- storage.availability_zone = zone # TODO: Give user choice here. Also, enable region selection.
+ storage.availability_zone = zone
# Persist
session = trans.sa_session
session.save_or_update( uci )
@@ -333,7 +333,7 @@
except AttributeError, ae:
inst_error = "No registered cloud images. You must contact administrator to add some before proceeding."
log.debug("AttributeError: %s " % str( ae ) )
-
+
return trans.fill_template( "cloud/configure_uci.mako",
instanceName = instanceName,
credName = storedCreds,
@@ -341,14 +341,7 @@
zone = zone,
error = error,
providersToZones = providersToZones )
-
- return trans.show_form(
- web.FormBuilder( web.url_for(), "Configure new instance", submit_text="Add" )
- .add_text( "instanceName", "Instance name", value="Unnamed instance", error=inst_error )
- .add_text( "credName", "Name of registered credentials to use", value="", error=cred_error )
- .add_text( "volSize", "Permanent storage size (1GB - 1000GB)"
- "<br />Note: you will be able to add more storage later", value='', error=vol_error ) )
-
+
@web.expose
@web.require_admin
def addNewImage( self, trans, image_id='', manifest='', state=None ):
@@ -851,6 +844,7 @@
user = trans.get_user()
stores = trans.sa_session.query( model.CloudStore ) \
.filter_by( user=user, uci_id=uci.id ) \
+ .filter( model.CloudStore.c.status != store_states.ERROR ) \
.all()
return stores
@@ -868,19 +862,6 @@
return instances
-def get_cloud_instance( conn, instance_id ):
- """
- Returns a cloud instance representation of the instance id, i.e., cloud instance object that cloud API can be invoked on
- """
- # get_all_instances func. takes a list of desired instance id's, so create a list first
- idLst = list()
- idLst.append( instance_id )
- # Retrieve cloud instance based on passed instance id. get_all_instances( idLst ) method returns reservation ID. Because
- # we are passing only 1 ID, we can retrieve only the first element of the returning list. Furthermore, because (for now!)
- # only 1 instance corresponds each individual reservation, grab only the first element of the returned list of instances.
- cloudInstance = conn.get_all_instances( [instance_id] )[0].instances[0]
- return cloudInstance
-
def get_connection( trans, credName ):
"""
Establishes EC2 connection using user's default credentials
@@ -900,22 +881,3 @@
else:
error( "You must specify default credentials before starting an instance." )
return 0
-
-def get_keypair_name( trans ):
- """
- Generate keypair using user's default credentials
- """
- conn = get_connection( trans )
-
- log.debug( "Getting user's keypair" )
- key_pair = conn.get_key_pair( 'galaxy-keypair' )
-
- try:
- return key_pair.name
- except AttributeError: # No keypair under this name exists so create it
- log.debug( 'No keypair found, creating keypair' )
- key_pair = conn.create_key_pair( 'galaxy-keypair' )
- # TODO: Store key_pair.material into instance table - this is the only time private key can be retrieved
- # Actually, probably return key_pair to calling method and store name & key from there...
-
- return key_pair.name
diff -r 89276f68513a -r 70ae74578254 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Tue Nov 03 13:20:26 2009 -0500
+++ b/templates/cloud/configure_cloud.mako Wed Nov 04 17:19:18 2009 -0500
@@ -26,19 +26,24 @@
var elem = '#' + data[i].id;
// Because of different list managing 'live' vs. 'available' instances, reload url on various state changes
old_state = $(elem + "-state").text();
+ prev_old_state = $(elem + "-state-p").text();
new_state = data[i].state;
//console.log( "old_state[%d] = %s", i, old_state );
+ //console.log( "prev_old_state[%d] = %s", i, prev_old_state );
//console.log( "new_state[%d] = %s", i, new_state );
if ( ( old_state=='pending' && new_state=='running' ) || ( old_state=='shutting-down' && new_state=='available' ) || \
( old_state=='running' && new_state=='available' ) || ( old_state=='running' && new_state=='error' ) || \
( old_state=='pending' && new_state=='error' ) || ( old_state=='pending' && new_state=='available' ) || \
- ( old_state=='submitted' && new_state=='available' ) ) {
+ ( old_state=='submitted' && new_state=='available' ) || ( prev_old_state.match('newUCI') && new_state=='available' ) || \
+ ( prev_old_state.match('new') && new_state=='available' ) ) {
var url = "${h.url_for( controller='cloud', action='list')}";
location.replace( url );
}
else if ( ( old_state=='running' && new_state=='error' ) || ( old_state=='pending' && new_state=='error' ) || \
( old_state=='submitted' && new_state=='error' ) || ( old_state=='submittedUCI' && new_state=='error' ) || \
- ( old_state=='shutting-down' && new_state=='error' ) ) {
+ ( old_state=='shutting-down' && new_state=='error' ) || ( prev_old_state.match('newUCI') && new_state=='error' ) || \
+ ( prev_old_state.match('new') && new_state=='error' ) || ( prev_old_state.match('available') && new_state=='error' ) || \
+ ( prev_old_state.match('deleting') && new_state=='error' ) || ( prev_old_state.match('deletingUCI') && new_state=='error' ) ) {
var url = "${h.url_for( controller='cloud', action='list')}";
location.replace( url );
}
@@ -56,13 +61,13 @@
if( !dns ) {
$(uci+"-link").text( 'Galaxy starting...' );
// http://stackoverflow.com/questions/275931/how-do-you-make-an-element-flash-…
- $(uci+"-link").stop().animate({ fontSize: "14px" }, 1000).animate({ fontSize: "12px" }, 1000);
+ //$(uci+"-link").stop().animate({ fontSize: "14px" }, 1000).animate({ fontSize: "12px" }, 1000);
}
else {
$(uci+"-link").html( '<div align="right"><a class="action-button" href="http://'+dns+'" target="_blank">' +
'<span>Access Galaxy</span>'+
'<img src="' + "${h.url_for( '/static/images/silk/resultset_next.png' )}" + '" /></div>' );
- $(uci+"-link").stop().animate({ fontSize: "14px" }, 1000).animate({ fontSize: "12px" }, 1000);
+ //$(uci+"-link").stop().animate({ fontSize: "14px" }, 1000).animate({ fontSize: "12px" }, 1000);
}
}
});
@@ -92,6 +97,7 @@
%if cloudCredentials:
## Manage user credentials
+ <h3>Your registered credentials</h3>
<ul class="manage-table-actions">
<li>
<a class="action-button" href="${h.url_for( action='add' )}">
@@ -131,7 +137,7 @@
## *****************************************************
## Manage live instances
<p />
- <h2>Manage your cloud instances</h2>
+ <h3>Manage your cloud instances</h3>
<ul class="manage-table-actions">
<li>
<a class="action-button" href="${h.url_for( action='configureNew' )}">
@@ -232,7 +238,7 @@
<a id="pi-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
</td>
<td>${str(prevInstance.total_size)}</td>
- <td>
+ <td id="${ prevInstance.id }-state-p">
<%state = str(prevInstance.state)%>
%if state =='error':
<div id="${prevInstance.name}-short">
@@ -249,7 +255,9 @@
error:</a><br />
${str(prevInstance.error)}
<p />
+ <div style="font-size:10px;">
<a href="${h.url_for( action='set_uci_state', id=trans.security.encode_id(prevInstance.id), state='available' )}">reset state</a>
+ </div>
</div>
%else:
${str(prevInstance.state)}
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/e19eef93584f
changeset: 3078:e19eef93584f
user: Enis Afgan <afgane(a)gmail.com>
date: Thu Oct 29 17:40:31 2009 -0400
description:
Added good bit of exception try/catch blocks in providers' code. Started on addressing zombie UCIs.
diffstat:
lib/galaxy/cloud/__init__.py | 9 +-
lib/galaxy/cloud/providers/ec2.py | 272 +++++++++++++++++++++-------------
lib/galaxy/cloud/providers/eucalyptus.py | 283 ++++++++++++++++++++++------------
lib/galaxy/web/controllers/cloud.py | 28 +--
templates/cloud/add_provider.mako | 3 -
templates/cloud/configure_cloud.mako | 9 +-
templates/cloud/configure_uci.mako | 4 +-
templates/cloud/viewInstance.mako | 7 +-
8 files changed, 382 insertions(+), 233 deletions(-)
diffs (918 lines):
diff -r be631ed97541 -r e19eef93584f lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Thu Oct 29 11:46:43 2009 -0400
+++ b/lib/galaxy/cloud/__init__.py Thu Oct 29 17:40:31 2009 -0400
@@ -34,8 +34,6 @@
CREATING = "creating"
)
-JOB_WAIT, JOB_ERROR, JOB_INPUT_ERROR, JOB_INPUT_DELETED, JOB_OK, JOB_READY, JOB_DELETED, JOB_ADMIN_DELETED = 'wait', 'error', 'input_error', 'input_deleted', 'ok', 'ready', 'deleted', 'admin_deleted'
-
class CloudManager( object ):
"""
Highest level interface to cloud management.
@@ -318,13 +316,16 @@
vol.i_id = instance_id
vol.flush()
- def set_error( self, error ):
+ def set_error( self, error, set_state=False ):
"""
- Sets error field of given UCI in local Galaxy database
+ Sets error field of given UCI in local Galaxy database. If set_state is set to 'true',
+ method also sets state of give UCI to 'error'
"""
uci = model.UCI.get( self.uci_id )
uci.refresh()
uci.error = error
+ if set_state:
+ uci.state = uci_states.ERROR
uci.flush()
# --------- Getter methods -----------------
diff -r be631ed97541 -r e19eef93584f lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Thu Oct 29 11:46:43 2009 -0400
+++ b/lib/galaxy/cloud/providers/ec2.py Thu Oct 29 17:40:31 2009 -0400
@@ -12,6 +12,7 @@
import galaxy.eggs
galaxy.eggs.require("boto")
from boto.ec2.connection import EC2Connection
+from boto.ec2.regioninfo import RegionInfo
import boto.exception
import logging
@@ -93,12 +94,23 @@
"""
log.debug( '##### Establishing EC2 cloud connection' )
provider = uci_wrapper.get_provider()
- region = RegionInfo( None, provider.region_name, provider.region_endpoint )
- conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(),
- aws_secret_access_key=uci_wrapper.get_secret_key(),
- is_secure=provider.is_secure,
- region=region,
- path=provider.path )
+ try:
+ region = RegionInfo( None, provider.region_name, provider.region_endpoint )
+ except Exception, e:
+ log.error( "Selecting region with cloud provider failed: %s" % str(e) )
+ uci_wrapper.set_error( "Selecting region with cloud provider failed: " + str(e), True )
+ return None
+ try:
+ conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(),
+ aws_secret_access_key=uci_wrapper.get_secret_key(),
+ is_secure=provider.is_secure,
+ region=region,
+ path=provider.path )
+ except Exception, e:
+ log.error( "Establishing connection with cloud failed: %s" % str(e) )
+ uci_wrapper.set_error( "Establishing connection with cloud failed: " + str(e), True )
+ return None
+
return conn
def set_keypair( self, uci_wrapper, conn ):
@@ -120,7 +132,7 @@
uci_wrapper.set_key_pair( inst, kp.name, kp.material )
else:
log.error( "EC2 response error: '%s'" % e )
- uci_wrapper.set_error( "EC2 response error while creating key pair: " + e )
+ uci_wrapper.set_error( "EC2 response error while creating key pair: " + str(e), True )
return kp.name
@@ -235,53 +247,65 @@
Starts instance(s) of given UCI on the cloud.
"""
conn = self.get_connection( uci_wrapper )
-#
- self.set_keypair( uci_wrapper, conn )
- i_indexes = uci_wrapper.get_instances_indexes() # Get indexes of i_indexes associated with this UCI whose state is 'None'
- log.debug( "Starting instances with IDs: '%s' associated with UCI '%s' " % ( uci_wrapper.get_name(), i_indexes ) )
- for i_index in i_indexes:
- mi_id = self.get_mi_id( uci_wrapper.get_type( i_index ) )
- uci_wrapper.set_mi( i_index, mi_id )
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ self.set_keypair( uci_wrapper, conn )
+ i_indexes = uci_wrapper.get_instances_indexes() # Get indexes of i_indexes associated with this UCI whose state is 'None'
+ log.debug( "Starting instances with IDs: '%s' associated with UCI '%s' " % ( uci_wrapper.get_name(), i_indexes ) )
- # Check if galaxy security group exists (and create it if it does not)
- security_group = 'galaxyWeb'
- log.debug( "Setting up '%s' security group." % security_group )
- sgs = conn.get_all_security_groups() # security groups
- gsgt = False # galaxy security group test
- for sg in sgs:
- if sg.name == security_group:
- gsgt = True
- # If security group does not exist, create it
- if not gsgt:
- gSecurityGroup = conn.create_security_group(security_group, 'Security group for Galaxy.')
- gSecurityGroup.authorize( 'tcp', 80, 80, '0.0.0.0/0' ) # Open HTTP port
- gSecurityGroup.authorize( 'tcp', 22, 22, '0.0.0.0/0' ) # Open SSH port
- # Start an instance
- log.debug( "***** Starting instance for UCI '%s'" % uci_wrapper.get_name() )
- #TODO: Once multiple volumes can be attached to a single instance, update 'userdata' composition
- userdata = uci_wrapper.get_store_volume_id()+"|"+uci_wrapper.get_access_key()+"|"+uci_wrapper.get_secret_key()
- log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s, security_groups=[%s], user_data=[OMITTED], instance_type=%s, placement=%s )'
- % ( mi_id, uci_wrapper.get_key_pair_name( i_index ), [security_group], uci_wrapper.get_type( i_index ), uci_wrapper.get_uci_availability_zone() ) )
- reservation = conn.run_instances( image_id=mi_id,
- key_name=uci_wrapper.get_key_pair_name( i_index ),
- security_groups=[security_group],
- user_data=userdata,
- instance_type=uci_wrapper.get_type( i_index ),
- placement=uci_wrapper.get_uci_availability_zone() )
- # Record newly available instance data into local Galaxy database
- l_time = datetime.utcnow()
- uci_wrapper.set_launch_time( l_time, i_index=i_index ) # format_time( reservation.i_indexes[0].launch_time ) )
- if not uci_wrapper.uci_launch_time_set():
- uci_wrapper.set_uci_launch_time( l_time )
- uci_wrapper.set_reservation_id( i_index, str( reservation ).split(":")[1] )
- # TODO: if more than a single instance will be started through single reservation, change this reference to element [0]
- i_id = str( reservation.instances[0]).split(":")[1]
- uci_wrapper.set_instance_id( i_index, i_id )
- s = reservation.instances[0].state
- uci_wrapper.change_state( s, i_id, s )
- log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
-
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ for i_index in i_indexes:
+ mi_id = self.get_mi_id( uci_wrapper.get_type( i_index ) )
+ uci_wrapper.set_mi( i_index, mi_id )
+
+ # Check if galaxy security group exists (and create it if it does not)
+ security_group = 'galaxyWeb'
+ log.debug( "Setting up '%s' security group." % security_group )
+ sgs = conn.get_all_security_groups() # security groups
+ gsgt = False # galaxy security group test
+ for sg in sgs:
+ if sg.name == security_group:
+ gsgt = True
+ # If security group does not exist, create it
+ if not gsgt:
+ gSecurityGroup = conn.create_security_group(security_group, 'Security group for Galaxy.')
+ gSecurityGroup.authorize( 'tcp', 80, 80, '0.0.0.0/0' ) # Open HTTP port
+ gSecurityGroup.authorize( 'tcp', 22, 22, '0.0.0.0/0' ) # Open SSH port
+
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ # Start an instance
+ log.debug( "***** Starting instance for UCI '%s'" % uci_wrapper.get_name() )
+ #TODO: Once multiple volumes can be attached to a single instance, update 'userdata' composition
+ userdata = uci_wrapper.get_store_volume_id()+"|"+uci_wrapper.get_access_key()+"|"+uci_wrapper.get_secret_key()
+ log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s, security_groups=[%s], user_data=[OMITTED], instance_type=%s, placement=%s )'
+ % ( mi_id, uci_wrapper.get_key_pair_name( i_index ), [security_group], uci_wrapper.get_type( i_index ), uci_wrapper.get_uci_availability_zone() ) )
+ try:
+ reservation = conn.run_instances( image_id=mi_id,
+ key_name=uci_wrapper.get_key_pair_name( i_index ),
+ security_groups=[security_group],
+ user_data=userdata,
+ instance_type=uci_wrapper.get_type( i_index ),
+ placement=uci_wrapper.get_uci_availability_zone() )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str(e) ) )
+ uci_wrapper.set_error( "EC2 response error when starting: " + str(e), True )
+ # Record newly available instance data into local Galaxy database
+ l_time = datetime.utcnow()
+ uci_wrapper.set_launch_time( l_time, i_index=i_index ) # format_time( reservation.i_indexes[0].launch_time ) )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( l_time )
+ try:
+ uci_wrapper.set_reservation_id( i_index, str( reservation ).split(":")[1] )
+ # TODO: if more than a single instance will be started through single reservation, change this reference to element [0]
+ i_id = str( reservation.instances[0]).split(":")[1]
+ uci_wrapper.set_instance_id( i_index, i_id )
+ s = reservation.instances[0].state
+ uci_wrapper.change_state( s, i_id, s )
+ log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error when retrieving instance information for UCI '%s': '%s'" % ( uci_wrapper.get_name(), str(e) ) )
+ uci_wrapper.set_error( "EC2 response error when retrieving instance information: " + str(e), True )
+
def stopUCI( self, uci_wrapper):
"""
Stops all of cloud instances associated with given UCI.
@@ -355,8 +379,8 @@
def update( self ):
"""
- Runs a global status update on all instances that are in 'running', 'pending', "creating", or 'shutting-down' state.
- Also, runs update on all storage volumes that are in "in-use", "creating", or 'None' state.
+ Runs a global status update on all instances that are in 'running', 'pending', or 'shutting-down' state.
+ Also, runs update on all storage volumes that are in 'in-use', 'creating', or 'None' state.
Reason behind this method is to sync state of local DB and real-world resources
"""
log.debug( "Running general status update for EC2 UCIs..." )
@@ -385,14 +409,28 @@
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
- region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
- conn = EC2Connection( aws_access_key_id=a_key,
- aws_secret_access_key=s_key,
- is_secure=uci.credentials.provider.is_secure,
- region=region,
- path=uci.credentials.provider.path )
+ try:
+ region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
+ aws_secret_access_key=s_key,
+ is_secure=uci.credentials.provider.is_secure,
+ region=region,
+ path=uci.credentials.provider.path )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Establishing connection with cloud failed: %s" % str(e) )
+ uci.error( "Establishing connection with cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
# Get reservations handle for given instance
- rl= conn.get_all_instances( [inst.instance_id] )
+ try:
+ rl= conn.get_all_instances( [inst.instance_id] )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Retrieving instance(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
+ uci.error( "Retrieving instance(s) from cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
# Because EPC deletes references to reservations after a short while after instances have terminated, getting an empty list as a response to a query
# typically means the instance has successfully shut down but the check was not performed in short enough amount of time. Until alternative solution
# is found, below code sets state of given UCI to 'error' to indicate to the user something out of ordinary happened.
@@ -409,27 +447,33 @@
# Update instance status in local DB with info from cloud provider
for r in rl:
for i, cInst in enumerate( r.instances ):
- s = cInst.update()
- log.debug( "Checking state of cloud instance '%s' associated with UCI '%s' and reservation '%s'. State='%s'" % ( cInst, uci.name, r, s ) )
- if s != inst.state:
- inst.state = s
- inst.flush()
- # After instance has shut down, ensure UCI is marked as 'available'
- if s == instance_states.TERMINATED and uci.state != uci_states.ERROR:
- uci.state = uci_states.AVAILABLE
- uci.launch_time = None
- uci.flush()
- # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
- if s != uci.state and s != instance_states.TERMINATED:
- uci.state = s
- uci.flush()
- if cInst.public_dns_name != inst.public_dns:
- inst.public_dns = cInst.public_dns_name
- inst.flush()
- if cInst.private_dns_name != inst.private_dns:
- inst.private_dns = cInst.private_dns_name
- inst.flush()
-
+ try:
+ s = cInst.update()
+ log.debug( "Checking state of cloud instance '%s' associated with UCI '%s' and reservation '%s'. State='%s'" % ( cInst, uci.name, r, s ) )
+ if s != inst.state:
+ inst.state = s
+ inst.flush()
+ # After instance has shut down, ensure UCI is marked as 'available'
+ if s == instance_states.TERMINATED and uci.state != uci_states.ERROR:
+ uci.state = uci_states.AVAILABLE
+ uci.launch_time = None
+ uci.flush()
+ # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
+ if s != uci.state and s != instance_states.TERMINATED:
+ uci.state = s
+ uci.flush()
+ if cInst.public_dns_name != inst.public_dns:
+ inst.public_dns = cInst.public_dns_name
+ inst.flush()
+ if cInst.private_dns_name != inst.private_dns:
+ inst.private_dns = cInst.private_dns_name
+ inst.flush()
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Updating status of instance(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
+ uci.error( "Updating instance status from cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
def updateStore( self, store ):
# Get credentials associated wit this store
uci_id = store.uci_id
@@ -438,35 +482,55 @@
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
- region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
- conn = EC2Connection( aws_access_key_id=a_key,
+ try:
+ region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
aws_secret_access_key=s_key,
is_secure=uci.credentials.provider.is_secure,
region=region,
path=uci.credentials.provider.path )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Establishing connection with cloud failed: %s" % str(e) )
+ uci.error( "Establishing connection with cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
# Get reservations handle for given store
- vl = conn.get_all_volumes( [store.volume_id] )
+ try:
+ vl = conn.get_all_volumes( [store.volume_id] )
# log.debug( "Store '%s' vl: '%s'" % ( store.volume_id, vl ) )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Retrieving volume(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
+ uci.error( "Retrieving volume(s) from cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
# Update store status in local DB with info from cloud provider
- if store.status != vl[0].status:
- # In case something failed during creation of UCI but actual storage volume was created and yet
- # UCI state remained as 'new', try to remedy this by updating UCI state here
- if ( store.status == None ) and ( store.volume_id != None ):
- uci.state = vl[0].status
- uci.flush()
-
- store.status = vl[0].status
- store.flush()
- if store.i_id != vl[0].instance_id:
- store.i_id = vl[0].instance_id
- store.flush()
- if store.attach_time != vl[0].attach_time:
- store.attach_time = vl[0].attach_time
- store.flush()
- if store.device != vl[0].device:
- store.device = vl[0].device
- store.flush()
-
+ try:
+ if store.status != vl[0].status:
+ # In case something failed during creation of UCI but actual storage volume was created and yet
+ # UCI state remained as 'new', try to remedy this by updating UCI state here
+ if ( store.status == None ) and ( store.volume_id != None ):
+ uci.state = vl[0].status
+ uci.flush()
+
+ store.status = vl[0].status
+ store.flush()
+ if store.i_id != vl[0].instance_id:
+ store.i_id = vl[0].instance_id
+ store.flush()
+ if store.attach_time != vl[0].attach_time:
+ store.attach_time = vl[0].attach_time
+ store.flush()
+ if store.device != vl[0].device:
+ store.device = vl[0].device
+ store.flush()
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Updating status of volume(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
+ uci.error( "Updating volume status from cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
# def updateUCI( self, uci ):
# """
# Runs a global status update on all storage volumes and all instances that are
diff -r be631ed97541 -r e19eef93584f lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Thu Oct 29 11:46:43 2009 -0400
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Thu Oct 29 17:40:31 2009 -0400
@@ -7,7 +7,7 @@
from galaxy.datatypes.data import nice_size
from galaxy.util.bunch import Bunch
from Queue import Queue
-from sqlalchemy import or_
+from sqlalchemy import or_, and_
import galaxy.eggs
galaxy.eggs.require("boto")
@@ -69,9 +69,8 @@
"""Run the next job, waiting until one is available if necessary"""
cnt = 0
while 1:
-
+ #log.debug( '[%d] run_next->queue.qsize(): %s' % ( cnt, self.queue.qsize() ) )
uci_wrapper = self.queue.get()
- log.debug( '[%d] uci type: %s' % ( cnt, uci_wrapper.get_name() ) )
uci_state = uci_wrapper.get_state()
if uci_state is self.STOP_SIGNAL:
return
@@ -82,6 +81,7 @@
self.deleteUCI( uci_wrapper )
elif uci_state==uci_states.SUBMITTED:
self.startUCI( uci_wrapper )
+ #self.dummyStartUCI( uci_wrapper )
elif uci_state==uci_states.SHUTTING_DOWN:
self.stopUCI( uci_wrapper )
except:
@@ -94,13 +94,25 @@
"""
log.debug( '##### Establishing eucalyptus cloud connection' )
provider = uci_wrapper.get_provider()
- euca_region = RegionInfo( None, provider.region_name, provider.region_endpoint )
- conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(),
- aws_secret_access_key=uci_wrapper.get_secret_key(),
- is_secure=provider.is_secure,
- port=provider.port,
- region=euca_region,
- path=provider.path )
+ try:
+ euca_region = RegionInfo( None, provider.region_name, provider.region_endpoint )
+ except Exception, e:
+ log.error( "Selecting region with cloud provider failed: %2" % str(e) )
+ uci_wrapper.set_error( "Selecting region with cloud provider failed: " + str(e), True )
+ return None
+
+ try:
+ conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(),
+ aws_secret_access_key=uci_wrapper.get_secret_key(),
+ is_secure=provider.is_secure,
+ port=provider.port,
+ region=euca_region,
+ path=provider.path )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Establishing connection with cloud failed: %s" % str(e) )
+ uci_wrapper.set_error( "Establishing connection with cloud failed: " + str(e), True )
+ return None
+
return conn
def set_keypair( self, uci_wrapper, conn ):
@@ -108,20 +120,22 @@
Generate keypair using user's default credentials
"""
log.debug( "Getting user's keypair: '%s'" % self.key_pair )
- kp = conn.get_key_pair( self.key_pair )
instances = uci_wrapper.get_instances_indexes()
-
try:
+ kp = conn.get_key_pair( self.key_pair )
for inst in instances:
- log.debug("inst: '%s'" % inst )
uci_wrapper.set_key_pair( inst, kp.name )
return kp.name
- except AttributeError: # No keypair under this name exists so create it
- log.info( "No keypair found, creating keypair '%s'" % self.key_pair )
- kp = conn.create_key_pair( self.key_pair )
- for inst in instances:
- uci_wrapper.set_key_pair( inst, kp.name, kp.material )
-
+ except boto.exception.EC2ResponseError, e: # No keypair under this name exists so create it
+ if e.code == 'InvalidKeyPair.NotFound':
+ log.info( "No keypair found, creating keypair '%s'" % self.key_pair )
+ kp = conn.create_key_pair( self.key_pair )
+ for inst in instances:
+ uci_wrapper.set_key_pair( inst, kp.name, kp.material )
+ else:
+ log.error( "EC2 response error: '%s'" % e )
+ uci_wrapper.set_error( "EC2 response error while creating key pair: " + str(e), True )
+
return kp.name
def get_mi_id( self, type ):
@@ -208,10 +222,11 @@
uci = uci_wrapper.get_uci()
log.debug( "Would be starting instance '%s'" % uci.name )
- uci_wrapper.change_state( uci_states.PENDING )
-# log.debug( "Sleeping a bit... (%s)" % uci.name )
-# time.sleep(20)
-# log.debug( "Woke up! (%s)" % uci.name )
+# uci_wrapper.change_state( uci_states.SUBMITTED_UCI )
+# log.debug( "Set UCI state to SUBMITTED_UCI" )
+ log.debug( "Sleeping a bit... (%s)" % uci.name )
+ time.sleep(10)
+ log.debug( "Woke up! (%s)" % uci.name )
def startUCI( self, uci_wrapper ):
"""
@@ -219,30 +234,43 @@
"""
conn = self.get_connection( uci_wrapper )
#
- self.set_keypair( uci_wrapper, conn )
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ self.set_keypair( uci_wrapper, conn )
i_indexes = uci_wrapper.get_instances_indexes() # Get indexes of i_indexes associated with this UCI
- for i_index in i_indexes:
- mi_id = self.get_mi_id( uci_wrapper.get_type( i_index ) )
- log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name( i_index ): %s" % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
- uci_wrapper.set_mi( i_index, mi_id )
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ for i_index in i_indexes:
+ mi_id = self.get_mi_id( uci_wrapper.get_type( i_index ) )
+ log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name( i_index ): %s" % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
+ uci_wrapper.set_mi( i_index, mi_id )
- log.debug( "***** Starting UCI instance '%s'" % uci_wrapper.get_name() )
- log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
- reservation = conn.run_instances( image_id=mi_id, key_name=uci_wrapper.get_key_pair_name( i_index ) )
- #reservation = conn.run_instances( image_id=instance.image, key_name=instance.keypair_name, security_groups=['galaxy'], instance_type=instance.type, placement=instance.availability_zone )
- l_time = datetime.utcnow()
- uci_wrapper.set_launch_time( l_time, i_index=i_index ) # format_time( reservation.i_indexes[0].launch_time ) )
- if not uci_wrapper.uci_launch_time_set():
- uci_wrapper.set_uci_launch_time( l_time )
- uci_wrapper.set_reservation_id( i_index, str( reservation ).split(":")[1] )
- # TODO: if more than a single instance will be started through single reservation, change this reference to element [0]
- i_id = str( reservation.instances[0]).split(":")[1]
- uci_wrapper.set_instance_id( i_index, i_id )
- s = reservation.instances[0].state
- uci_wrapper.change_state( s, i_id, s )
- log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ log.debug( "***** Starting UCI instance '%s'" % uci_wrapper.get_name() )
+ log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
+ try:
+ reservation = conn.run_instances( image_id=mi_id, key_name=uci_wrapper.get_key_pair_name( i_index ) )
+ #reservation = conn.run_instances( image_id=instance.image, key_name=instance.keypair_name, security_groups=['galaxy'], instance_type=instance.type, placement=instance.availability_zone )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str(e) ) )
+ uci_wrapper.set_error( "EC2 response error when starting: " + str(e), True )
+
+ l_time = datetime.utcnow()
+ uci_wrapper.set_launch_time( l_time, i_index=i_index ) # format_time( reservation.i_indexes[0].launch_time ) )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( l_time )
+ try:
+ uci_wrapper.set_reservation_id( i_index, str( reservation ).split(":")[1] )
+ # TODO: if more than a single instance will be started through single reservation, change this reference to element [0]
+ i_id = str( reservation.instances[0]).split(":")[1]
+ uci_wrapper.set_instance_id( i_index, i_id )
+ s = reservation.instances[0].state
+ uci_wrapper.change_state( s, i_id, s )
+ log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error when retrieving instance information for UCI '%s': '%s'" % ( uci_wrapper.get_name(), str(e) ) )
+ uci_wrapper.set_error( "EC2 response error when retrieving instance information: " + str(e), True )
+
def stopUCI( self, uci_wrapper):
"""
@@ -316,11 +344,12 @@
def update( self ):
"""
- Runs a global status update on all instances that are in 'running', 'pending', "creating", or 'shutting-down' state.
- Also, runs update on all storage volumes that are in "in-use", "creating", or 'None' state.
+ Runs a global status update on all instances that are in 'running', 'pending', or 'shutting-down' state.
+ Also, runs update on all storage volumes that are in 'in-use', 'creating', or 'None' state.
Reason behind this method is to sync state of local DB and real-world resources
"""
log.debug( "Running general status update for EPC UCIs..." )
+ # Update instances
instances = model.CloudInstance.filter( or_( model.CloudInstance.c.state==instance_states.RUNNING,
model.CloudInstance.c.state==instance_states.PENDING,
model.CloudInstance.c.state==instance_states.SHUTTING_DOWN ) ).all()
@@ -328,7 +357,8 @@
if self.type == inst.uci.credentials.provider.type:
log.debug( "[%s] Running general status update on instance '%s'" % ( inst.uci.credentials.provider.type, inst.instance_id ) )
self.updateInstance( inst )
-
+
+ # Update storage volume(s)
stores = model.CloudStore.filter( or_( model.CloudStore.c.status==store_states.IN_USE,
model.CloudStore.c.status==store_states.CREATING,
model.CloudStore.c.status==None ) ).all()
@@ -337,6 +367,20 @@
log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider.type, store.volume_id ) )
self.updateStore( store )
+ # Attempt at updating any zombie UCIs (i.e., instances that have been in SUBMITTED state for longer than expected - see below for exact time)
+ zombies = model.UCI.filter_by( state=uci_states.SUBMITTED ).all()
+ for zombie in zombies:
+ z_instances = model.CloudInstance.filter_by( uci_id=zombie.id) \
+ .filter( or_( model.CloudInstance.c.state!=instance_states.TERMINATED,
+ model.CloudInstance.c.state == None ) ) \
+ .all()
+ for z_inst in z_instances:
+ if self.type == z_inst.uci.credentials.provider.type:
+ log.debug( "z_inst.id: '%s', state: '%s'" % ( z_inst.id, z_inst.state ) )
+# td = datetime.utcnow() - zombie.update_time
+# if td.seconds > 180: # if instance has been in SUBMITTED state for more than 3 minutes
+# log.debug( "[%s] Running zombie repair update on instance '%s'" % ( inst.uci.credentials.provider.type, inst.id ) )
+
def updateInstance( self, inst ):
# Get credentials associated wit this instance
@@ -346,15 +390,29 @@
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
- euca_region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
- conn = EC2Connection( aws_access_key_id=a_key,
- aws_secret_access_key=s_key,
- is_secure=uci.credentials.provider.is_secure,
- port=uci.credentials.provider.port,
- region=euca_region,
- path=uci.credentials.provider.path )
+ try:
+ euca_region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
+ aws_secret_access_key=s_key,
+ is_secure=uci.credentials.provider.is_secure,
+ port=uci.credentials.provider.port,
+ region=euca_region,
+ path=uci.credentials.provider.path )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Establishing connection with cloud failed: %s" % str(e) )
+ uci.error( "Establishing connection with cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
# Get reservations handle for given instance
- rl= conn.get_all_instances( [inst.instance_id] )
+ try:
+ rl= conn.get_all_instances( [inst.instance_id] )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Retrieving instance(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
+ uci.error( "Retrieving instance(s) from cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
# Because EPC deletes references to reservations after a short while after instances have terminated, getting an empty list as a response to a query
# typically means the instance has successfully shut down but the check was not performed in short enough amount of time. Until alternative solution
# is found, below code sets state of given UCI to 'error' to indicate to the user something out of ordinary happened.
@@ -371,26 +429,32 @@
# Update instance status in local DB with info from cloud provider
for r in rl:
for i, cInst in enumerate( r.instances ):
- s = cInst.update()
- log.debug( "Checking state of cloud instance '%s' associated with reservation '%s'. State='%s'" % ( cInst, r, s ) )
- if s != inst.state:
- inst.state = s
+ try:
+ s = cInst.update()
+ log.debug( "Checking state of cloud instance '%s' associated with reservation '%s'. State='%s'" % ( cInst, r, s ) )
+ if s != inst.state:
+ inst.state = s
+ inst.flush()
+ # After instance has shut down, ensure UCI is marked as 'available'
+ if s == instance_states.TERMINATED and uci.state != uci_states.ERROR:
+ uci.state = uci_states.AVAILABLE
+ uci.launch_time = None
+ uci.flush()
+ # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
+ if s != uci.state and s != instance_states.TERMINATED:
+ uci.state = s
+ uci.flush()
+ if cInst.public_dns_name != inst.public_dns:
+ inst.public_dns = cInst.public_dns_name
+ inst.flush()
+ if cInst.private_dns_name != inst.private_dns:
+ inst.private_dns = cInst.private_dns_name
inst.flush()
- # After instance has shut down, ensure UCI is marked as 'available'
- if s == instance_states.TERMINATED and uci.state != uci_states.ERROR:
- uci.state = uci_states.AVAILABLE
- uci.launch_time = None
- uci.flush()
- # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
- if s != uci.state and s != instance_states.TERMINATED:
- uci.state = s
- uci.flush()
- if cInst.public_dns_name != inst.public_dns:
- inst.public_dns = cInst.public_dns_name
- inst.flush()
- if cInst.private_dns_name != inst.private_dns:
- inst.private_dns = cInst.private_dns_name
- inst.flush()
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Updating status of instance(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
+ uci.error( "Updating volume status from cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
def updateStore( self, store ):
# Get credentials associated wit this store
@@ -400,34 +464,53 @@
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
- euca_region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
- conn = EC2Connection( aws_access_key_id=a_key,
- aws_secret_access_key=s_key,
- is_secure=uci.credentials.provider.is_secure,
- port=uci.credentials.provider.port,
- region=euca_region,
- path=uci.credentials.provider.path )# Get reservations handle for given store
- vl = conn.get_all_volumes( [store.volume_id] )
+ try:
+ euca_region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
+ aws_secret_access_key=s_key,
+ is_secure=uci.credentials.provider.is_secure,
+ port=uci.credentials.provider.port,
+ region=euca_region,
+ path=uci.credentials.provider.path )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Establishing connection with cloud failed: %s" % str(e) )
+ uci.error( "Establishing connection with cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
+ try:
+ vl = conn.get_all_volumes( [store.volume_id] )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Retrieving volume(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
+ uci.error( "Retrieving volume(s) from cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
+
# Update store status in local DB with info from cloud provider
- if store.status != vl[0].status:
- # In case something failed during creation of UCI but actual storage volume was created and yet
- # UCI state remained as 'new', try to remedy this by updating UCI state here
- if ( store.status == None ) and ( store.volume_id != None ):
- uci.state = vl[0].status
- uci.flush()
-
- store.status = vl[0].status
- store.flush()
- if store.i_id != vl[0].instance_id:
- store.i_id = vl[0].instance_id
- store.flush()
- if store.attach_time != vl[0].attach_time:
- store.attach_time = vl[0].attach_time
- store.flush()
- if store.device != vl[0].device:
- store.device = vl[0].device
- store.flush()
-
+ try:
+ if store.status != vl[0].status:
+ # In case something failed during creation of UCI but actual storage volume was created and yet
+ # UCI state remained as 'new', try to remedy this by updating UCI state here
+ if ( store.status == None ) and ( store.volume_id != None ):
+ uci.state = vl[0].status
+ uci.flush()
+
+ store.status = vl[0].status
+ store.flush()
+ if store.i_id != vl[0].instance_id:
+ store.i_id = vl[0].instance_id
+ store.flush()
+ if store.attach_time != vl[0].attach_time:
+ store.attach_time = vl[0].attach_time
+ store.flush()
+ if store.device != vl[0].device:
+ store.device = vl[0].device
+ store.flush()
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Updating status of volume(s) from cloud for UCI '%s' failed: " % ( uci.name, str(e) ) )
+ uci.error( "Updating volume status from cloud failed: " + str(e) )
+ uci.state( uci_states.ERROR )
+ return None
# def updateUCI( self, uci ):
# """
# Runs a global status update on all storage volumes and all instances that are
diff -r be631ed97541 -r e19eef93584f lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Thu Oct 29 11:46:43 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Thu Oct 29 17:40:31 2009 -0400
@@ -104,9 +104,9 @@
model.UCI.c.state==uci_states.SUBMITTED_UCI ) ) \
.all()
if pendingInstances:
- trans.set_message( "Galaxy instance started. NOTE: Please wait about 3-5 minutes for the instance to "
- "start up and then refresh this page. A button to connect to the instance will then appear alongside "
- "instance description." )
+ trans.set_message( "Galaxy instance started. Note that it will take several minutes for the instance to start "
+ "(typically, 3-5 minutes). Once the instance is running and Galaxy is available, "
+ "a button to connect to the instance will then appear alongside instance description." )
# log.debug( "provider.is_secure: '%s'" % trans.sa_session.query( model.CloudProvider).filter_by(id=1).first().is_secure )
# trans.sa_session.query( model.CloudProvider).filter_by(id=1).first().is_secure=False
@@ -151,17 +151,7 @@
stores = get_stores( trans, uci )
# Ensure instance is not already running (or related state) and store relevant data
# into DB to initiate instance startup by cloud manager
- if ( len(stores) is not 0 ) and \
- ( uci.state != uci_states.SUBMITTED ) and \
- ( uci.state != uci_states.SUBMITTED_UCI ) and \
- ( uci.state != uci_states.PENDING ) and \
- ( uci.state != uci_states.DELETING ) and \
- ( uci.state != uci_states.DELETING_UCI ) and \
- ( uci.state != uci_states.DELETED ) and \
- ( uci.state != uci_states.RUNNING ) and \
- ( uci.state != uci_states.NEW_UCI ) and \
- ( uci.state != uci_states.NEW ) and \
- ( uci.state != uci_states.ERROR ):
+ if ( len(stores) is not 0 ) and ( uci.state == uci_states.AVAILABLE ):
instance = model.CloudInstance()
instance.user = user
instance.image = mi
@@ -175,7 +165,7 @@
session.save_or_update( uci )
session.flush()
# Log
- trans.log_event ("User initiated starting of cloud instance '%s'." % uci.name )
+ trans.log_event ("User initiated starting of UCI '%s'." % uci.name )
trans.set_message( "Galaxy instance started. NOTE: Please wait about 3-5 minutes for the instance to "
"start up and then refresh this page. A button to connect to the instance will then appear alongside "
"instance description." )
@@ -268,14 +258,20 @@
storedCreds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user ).all()
if len( storedCreds ) == 0:
return trans.show_error_message( "You must register credentials before configuring a Galaxy instance." )
+ # TODO: This should be filled automatically but ties to implementation for diff provider is a problem...
# Create dict mapping of cloud providers to zones available by those providers
providersToZones = {}
for storedCred in storedCreds:
- if storedCred.provider.type == 'ec2':
+ if storedCred.provider.region_name == 'us-east-1':
ec2_zones = ['us-east-1a', 'us-east-1b', 'us-east-1c', 'us-east-1d']
providersToZones[storedCred.name] = ec2_zones
+ elif storedCred.provider.region_name == 'eu-west-1':
+ ec2_zones = ['eu-west-1a', 'eu-west-1b']
+ providersToZones[storedCred.name] = ec2_zones
elif storedCred.provider.type == 'eucalyptus':
providersToZones[storedCred.name] = ['epc']
+ else:
+ providersToZones[storedCred.name] = ['Unknown provider zone']
if instanceName:
# Create new user configured instance
diff -r be631ed97541 -r e19eef93584f templates/cloud/add_provider.mako
--- a/templates/cloud/add_provider.mako Thu Oct 29 11:46:43 2009 -0400
+++ b/templates/cloud/add_provider.mako Thu Oct 29 17:40:31 2009 -0400
@@ -117,9 +117,6 @@
<div id="region_selection" class="form-row-input">
<input type="text" name="region_name" id="region_name" value="${region_name}" size="40">
</div>
- %if error.has_key('name_error'):
- <div class="form-row-error-message">${error['name_error']}</div>
- %endif
<div style="clear: both"></div>
</div>
diff -r be631ed97541 -r e19eef93584f templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Thu Oct 29 11:46:43 2009 -0400
+++ b/templates/cloud/configure_cloud.mako Thu Oct 29 17:40:31 2009 -0400
@@ -33,6 +33,11 @@
if ( old_state=='pending' && new_state=='running' ) {
location.reload(true);
}
+ else if ( ( old_state=='running' && new_state=='error' ) || ( old_state=='pending' && new_state=='error' ) || \
+ ( old_state=='submitted' && new_state=='error' ) || ( old_state=='submittedUCI' && new_state=='error' ) || \
+ ( old_state=='shutting-down' && new_state=='error' ) ) {
+ location.reload(true);
+ }
else if ( old_state=='shutting-down' && new_state=='available' ) {
location.reload(true);
}
@@ -244,14 +249,14 @@
%if state =='error':
<div id="short">
<a onclick="document.getElementById('full').style.display = 'block';
- document.getElementById('short').style.display = 'none'; return 0"
+ document.getElementById('short').style.display = 'none'; return 0"
href="javascript:void(0)">
error
</a>
</div>
<div id="full" style="DISPLAY: none">
<a onclick="document.getElementById('short').style.display = 'block';
- document.getElementById('full').style.display = 'none'; return 0;"
+ document.getElementById('full').style.display = 'none'; return 0;"
href="javascript:void(0)">
error:</a><br />
${str(prevInstance.error)}
diff -r be631ed97541 -r e19eef93584f templates/cloud/configure_uci.mako
--- a/templates/cloud/configure_uci.mako Thu Oct 29 11:46:43 2009 -0400
+++ b/templates/cloud/configure_uci.mako Thu Oct 29 17:40:31 2009 -0400
@@ -81,7 +81,7 @@
cls += " form-row-error"
%>
<div class="${cls}">
- <label>Permanent storage size (1-1000GB)<br/>NOTE: you will be able to add more storage later:</label>
+ <label>Permanent storage size (1-1000GB):<br/>(Note: you will be able to add more storage later)</label>
<div class="form-row-input">
<input type="text" name="volSize" value="${volSize}" size="40">
</div>
@@ -97,7 +97,7 @@
cls += " form-row-error"
%>
<div class="${cls}">
- <label>Zone to create storage in</label>
+ <label>Zone to create storage in:</label>
<div class="form-row-input">
<select id="zones" name="zone" style="width:40em">
</select>
diff -r be631ed97541 -r e19eef93584f templates/cloud/viewInstance.mako
--- a/templates/cloud/viewInstance.mako Thu Oct 29 11:46:43 2009 -0400
+++ b/templates/cloud/viewInstance.mako Thu Oct 29 17:40:31 2009 -0400
@@ -83,12 +83,15 @@
</tr>
<tr>
<td> Public DNS:</td>
- <td> ${liveInstance.public_dns} </td>
+ <%
+ lnk="http://"+str(liveInstance.public_dns)
+ %>
+ <td> <a href="${lnk}" target="_blank">${liveInstance.public_dns}</a></td>
</tr>
%if liveInstance.private_dns != None:
<tr>
<td> Private DNS:</td>
- <td> ${liveInstance.private_dns} </td>
+ <td> ${liveInstance.private_dns}</td>
</tr>
%endif
%if liveInstance.availability_zone != None:
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/be631ed97541
changeset: 3077:be631ed97541
user: Enis Afgan <afgane(a)gmail.com>
date: Thu Oct 29 11:46:43 2009 -0400
description:
Cleaned up code a bit (i.e., deleted temporary code/comments).
diffstat:
lib/galaxy/cloud/__init__.py | 701 +------------------------------------
lib/galaxy/cloud/providers/ec2.py | 93 +----
lib/galaxy/cloud/providers/eucalyptus.py | 100 +----
lib/galaxy/web/controllers/cloud.py | 235 ------------
4 files changed, 23 insertions(+), 1106 deletions(-)
diffs (1457 lines):
diff -r 022ac0f64679 -r be631ed97541 lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Wed Oct 28 16:35:36 2009 -0400
+++ b/lib/galaxy/cloud/__init__.py Thu Oct 29 11:46:43 2009 -0400
@@ -47,40 +47,11 @@
self.provider = CloudProvider( app )
# Monitor for updating status of cloud instances
self.cloud_monitor = CloudMonitor( self.app, self.provider )
-# self.job_stop_queue = JobStopQueue( app, self.dispatcher )
else:
self.job_queue = self.job_stop_queue = NoopCloudMonitor()
def shutdown( self ):
self.cloud_monitor.shutdown()
-# self.job_stop_queue.shutdown()
-
-# def createUCI( self, user, name, storage_size, zone=None):
-# """
-# Createse User Configured Instance (UCI). Essentially, creates storage volume.
-# """
-# self.provider.createUCI( user, name, storage_size, zone )
-#
-# def deleteUCI( self, name ):
-# """
-# Deletes UCI. NOTE that this implies deletion of any and all data associated
-# with this UCI from the cloud. All data will be deleted.
-# """
-#
-# def addStorageToUCI( self, name ):
-# """ Adds more storage to specified UCI """
-#
-# def startUCI( self, name, type ):
-# """
-# Starts an instance of named UCI on the cloud. This implies, mounting of
-# storage and starting Galaxy instance.
-# """
-#
-# def stopUCI( self, name ):
-# """
-# Stops cloud instance associated with named UCI. This also implies
-# stopping of Galaxy and unmounting of the file system.
-# """
class Sleeper( object ):
"""
@@ -110,9 +81,7 @@
# Keep track of the pid that started the cloud manager, only it
# has valid threads
self.parent_pid = os.getpid()
- # Contains new jobs. Note this is not used if track_jobs_in_database is True
-# self.queue = Queue()
-
+
# Contains requests that are waiting (only use from monitor thread)
self.waiting = []
@@ -123,33 +92,6 @@
self.monitor_thread = threading.Thread( target=self.__monitor )
self.monitor_thread.start()
log.info( "Cloud manager started" )
-# if app.config.get_bool( 'enable_job_recovery', True ):
-# self.__check_jobs_at_startup()
-
- def __check_jobs_at_startup( self ):
- """
- Checks all jobs that are in the 'new', 'queued' or 'running' state in
- the database and requeues or cleans up as necessary. Only run as the
- job manager starts.
- """
- model = self.app.model
- for job in model.Job.filter( model.Job.c.state==model.Job.states.NEW ).all():
- if job.tool_id not in self.app.toolbox.tools_by_id:
- log.warning( "Tool '%s' removed from tool config, unable to recover job: %s" % ( job.tool_id, job.id ) )
- JobWrapper( job, None, self ).fail( 'This tool was disabled before the job completed. Please contact your Galaxy administrator, or' )
- else:
- log.debug( "no runner: %s is still in new state, adding to the jobs queue" %job.id )
- self.queue.put( ( job.id, job.tool_id ) )
- for job in model.Job.filter( (model.Job.c.state == model.Job.states.RUNNING) | (model.Job.c.state == model.Job.states.QUEUED) ).all():
- if job.tool_id not in self.app.toolbox.tools_by_id:
- log.warning( "Tool '%s' removed from tool config, unable to recover job: %s" % ( job.tool_id, job.id ) )
- JobWrapper( job, None, self ).fail( 'This tool was disabled before the job completed. Please contact your Galaxy administrator, or' )
- elif job.job_runner_name is None:
- log.debug( "no runner: %s is still in queued state, adding to the jobs queue" %job.id )
- self.queue.put( ( job.id, job.tool_id ) )
- else:
- job_wrapper = JobWrapper( job, self.app.toolbox.tools_by_id[ job.tool_id ], self )
- self.dispatcher.recover( job, job_wrapper )
def __monitor( self ):
"""
@@ -164,7 +106,7 @@
try:
# log.debug( "Calling monitor_step" )
self.__monitor_step()
- if cnt%30 == 0: # Run global update every 30 seconds (1 minute)
+ if cnt%30 == 0: # Run global update every 30 iterations (1 minute)
self.provider.update()
cnt = 0
except:
@@ -186,166 +128,24 @@
"""
# Get an orm (object relational mapping) session
session = mapping.Session()
- # Pull all new jobs from the queue at once
new_requests = []
-# new_instances = []
-# new_UCIs = []
-# stop_UCIs = []
-# delete_UCIs = []
-
-# for r in session.query( model.cloud_instance ).filter( model.cloud_instance.s.state == model.cloud_instance.states.NEW ).all():
-# new_instances
-
+
for r in session.query( model.UCI ) \
- .filter( or_( model.UCI.c.state==uci_states.NEW_UCI, #"newUCI",
- model.UCI.c.state==uci_states.SUBMITTED_UCI, #"submittedUCI",
- model.UCI.c.state==uci_states.SHUTTING_DOWN_UCI, #"shutting-downUCI",
+ .filter( or_( model.UCI.c.state==uci_states.NEW_UCI,
+ model.UCI.c.state==uci_states.SUBMITTED_UCI,
+ model.UCI.c.state==uci_states.SHUTTING_DOWN_UCI,
model.UCI.c.state==uci_states.DELETING_UCI ) ) \
.all():
uci_wrapper = UCIwrapper( r )
new_requests.append( uci_wrapper )
-# log.debug( 'new_requests: %s' % new_requests )
+
for uci_wrapper in new_requests:
session.clear()
-# log.debug( 'r.name: %s, state: %s' % ( r.name, r.state ) )
-# session.save_or_update( r )
-# session.flush()
self.provider.put( uci_wrapper )
# Done with the session
mapping.Session.remove()
-
-# for r in session.query( model.UCI ).filter( model.UCI.c.state == "submitted" ).all():
-# new_instances.append( r )
-# for r in new_instances:
-# self.provider.startUCI( r )
-#
-# for r in session.query( model.UCI ).filter( model.UCI.c.state == "shutting-down" ).all():
-# stop_UCIs.append( r )
-# for r in stop_UCIs:
-# self.provider.stopUCI( r )
-#
-# for r in session.query( model.UCI ).filter( model.UCI.c.state == "deleting" ).all():
-# delete_UCIs.append( r )
-# for r in delete_UCIs:
-# self.provider.deleteUCI( r )
-
-
-
-# if self.track_jobs_in_database:
-# for j in session.query( model.Job ).options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ).filter( model.Job.c.state == model.Job.states.NEW ).all():
-# job = JobWrapper( j, self.app.toolbox.tools_by_id[ j.tool_id ], self )
-# new_jobs.append( job )
-# else:
-# try:
-# while 1:
-# message = self.queue.get_nowait()
-# if message is self.STOP_SIGNAL:
-# return
-# # Unpack the message
-# job_id, tool_id = message
-# # Create a job wrapper from it
-# job_entity = session.query( model.Job ).get( job_id )
-# job = JobWrapper( job_entity, self.app.toolbox.tools_by_id[ tool_id ], self )
-# # Append to watch queue
-# new_jobs.append( job )
-# except Empty:
-# pass
-# # Iterate over new and waiting jobs and look for any that are
-# # ready to run
-# new_waiting = []
-# for job in ( new_jobs + self.waiting ):
-# try:
-# # Clear the session for each job so we get fresh states for
-# # job and all datasets
-# session.clear()
-# # Get the real job entity corresponding to the wrapper (if we
-# # are tracking in the database this is probably cached in
-# # the session from the origianl query above)
-# job_entity = session.query( model.Job ).get( job.job_id )
-# # Check the job's dependencies, requeue if they're not done
-# job_state = self.__check_if_ready_to_run( job, job_entity )
-# if job_state == JOB_WAIT:
-# if not self.track_jobs_in_database:
-# new_waiting.append( job )
-# elif job_state == JOB_ERROR:
-# log.info( "job %d ended with an error" % job.job_id )
-# elif job_state == JOB_INPUT_ERROR:
-# log.info( "job %d unable to run: one or more inputs in error state" % job.job_id )
-# elif job_state == JOB_INPUT_DELETED:
-# log.info( "job %d unable to run: one or more inputs deleted" % job.job_id )
-# elif job_state == JOB_READY:
-# # If special queuing is enabled, put the ready jobs in the special queue
-# if self.use_policy :
-# self.squeue.put( job )
-# log.debug( "job %d put in policy queue" % job.job_id )
-# else: # or dispatch the job directly
-# self.dispatcher.put( job )
-# log.debug( "job %d dispatched" % job.job_id)
-# elif job_state == JOB_DELETED:
-# msg = "job %d deleted by user while still queued" % job.job_id
-# job.info = msg
-# log.debug( msg )
-# elif job_state == JOB_ADMIN_DELETED:
-# job.fail( job_entity.info )
-# log.info( "job %d deleted by admin while still queued" % job.job_id )
-# else:
-# msg = "unknown job state '%s' for job %d" % ( job_state, job.job_id )
-# job.info = msg
-# log.error( msg )
-# except Exception, e:
-# job.info = "failure running job %d: %s" % ( job.job_id, str( e ) )
-# log.exception( "failure running job %d" % job.job_id )
-# # Update the waiting list
-# self.waiting = new_waiting
-# # If special (e.g. fair) scheduling is enabled, dispatch all jobs
-# # currently in the special queue
-# if self.use_policy :
-# while 1:
-# try:
-# sjob = self.squeue.get()
-# self.dispatcher.put( sjob )
-# log.debug( "job %d dispatched" % sjob.job_id )
-# except Empty:
-# # squeue is empty, so stop dispatching
-# break
-# except Exception, e: # if something else breaks while dispatching
-# job.fail( "failure running job %d: %s" % ( sjob.job_id, str( e ) ) )
-# log.exception( "failure running job %d" % sjob.job_id )
-# # Done with the session
-# mapping.Session.remove()
-
- def __check_if_ready_to_run( self, job_wrapper, job ):
- """
- Check if a job is ready to run by verifying that each of its input
- datasets is ready (specifically in the OK state). If any input dataset
- has an error, fail the job and return JOB_INPUT_ERROR. If any input
- dataset is deleted, fail the job and return JOB_INPUT_DELETED. If all
- input datasets are in OK state, return JOB_READY indicating that the
- job can be dispatched. Otherwise, return JOB_WAIT indicating that input
- datasets are still being prepared.
- """
- if job.state == model.Job.states.DELETED:
- return JOB_DELETED
- elif job.state == model.Job.states.ERROR:
- return JOB_ADMIN_DELETED
- for dataset_assoc in job.input_datasets:
- idata = dataset_assoc.dataset
- if not idata:
- continue
- # don't run jobs for which the input dataset was deleted
- if idata.deleted:
- job_wrapper.fail( "input data %d (file: %s) was deleted before the job started" % ( idata.hid, idata.file_name ) )
- return JOB_INPUT_DELETED
- # an error in the input data causes us to bail immediately
- elif idata.state == idata.states.ERROR:
- job_wrapper.fail( "input data %d is in error state" % ( idata.hid ) )
- return JOB_INPUT_ERROR
- elif idata.state != idata.states.OK and not ( idata.state == idata.states.SETTING_METADATA and job.tool_id is not None and job.tool_id == self.app.datatypes_registry.set_external_metadata_tool.id ):
- # need to requeue
- return JOB_WAIT
- return JOB_READY
-
+
def put( self, job_id, tool ):
"""Add a job to the queue (by job identifier)"""
if not self.track_jobs_in_database:
@@ -355,13 +155,11 @@
def shutdown( self ):
"""Attempts to gracefully shut down the worker thread"""
if self.parent_pid != os.getpid():
- # We're not the real job queue, do nothing
+ # We're not the real queue, do nothing
return
else:
log.info( "sending stop signal to worker thread" )
self.running = False
-# if not self.track_jobs_in_database:
-# self.queue.put( self.STOP_SIGNAL )
self.sleeper.wake()
log.info( "cloud manager stopped" )
self.dispatcher.shutdown()
@@ -649,11 +447,6 @@
def get_all_stores( self ):
""" Returns all storage volumes' database objects associated with this UCI. """
return model.CloudStore.filter( model.CloudStore.c.uci_id == self.uci_id ).all()
-# svs = model.CloudStore.filter( model.CloudStore.c.uci_id == self.uci_id ).all()
-# svl = [] # storage volume list
-# for sv in svs:
-# svl.append( sv.volume_id )
-# return svl
def get_uci( self ):
""" Returns database object for given UCI. """
@@ -679,336 +472,6 @@
uci.state = 'deleted' # for bookkeeping reasons, mark as deleted but don't actually delete.
uci.flush()
-#class JobWrapper( object ):
-# """
-# Wraps a 'model.Job' with convience methods for running processes and
-# state management.
-# """
-# def __init__(self, job, tool, queue ):
-# self.job_id = job.id
-# # This is immutable, we cache it for the scheduling policy to use if needed
-# self.session_id = job.session_id
-# self.tool = tool
-# self.queue = queue
-# self.app = queue.app
-# self.extra_filenames = []
-# self.command_line = None
-# self.galaxy_lib_dir = None
-# # With job outputs in the working directory, we need the working
-# # directory to be set before prepare is run, or else premature deletion
-# # and job recovery fail.
-# self.working_directory = \
-# os.path.join( self.app.config.job_working_directory, str( self.job_id ) )
-# self.output_paths = None
-# self.external_output_metadata = metadata.JobExternalOutputMetadataWrapper( job ) #wrapper holding the info required to restore and clean up from files used for setting metadata externally
-#
-# def get_param_dict( self ):
-# """
-# Restore the dictionary of parameters from the database.
-# """
-# job = model.Job.get( self.job_id )
-# param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] )
-# param_dict = self.tool.params_from_strings( param_dict, self.app )
-# return param_dict
-#
-# def prepare( self ):
-# """
-# Prepare the job to run by creating the working directory and the
-# config files.
-# """
-# mapping.context.current.clear() #this prevents the metadata reverting that has been seen in conjunction with the PBS job runner
-# if not os.path.exists( self.working_directory ):
-# os.mkdir( self.working_directory )
-# # Restore parameters from the database
-# job = model.Job.get( self.job_id )
-# incoming = dict( [ ( p.name, p.value ) for p in job.parameters ] )
-# incoming = self.tool.params_from_strings( incoming, self.app )
-# # Do any validation that could not be done at job creation
-# self.tool.handle_unvalidated_param_values( incoming, self.app )
-# # Restore input / output data lists
-# inp_data = dict( [ ( da.name, da.dataset ) for da in job.input_datasets ] )
-# out_data = dict( [ ( da.name, da.dataset ) for da in job.output_datasets ] )
-# # These can be passed on the command line if wanted as $userId $userEmail
-# if job.history.user: # check for anonymous user!
-# userId = '%d' % job.history.user.id
-# userEmail = str(job.history.user.email)
-# else:
-# userId = 'Anonymous'
-# userEmail = 'Anonymous'
-# incoming['userId'] = userId
-# incoming['userEmail'] = userEmail
-# # Build params, done before hook so hook can use
-# param_dict = self.tool.build_param_dict( incoming, inp_data, out_data, self.get_output_fnames(), self.working_directory )
-# # Certain tools require tasks to be completed prior to job execution
-# # ( this used to be performed in the "exec_before_job" hook, but hooks are deprecated ).
-# if self.tool.tool_type is not None:
-# out_data = self.tool.exec_before_job( self.queue.app, inp_data, out_data, param_dict )
-# # Run the before queue ("exec_before_job") hook
-# self.tool.call_hook( 'exec_before_job', self.queue.app, inp_data=inp_data,
-# out_data=out_data, tool=self.tool, param_dict=incoming)
-# mapping.context.current.flush()
-# # Build any required config files
-# config_filenames = self.tool.build_config_files( param_dict, self.working_directory )
-# # FIXME: Build the param file (might return None, DEPRECATED)
-# param_filename = self.tool.build_param_file( param_dict, self.working_directory )
-# # Build the job's command line
-# self.command_line = self.tool.build_command_line( param_dict )
-# # FIXME: for now, tools get Galaxy's lib dir in their path
-# if self.command_line and self.command_line.startswith( 'python' ):
-# self.galaxy_lib_dir = os.path.abspath( "lib" ) # cwd = galaxy root
-# # We need command_line persisted to the db in order for Galaxy to re-queue the job
-# # if the server was stopped and restarted before the job finished
-# job.command_line = self.command_line
-# job.flush()
-# # Return list of all extra files
-# extra_filenames = config_filenames
-# if param_filename is not None:
-# extra_filenames.append( param_filename )
-# self.param_dict = param_dict
-# self.extra_filenames = extra_filenames
-# return extra_filenames
-#
-# def fail( self, message, exception=False ):
-# """
-# Indicate job failure by setting state and message on all output
-# datasets.
-# """
-# job = model.Job.get( self.job_id )
-# job.refresh()
-# # if the job was deleted, don't fail it
-# if not job.state == model.Job.states.DELETED:
-# # Check if the failure is due to an exception
-# if exception:
-# # Save the traceback immediately in case we generate another
-# # below
-# job.traceback = traceback.format_exc()
-# # Get the exception and let the tool attempt to generate
-# # a better message
-# etype, evalue, tb = sys.exc_info()
-# m = self.tool.handle_job_failure_exception( evalue )
-# if m:
-# message = m
-# if self.app.config.outputs_to_working_directory:
-# for dataset_path in self.get_output_fnames():
-# try:
-# shutil.move( dataset_path.false_path, dataset_path.real_path )
-# log.debug( "fail(): Moved %s to %s" % ( dataset_path.false_path, dataset_path.real_path ) )
-# except ( IOError, OSError ), e:
-# log.error( "fail(): Missing output file in working directory: %s" % e )
-# for dataset_assoc in job.output_datasets:
-# dataset = dataset_assoc.dataset
-# dataset.refresh()
-# dataset.state = dataset.states.ERROR
-# dataset.blurb = 'tool error'
-# dataset.info = message
-# dataset.set_size()
-# dataset.flush()
-# job.state = model.Job.states.ERROR
-# job.command_line = self.command_line
-# job.info = message
-# job.flush()
-# # If the job was deleted, just clean up
-# self.cleanup()
-#
-# def change_state( self, state, info = False ):
-# job = model.Job.get( self.job_id )
-# job.refresh()
-# for dataset_assoc in job.output_datasets:
-# dataset = dataset_assoc.dataset
-# dataset.refresh()
-# dataset.state = state
-# if info:
-# dataset.info = info
-# dataset.flush()
-# if info:
-# job.info = info
-# job.state = state
-# job.flush()
-#
-# def get_state( self ):
-# job = model.Job.get( self.job_id )
-# job.refresh()
-# return job.state
-#
-# def set_runner( self, runner_url, external_id ):
-# job = model.Job.get( self.job_id )
-# job.refresh()
-# job.job_runner_name = runner_url
-# job.job_runner_external_id = external_id
-# job.flush()
-#
-# def finish( self, stdout, stderr ):
-# """
-# Called to indicate that the associated command has been run. Updates
-# the output datasets based on stderr and stdout from the command, and
-# the contents of the output files.
-# """
-# # default post job setup
-# mapping.context.current.clear()
-# job = model.Job.get( self.job_id )
-# # if the job was deleted, don't finish it
-# if job.state == job.states.DELETED:
-# self.cleanup()
-# return
-# elif job.state == job.states.ERROR:
-# # Job was deleted by an administrator
-# self.fail( job.info )
-# return
-# if stderr:
-# job.state = "error"
-# else:
-# job.state = 'ok'
-# if self.app.config.outputs_to_working_directory:
-# for dataset_path in self.get_output_fnames():
-# try:
-# shutil.move( dataset_path.false_path, dataset_path.real_path )
-# log.debug( "finish(): Moved %s to %s" % ( dataset_path.false_path, dataset_path.real_path ) )
-# except ( IOError, OSError ):
-# self.fail( "Job %s's output dataset(s) could not be read" % job.id )
-# return
-# for dataset_assoc in job.output_datasets:
-# #should this also be checking library associations? - can a library item be added from a history before the job has ended? - lets not allow this to occur
-# for dataset in dataset_assoc.dataset.dataset.history_associations: #need to update all associated output hdas, i.e. history was shared with job running
-# dataset.blurb = 'done'
-# dataset.peek = 'no peek'
-# dataset.info = stdout + stderr
-# dataset.set_size()
-# if stderr:
-# dataset.blurb = "error"
-# elif dataset.has_data():
-# #if a dataset was copied, it won't appear in our dictionary:
-# #either use the metadata from originating output dataset, or call set_meta on the copies
-# #it would be quicker to just copy the metadata from the originating output dataset,
-# #but somewhat trickier (need to recurse up the copied_from tree), for now we'll call set_meta()
-# if not self.external_output_metadata.external_metadata_set_successfully( dataset ):
-# # Only set metadata values if they are missing...
-# dataset.set_meta( overwrite = False )
-# else:
-# #load metadata from file
-# #we need to no longer allow metadata to be edited while the job is still running,
-# #since if it is edited, the metadata changed on the running output will no longer match
-# #the metadata that was stored to disk for use via the external process,
-# #and the changes made by the user will be lost, without warning or notice
-# dataset.metadata.from_JSON_dict( self.external_output_metadata.get_output_filenames_by_dataset( dataset ).filename_out )
-# if self.tool.is_multi_byte:
-# dataset.set_multi_byte_peek()
-# else:
-# dataset.set_peek()
-# else:
-# dataset.blurb = "empty"
-# dataset.flush()
-# if stderr:
-# dataset_assoc.dataset.dataset.state = model.Dataset.states.ERROR
-# else:
-# dataset_assoc.dataset.dataset.state = model.Dataset.states.OK
-# dataset_assoc.dataset.dataset.flush()
-#
-# # Save stdout and stderr
-# if len( stdout ) > 32768:
-# log.error( "stdout for job %d is greater than 32K, only first part will be logged to database" % job.id )
-# job.stdout = stdout[:32768]
-# if len( stderr ) > 32768:
-# log.error( "stderr for job %d is greater than 32K, only first part will be logged to database" % job.id )
-# job.stderr = stderr[:32768]
-# # custom post process setup
-# inp_data = dict( [ ( da.name, da.dataset ) for da in job.input_datasets ] )
-# out_data = dict( [ ( da.name, da.dataset ) for da in job.output_datasets ] )
-# param_dict = dict( [ ( p.name, p.value ) for p in job.parameters ] ) # why not re-use self.param_dict here? ##dunno...probably should, this causes tools.parameters.basic.UnvalidatedValue to be used in following methods instead of validated and transformed values during i.e. running workflows
-# param_dict = self.tool.params_from_strings( param_dict, self.app )
-# # Check for and move associated_files
-# self.tool.collect_associated_files(out_data, self.working_directory)
-# # Create generated output children and primary datasets and add to param_dict
-# collected_datasets = {'children':self.tool.collect_child_datasets(out_data),'primary':self.tool.collect_primary_datasets(out_data)}
-# param_dict.update({'__collected_datasets__':collected_datasets})
-# # Certain tools require tasks to be completed after job execution
-# # ( this used to be performed in the "exec_after_process" hook, but hooks are deprecated ).
-# if self.tool.tool_type is not None:
-# self.tool.exec_after_process( self.queue.app, inp_data, out_data, param_dict, job = job )
-# # Call 'exec_after_process' hook
-# self.tool.call_hook( 'exec_after_process', self.queue.app, inp_data=inp_data,
-# out_data=out_data, param_dict=param_dict,
-# tool=self.tool, stdout=stdout, stderr=stderr )
-# # TODO
-# # validate output datasets
-# job.command_line = self.command_line
-# mapping.context.current.flush()
-# log.debug( 'job %d ended' % self.job_id )
-# self.cleanup()
-#
-# def cleanup( self ):
-# # remove temporary files
-# try:
-# for fname in self.extra_filenames:
-# os.remove( fname )
-# if self.working_directory is not None:
-# shutil.rmtree( self.working_directory )
-# if self.app.config.set_metadata_externally:
-# self.external_output_metadata.cleanup_external_metadata()
-# except:
-# log.exception( "Unable to cleanup job %d" % self.job_id )
-#
-# def get_command_line( self ):
-# return self.command_line
-#
-# def get_session_id( self ):
-# return self.session_id
-#
-# def get_input_fnames( self ):
-# job = model.Job.get( self.job_id )
-# filenames = []
-# for da in job.input_datasets: #da is JobToInputDatasetAssociation object
-# if da.dataset:
-# filenames.append( da.dataset.file_name )
-# #we will need to stage in metadata file names also
-# #TODO: would be better to only stage in metadata files that are actually needed (found in command line, referenced in config files, etc.)
-# for key, value in da.dataset.metadata.items():
-# if isinstance( value, model.MetadataFile ):
-# filenames.append( value.file_name )
-# return filenames
-#
-# def get_output_fnames( self ):
-# if self.output_paths is not None:
-# return self.output_paths
-#
-# class DatasetPath( object ):
-# def __init__( self, real_path, false_path = None ):
-# self.real_path = real_path
-# self.false_path = false_path
-# def __str__( self ):
-# if self.false_path is None:
-# return self.real_path
-# else:
-# return self.false_path
-#
-# job = model.Job.get( self.job_id )
-# if self.app.config.outputs_to_working_directory:
-# self.output_paths = []
-# for name, data in [ ( da.name, da.dataset.dataset ) for da in job.output_datasets ]:
-# false_path = os.path.abspath( os.path.join( self.working_directory, "galaxy_dataset_%d.dat" % data.id ) )
-# self.output_paths.append( DatasetPath( data.file_name, false_path ) )
-# else:
-# self.output_paths = [ DatasetPath( da.dataset.file_name ) for da in job.output_datasets ]
-# return self.output_paths
-#
-# def check_output_sizes( self ):
-# sizes = []
-# output_paths = self.get_output_fnames()
-# for outfile in [ str( o ) for o in output_paths ]:
-# sizes.append( ( outfile, os.stat( outfile ).st_size ) )
-# return sizes
-# def setup_external_metadata( self, exec_dir = None, tmp_dir = None, dataset_files_path = None, config_root = None, datatypes_config = None, **kwds ):
-# if tmp_dir is None:
-# #this dir should should relative to the exec_dir
-# tmp_dir = self.app.config.new_file_path
-# if dataset_files_path is None:
-# dataset_files_path = self.app.model.Dataset.file_path
-# if config_root is None:
-# config_root = self.app.config.root
-# if datatypes_config is None:
-# datatypes_config = self.app.config.datatypes_config
-# job = model.Job.get( self.job_id )
-# return self.external_output_metadata.setup_external_metadata( [ output_dataset_assoc.dataset for output_dataset_assoc in job.output_datasets ], exec_dir = exec_dir, tmp_dir = tmp_dir, dataset_files_path = dataset_files_path, config_root = config_root, datatypes_config = datatypes_config, **kwds )
-
class CloudProvider( object ):
def __init__( self, app ):
import providers.eucalyptus
@@ -1018,63 +481,11 @@
self.cloud_provider = {}
self.cloud_provider["eucalyptus"] = providers.eucalyptus.EucalyptusCloudProvider( app )
self.cloud_provider["ec2"] = providers.ec2.EC2CloudProvider( app )
-
-# start_cloud_provider = None
-# if app.config.start_job_runners is not None:
-# start_cloud_provider.extend( app.config.start_job_runners.split(",") )
-# for provider_name in start_cloud_provider:
-# self.provider_name = app.config.cloud_provider
-# if self.provider_name == "eucalyptus":
-# import providers.eucalyptus
-# self.cloud_provider[self.provider_name] = providers.eucalyptus.EucalyptusCloudProvider( app )
-# elif self.provider_name == "ec2":
-# import providers.ec2
-# self.cloud_provider[self.provider_name] = providers.ec2.EC2CloudProvider( app )
-# else:
-# log.error( "Unable to start unknown cloud provider: %s" %self.provider_name )
-
+
def put( self, uci_wrapper ):
""" Put given request for UCI manipulation into provider's request queue."""
-# log.debug( "Adding UCI '%s' manipulation request into cloud manager's queue." % uci_wrapper.name )
self.cloud_provider[uci_wrapper.get_provider_type()].put( uci_wrapper )
-
-
-
- def createUCI( self, uci ):
- """
- Createse User Configured Instance (UCI). Essentially, creates storage volume.
- """
- log.debug( "Creating UCI '%s'" % uci.name )
- self.cloud_provider[self.provider_name].createUCI( uci )
-
- def deleteUCI( self, uci ):
- """
- Deletes UCI. NOTE that this implies deletion of any and all data associated
- with this UCI from the cloud. All data will be deleted.
- """
- log.debug( "Deleting UCI '%s'" % uci.name )
- self.cloud_provider[self.provider_name].deleteUCI( uci )
-
- def addStorageToUCI( self, uci ):
- """ Adds more storage to specified UCI """
-
- def startUCI( self, uci ):
- """
- Starts an instance of named UCI on the cloud. This implies, mounting of
- storage and starting Galaxy instance.
- """
- log.debug( "Starting UCI '%s'" % uci.name )
- self.cloud_provider[self.provider_name].startUCI( uci )
-
- def stopUCI( self, uci ):
- """
- Stops cloud instance associated with named UCI. This also implies
- stopping of Galaxy and unmounting of the file system.
- """
- log.debug( "Stopping UCI '%s'" % uci.name )
- self.cloud_provider[self.provider_name].stopUCI( uci )
-
def update( self ):
"""
Runs a global status update across all providers for all UCIs in state other than 'terminated' and 'available'.
@@ -1084,102 +495,10 @@
# log.debug( "Running global update for provider: '%s'" % provider )
self.cloud_provider[provider].update()
- def recover( self, job, job_wrapper ):
- runner_name = ( job.job_runner_name.split(":", 1) )[0]
- log.debug( "recovering job %d in %s runner" %( job.id, runner_name ) )
- self.cloud_provider[runner_name].recover( job, job_wrapper )
-
def shutdown( self ):
for runner in self.cloud_provider.itervalues():
runner.shutdown()
-class JobStopQueue( object ):
- """
- A queue for jobs which need to be terminated prematurely.
- """
- STOP_SIGNAL = object()
- def __init__( self, app, dispatcher ):
- self.app = app
- self.dispatcher = dispatcher
-
- # Keep track of the pid that started the job manager, only it
- # has valid threads
- self.parent_pid = os.getpid()
- # Contains new jobs. Note this is not used if track_jobs_in_database is True
- self.queue = Queue()
-
- # Contains jobs that are waiting (only use from monitor thread)
- self.waiting = []
-
- # Helper for interruptable sleep
- self.sleeper = Sleeper()
- self.running = True
- self.monitor_thread = threading.Thread( target=self.monitor )
- self.monitor_thread.start()
- log.info( "job stopper started" )
-
- def monitor( self ):
- """
- Continually iterate the waiting jobs, stop any that are found.
- """
- # HACK: Delay until after forking, we need a way to do post fork notification!!!
- time.sleep( 10 )
- while self.running:
- try:
- self.monitor_step()
- except:
- log.exception( "Exception in monitor_step" )
- # Sleep
- self.sleeper.sleep( 1 )
-
- def monitor_step( self ):
- """
- Called repeatedly by `monitor` to stop jobs.
- """
- # Pull all new jobs from the queue at once
- jobs = []
- try:
- while 1:
- ( job_id, error_msg ) = self.queue.get_nowait()
- if job_id is self.STOP_SIGNAL:
- return
- # Append to watch queue
- jobs.append( ( job_id, error_msg ) )
- except Empty:
- pass
-
- for job_id, error_msg in jobs:
- job = model.Job.get( job_id )
- job.refresh()
- # if desired, error the job so we can inform the user.
- if error_msg is not None:
- job.state = job.states.ERROR
- job.info = error_msg
- else:
- job.state = job.states.DELETED
- job.flush()
- # if job is in JobQueue or FooJobRunner's put method,
- # job_runner_name will be unset and the job will be dequeued due to
- # state change above
- if job.job_runner_name is not None:
- # tell the dispatcher to stop the job
- self.dispatcher.stop( job )
-
- def put( self, job_id, error_msg=None ):
- self.queue.put( ( job_id, error_msg ) )
-
- def shutdown( self ):
- """Attempts to gracefully shut down the worker thread"""
- if self.parent_pid != os.getpid():
- # We're not the real job queue, do nothing
- return
- else:
- log.info( "sending stop signal to worker thread" )
- self.running = False
- self.queue.put( ( self.STOP_SIGNAL, None ) )
- self.sleeper.wake()
- log.info( "job stopper stopped" )
-
class NoopCloudMonitor( object ):
"""
Implements the CloudMonitor interface but does nothing
diff -r 022ac0f64679 -r be631ed97541 lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Wed Oct 28 16:35:36 2009 -0400
+++ b/lib/galaxy/cloud/providers/ec2.py Thu Oct 29 11:46:43 2009 -0400
@@ -56,7 +56,6 @@
self.key_pair = "galaxy-keypair"
self.queue = Queue()
- #TODO: Use multiple threads to process requests?
self.threads = []
nworkers = 5
log.info( "Starting EC2 cloud controller workers" )
@@ -72,19 +71,17 @@
while 1:
uci_wrapper = self.queue.get()
-# uci = uci_wrapper.get_uci()
- log.debug( '[%d] uci type: %s' % ( cnt, uci_wrapper.get_name() ) )
uci_state = uci_wrapper.get_state()
if uci_state is self.STOP_SIGNAL:
return
try:
- if uci_state==uci_states.NEW: # "new":
+ if uci_state==uci_states.NEW:
self.createUCI( uci_wrapper )
- elif uci_state==uci_states.DELETING: #"deleting":
+ elif uci_state==uci_states.DELETING:
self.deleteUCI( uci_wrapper )
- elif uci_state==uci_states.SUBMITTED: #"submitted":
+ elif uci_state==uci_states.SUBMITTED:
self.startUCI( uci_wrapper )
- elif uci_state==uci_states.SHUTTING_DOWN: #"shutting-down":
+ elif uci_state==uci_states.SHUTTING_DOWN:
self.stopUCI( uci_wrapper )
except:
log.exception( "Uncaught exception executing request." )
@@ -113,7 +110,6 @@
try:
kp = conn.get_key_pair( self.key_pair )
for inst in instances:
-# log.debug("inst: '%s'" % inst )
uci_wrapper.set_key_pair( inst, kp.name )
return kp.name
except boto.exception.EC2ResponseError, e: # No keypair under this name exists so create it
@@ -135,22 +131,7 @@
For valid sizes, see http://aws.amazon.com/ec2/instance-types/
"""
return model.CloudImage.filter( model.CloudImage.table.c.id==2 ).first().image_id
-
-# def get_instances( self, uci ):
-# """
-# Get objects of instances that are pending or running and are connected to uci object
-# """
-# instances = trans.sa_session.query( model.CloudInstance ) \
-# .filter_by( user=user, uci_id=uci.id ) \
-# .filter( or_(model.CloudInstance.table.c.state=="running", model.CloudInstance.table.c.state=="pending" ) ) \
-# .first()
-# #.all() #TODO: return all but need to edit calling method(s) to handle list
-#
-# instances = uci.instance
-#
-# return instances
-
-
+
def shutdown( self ):
"""Attempts to gracefully shut down the monitor thread"""
log.info( "sending stop signal to worker threads in EC2 cloud manager" )
@@ -170,7 +151,6 @@
and registers relevant information in Galaxy database.
"""
conn = self.get_connection( uci_wrapper )
- # Temporary code - need to ensure user selects zone at UCI creation time!
if uci_wrapper.get_uci_availability_zone()=='':
log.info( "Availability zone for UCI (i.e., storage volume) was not selected, using default zone: %s" % self.zone )
uci_wrapper.set_store_availability_zone( self.zone )
@@ -262,12 +242,11 @@
for i_index in i_indexes:
mi_id = self.get_mi_id( uci_wrapper.get_type( i_index ) )
-# log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name( i_index ): %s" % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
uci_wrapper.set_mi( i_index, mi_id )
# Check if galaxy security group exists (and create it if it does not)
-# log.debug( '***** Setting up security group' )
security_group = 'galaxyWeb'
+ log.debug( "Setting up '%s' security group." % security_group )
sgs = conn.get_all_security_groups() # security groups
gsgt = False # galaxy security group test
for sg in sgs:
@@ -303,39 +282,6 @@
uci_wrapper.change_state( s, i_id, s )
log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
-
-
-# # Wait until instance gets running and then update the DB
-# while s!="running":
-# log.debug( "Waiting on instance '%s' to start up (reservation ID: %s); current state: %s" % ( uci.instance[0].instance_id, uci.instance[0].reservation_id, s ) )
-# time.sleep( 15 )
-# s = reservation.i_indexes[0].update()
-#
-# # Update instance data in local DB
-# uci.instance[0].state = s
-# uci.instance[0].public_dns = reservation.i_indexes[0].dns_name
-# uci.instance[0].private_dns = reservation.i_indexes[0].private_dns_name
-# uci.instance[0].flush()
-# # Update storage data in local DB w/ volume state info. NOTE that this only captures current volume state
-# # and does not connect or wait on connection between instance and volume to be established
-# vl = model.CloudStore.filter( model.CloudStore.c.uci_id == uci.id ).all()
-# vols = []
-# for v in vl:
-# vols.append( v.volume_id )
-# try:
-# volumes = conn.get_all_volumes( vols )
-# for i, v in enumerate( volumes ):
-# uci.store[i].i_id = v.instance_id
-# uci.store[i].status = v.status
-# uci.store[i].device = v.device
-# uci.store[i].flush()
-# except BotoServerError:
-# log.debug( "Error getting volume(s) attached to instance. Volume status was not updated." )
-#
-# uci.state = s
-# uci.flush()
-
-
def stopUCI( self, uci_wrapper):
"""
Stops all of cloud instances associated with given UCI.
@@ -344,14 +290,8 @@
# Get all instances associated with given UCI
il = uci_wrapper.get_instances_ids() # instance list
-# log.debug( 'List of instances being terminated: %s' % il )
rl = conn.get_all_instances( il ) # Reservation list associated with given instances
-
-# tState = conn.terminate_instances( il )
-# # TODO: Need to update instance stop time (for all individual instances)
-# stop_time = datetime.utcnow()
-# uci_wrapper.set_stop_time( stop_time )
-
+
# Initiate shutdown of all instances under given UCI
cnt = 0
stopped = []
@@ -364,26 +304,7 @@
uci_wrapper.change_state( instance_id=inst.id, i_state=inst.update() )
stopped.append( inst )
-# uci_wrapper.change_state( uci_state='available' )
uci_wrapper.reset_uci_launch_time()
-
-# # Wait for all instances to actually terminate and update local DB
-# terminated=0
-# while terminated!=len( rl ):
-# for i, r in enumerate( rl ):
-# log.debug( "r state: %s" % r.instances[0].state )
-# state = r.instances[0].update()
-# if state=='terminated':
-# uci.instance[i].state = state
-# uci.instance[i].flush()
-# terminated += 1
-# time.sleep ( 5 )
-#
-# # Reset UCI state
-# uci.state = 'available'
-# uci.launch_time = None
-# uci.flush()
-#
log.debug( "Termination was initiated for all instances of UCI '%s'." % uci_wrapper.get_name() )
diff -r 022ac0f64679 -r be631ed97541 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Wed Oct 28 16:35:36 2009 -0400
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Thu Oct 29 11:46:43 2009 -0400
@@ -56,7 +56,6 @@
self.key_pair = "galaxy-keypair"
self.queue = Queue()
- #TODO: Use multiple threads to process requests?
self.threads = []
nworkers = 5
log.info( "Starting eucalyptus cloud controller workers" )
@@ -72,19 +71,18 @@
while 1:
uci_wrapper = self.queue.get()
-# uci = uci_wrapper.get_uci()
log.debug( '[%d] uci type: %s' % ( cnt, uci_wrapper.get_name() ) )
uci_state = uci_wrapper.get_state()
if uci_state is self.STOP_SIGNAL:
return
try:
- if uci_state==uci_states.NEW: # "new":
+ if uci_state==uci_states.NEW:
self.createUCI( uci_wrapper )
- elif uci_state==uci_states.DELETING: #"deleting":
+ elif uci_state==uci_states.DELETING:
self.deleteUCI( uci_wrapper )
- elif uci_state==uci_states.SUBMITTED: #"submitted":
+ elif uci_state==uci_states.SUBMITTED:
self.startUCI( uci_wrapper )
- elif uci_state==uci_states.SHUTTING_DOWN: #"shutting-down":
+ elif uci_state==uci_states.SHUTTING_DOWN:
self.stopUCI( uci_wrapper )
except:
log.exception( "Uncaught exception executing request." )
@@ -134,22 +132,7 @@
"""
log.debug( "image id: '%s'" % model.CloudImage.get( 1 ).image_id )
return model.CloudImage.get( 1 ).image_id
-
-# def get_instances( self, uci ):
-# """
-# Get objects of instances that are pending or running and are connected to uci object
-# """
-# instances = trans.sa_session.query( model.CloudInstance ) \
-# .filter_by( user=user, uci_id=uci.id ) \
-# .filter( or_(model.CloudInstance.table.c.state=="running", model.CloudInstance.table.c.state=="pending" ) ) \
-# .first()
-# #.all() #TODO: return all but need to edit calling method(s) to handle list
-#
-# instances = uci.instance
-#
-# return instances
-
-
+
def shutdown( self ):
"""Attempts to gracefully shut down the monitor thread"""
log.info( "sending stop signal to worker threads in eucalyptus cloud manager" )
@@ -169,7 +152,6 @@
and registers relevant information in Galaxy database.
"""
conn = self.get_connection( uci_wrapper )
- # Temporary code - need to ensure user selects zone at UCI creation time!
if uci_wrapper.get_uci_availability_zone()=='':
log.info( "Availability zone for UCI (i.e., storage volume) was not selected, using default zone: %s" % self.zone )
uci_wrapper.set_store_availability_zone( self.zone )
@@ -245,21 +227,7 @@
mi_id = self.get_mi_id( uci_wrapper.get_type( i_index ) )
log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name( i_index ): %s" % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
uci_wrapper.set_mi( i_index, mi_id )
-
- # log.debug( '***** Setting up security group' )
- # If not existent, setup galaxy security group
- # try:
- # gSecurityGroup = conn.create_security_group('galaxy', 'Security group for Galaxy.')
- # gSecurityGroup.authorize( 'tcp', 80, 80, '0.0.0.0/0' ) # Open HTTP port
- # gSecurityGroup.authorize( 'tcp', 22, 22, '0.0.0.0/0' ) # Open SSH port
- # except:
- # pass
- # sgs = conn.get_all_security_groups()
- # for i in range( len( sgs ) ):
- # if sgs[i].name == "galaxy":
- # sg.append( sgs[i] )
- # break # only 1 security group w/ this type can exist, so continue
-
+
log.debug( "***** Starting UCI instance '%s'" % uci_wrapper.get_name() )
log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
reservation = conn.run_instances( image_id=mi_id, key_name=uci_wrapper.get_key_pair_name( i_index ) )
@@ -276,39 +244,6 @@
uci_wrapper.change_state( s, i_id, s )
log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
-
-
-# # Wait until instance gets running and then update the DB
-# while s!="running":
-# log.debug( "Waiting on instance '%s' to start up (reservation ID: %s); current state: %s" % ( uci.instance[0].instance_id, uci.instance[0].reservation_id, s ) )
-# time.sleep( 15 )
-# s = reservation.i_indexes[0].update()
-#
-# # Update instance data in local DB
-# uci.instance[0].state = s
-# uci.instance[0].public_dns = reservation.i_indexes[0].dns_name
-# uci.instance[0].private_dns = reservation.i_indexes[0].private_dns_name
-# uci.instance[0].flush()
-# # Update storage data in local DB w/ volume state info. NOTE that this only captures current volume state
-# # and does not connect or wait on connection between instance and volume to be established
-# vl = model.CloudStore.filter( model.CloudStore.c.uci_id == uci.id ).all()
-# vols = []
-# for v in vl:
-# vols.append( v.volume_id )
-# try:
-# volumes = conn.get_all_volumes( vols )
-# for i, v in enumerate( volumes ):
-# uci.store[i].i_id = v.instance_id
-# uci.store[i].status = v.status
-# uci.store[i].device = v.device
-# uci.store[i].flush()
-# except BotoServerError:
-# log.debug( "Error getting volume(s) attached to instance. Volume status was not updated." )
-#
-# uci.state = s
-# uci.flush()
-
-
def stopUCI( self, uci_wrapper):
"""
Stops all of cloud instances associated with given UCI.
@@ -332,30 +267,9 @@
uci_wrapper.change_state( instance_id=inst.id, i_state=inst.update() )
stopped.append( inst )
-# uci_wrapper.change_state( uci_state='available' )
uci_wrapper.reset_uci_launch_time()
-
-# # Wait for all instances to actually terminate and update local DB
-# terminated=0
-# while terminated!=len( rl ):
-# for i, r in enumerate( rl ):
-# log.debug( "r state: %s" % r.instances[0].state )
-# state = r.instances[0].update()
-# if state=='terminated':
-# uci.instance[i].state = state
-# uci.instance[i].flush()
-# terminated += 1
-# time.sleep ( 5 )
-#
-# # Reset UCI state
-# uci.state = 'available'
-# uci.launch_time = None
-# uci.flush()
-#
log.debug( "Termination was initiated for all instances of UCI '%s'." % uci_wrapper.get_name() )
-
-
# dbInstances = get_instances( trans, uci ) #TODO: handle list!
#
# # Get actual cloud instance object
@@ -377,7 +291,6 @@
# store.i_id = None
# store.status = volStat
# log.debug ( '***** volume status: %s' % volStat )
-#
#
# # Stop the instance and update status in local database
# cloudInstance.stop()
@@ -495,7 +408,6 @@
region=euca_region,
path=uci.credentials.provider.path )# Get reservations handle for given store
vl = conn.get_all_volumes( [store.volume_id] )
-# log.debug( "Store '%s' vl: '%s'" % ( store.volume_id, vl ) )
# Update store status in local DB with info from cloud provider
if store.status != vl[0].status:
# In case something failed during creation of UCI but actual storage volume was created and yet
diff -r 022ac0f64679 -r be631ed97541 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Wed Oct 28 16:35:36 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Thu Oct 29 11:46:43 2009 -0400
@@ -12,7 +12,6 @@
from galaxy.util.odict import odict
from galaxy.util.bunch import Bunch
from galaxy.util.topsort import topsort, topsort_levels, CycleError
-from galaxy.workflow.modules import *
from galaxy.model.mapping import desc
from galaxy.model.orm import *
from datetime import datetime, timedelta
@@ -69,12 +68,6 @@
Render cloud main page (management of cloud resources)
"""
user = trans.get_user()
-# pendingInstances = trans.sa_session.query( model.UCI ) \
-# .filter_by( user=user, state="pending" ) \
-# .all()
-#
-# for i inupdate_in range( len ( pendingInstances ) ):
-# stance_state( trans, pendingInstances[i].id )
cloudCredentials = trans.sa_session.query( model.CloudUserCredentials ) \
.filter_by( user=user ) \
@@ -104,7 +97,6 @@
.all()
# Check after update there are instances in pending state; if so, display message
- # TODO: Auto-refresh once instance is running
pendingInstances = trans.sa_session.query( model.UCI ) \
.filter_by( user=user ) \
.filter( or_( model.UCI.c.state==uci_states.PENDING, #"pending" , \
@@ -128,7 +120,6 @@
liveInstances = liveInstances,
prevInstances = prevInstances )
- @web.expose
@web.require_login( "use Galaxy cloud" )
def makeDefault( self, trans, id=None ):
"""
@@ -337,8 +328,6 @@
inst_error = "No registered cloud images. You must contact administrator to add some before proceeding."
log.debug("AttributeError: %s " % str( ae ) )
- #TODO: based on user credentials (i.e., provider) selected, zone options will be different (e.g., EC2: us-east-1a vs EPC: epc)
-
return trans.fill_template( "cloud/configure_uci.mako",
instanceName = instanceName,
credName = storedCreds,
@@ -427,8 +416,6 @@
session.flush()
# Log and display the management page
trans.set_message( "Credential '%s' edited." % credentials.name )
-# if defaultCred:
-# self.makeDefault( trans, credentials.id)
return self.list( trans )
@web.expose
@@ -507,8 +494,6 @@
# Log and display the management page
trans.log_event( "User added new credentials" )
trans.set_message( "Credential '%s' created" % credentials.name )
-# if defaultCred:
-# self.makeDefault( trans, credentials.id)
return self.list( trans )
providers = trans.sa_session.query( model.CloudProvider ).filter_by( user=user ).all()
@@ -521,13 +506,6 @@
providers = providers
)
-# return trans.show_form(
-# web.FormBuilder( web.url_for(), "Add credentials", submit_text="Add" )
-# .add_text( "credName", "Credentials name", value="Unnamed credentials", error=cred_error )
-# .add_text( "providerName", "Cloud provider name", value="ec2 or eucalyptus", error=provider_error )
-# .add_text( "accessKey", "Access key", value='', error=accessKey_error )
-# .add_password( "secretKey", "Secret key", value='', error=secretKey_error ) )
-
@web.expose
@web.require_login( "view credentials" )
def view( self, trans, id=None ):
@@ -770,18 +748,6 @@
# Looks good
return stored
-def get_default_credentials( trans, check_ownership=True ):
- """
- Get a StoredUserCredntials from the database by 'default' setting, verifying ownership.
- """
- user = trans.get_user()
- # Load credentials from database
- stored = trans.sa_session.query( model.CloudUserCredentials ) \
- .filter_by (user=user, defaultCred=True) \
- .first()
-
- return stored
-
def get_uci( trans, id, check_ownership=True ):
"""
Get a UCI object from the database by id, verifying ownership.
@@ -891,204 +857,3 @@
# Actually, probably return key_pair to calling method and store name & key from there...
return key_pair.name
-
-def update_instance_state( trans, id ):
- """
- Update state of instances associated with given UCI id and store state in local database. Also update
- state of the given UCI.
- """
- uci = get_uci( trans, id )
- # Get list of instances associated with given uci as they are stored in local database
- dbInstances = get_instances( trans, uci ) # TODO: handle list (currently only 1 instance can correspond to 1 UCI)
- oldState = dbInstances.state
- # Establish connection with cloud
- conn = get_connection( trans )
- # Get actual cloud instance object
- cloudInstance = get_cloud_instance( conn, dbInstances.instance_id )
- # Update instance status
- cloudInstance.update()
- dbInstances.state = cloudInstance.state
- log.debug( "Updating instance %s state; current state: %s" % ( str( cloudInstance ).split(":")[1], cloudInstance.state ) )
- # Update state of UCI (TODO: once more than 1 instance is assoc. w/ 1 UCI, this will be need to be updated differently)
- uci.state = dbInstances.state
- # Persist
- session = trans.sa_session
- session.save_or_update( dbInstances )
- session.save_or_update( uci )
- session.flush()
-
- # If instance is now running, update/process instance (i.e., mount file system, start Galaxy, update DB with DNS)
- if oldState==instance_states.PENDING and dbInstances.state==instance_states.RUNNING:
- update_instance( trans, dbInstances, cloudInstance, conn, uci )
-
-
-def update_instance( trans, dbInstance, cloudInstance, conn, uci ):
- """
- Update instance: connect EBS volume, mount file system, start Galaxy, and update local DB w/ DNS info
-
- Keyword arguments:
- trans -- current transaction
- dbInstance -- object of 'instance' as it is stored in local database
- cloudInstance -- object of 'instance' as it resides in the cloud. Functions supported by the cloud API can be
- instantiated directly on this object.
- conn -- cloud connection object
- uci -- UCI object
- """
- dbInstance.public_dns = cloudInstance.dns_name
- dbInstance.private_dns = cloudInstance.private_dns_name
-
- # Attach storage volume(s) to instance
- stores = get_stores( trans, uci )
- for i, store in enumerate( stores ):
- log.debug( "Attaching volume '%s' to instance '%s'." % ( store.volume_id, dbInstance.instance_id ) )
- mntDevice = '/dev/sdb'+str(i)
- volStat = conn.attach_volume( store.volume_id, dbInstance.instance_id, mntDevice )
- store.attach_time = datetime.utcnow()
- store.device = mntDevice
- store.i_id = dbInstance.instance_id
- store.status = volStat
- log.debug ( '***** volume status: %s' % volStat )
-
- # Wait until instances have attached and add file system
-
-
-
- # TODO: mount storage through ZFS
- # TODO: start Galaxy
-
- # Persist
- session = trans.sa_session
- session.save_or_update( dbInstance )
- session.flush()
-
-def attach_ordered_steps( workflow, steps ):
- ordered_steps = order_workflow_steps( steps )
- if ordered_steps:
- workflow.has_cycles = False
- for i, step in enumerate( ordered_steps ):
- step.order_index = i
- workflow.steps.append( step )
- else:
- workflow.has_cycles = True
- workflow.steps = steps
-
-def edgelist_for_workflow_steps( steps ):
- """
- Create a list of tuples representing edges between `WorkflowSteps` based
- on associated `WorkflowStepConnection`s
- """
- edges = []
- steps_to_index = dict( ( step, i ) for i, step in enumerate( steps ) )
- for step in steps:
- edges.append( ( steps_to_index[step], steps_to_index[step] ) )
- for conn in step.input_connections:
- edges.append( ( steps_to_index[conn.output_step], steps_to_index[conn.input_step] ) )
- return edges
-
-def order_workflow_steps( steps ):
- """
- Perform topological sort of the steps, return ordered or None
- """
- try:
- edges = edgelist_for_workflow_steps( steps )
- node_order = topsort( edges )
- return [ steps[i] for i in node_order ]
- except CycleError:
- return None
-
-def order_workflow_steps_with_levels( steps ):
- try:
- return topsort_levels( edgelist_for_workflow_steps( steps ) )
- except CycleError:
- return None
-
-class FakeJob( object ):
- """
- Fake job object for datasets that have no creating_job_associations,
- they will be treated as "input" datasets.
- """
- def __init__( self, dataset ):
- self.is_fake = True
- self.id = "fake_%s" % dataset.id
-
-def get_job_dict( trans ):
- """
- Return a dictionary of Job -> [ Dataset ] mappings, for all finished
- active Datasets in the current history and the jobs that created them.
- """
- history = trans.get_history()
- # Get the jobs that created the datasets
- warnings = set()
- jobs = odict()
- for dataset in history.active_datasets:
- # FIXME: Create "Dataset.is_finished"
- if dataset.state in ( 'new', 'running', 'queued' ):
- warnings.add( "Some datasets still queued or running were ignored" )
- continue
-
- #if this hda was copied from another, we need to find the job that created the origial hda
- job_hda = dataset
- while job_hda.copied_from_history_dataset_association:
- job_hda = job_hda.copied_from_history_dataset_association
-
- if not job_hda.creating_job_associations:
- jobs[ FakeJob( dataset ) ] = [ ( None, dataset ) ]
-
- for assoc in job_hda.creating_job_associations:
- job = assoc.job
- if job in jobs:
- jobs[ job ].append( ( assoc.name, dataset ) )
- else:
- jobs[ job ] = [ ( assoc.name, dataset ) ]
- return jobs, warnings
-
-def cleanup_param_values( inputs, values ):
- """
- Remove 'Data' values from `param_values`, along with metadata cruft,
- but track the associations.
- """
- associations = []
- names_to_clean = []
- # dbkey is pushed in by the framework
- if 'dbkey' in values:
- del values['dbkey']
- root_values = values
- # Recursively clean data inputs and dynamic selects
- def cleanup( prefix, inputs, values ):
- for key, input in inputs.items():
- if isinstance( input, ( SelectToolParameter, DrillDownSelectToolParameter ) ):
- if input.is_dynamic:
- values[key] = UnvalidatedValue( values[key] )
- if isinstance( input, DataToolParameter ):
- tmp = values[key]
- values[key] = None
- # HACK: Nested associations are not yet working, but we
- # still need to clean them up so we can serialize
- # if not( prefix ):
- if tmp: #this is false for a non-set optional dataset
- associations.append( ( tmp.hid, prefix + key ) )
- # Cleanup the other deprecated crap associated with datasets
- # as well. Worse, for nested datasets all the metadata is
- # being pushed into the root. FIXME: MUST REMOVE SOON
- key = prefix + key + "_"
- for k in root_values.keys():
- if k.startswith( key ):
- del root_values[k]
- elif isinstance( input, Repeat ):
- group_values = values[key]
- for i, rep_values in enumerate( group_values ):
- rep_index = rep_values['__index__']
- prefix = "%s_%d|" % ( key, rep_index )
- cleanup( prefix, input.inputs, group_values[i] )
- elif isinstance( input, Conditional ):
- group_values = values[input.name]
- current_case = group_values['__current_case__']
- prefix = "%s|" % ( key )
- cleanup( prefix, input.cases[current_case].inputs, group_values )
- cleanup( "", inputs, values )
- return associations
-
-
-
-
-
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/6632a5d39f41
changeset: 3073:6632a5d39f41
user: Enis Afgan <afgane(a)gmail.com>
date: Mon Oct 26 17:04:47 2009 -0400
description:
Added UCI usage record and some automated page refreshes to cope with proper status reporting/visible links.
diffstat:
lib/galaxy/web/controllers/cloud.py | 11 +++-
templates/cloud/configure_cloud.mako | 39 +++++++++++-
templates/cloud/view.mako | 35 +++++++----
templates/cloud/view_usage.mako | 88 ++++++++++++++++++++++++++---
4 files changed, 144 insertions(+), 29 deletions(-)
diffs (285 lines):
diff -r 676fae1a35f9 -r 6632a5d39f41 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Fri Oct 23 17:41:35 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Mon Oct 26 17:04:47 2009 -0400
@@ -165,6 +165,10 @@
( uci.state != uci_states.PENDING ) and \
( uci.state != uci_states.DELETING ) and \
( uci.state != uci_states.DELETING_UCI ) and \
+ ( uci.state != uci_states.DELETED ) and \
+ ( uci.state != uci_states.RUNNING ) and \
+ ( uci.state != uci_states.NEW_UCI ) and \
+ ( uci.state != uci_states.NEW ) and \
( uci.state != uci_states.ERROR ):
instance = model.CloudInstance()
instance.user = user
@@ -247,13 +251,16 @@
@web.expose
@web.require_login( "use Galaxy cloud" )
- def usageReport( self, trans ):
+ def usageReport( self, trans, id ):
user = trans.get_user()
+ id = trans.security.decode_id( id )
prevInstances = trans.sa_session.query( model.CloudInstance ) \
- .filter_by( user=user, state=instance_states.TERMINATED ) \
+ .filter_by( user=user, state=instance_states.TERMINATED, uci_id=id ) \
.order_by( desc( model.CloudInstance.c.update_time ) ) \
.all()
+
+ log.debug( "id: %s" % id )
return trans.fill_template( "cloud/view_usage.mako", prevInstances = prevInstances )
diff -r 676fae1a35f9 -r 6632a5d39f41 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Fri Oct 23 17:41:35 2009 -0400
+++ b/templates/cloud/configure_cloud.mako Mon Oct 26 17:04:47 2009 -0400
@@ -24,16 +24,46 @@
$.getJSON( "${h.url_for( action='json_update' )}", {}, function ( data ) {
for (var i in data) {
var elem = '#' + data[i].id;
+ // Because of different list managing 'live' vs. 'available' instances, refresh entire
+ // page on necessary state change.
+ old_state = $(elem + "-state").text();
+ new_state = data[i].state;
+ console.log( "old_state[%d] = %s", i, old_state );
+ console.log( "new_state[%d] = %s", i, new_state );
+ if ( old_state=='pending' && new_state=='running' ) {
+ location.reload(true);
+ }
+ else if ( old_state=='shutting-down' && new_state=='available' ) {
+ location.reload(true);
+ }
+ else if ( new_state=='shutting-down' || new_state=='shutting-downUCI' ) {
+ $(elem + "-link").text( "" );
+ }
+ xmlhttp = new XMLHttpRequest();
+ xmlhttp.open( "HEAD", "http://127.0.0.1:8080/admin", false );
+ xmlhttp.send( null );
+ //alert(xmlhttp.getAllResponseHeaders())
+ console.log( "xmlhttp.readyState: %s", xmlhttp.readyState );
+ console.log( "xmlhttp.status: %s", xmlhttp.status );
+ if ( new_state=='running' && xmlhttp.readyState==1 ) {
+ console.log ("in ready statsus = 1");
+ //if (xmlhttp.status==200) {
+ // console.log( "in status = 200" );
+ // location.reload(true);
+ //}
+ }
+
+ // Update 'state' and 'time alive' fields
$(elem + "-state").text( data[i].state );
if (data[i].launch_time) {
- $(elem + "-launch_time").text( data[i].launch_time.substring(0, 16 ) + " (" + data[i].time_ago + ")" );
+ $(elem + "-launch_time").text( data[i].launch_time.substring(0, 16 ) + " UTC (" + data[i].time_ago + ")" );
}
else {
$(elem + "-launch_time").text( "N/A" );
}
}
});
- setTimeout("update_state()", 10000);
+ setTimeout("update_state()", 15000);
}
$(function() {
@@ -141,7 +171,7 @@
context.write( ')' )
%>
</td>
- <td><div align="right">
+ <td id="${ liveInstance.id }-link"><div align="right">
%for j, instance in enumerate( liveInstance.instance ):
## TODO: Once more instances will be running under the same liveInstance, additional logic will need to be added to account for that
%if instance.state == "running":
@@ -165,6 +195,7 @@
<a class="action-button" confirm="Are you sure you want to stop instance '${liveInstance.name}'? Please note that this may take up to 1 minute during which time the page will not refresh." href="${h.url_for( action='stop', id=trans.security.encode_id(liveInstance.id) )}">Stop</a>
<a class="action-button" href="${h.url_for( action='renameInstance', id=trans.security.encode_id(liveInstance.id) )}">Rename</a>
<a class="action-button" href="${h.url_for( action='viewInstance', id=trans.security.encode_id(liveInstance.id) )}">View details</a>
+ <a class="action-button" href="${h.url_for( action='usageReport', id=trans.security.encode_id(liveInstance.id) )}">Usage report</a>
</div>
</td>
</tr>
@@ -230,7 +261,7 @@
<a class="action-button" href="${h.url_for( action='start', id=trans.security.encode_id(prevInstance.id), type='c1.medium' )}"> Start c1.medium</a>
<a class="action-button" href="${h.url_for( action='renameInstance', id=trans.security.encode_id(prevInstance.id) )}">Rename</a>
<a class="action-button" href="${h.url_for( action='addStorage', id=trans.security.encode_id(prevInstance.id) )}" target="_parent">Add storage</a>
- <a class="action-button" href="${h.url_for( action='usageReport' )}">Usage report</a>
+ <a class="action-button" href="${h.url_for( action='usageReport', id=trans.security.encode_id(prevInstance.id) )}">Usage report</a>
<a class="action-button" confirm="Are you sure you want to delete instance '${prevInstance.name}'? This will delete all of your data assocaiated with this instance!" href="${h.url_for( action='deleteInstance', id=trans.security.encode_id(prevInstance.id) )}">Delete</a>
</div>
</td>
diff -r 676fae1a35f9 -r 6632a5d39f41 templates/cloud/view.mako
--- a/templates/cloud/view.mako Fri Oct 23 17:41:35 2009 -0400
+++ b/templates/cloud/view.mako Mon Oct 26 17:04:47 2009 -0400
@@ -1,20 +1,29 @@
<%inherit file="/base.mako"/>
-<%def name="title()">Cloud home</%def>
+<%def name="title()">Cloud credentials</%def>
<h2>Credentials details</h2>
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( action='list' )}">
+ <img src="${h.url_for('/static/images/silk/resultset_previous.png')}" />
+ <span>Return to cloud management console</span>
+ </a>
+ </li>
+</ul>
+
%if credDetails:
- <ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( action='list' )}">
- <img src="${h.url_for('/static/images/silk/resultset_previous.png')}" />
- <span>Return to cloud management console</span>
- </a>
- </li>
- </ul>
+ ${view_cred( credDetails )}
+%else:
+ There are no credentials under that name.
+%endif
- <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+
+
+
+<%def name="view_cred( credDetails )">
+ <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td> Credentials name: </td>
<td>
@@ -71,7 +80,7 @@
</div>
</td>
</tr>
- <tr><td><b>Additional cloud provider information (if available):</b></td></tr>
+ <tr><td id="addl"><b>Additional cloud provider information (if available):</b></td></tr>
%if credDetails.provider.region_connection != None:
<tr>
<td> Region connection: </td>
@@ -145,6 +154,4 @@
</tr>
%endif
</table>
-%else:
- There are no credentials under that name.
-%endif
+</%def>
diff -r 676fae1a35f9 -r 6632a5d39f41 templates/cloud/view_usage.mako
--- a/templates/cloud/view_usage.mako Fri Oct 23 17:41:35 2009 -0400
+++ b/templates/cloud/view_usage.mako Mon Oct 26 17:04:47 2009 -0400
@@ -18,30 +18,100 @@
</div>
%endif
-<h2>Instance usage report</h2>
+%if prevInstances:
+ <h2>Usage report for instance ${prevInstances[0].uci.name}</h2>
+%else:
+ <h2>Selected instance has no record of being used.</h2>
+%endif
+
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( action='list' )}">
+ <img src="${h.url_for('/static/images/silk/resultset_previous.png')}" />
+ <span>Return to cloud management console</span>
+ </a>
+ </li>
+</ul>
%if prevInstances:
<table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<colgroup width="2%"></colgroup>
- <colgroup width="20%"></colgroup>
- <colgroup width="20%"></colgroup>
+ <colgroup width="16%"></colgroup>
+ <colgroup width="16%"></colgroup>
+ <colgroup width="10%"></colgroup>
<colgroup width="5%"></colgroup>
<tr class="header">
<th>#</th>
<th>Launch time</th>
<th>Termination time</th>
+ <th>Time alive</th>
<th>Type</th>
<th></th>
</tr>
+ <%
+ total_hours = 0
+ %>
%for i, prevInstance in enumerate( prevInstances ):
<tr>
<td>${i+1}</td>
- <td>${str(prevInstance.launch_time)[:16]} UCT</td>
- <td>${str(prevInstance.stop_time)[:16]} UCT</td>
+ <td>
+ %if prevInstance.launch_time:
+ ${str(prevInstance.launch_time)[:16]} UCT
+ %else:
+ N/A
+ %endif
+ </td>
+ <td>
+ %if prevInstance.stop_time:
+ ${str(prevInstance.stop_time)[:16]} UCT
+ %else:
+ N/A
+ %endif
+ </td>
+ <td>
+ <%
+ # This is where current time and since duration is calculated
+ if prevInstance.launch_time is None or prevInstance.stop_time is None:
+ context.write( 'N/A' )
+ else:
+ context.write( str(h.date.distance_of_time_in_words (prevInstance.launch_time, prevInstance.stop_time ) ) )
+ time_delta = prevInstance.stop_time - prevInstance.launch_time
+ total_hours += time_delta.seconds / 3600
+ if time_delta.seconds != 0:
+ total_hours += 1
+
+ %>
+ </td>
<td>${prevInstance.type}</td>
- </tr>
+ </tr>
%endfor
</table>
-%else:
- Selected instance has no record of being used.
-%endif
\ No newline at end of file
+ <br/>Total number of hours instance was alive: ${total_hours} <br />
+ Note that these are just best effort estimates - true usage should be obtained from respective cloud provider. <br />
+ <%namespace name="view_cred" file="view.mako" />
+
+ <div id="hide_cred_details">
+ This instance uses credentials:
+ <a onclick="document.getElementById('show_cred_details').style.display = 'block';
+ document.getElementById('hide_cred_details').style.display = 'none'; return 0"
+ href="javascript:void(0)">
+ ${prevInstances[0].uci.credentials.name}
+ </a>
+ </div>
+ <div id="show_cred_details" style="DISPLAY: none">
+ This instance uses credentials:
+ <a onclick="document.getElementById('hide_cred_details').style.display = 'block';
+ document.getElementById('show_cred_details').style.display = 'none'; return 0;"
+ href="javascript:void(0)">
+ ${prevInstances[0].uci.credentials.name}
+ </a>
+ ${view_cred.view_cred( prevInstances[0].uci.credentials ) }
+ </div>
+
+
+%endif
+
+
+
+
+
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/7b642483d18c
changeset: 3075:7b642483d18c
user: Enis Afgan <afgane(a)gmail.com>
date: Wed Oct 28 12:28:57 2009 -0400
description:
Added editing of credential properties.
diffstat:
lib/galaxy/web/controllers/cloud.py | 91 +++++++++++++++++++++++------
templates/cloud/add_credentials.mako | 40 ++++++-------
templates/cloud/configure_cloud.mako | 2 +-
templates/cloud/edit_credentials.mako | 91 ++++++++++++++++++++++++++++++
templates/cloud/view.mako | 6 +-
5 files changed, 185 insertions(+), 45 deletions(-)
diffs (315 lines):
diff -r 802b761c5032 -r 7b642483d18c lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Tue Oct 27 15:32:44 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Wed Oct 28 12:28:57 2009 -0400
@@ -77,7 +77,7 @@
cloudCredentials = trans.sa_session.query( model.CloudUserCredentials ) \
.filter_by( user=user ) \
- .order_by( desc( model.CloudUserCredentials.c.name ) ) \
+ .order_by( model.CloudUserCredentials.c.name ) \
.all()
liveInstances = trans.sa_session.query( model.UCI ) \
@@ -387,23 +387,64 @@
@web.expose
@web.require_login( "use Galaxy cloud" )
- def rename( self, trans, id, new_name=None ):
- stored = get_stored_credentials( trans, id )
- if new_name is not None:
- stored.name = new_name
- trans.sa_session.flush()
- trans.set_message( "Credentials renamed to '%s'." % new_name )
- return self.list( trans )
+ def edit( self, trans, id, credName=None, accessKey=None, secretKey=None, edited=False ):
+ error = {}
+ if not edited:
+ credentials = get_stored_credentials( trans, id )
+ return trans.fill_template( "cloud/edit_credentials.mako",
+ credential = credentials,
+ error = error
+ )
else:
- return trans.show_form(
- web.FormBuilder( url_for( id=trans.security.encode_id(stored.id) ), "Rename credentials", submit_text="Rename" )
- .add_text( "new_name", "Credentials Name", value=stored.name ) )
-
+ user = trans.get_user()
+ credentials = get_stored_credentials( trans, id )
+ if credName=='' or len( credName ) > 255:
+ error['cred_error'] = "Credentials name must be between 1 and 255 characters in length."
+ elif trans.app.model.CloudUserCredentials \
+ .filter_by( user=user ) \
+ .filter( and_( trans.app.model.CloudUserCredentials.table.c.id != credentials.id, trans.app.model.CloudUserCredentials.table.c.name==credName ) ) \
+ .first():
+ error['cred_error'] = "Credentials with name '" + credName + "' already exist. Please choose an alternative name."
+ elif accessKey=='' or len( accessKey ) > 255:
+ error['access_key_error'] = "Access key must be between 1 and 255 characters long."
+ elif secretKey=='' or len( secretKey ) > 255:
+ error['secret_key_error'] = "Secret key must be between 1 and 255 characters long."
+
+ if error:
+ return trans.fill_template( "cloud/edit_credentials.mako",
+ credential = credentials,
+ error = error
+ )
+ else:
+ # Edit user stored credentials
+ credentials.name = credName
+ credentials.access_key = accessKey
+ credentials.secret_key = secretKey
+ # Persist
+ session = trans.sa_session
+ session.save_or_update( credentials )
+ session.flush()
+ # Log and display the management page
+ trans.set_message( "Credential '%s' edited." % credentials.name )
+# if defaultCred:
+# self.makeDefault( trans, credentials.id)
+ return self.list( trans )
+
@web.expose
@web.require_login( "use Galaxy cloud" )
def renameInstance( self, trans, id, new_name=None ):
instance = get_uci( trans, id )
if new_name is not None:
+ if len(new_name) > 255:
+ error( "Instance name must be less than 255 characters long." )
+ user = trans.get_user()
+ name_exists = trans.sa_session.query( model.UCI ) \
+ .filter_by( user=user, name=new_name ) \
+ .first()
+ if name_exists:
+ error( "Specified name ('%s') is already used by an existing instance. Please choose an alternative name." % new_name )
+
+ # Update name in local DB
instance.name = new_name
trans.sa_session.flush()
trans.set_message( "Instance renamed to '%s'." % new_name )
@@ -522,17 +563,27 @@
@web.require_login( "delete credentials" )
def delete( self, trans, id=None ):
"""
- Delete user's cloud credentials
- TODO: Because UCI's depend on specific credentials, need to handle case where given credentials are being used by a UCI
+ Delete user's cloud credentials checking that no registered instances are tied to given credentials.
"""
# Load credentials from database
+ user = trans.get_user()
stored = get_stored_credentials( trans, id )
- # Delete and save
- sess = trans.sa_session
- sess.delete( stored )
- stored.flush()
- # Display the management page
- trans.set_message( "Credentials '%s' deleted." % stored.name )
+ UCIs = trans.sa_session.query( model.UCI ) \
+ .filter_by( user=user, credentials_id=stored.id ) \
+ .filter( model.UCI.c.state!=uci_states.DELETED ) \
+ .all()
+
+ if len(UCIs) == 0:
+ # Delete and save
+ sess = trans.sa_session
+ sess.delete( stored )
+ stored.flush()
+ # Display the management page
+ trans.set_message( "Credentials '%s' deleted." % stored.name )
+ return self.list( trans )
+
+ error( "Existing instance(s) depend on credentials '%s'. You must delete those instances before being able \
+ to delete these credentials." % stored.name )
return self.list( trans )
@web.expose
diff -r 802b761c5032 -r 7b642483d18c templates/cloud/add_credentials.mako
--- a/templates/cloud/add_credentials.mako Tue Oct 27 15:32:44 2009 -0400
+++ b/templates/cloud/add_credentials.mako Wed Oct 28 12:28:57 2009 -0400
@@ -20,8 +20,8 @@
<div class="form">
<div class="form-title">Add credentials</div>
<div class="form-body">
- <form name="Add credentials" action="${h.url_for( action='add' )}" method="post" >
-
+ <form name="Add credentials" action="${h.url_for( action='add' )}" method="post" >
+
<%
cls = "form-row"
if error.has_key('cred_error'):
@@ -44,23 +44,23 @@
if error.has_key('provider_error'):
cls += " form-row-error"
%>
- <div class="${cls}">
- <label>Cloud provider name:</label>
- <div class="form-row-input">
- <select name="providerName" style="width:40em">
- <option value="">Select Provider...</option>
- %for provider in providers:
- <option value="${provider.name}">${provider.name}</option>
- %endfor
- </select>
- <br/>or <a href="${h.url_for( action='add_provider' )}">
- <span>register additional cloud provider</span></a>
- </div>
- %if error.has_key('provider_error'):
- <div class="form-row-error-message">${error['provider_error']}</div>
- %endif
- <div style="clear: both"></div>
- </div>
+ <div class="${cls}">
+ <label>Cloud provider name:</label>
+ <div class="form-row-input">
+ <select name="providerName" style="width:40em">
+ <option value="">Select Provider...</option>
+ %for provider in providers:
+ <option value="${provider.name}">${provider.name}</option>
+ %endfor
+ </select>
+ <br/>or <a href="${h.url_for( action='add_provider' )}">
+ <span>register additional cloud provider</span></a>
+ </div>
+ %if error.has_key('provider_error'):
+ <div class="form-row-error-message">${error['provider_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
<%
cls = "form-row"
@@ -95,9 +95,7 @@
<div style="clear: both"></div>
</div>
-
<div class="form-row"><input type="submit" value="Add"></div>
-
</form>
</div>
</div>
diff -r 802b761c5032 -r 7b642483d18c templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Tue Oct 27 15:32:44 2009 -0400
+++ b/templates/cloud/configure_cloud.mako Wed Oct 28 12:28:57 2009 -0400
@@ -109,7 +109,7 @@
<td>
<div popupmenu="cr-${i}-popup">
<a class="action-button" href="${h.url_for( action='view', id=trans.security.encode_id(cloudCredential.id) )}">View</a>
- <a class="action-button" href="${h.url_for( action='rename', id=trans.security.encode_id(cloudCredential.id) )}">Rename</a>
+ <a class="action-button" href="${h.url_for( action='edit', id=trans.security.encode_id(cloudCredential.id) )}">Edit</a>
<a class="action-button" confirm="Are you sure you want to delete credentials '${cloudCredential.name}'?" href="${h.url_for( action='delete', id=trans.security.encode_id(cloudCredential.id) )}">Delete</a>
</div>
</td>
diff -r 802b761c5032 -r 7b642483d18c templates/cloud/edit_credentials.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/cloud/edit_credentials.mako Wed Oct 28 12:28:57 2009 -0400
@@ -0,0 +1,91 @@
+<% _=n_ %>
+<%inherit file="/base.mako"/>
+<%def name="title()">Add credentials</%def>
+
+<%def name="javascripts()">
+${parent.javascripts()}
+<script type="text/javascript">
+$(function(){
+ $("input:text:first").focus();
+})
+</script>
+</%def>
+
+%if header:
+ ${header}
+%endif
+
+%if credential:
+
+<div class="form">
+ <div class="form-title">Edit credentials</div>
+ <div class="form-body">
+ <form name="edit_credentials" action="${h.url_for( action='edit', id=trans.security.encode_id(credential.id), edited="true" )}" method="post" >
+
+ <%
+ cls = "form-row"
+ if error.has_key('cred_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Credentials name:</label>
+ <div class="form-row-input">
+ <input type="text" name="credName" value="${credential.name}" size="40">
+ </div>
+ %if error.has_key('cred_error'):
+ <div class="form-row-error-message">${error['cred_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+
+ <%
+ cls = "form-row"
+ if error.has_key('provider_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Cloud provider name (type):</label>
+ <div class="form-row-input">${credential.provider.name} (${credential.provider.type})</div>
+ <div style="clear: both"></div>
+ </div>
+ <%
+ cls = "form-row"
+ if error.has_key('access_key_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Access key:</label>
+ <div class="form-row-input">
+ <input type="text" name="accessKey" value="${credential.access_key}" size="40">
+ </div>
+ %if error.has_key('access_key_error'):
+ <div class="form-row-error-message">${error['access_key_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ if error.has_key('secret_key_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Secret key:</label>
+ <div class="form-row-input">
+ <input type="password" name="secretKey" value="${credential.secret_key}" size="40">
+ </div>
+ %if error.has_key('secret_key_error'):
+ <div class="form-row-error-message">${error['secret_key_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+ <div class="form-row"><input type="submit" value="Edit"></div>
+ </form>
+ </div>
+</div>
+
+%else:
+ Specified credentials could not be found.
+%endif
diff -r 802b761c5032 -r 7b642483d18c templates/cloud/view.mako
--- a/templates/cloud/view.mako Tue Oct 27 15:32:44 2009 -0400
+++ b/templates/cloud/view.mako Wed Oct 28 12:28:57 2009 -0400
@@ -71,12 +71,12 @@
</a>
</div>
<div id="fullComment2" style="DISPLAY: none">
- <nobr><b>${credDetails.secret_key}</b></nobr><br/>
- <a onclick="document.getElementById('shortComment2').style.display = 'block';
+ <a onclick="document.getElementById('shortComment2').style.display = 'block';
document.getElementById('fullComment2').style.display = 'none'; return 0;"
href="javascript:void(0)">
- Hide
- </a>
+ </a><br />
+ <nobr><b>${credDetails.secret_key}</b></nobr><br/>
</div>
</td>
</tr>
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/802b761c5032
changeset: 3074:802b761c5032
user: Enis Afgan <afgane(a)gmail.com>
date: Tue Oct 27 15:32:44 2009 -0400
description:
UI enhancements regarding provider registration and error reporting.
diffstat:
lib/galaxy/cloud/providers/ec2.py | 37 +-
lib/galaxy/cloud/providers/eucalyptus.py | 15 +-
lib/galaxy/web/controllers/cloud.py | 553 ++++++------------------------------
templates/cloud/add_credentials.mako | 4 +-
templates/cloud/add_provider.mako | 52 ++-
templates/cloud/configure_cloud.mako | 43 +-
6 files changed, 200 insertions(+), 504 deletions(-)
diffs (922 lines):
diff -r 6632a5d39f41 -r 802b761c5032 lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Mon Oct 26 17:04:47 2009 -0400
+++ b/lib/galaxy/cloud/providers/ec2.py Tue Oct 27 15:32:44 2009 -0400
@@ -95,7 +95,13 @@
Establishes EC2 cloud connection using user's credentials associated with given UCI
"""
log.debug( '##### Establishing EC2 cloud connection' )
- conn = EC2Connection( uci_wrapper.get_access_key(), uci_wrapper.get_secret_key() )
+ provider = uci_wrapper.get_provider()
+ region = RegionInfo( None, provider.region_name, provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(),
+ aws_secret_access_key=uci_wrapper.get_secret_key(),
+ is_secure=provider.is_secure,
+ region=region,
+ path=provider.path )
return conn
def set_keypair( self, uci_wrapper, conn ):
@@ -458,16 +464,24 @@
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
- conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key )
+ region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
+ aws_secret_access_key=s_key,
+ is_secure=uci.credentials.provider.is_secure,
+ region=region,
+ path=uci.credentials.provider.path )
# Get reservations handle for given instance
rl= conn.get_all_instances( [inst.instance_id] )
# Because EPC deletes references to reservations after a short while after instances have terminated, getting an empty list as a response to a query
- # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. As a result, below code simply
- # marks given instance as having terminated. Note that an instance might have also crashed and this code will not catch the difference...
+ # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. Until alternative solution
+ # is found, below code sets state of given UCI to 'error' to indicate to the user something out of ordinary happened.
if len( rl ) == 0:
log.info( "Instance ID '%s' was not found by the cloud provider. Instance might have crashed or otherwise been terminated." % inst.instance_id )
+ inst.error = "Instance ID was not found by the cloud provider. Instance might have crashed or otherwise been terminated. State set to 'terminated'."
+ uci.error = "Instance ID '"+inst.instance_id+"' was not found by the cloud provider. Instance might have crashed or otherwise been terminated."+ \
+ "Manual check is recommended."
inst.state = instance_states.TERMINATED
- uci.state = uci_states.AVAILABLE
+ uci.state = uci_states.ERROR
uci.launch_time = None
inst.flush()
uci.flush()
@@ -479,11 +493,13 @@
if s != inst.state:
inst.state = s
inst.flush()
- if s == instance_states.TERMINATED: # After instance has shut down, ensure UCI is marked as 'available'
+ # After instance has shut down, ensure UCI is marked as 'available'
+ if s == instance_states.TERMINATED and uci.state != uci_states.ERROR:
uci.state = uci_states.AVAILABLE
+ uci.launch_time = None
uci.flush()
+ # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
if s != uci.state and s != instance_states.TERMINATED:
- # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
uci.state = s
uci.flush()
if cInst.public_dns_name != inst.public_dns:
@@ -501,7 +517,12 @@
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
- conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key )
+ region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
+ aws_secret_access_key=s_key,
+ is_secure=uci.credentials.provider.is_secure,
+ region=region,
+ path=uci.credentials.provider.path )
# Get reservations handle for given store
vl = conn.get_all_volumes( [store.volume_id] )
# log.debug( "Store '%s' vl: '%s'" % ( store.volume_id, vl ) )
diff -r 6632a5d39f41 -r 802b761c5032 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Mon Oct 26 17:04:47 2009 -0400
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Tue Oct 27 15:32:44 2009 -0400
@@ -443,12 +443,15 @@
# Get reservations handle for given instance
rl= conn.get_all_instances( [inst.instance_id] )
# Because EPC deletes references to reservations after a short while after instances have terminated, getting an empty list as a response to a query
- # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. As a result, below code simply
- # marks given instance as having terminated. Note that an instance might have also crashed and this code will not catch the difference...
+ # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. Until alternative solution
+ # is found, below code sets state of given UCI to 'error' to indicate to the user something out of ordinary happened.
if len( rl ) == 0:
log.info( "Instance ID '%s' was not found by the cloud provider. Instance might have crashed or otherwise been terminated." % inst.instance_id )
+ inst.error = "Instance ID was not found by the cloud provider. Instance might have crashed or otherwise been terminated. State set to 'terminated'."
+ uci.error = "Instance ID '"+inst.instance_id+"' was not found by the cloud provider. Instance might have crashed or otherwise been terminated."+ \
+ "Manual check is recommended."
inst.state = instance_states.TERMINATED
- uci.state = uci_states.AVAILABLE
+ uci.state = uci_states.ERROR
uci.launch_time = None
inst.flush()
uci.flush()
@@ -460,11 +463,13 @@
if s != inst.state:
inst.state = s
inst.flush()
- if s == instance_states.TERMINATED: # After instance has shut down, ensure UCI is marked as 'available'
+ # After instance has shut down, ensure UCI is marked as 'available'
+ if s == instance_states.TERMINATED and uci.state != uci_states.ERROR:
uci.state = uci_states.AVAILABLE
+ uci.launch_time = None
uci.flush()
+ # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
if s != uci.state and s != instance_states.TERMINATED:
- # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
uci.state = s
uci.flush()
if cInst.public_dns_name != inst.public_dns:
diff -r 6632a5d39f41 -r 802b761c5032 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Mon Oct 26 17:04:47 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Tue Oct 27 15:32:44 2009 -0400
@@ -273,11 +273,10 @@
inst_error = vol_error = cred_error = None
error = {}
user = trans.get_user()
- # TODO: Hack until present user w/ bullet list w/ registered credentials
storedCreds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user ).all()
if len( storedCreds ) == 0:
return trans.show_error_message( "You must register credentials before configuring a Galaxy instance." )
-
+ # Create dict mapping of cloud providers to zones available by those providers
providersToZones = {}
for storedCred in storedCreds:
if storedCred.provider.type == 'ec2':
@@ -413,6 +412,21 @@
return trans.show_form(
web.FormBuilder( url_for( id=trans.security.encode_id(instance.id) ), "Rename instance", submit_text="Rename" )
.add_text( "new_name", "Instance name", value=instance.name ) )
+
+ @web.expose
+ @web.require_login( "use Galaxy cloud" )
+ def set_uci_state( self, trans, id, state='available', clear_error=True ):
+ """
+ Sets state of UCI to given state, optionally resets error field, and resets UCI's launch time field to 'None'.
+ """
+ uci = get_uci( trans, id )
+ uci.state = state
+ if clear_error:
+ uci.error = None
+ uci.launch_time = None
+ trans.sa_session.flush()
+ trans.set_message( "Instance '%s' state reset." % uci.name )
+ return self.list( trans )
@web.expose
@web.require_login( "add credentials" )
@@ -527,42 +541,94 @@
proxy_user='', proxy_pass='', debug='', https_connection_factory='', path='' ):
user = trans.get_user()
error = {}
+ try:
+ is_secure = int(is_secure)
+ except ValueError:
+ pass
# Check if Amazon EC2 has already been registered by this user
ec2_registered = trans.sa_session.query( model.CloudProvider ).filter_by( user=user, type='ec2' ).first()
if region_name or region_endpoint or name or is_secure or port or proxy or debug or path:
+ log.debug (" in if ")
if trans.app.model.CloudProvider \
.filter_by (user=user, name=name) \
.first():
+ log.debug (" in if 2 ")
error['name_error'] = "A provider with that name already exist."
elif name=='' or len( name ) > 255:
+ log.debug (" in if 3")
error['name_error'] = "Provider name must be between 1 and 255 characters long."
elif type=='':
+ log.debug (" in if 4")
error['type_error'] = "Provider type must be selected."
elif ec2_registered:
+ log.debug (" in if 5")
error['type_error'] = "Amazon EC2 has already been registered as a provider."
- elif is_secure!=0 or is_secure!=1:
- error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'."
+ elif not (is_secure == 0 or is_secure == 1):
+ log.debug (" in if 6")
+ error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'"
else:
+ log.debug (" in else ")
provider = model.CloudProvider()
provider.user = user
provider.type = type
provider.name = name
- provider.region_name = region_name
- provider.region_endpoint = region_endpoint
+ if region_name:
+ provider.region_name = region_name
+ else:
+ provider.region_name = None
+
+ if region_endpoint:
+ provider.region_endpoint = region_endpoint
+ else:
+ provider.region_endpoint = None
+
if is_secure=='0':
provider.is_secure = False
else:
provider.is_secure = True
- provider.host = host
- provider.port = port
- provider.proxy = proxy
- provider.proxy_port = proxy_port
- provider.proxy_user = proxy_user
- provider.proxy_pass = proxy_pass
- provider.debug = debug
- provider.https_connection_factory = https_connection_factory
+
+ if host:
+ provider.host = host
+ else:
+ provider.host = None
+
+ if port:
+ provider.port = port
+ else:
+ provider.port = None
+
+ if proxy:
+ provider.proxy = proxy
+ else:
+ provider.proxy = None
+
+ if proxy_port:
+ provider.proxy_port = proxy_port
+ else:
+ provider.proxy_port = None
+
+ if proxy_user:
+ provider.proxy_user = proxy_user
+ else:
+ provider.proxy_user = None
+
+ if proxy_pass:
+ provider.proxy_pass = proxy_pass
+ else:
+ provider.proxy_pass = None
+
+ if debug:
+ provider.debug = debug
+ else:
+ provider.debug = None
+
+ if https_connection_factory:
+ provider.https_connection_factory = https_connection_factory
+ else:
+ provider.https_connection_factory = None
+
provider.path = path
# Persist
session = trans.sa_session
@@ -601,456 +667,21 @@
.filter_by( user=user, type='ec2' ).first()
if not exists:
- provider = model.CloudProvider()
- provider.user = user
- provider.type = 'ec2'
- provider.name = 'EC2'
- # Persist
- session = trans.sa_session
- session.save_or_update( provider )
- session.flush()
+ self.add_provider( trans, name='Amazon EC2', type='ec2', region_name='us-east-1', region_endpoint='us-east-1.ec2.amazonaws.com', is_secure=1, path='/' )
+ return self.add( trans )
+# providers = trans.sa_session.query( model.CloudProvider ).filter_by( user=user ).all()
+# return trans.fill_template( "cloud/add_credentials.mako",
+# credName = '',
+# providerName = '',
+# accessKey = '',
+# secretKey = '',
+# error = {},
+# providers = providers
+# )
+ trans.show_error_message( "EC2 is already registered as a cloud provider under name '%s'." % exists.name )
return self.list( trans )
- @web.expose
- @web.require_login( "edit workflows" )
- def editor( self, trans, id=None ):
- """
- Render the main workflow editor interface. The canvas is embedded as
- an iframe (neccesary for scrolling to work properly), which is
- rendered by `editor_canvas`.
- """
- if not id:
- error( "Invalid workflow id" )
- id = trans.security.decode_id( id )
- return trans.fill_template( "workflow/editor.mako", workflow_id=id )
-
- @web.json
- def editor_form_post( self, trans, type='tool', tool_id=None, **incoming ):
- """
- Accepts a tool state and incoming values, and generates a new tool
- form and some additional information, packed into a json dictionary.
- This is used for the form shown in the right pane when a node
- is selected.
- """
- trans.workflow_building_mode = True
- module = module_factory.from_dict( trans, {
- 'type': type,
- 'tool_id': tool_id,
- 'tool_state': incoming.pop("tool_state")
- } )
- module.update_state( incoming )
- return {
- 'tool_state': module.get_state(),
- 'data_inputs': module.get_data_inputs(),
- 'data_outputs': module.get_data_outputs(),
- 'tool_errors': module.get_errors(),
- 'form_html': module.get_config_form()
- }
-
- @web.json
- def get_new_module_info( self, trans, type, **kwargs ):
- """
- Get the info for a new instance of a module initialized with default
- paramters (any keyword arguments will be passed along to the module).
- Result includes data inputs and outputs, html representation
- of the initial form, and the initial tool state (with default values).
- This is called asynchronously whenever a new node is added.
- """
- trans.workflow_building_mode = True
- module = module_factory.new( trans, type, **kwargs )
- return {
- 'type': module.type,
- 'name': module.get_name(),
- 'tool_id': module.get_tool_id(),
- 'tool_state': module.get_state(),
- 'data_inputs': module.get_data_inputs(),
- 'data_outputs': module.get_data_outputs(),
- 'form_html': module.get_config_form()
- }
-
- @web.json
- def load_workflow( self, trans, id ):
- """
- Get the latest Workflow for the StoredWorkflow identified by `id` and
- encode it as a json string that can be read by the workflow editor
- web interface.
- """
- user = trans.get_user()
- id = trans.security.decode_id( id )
- trans.workflow_building_mode = True
- # Load encoded workflow from database
- stored = trans.sa_session.query( model.StoredWorkflow ).get( id )
- assert stored.user == user
- workflow = stored.latest_workflow
- # Pack workflow data into a dictionary and return
- data = {}
- data['name'] = workflow.name
- data['steps'] = {}
- data['upgrade_messages'] = {}
- # For each step, rebuild the form and encode the state
- for step in workflow.steps:
- # Load from database representation
- module = module_factory.from_workflow_step( trans, step )
- # Fix any missing parameters
- upgrade_message = module.check_and_update_state()
- if upgrade_message:
- data['upgrade_messages'][step.order_index] = upgrade_message
- # Pack atrributes into plain dictionary
- step_dict = {
- 'id': step.order_index,
- 'type': module.type,
- 'tool_id': module.get_tool_id(),
- 'name': module.get_name(),
- 'tool_state': module.get_state(),
- 'tool_errors': module.get_errors(),
- 'data_inputs': module.get_data_inputs(),
- 'data_outputs': module.get_data_outputs(),
- 'form_html': module.get_config_form(),
- }
- # Connections
- input_conn_dict = {}
- for conn in step.input_connections:
- input_conn_dict[ conn.input_name ] = \
- dict( id=conn.output_step.order_index, output_name=conn.output_name )
- step_dict['input_connections'] = input_conn_dict
- # Position
- step_dict['position'] = step.position
- # Add to return value
- data['steps'][step.order_index] = step_dict
- print data['upgrade_messages']
- return data
-
- @web.json
- def save_workflow( self, trans, id, workflow_data ):
- """
- Save the workflow described by `workflow_data` with id `id`.
- """
- # Get the stored workflow
- stored = get_stored_workflow( trans, id )
- # Put parameters in workflow mode
- trans.workflow_building_mode = True
- # Convert incoming workflow data from json
- data = simplejson.loads( workflow_data )
- # Create new workflow from incoming data
- workflow = model.Workflow()
- # Just keep the last name (user can rename later)
- workflow.name = stored.name
- # Assume no errors until we find a step that has some
- workflow.has_errors = False
- # Create each step
- steps = []
- # The editor will provide ids for each step that we don't need to save,
- # but do need to use to make connections
- steps_by_external_id = {}
- # First pass to build step objects and populate basic values
- for key, step_dict in data['steps'].iteritems():
- # Create the model class for the step
- step = model.WorkflowStep()
- steps.append( step )
- steps_by_external_id[ step_dict['id' ] ] = step
- # FIXME: Position should be handled inside module
- step.position = step_dict['position']
- module = module_factory.from_dict( trans, step_dict )
- module.save_to_step( step )
- if step.tool_errors:
- workflow.has_errors = True
- # Stick this in the step temporarily
- step.temp_input_connections = step_dict['input_connections']
- # Second pass to deal with connections between steps
- for step in steps:
- # Input connections
- for input_name, conn_dict in step.temp_input_connections.iteritems():
- if conn_dict:
- conn = model.WorkflowStepConnection()
- conn.input_step = step
- conn.input_name = input_name
- conn.output_name = conn_dict['output_name']
- conn.output_step = steps_by_external_id[ conn_dict['id'] ]
- del step.temp_input_connections
- # Order the steps if possible
- attach_ordered_steps( workflow, steps )
- # Connect up
- workflow.stored_workflow = stored
- stored.latest_workflow = workflow
- # Persist
- trans.sa_session.flush()
- # Return something informative
- errors = []
- if workflow.has_errors:
- errors.append( "Some steps in this workflow have validation errors" )
- if workflow.has_cycles:
- errors.append( "This workflow contains cycles" )
- if errors:
- rval = dict( message="Workflow saved, but will not be runnable due to the following errors",
- errors=errors )
- else:
- rval = dict( message="Workflow saved" )
- rval['name'] = workflow.name
- return rval
-
- @web.json
- def get_datatypes( self, trans ):
- ext_to_class_name = dict()
- classes = []
- for k, v in trans.app.datatypes_registry.datatypes_by_extension.iteritems():
- c = v.__class__
- ext_to_class_name[k] = c.__module__ + "." + c.__name__
- classes.append( c )
- class_to_classes = dict()
- def visit_bases( types, cls ):
- for base in cls.__bases__:
- if issubclass( base, Data ):
- types.add( base.__module__ + "." + base.__name__ )
- visit_bases( types, base )
- for c in classes:
- n = c.__module__ + "." + c.__name__
- types = set( [ n ] )
- visit_bases( types, c )
- class_to_classes[ n ] = dict( ( t, True ) for t in types )
- return dict( ext_to_class_name=ext_to_class_name, class_to_classes=class_to_classes )
-
- @web.expose
- def build_from_current_history( self, trans, job_ids=None, dataset_ids=None, workflow_name=None ):
- user = trans.get_user()
- history = trans.get_history()
- if not user:
- return trans.show_error_message( "Must be logged in to create workflows" )
- if ( job_ids is None and dataset_ids is None ) or workflow_name is None:
- jobs, warnings = get_job_dict( trans )
- # Render
- return trans.fill_template(
- "workflow/build_from_current_history.mako",
- jobs=jobs,
- warnings=warnings,
- history=history )
- else:
- # Ensure job_ids and dataset_ids are lists (possibly empty)
- if job_ids is None:
- job_ids = []
- elif type( job_ids ) is not list:
- job_ids = [ job_ids ]
- if dataset_ids is None:
- dataset_ids = []
- elif type( dataset_ids ) is not list:
- dataset_ids = [ dataset_ids ]
- # Convert both sets of ids to integers
- job_ids = [ int( id ) for id in job_ids ]
- dataset_ids = [ int( id ) for id in dataset_ids ]
- # Find each job, for security we (implicately) check that they are
- # associated witha job in the current history.
- jobs, warnings = get_job_dict( trans )
- jobs_by_id = dict( ( job.id, job ) for job in jobs.keys() )
- steps = []
- steps_by_job_id = {}
- hid_to_output_pair = {}
- # Input dataset steps
- for hid in dataset_ids:
- step = model.WorkflowStep()
- step.type = 'data_input'
- hid_to_output_pair[ hid ] = ( step, 'output' )
- steps.append( step )
- # Tool steps
- for job_id in job_ids:
- assert job_id in jobs_by_id, "Attempt to create workflow with job not connected to current history"
- job = jobs_by_id[ job_id ]
- tool = trans.app.toolbox.tools_by_id[ job.tool_id ]
- param_values = job.get_param_values( trans.app )
- associations = cleanup_param_values( tool.inputs, param_values )
- step = model.WorkflowStep()
- step.type = 'tool'
- step.tool_id = job.tool_id
- step.tool_inputs = tool.params_to_strings( param_values, trans.app )
- # NOTE: We shouldn't need to do two passes here since only
- # an earlier job can be used as an input to a later
- # job.
- for other_hid, input_name in associations:
- if other_hid in hid_to_output_pair:
- other_step, other_name = hid_to_output_pair[ other_hid ]
- conn = model.WorkflowStepConnection()
- conn.input_step = step
- conn.input_name = input_name
- # Should always be connected to an earlier step
- conn.output_step = other_step
- conn.output_name = other_name
- steps.append( step )
- steps_by_job_id[ job_id ] = step
- # Store created dataset hids
- for assoc in job.output_datasets:
- hid_to_output_pair[ assoc.dataset.hid ] = ( step, assoc.name )
- # Workflow to populate
- workflow = model.Workflow()
- workflow.name = workflow_name
- # Order the steps if possible
- attach_ordered_steps( workflow, steps )
- # And let's try to set up some reasonable locations on the canvas
- # (these are pretty arbitrary values)
- levorder = order_workflow_steps_with_levels( steps )
- base_pos = 10
- for i, steps_at_level in enumerate( levorder ):
- for j, index in enumerate( steps_at_level ):
- step = steps[ index ]
- step.position = dict( top = ( base_pos + 120 * j ),
- left = ( base_pos + 220 * i ) )
- # Store it
- stored = model.StoredWorkflow()
- stored.user = user
- stored.name = workflow_name
- workflow.stored_workflow = stored
- stored.latest_workflow = workflow
- trans.sa_session.save_or_update( stored )
- trans.sa_session.flush()
- # Index page with message
- return trans.show_message( "Workflow '%s' created from current history." % workflow_name )
- ## return trans.show_ok_message( "<p>Workflow '%s' created.</p><p><a target='_top' href='%s'>Click to load in workflow editor</a></p>"
- ## % ( workflow_name, web.url_for( action='editor', id=trans.security.encode_id(stored.id) ) ) )
-
- @web.expose
- def run( self, trans, id, check_user=True, **kwargs ):
- stored = get_stored_workflow( trans, id, check_ownership=False )
- if check_user:
- user = trans.get_user()
- if stored.user != user:
- if trans.sa_session.query( model.StoredWorkflowUserShareAssociation ) \
- .filter_by( user=user, stored_workflow=stored ).count() == 0:
- error( "Workflow is not owned by or shared with current user" )
- # Get the latest revision
- workflow = stored.latest_workflow
- # It is possible for a workflow to have 0 steps
- if len( workflow.steps ) == 0:
- error( "Workflow cannot be run because it does not have any steps" )
- #workflow = Workflow.from_simple( simplejson.loads( stored.encoded_value ), trans.app )
- if workflow.has_cycles:
- error( "Workflow cannot be run because it contains cycles" )
- if workflow.has_errors:
- error( "Workflow cannot be run because of validation errors in some steps" )
- # Build the state for each step
- errors = {}
- if kwargs:
- # If kwargs were provided, the states for each step should have
- # been POSTed
- for step in workflow.steps:
- # Connections by input name
- step.input_connections_by_name = \
- dict( ( conn.input_name, conn ) for conn in step.input_connections )
- # Extract just the arguments for this step by prefix
- p = "%s|" % step.id
- l = len(p)
- step_args = dict( ( k[l:], v ) for ( k, v ) in kwargs.iteritems() if k.startswith( p ) )
- step_errors = None
- if step.type == 'tool' or step.type is None:
- module = module_factory.from_workflow_step( trans, step )
- # Any connected input needs to have value DummyDataset (these
- # are not persisted so we need to do it every time)
- module.add_dummy_datasets( connections=step.input_connections )
- # Get the tool
- tool = module.tool
- # Get the state
- step.state = state = module.state
- # Get old errors
- old_errors = state.inputs.pop( "__errors__", {} )
- # Update the state
- step_errors = tool.update_state( trans, tool.inputs, step.state.inputs, step_args,
- update_only=True, old_errors=old_errors )
- else:
- module = step.module = module_factory.from_workflow_step( trans, step )
- state = step.state = module.decode_runtime_state( trans, step_args.pop( "tool_state" ) )
- step_errors = module.update_runtime_state( trans, state, step_args )
- if step_errors:
- errors[step.id] = state.inputs["__errors__"] = step_errors
- if 'run_workflow' in kwargs and not errors:
- # Run each step, connecting outputs to inputs
- outputs = odict()
- for i, step in enumerate( workflow.steps ):
- if step.type == 'tool' or step.type is None:
- tool = trans.app.toolbox.tools_by_id[ step.tool_id ]
- input_values = step.state.inputs
- # Connect up
- def callback( input, value, prefixed_name, prefixed_label ):
- if isinstance( input, DataToolParameter ):
- if prefixed_name in step.input_connections_by_name:
- conn = step.input_connections_by_name[ prefixed_name ]
- return outputs[ conn.output_step.id ][ conn.output_name ]
- visit_input_values( tool.inputs, step.state.inputs, callback )
- # Execute it
- outputs[ step.id ] = tool.execute( trans, step.state.inputs )
- else:
- outputs[ step.id ] = step.module.execute( trans, step.state )
-
- return trans.fill_template( "workflow/run_complete.mako",
- workflow=stored,
- outputs=outputs )
- else:
- for step in workflow.steps:
- if step.type == 'tool' or step.type is None:
- # Restore the tool state for the step
- module = module_factory.from_workflow_step( trans, step )
- # Any connected input needs to have value DummyDataset (these
- # are not persisted so we need to do it every time)
- module.add_dummy_datasets( connections=step.input_connections )
- # Store state with the step
- step.module = module
- step.state = module.state
- # Error dict
- if step.tool_errors:
- errors[step.id] = step.tool_errors
- else:
- ## Non-tool specific stuff?
- step.module = module_factory.from_workflow_step( trans, step )
- step.state = step.module.get_runtime_state()
- # Connections by input name
- step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections )
- # Render the form
- return trans.fill_template(
- "workflow/run.mako",
- steps=workflow.steps,
- workflow=stored,
- errors=errors,
- incoming=kwargs )
-
- @web.expose
- def configure_menu( self, trans, workflow_ids=None ):
- user = trans.get_user()
- if trans.request.method == "POST":
- if workflow_ids is None:
- workflow_ids = []
- elif type( workflow_ids ) != list:
- workflow_ids = [ workflow_ids ]
- sess = trans.sa_session
- # This explicit remove seems like a hack, need to figure out
- # how to make the association do it automatically.
- for m in user.stored_workflow_menu_entries:
- sess.delete( m )
- user.stored_workflow_menu_entries = []
- q = sess.query( model.StoredWorkflow )
- # To ensure id list is unique
- seen_workflow_ids = set()
- for id in workflow_ids:
- if id in seen_workflow_ids:
- continue
- else:
- seen_workflow_ids.add( id )
- m = model.StoredWorkflowMenuEntry()
- m.stored_workflow = q.get( id )
- user.stored_workflow_menu_entries.append( m )
- sess.flush()
- return trans.show_message( "Menu updated", refresh_frames=['tools'] )
- else:
- user = trans.get_user()
- ids_in_menu = set( [ x.stored_workflow_id for x in user.stored_workflow_menu_entries ] )
- workflows = trans.sa_session.query( model.StoredWorkflow ) \
- .filter_by( user=user, deleted=False ) \
- .order_by( desc( model.StoredWorkflow.c.update_time ) ) \
- .all()
- shared_by_others = trans.sa_session \
- .query( model.StoredWorkflowUserShareAssociation ) \
- .filter_by( user=user ) \
- .filter( model.StoredWorkflow.c.deleted == False ) \
- .all()
- return trans.fill_template( "workflow/configure_menu.mako",
- workflows=workflows,
- shared_by_others=shared_by_others,
- ids_in_menu=ids_in_menu )
@web.json
def json_update( self, trans ):
user = trans.get_user()
diff -r 6632a5d39f41 -r 802b761c5032 templates/cloud/add_credentials.mako
--- a/templates/cloud/add_credentials.mako Mon Oct 26 17:04:47 2009 -0400
+++ b/templates/cloud/add_credentials.mako Tue Oct 27 15:32:44 2009 -0400
@@ -103,9 +103,9 @@
</div>
%else:
- In order to add credentials, desired cloud provider needs to be registered.<p/>
+ In order to add credentials, desired cloud provider needs to be registered first.<p/>
Register <a href="${h.url_for( action='add_ec2' )}">
- <span>Amazon EC2 automatically</span></a>
+ <span>Amazon EC2 (us-east-1 region) automatically</span></a>
or add
<a href="${h.url_for( action='add_provider' )}">
<span>custom cloud provider</span></a>.
diff -r 6632a5d39f41 -r 802b761c5032 templates/cloud/add_provider.mako
--- a/templates/cloud/add_provider.mako Mon Oct 26 17:04:47 2009 -0400
+++ b/templates/cloud/add_provider.mako Tue Oct 27 15:32:44 2009 -0400
@@ -11,7 +11,19 @@
$("#type").change(function() {
if ($(this).val() == 'ec2') {
- $("#name").val("EC2");
+ clear();
+ $("#autofill").attr( 'disabled', true );
+ $("#autofill").attr( 'checked', false );
+ $("#name").val( "EC2" );
+ $("#region_name").val( "us-east-1" );
+ $("#region_endpoint").val( "us-east-1.ec2.amazonaws.com" );
+ $("#is_secure").val("1");
+ $("#debug").val("");
+ $("#path").val("/");
+ }
+ else if ($(this).val() == 'eucalyptus') {
+ clear();
+ $("#autofill").attr( 'disabled', false );
}
});
})
@@ -27,12 +39,23 @@
$("#path").val("/services/Eucalyptus");
}
else {
- $("#region_name").val("");
- $("#region_endpoint").val("");
- $("#is_secure").val("");
- $("#port").val("");
- $("#path").val("");
+ clear();
}
+}
+
+function clear() {
+ $("#name").val("");
+ $("#region_name").val("");
+ $("#region_endpoint").val("");
+ $("#is_secure").val("");
+ $("#port").val("");
+ $("#proxy").val("");
+ $("#proxy_port").val("");
+ $("#proxy_user").val("");
+ $("#proxy_pass").val("");
+ $("#debug").val("");
+ $("#https_connection_factory").val("");
+ $("#path").val("");
}
@@ -60,8 +83,9 @@
<option value="eucalyptus">Eucalyptus</option>
<option value="ec2">Amazon EC2</option>
</select>
- <br/><input type="checkbox" id="autofill" onclick="javascript:af()">
- auto fill using Eucalyptus Public Cloud values
+ <br/>
+ <input type="checkbox" id="autofill" onclick="javascript:af()" disabled="true">
+ auto fill using Eucalyptus Public Cloud values
</div>
%if error.has_key('type_error'):
<div class="form-row-error-message">${error['type_error']}</div>
@@ -90,9 +114,12 @@
%>
<div class="${cls}">
<label>Region name:</label>
- <div class="form-row-input">
+ <div id="region_selection" class="form-row-input">
<input type="text" name="region_name" id="region_name" value="${region_name}" size="40">
</div>
+ %if error.has_key('name_error'):
+ <div class="form-row-error-message">${error['name_error']}</div>
+ %endif
<div style="clear: both"></div>
</div>
@@ -107,14 +134,19 @@
<div style="clear: both"></div>
</div>
- <%
+ <%
cls = "form-row"
+ if error.has_key('is_secure_error'):
+ cls += " form-row-error"
%>
<div class="${cls}">
<label>Is secure ('O' for False or '1' for True):</label>
<div class="form-row-input">
<input type="text" name="is_secure" id="is_secure" value="${is_secure}" size="40">
</div>
+ %if error.has_key('is_secure_error'):
+ <div class="form-row-error-message">${error['is_secure_error']}; you entered: '${is_secure}'</div>
+ %endif
<div style="clear: both"></div>
</div>
diff -r 6632a5d39f41 -r 802b761c5032 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Mon Oct 26 17:04:47 2009 -0400
+++ b/templates/cloud/configure_cloud.mako Tue Oct 27 15:32:44 2009 -0400
@@ -35,24 +35,29 @@
}
else if ( old_state=='shutting-down' && new_state=='available' ) {
location.reload(true);
+ }
+ else if ( old_state=='running' && new_state=='available' ) {
+ location.reload(true);
+ }
+ else if ( old_state=='running' && new_state=='error' ) {
+ location.reload(true);
+ }
+ else if ( old_state=='pending' && new_state=='error' ) {
+ location.reload(true);
+ }
+ else if ( old_state=='pending' && new_state=='available' ) {
+ location.reload(true);
}
else if ( new_state=='shutting-down' || new_state=='shutting-downUCI' ) {
$(elem + "-link").text( "" );
}
- xmlhttp = new XMLHttpRequest();
- xmlhttp.open( "HEAD", "http://127.0.0.1:8080/admin", false );
- xmlhttp.send( null );
- //alert(xmlhttp.getAllResponseHeaders())
- console.log( "xmlhttp.readyState: %s", xmlhttp.readyState );
- console.log( "xmlhttp.status: %s", xmlhttp.status );
- if ( new_state=='running' && xmlhttp.readyState==1 ) {
- console.log ("in ready statsus = 1");
- //if (xmlhttp.status==200) {
- // console.log( "in status = 200" );
- // location.reload(true);
- //}
- }
-
+ // In order to show 'Access Galaxy' button, the whole page needs to be refreshed. So, while Galaxy is starting,
+ // keep refreshing the page. Should be handled better...
+ else if ( $(elem+"-link").text().search('starting') && old_state=='running' && new_state=='running' ) {
+ //location.reload(true);
+ $(elem + "-link").text("Still starting...");
+ }
+
// Update 'state' and 'time alive' fields
$(elem + "-state").text( data[i].state );
if (data[i].launch_time) {
@@ -184,7 +189,7 @@
context.write( '<span>Access Galaxy</span>' )
context.write( '<img src="'+h.url_for('/static/images/silk/resultset_next.png')+'" /></a></div>' )
except urllib2.URLError:
- context.write( '<span>Galaxy starting...</span>' )
+ context.write( 'Galaxy starting...' )
%>
%endif
@@ -247,8 +252,10 @@
<a onclick="document.getElementById('short').style.display = 'block';
document.getElementById('full').style.display = 'none'; return 0;"
href="javascript:void(0)">
- ${str(prevInstance.error)}
- </a>
+ error:</a><br />
+ ${str(prevInstance.error)}
+ <p />
+ <a href="${h.url_for( action='set_uci_state', id=trans.security.encode_id(prevInstance.id), state='available' )}">reset state</a>
</div>
%else:
${str(prevInstance.state)}
@@ -278,7 +285,7 @@
You have no AWS credentials associated with your Galaxy account:
<a class="action-button" href="${h.url_for( action='add' )}">
<img src="${h.url_for('/static/images/silk/add.png')}" />
- <span>Add AWS credentials</span>
+ <span>add credentials</span>
</a>
or
<a href="http://aws.amazon.com/" target="_blank">
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/022ac0f64679
changeset: 3076:022ac0f64679
user: Enis Afgan <afgane(a)gmail.com>
date: Wed Oct 28 16:35:36 2009 -0400
description:
Fixed refreshing of the status of an instance and assocaited Galaxy web server on main Galaxy Cloud web page.
diffstat:
lib/galaxy/web/controllers/cloud.py | 54 +++++++++++----------------
templates/cloud/configure_cloud.mako | 55 ++++++++++++++-------------
2 files changed, 50 insertions(+), 59 deletions(-)
diffs (194 lines):
diff -r 7b642483d18c -r 022ac0f64679 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Wed Oct 28 12:28:57 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Wed Oct 28 16:35:36 2009 -0400
@@ -3,6 +3,7 @@
import pkg_resources
pkg_resources.require( "simplejson" )
import simplejson
+import urllib2
from galaxy.tools.parameters import *
from galaxy.tools import DefaultToolState
@@ -597,30 +598,18 @@
except ValueError:
pass
- # Check if Amazon EC2 has already been registered by this user
- ec2_registered = trans.sa_session.query( model.CloudProvider ).filter_by( user=user, type='ec2' ).first()
-
if region_name or region_endpoint or name or is_secure or port or proxy or debug or path:
- log.debug (" in if ")
if trans.app.model.CloudProvider \
.filter_by (user=user, name=name) \
.first():
- log.debug (" in if 2 ")
error['name_error'] = "A provider with that name already exist."
elif name=='' or len( name ) > 255:
- log.debug (" in if 3")
error['name_error'] = "Provider name must be between 1 and 255 characters long."
elif type=='':
- log.debug (" in if 4")
error['type_error'] = "Provider type must be selected."
- elif ec2_registered:
- log.debug (" in if 5")
- error['type_error'] = "Amazon EC2 has already been registered as a provider."
elif not (is_secure == 0 or is_secure == 1):
- log.debug (" in if 6")
error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'"
else:
- log.debug (" in else ")
provider = model.CloudProvider()
provider.user = user
provider.type = type
@@ -712,26 +701,8 @@
@web.require_login( "add Amazon EC2 provider" )
def add_ec2( self, trans ):
""" Default provider setup for Amazon's EC2. """
- user = trans.get_user()
- # Check if EC2 has already been registered by this user.
- exists = trans.sa_session.query( model.CloudProvider ) \
- .filter_by( user=user, type='ec2' ).first()
-
- if not exists:
- self.add_provider( trans, name='Amazon EC2', type='ec2', region_name='us-east-1', region_endpoint='us-east-1.ec2.amazonaws.com', is_secure=1, path='/' )
- return self.add( trans )
-# providers = trans.sa_session.query( model.CloudProvider ).filter_by( user=user ).all()
-# return trans.fill_template( "cloud/add_credentials.mako",
-# credName = '',
-# providerName = '',
-# accessKey = '',
-# secretKey = '',
-# error = {},
-# providers = providers
-# )
-
- trans.show_error_message( "EC2 is already registered as a cloud provider under name '%s'." % exists.name )
- return self.list( trans )
+ self.add_provider( trans, name='Amazon EC2', type='ec2', region_name='us-east-1', region_endpoint='us-east-1.ec2.amazonaws.com', is_secure=1, path='/' )
+ return self.add( trans )
@web.json
def json_update( self, trans ):
@@ -751,6 +722,25 @@
insd[uci.name] = dict
return insd
+ @web.json
+ def link_update( self, trans, uci_id=0 ):
+ ild = {} # instance-link-dict
+ dict = {}
+ dict['uci_id'] = uci_id
+ try:
+ user = trans.get_user()
+ # TODO: This query can assumes only one instance under given UCI can be running (i.e., started).
+ inst = trans.sa_session.query( model.CloudInstance ).filter_by( user=user, uci_id=uci_id, state=uci_states.RUNNING ).first()
+ urllib2.urlopen( "http://" + inst.public_dns )
+ dict['public_dns'] = inst.public_dns
+ dict['inst_id'] = inst.id
+ ild['data'] = dict
+ return ild
+ except urllib2.URLError:
+ dict['public_dns'] = False
+ ild['data'] = dict
+ return ild
+
## ---- Utility methods -------------------------------------------------------
def get_provider( trans, name ):
diff -r 7b642483d18c -r 022ac0f64679 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Wed Oct 28 12:28:57 2009 -0400
+++ b/templates/cloud/configure_cloud.mako Wed Oct 28 16:35:36 2009 -0400
@@ -23,13 +23,13 @@
function update_state() {
$.getJSON( "${h.url_for( action='json_update' )}", {}, function ( data ) {
for (var i in data) {
- var elem = '#' + data[i].id;
+ var elem = '#' + data[i].id;
// Because of different list managing 'live' vs. 'available' instances, refresh entire
// page on necessary state change.
old_state = $(elem + "-state").text();
new_state = data[i].state;
- console.log( "old_state[%d] = %s", i, old_state );
- console.log( "new_state[%d] = %s", i, new_state );
+ //console.log( "old_state[%d] = %s", i, old_state );
+ //console.log( "new_state[%d] = %s", i, new_state );
if ( old_state=='pending' && new_state=='running' ) {
location.reload(true);
}
@@ -51,11 +51,27 @@
else if ( new_state=='shutting-down' || new_state=='shutting-downUCI' ) {
$(elem + "-link").text( "" );
}
- // In order to show 'Access Galaxy' button, the whole page needs to be refreshed. So, while Galaxy is starting,
- // keep refreshing the page. Should be handled better...
- else if ( $(elem+"-link").text().search('starting') && old_state=='running' && new_state=='running' ) {
- //location.reload(true);
- $(elem + "-link").text("Still starting...");
+
+ // Check if Galaxy website is accessible on given instance; if so, provide link. Otherwise, wait more.
+ else if ( ( $(elem+"-link").text().match('starting') || $(elem+"-link").text()=='' ) && new_state=='running' ) {
+ //console.log ( 'elem.text: ' + $(elem+"-link").text() );
+ $.getJSON( "${h.url_for( action='link_update' )}", { uci_id: data[i].id }, function ( data ) {
+ for (var i in data) {
+ var dns = data[i].public_dns;
+ var uci = '#' + data[i].uci_id;
+ if( !dns ) {
+ $(uci+"-link").text( 'Galaxy starting...' );
+ // http://stackoverflow.com/questions/275931/how-do-you-make-an-element-flash-…
+ $(uci+"-link").stop().animate({ fontSize: "14px" }, 1000).animate({ fontSize: "12px" }, 1000);
+ }
+ else {
+ $(uci+"-link").html( '<div align="right"><a class="action-button" href="http://'+dns+'" target="_blank">' +
+ '<span>Access Galaxy</span>'+
+ '<img src="' + "${h.url_for( '/static/images/silk/resultset_next.png' )}" + '" /></div>' );
+ $(uci+"-link").stop().animate({ fontSize: "14px" }, 1000).animate({ fontSize: "12px" }, 1000);
+ }
+ }
+ });
}
// Update 'state' and 'time alive' fields
@@ -67,6 +83,7 @@
$(elem + "-launch_time").text( "N/A" );
}
}
+ console.log('');
});
setTimeout("update_state()", 15000);
}
@@ -74,6 +91,7 @@
$(function() {
update_state();
});
+
</script>
</%def>
@@ -176,25 +194,8 @@
context.write( ')' )
%>
</td>
- <td id="${ liveInstance.id }-link"><div align="right">
- %for j, instance in enumerate( liveInstance.instance ):
- ## TODO: Once more instances will be running under the same liveInstance, additional logic will need to be added to account for that
- %if instance.state == "running":
- ## Wait until Galaxy server starts to show 'Access Galaxy' button
- <%
- import urllib2
- try:
- urllib2.urlopen("http://"+instance.public_dns)
- context.write( '<a class="action-button" href="http://'+instance.public_dns+'" target="_blank">' )
- context.write( '<span>Access Galaxy</span>' )
- context.write( '<img src="'+h.url_for('/static/images/silk/resultset_next.png')+'" /></a></div>' )
- except urllib2.URLError:
- context.write( 'Galaxy starting...' )
- %>
-
- %endif
- %endfor
- </td>
+ ## Handled by JavaScript function
+ <td id="${ liveInstance.id }-link"></td>
<td>
<div popupmenu="li-${i}-popup">
<a class="action-button" confirm="Are you sure you want to stop instance '${liveInstance.name}'? Please note that this may take up to 1 minute during which time the page will not refresh." href="${h.url_for( action='stop', id=trans.security.encode_id(liveInstance.id) )}">Stop</a>
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/676fae1a35f9
changeset: 3072:676fae1a35f9
user: Enis Afgan <afgane(a)gmail.com>
date: Fri Oct 23 17:41:35 2009 -0400
description:
A lot of usability and UI enhancements. Current state of automatic state upadtes causes some inconsistencies though.
diffstat:
lib/galaxy/cloud/__init__.py | 27 +-
lib/galaxy/cloud/providers/ec2.py | 22 +-
lib/galaxy/cloud/providers/eucalyptus.py | 67 ++-
lib/galaxy/model/__init__.py | 6 +
lib/galaxy/model/mapping.py | 41 ++-
lib/galaxy/model/migrate/versions/0014_cloud_tables.py | 59 +++-
lib/galaxy/web/controllers/cloud.py | 351 ++++++++++++---------
templates/cloud/add_credentials.mako | 27 +-
templates/cloud/add_provider.mako | 225 ++++++++++++++
templates/cloud/configure_cloud.mako | 98 ++++-
templates/cloud/view.mako | 81 ++++-
templates/cloud/view_usage.mako | 47 ++
12 files changed, 810 insertions(+), 241 deletions(-)
diffs (1661 lines):
diff -r 7f3054622e87 -r 676fae1a35f9 lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Wed Oct 21 11:40:51 2009 -0400
+++ b/lib/galaxy/cloud/__init__.py Fri Oct 23 17:41:35 2009 -0400
@@ -519,16 +519,25 @@
vol = model.CloudStore.filter( model.CloudStore.c.volume_id == vol_id ).first()
vol.i_id = instance_id
vol.flush()
+
+ def set_error( self, error ):
+ """
+ Sets error field of given UCI in local Galaxy database
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.error = error
+ uci.flush()
# --------- Getter methods -----------------
- def get_provider_name( self ):
- """ Returns name of cloud provider associated with given UCI. """
+ def get_provider_type( self ):
+ """ Returns type of cloud provider associated with given UCI. """
uci = model.UCI.get( self.uci_id )
uci.refresh()
- cred_id = uci.credentials_id
- cred = model.CloudUserCredentials.get( cred_id )
- return cred.provider_name
+# cred_id = uci.credentials_id
+# cred = model.CloudUserCredentials.get( cred_id )
+ return uci.credentials.provider.type
def get_instances_indexes( self, state=None ):
"""
@@ -652,6 +661,12 @@
uci.refresh()
return uci
+ def get_provider( self ):
+ """ Returns database object of cloud provider associated with credentials of given UCI. """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.credentials.provider
+
def uci_launch_time_set( self ):
uci = model.UCI.get( self.uci_id )
uci.refresh()
@@ -1021,7 +1036,7 @@
def put( self, uci_wrapper ):
""" Put given request for UCI manipulation into provider's request queue."""
# log.debug( "Adding UCI '%s' manipulation request into cloud manager's queue." % uci_wrapper.name )
- self.cloud_provider[uci_wrapper.get_provider_name()].put( uci_wrapper )
+ self.cloud_provider[uci_wrapper.get_provider_type()].put( uci_wrapper )
diff -r 7f3054622e87 -r 676fae1a35f9 lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Wed Oct 21 11:40:51 2009 -0400
+++ b/lib/galaxy/cloud/providers/ec2.py Fri Oct 23 17:41:35 2009 -0400
@@ -51,7 +51,7 @@
"""
STOP_SIGNAL = object()
def __init__( self, app ):
- self.name = "ec2"
+ self.type = "ec2" # cloud provider type (e.g., ec2, eucalyptus, opennebula)
self.zone = "us-east-1a"
self.key_pair = "galaxy-keypair"
self.queue = Queue()
@@ -73,7 +73,7 @@
uci_wrapper = self.queue.get()
# uci = uci_wrapper.get_uci()
- log.debug( '[%d] uci name: %s' % ( cnt, uci_wrapper.get_name() ) )
+ log.debug( '[%d] uci type: %s' % ( cnt, uci_wrapper.get_name() ) )
uci_state = uci_wrapper.get_state()
if uci_state is self.STOP_SIGNAL:
return
@@ -118,6 +118,7 @@
uci_wrapper.set_key_pair( inst, kp.name, kp.material )
else:
log.error( "EC2 response error: '%s'" % e )
+ uci_wrapper.set_error( "EC2 response error while creating key pair: " + e )
return kp.name
@@ -194,6 +195,7 @@
else:
uci_wrapper.change_state( uci_state=uci_states.ERROR )
uci_wrapper.set_store_status( vol.id, uci_states.ERROR )
+ uci_wrapper.set_error( "Volume '%s' not found by cloud provider after being created" % vol.id )
def deleteUCI( self, uci_wrapper ):
"""
@@ -226,6 +228,8 @@
log.error( "Deleting following volume(s) failed: %s. However, these volumes were successfully deleted: %s. \
MANUAL intervention and processing needed." % ( failedList, deletedList ) )
uci_wrapper.change_state( uci_state=uci_state.ERROR )
+ uci_wrapper.set_error( "Deleting following volume(s) failed: "+failedList+". However, these volumes were successfully deleted: "+deletedList+". \
+ MANUAL intervention and processing needed." )
def addStorageToUCI( self, name ):
""" Adds more storage to specified UCI
@@ -291,7 +295,7 @@
uci_wrapper.set_instance_id( i_index, i_id )
s = reservation.instances[0].state
uci_wrapper.change_state( s, i_id, s )
- log.debug( "Instance of UCI '%s' started, current state: %s" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
+ log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
@@ -429,20 +433,20 @@
Reason behind this method is to sync state of local DB and real-world resources
"""
log.debug( "Running general status update for EC2 UCIs..." )
- instances = model.CloudInstance.filter( or_( model.CloudInstance.c.state==instance_states.RUNNING, #"running",
- model.CloudInstance.c.state==instance_states.PENDING, #"pending",
+ instances = model.CloudInstance.filter( or_( model.CloudInstance.c.state==instance_states.RUNNING,
+ model.CloudInstance.c.state==instance_states.PENDING,
model.CloudInstance.c.state==instance_states.SHUTTING_DOWN ) ).all()
for inst in instances:
- if self.name == inst.uci.credentials.provider_name:
- log.debug( "[%s] Running general status update on instance '%s'" % ( inst.uci.credentials.provider_name, inst.instance_id ) )
+ if self.type == inst.uci.credentials.provider.type:
+ log.debug( "[%s] Running general status update on instance '%s'" % ( inst.uci.credentials.provider.type, inst.instance_id ) )
self.updateInstance( inst )
stores = model.CloudStore.filter( or_( model.CloudStore.c.status==store_states.IN_USE,
model.CloudStore.c.status==store_states.CREATING,
model.CloudStore.c.status==None ) ).all()
for store in stores:
- if self.name == store.uci.credentials.provider_name:
- log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider_name, store.volume_id ) )
+ if self.type == store.uci.credentials.provider.type:
+ log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider.type, store.volume_id ) )
self.updateStore( store )
def updateInstance( self, inst ):
diff -r 7f3054622e87 -r 676fae1a35f9 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Wed Oct 21 11:40:51 2009 -0400
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Fri Oct 23 17:41:35 2009 -0400
@@ -51,7 +51,7 @@
"""
STOP_SIGNAL = object()
def __init__( self, app ):
- self.name = "eucalyptus"
+ self.type = "eucalyptus" # cloud provider type (e.g., ec2, eucalyptus, opennebula)
self.zone = "epc"
self.key_pair = "galaxy-keypair"
self.queue = Queue()
@@ -73,7 +73,7 @@
uci_wrapper = self.queue.get()
# uci = uci_wrapper.get_uci()
- log.debug( '[%d] uci name: %s' % ( cnt, uci_wrapper.get_name() ) )
+ log.debug( '[%d] uci type: %s' % ( cnt, uci_wrapper.get_name() ) )
uci_state = uci_wrapper.get_state()
if uci_state is self.STOP_SIGNAL:
return
@@ -95,17 +95,21 @@
Establishes eucalyptus cloud connection using user's credentials associated with given UCI
"""
log.debug( '##### Establishing eucalyptus cloud connection' )
- # Eucalyptus Public Cloud
- # TODO: Add option in Galaxy config file to specify these values (i.e., for locally managed Eucalyptus deployments)
- euca_region = RegionInfo( None, "eucalyptus", "mayhem9.cs.ucsb.edu" )
- conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(), aws_secret_access_key=uci_wrapper.get_secret_key(), is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
+ provider = uci_wrapper.get_provider()
+ euca_region = RegionInfo( None, provider.region_name, provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(),
+ aws_secret_access_key=uci_wrapper.get_secret_key(),
+ is_secure=provider.is_secure,
+ port=provider.port,
+ region=euca_region,
+ path=provider.path )
return conn
def set_keypair( self, uci_wrapper, conn ):
"""
Generate keypair using user's default credentials
"""
- log.debug( "Getting user's keypair" )
+ log.debug( "Getting user's keypair: '%s'" % self.key_pair )
kp = conn.get_key_pair( self.key_pair )
instances = uci_wrapper.get_instances_indexes()
@@ -128,7 +132,8 @@
TODO: Dummy method - need to implement logic
For valid sizes, see http://aws.amazon.com/ec2/instance-types/
"""
- return model.CloudImage.filter( model.CloudImage.table.c.id==1 ).first().image_id
+ log.debug( "image id: '%s'" % model.CloudImage.get( 1 ).image_id )
+ return model.CloudImage.get( 1 ).image_id
# def get_instances( self, uci ):
# """
@@ -211,6 +216,8 @@
log.error( "Deleting following volume(s) failed: %s. However, these volumes were successfully deleted: %s. \
MANUAL intervention and processing needed." % ( failedList, deletedList ) )
uci_wrapper.change_state( uci_state=uci_states.ERROR )
+ uci_wrapper.set_error( "Deleting following volume(s) failed: "+failedList+". However, these volumes were successfully deleted: "+deletedList+". \
+ MANUAL intervention and processing needed." )
def addStorageToUCI( self, name ):
""" Adds more storage to specified UCI """
@@ -251,7 +258,7 @@
# for i in range( len( sgs ) ):
# if sgs[i].name == "galaxy":
# sg.append( sgs[i] )
- # break # only 1 security group w/ this name can exist, so continue
+ # break # only 1 security group w/ this type can exist, so continue
log.debug( "***** Starting UCI instance '%s'" % uci_wrapper.get_name() )
log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
@@ -267,7 +274,7 @@
uci_wrapper.set_instance_id( i_index, i_id )
s = reservation.instances[0].state
uci_wrapper.change_state( s, i_id, s )
- log.debug( "Instance of UCI '%s' started, current state: %s" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
+ log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
@@ -310,14 +317,9 @@
# Get all instances associated with given UCI
il = uci_wrapper.get_instances_ids() # instance list
-# log.debug( 'List of instances being terminated: %s' % il )
+ log.debug( 'List of instances being terminated: %s' % il )
rl = conn.get_all_instances( il ) # Reservation list associated with given instances
-
-# tState = conn.terminate_instances( il )
-# # TODO: Need to update instance stop time (for all individual instances)
-# stop_time = datetime.utcnow()
-# uci_wrapper.set_stop_time( stop_time )
-
+
# Initiate shutdown of all instances under given UCI
cnt = 0
stopped = []
@@ -406,20 +408,20 @@
Reason behind this method is to sync state of local DB and real-world resources
"""
log.debug( "Running general status update for EPC UCIs..." )
- instances = model.CloudInstance.filter( or_( model.CloudInstance.c.state==instance_states.RUNNING, #"running",
- model.CloudInstance.c.state==instance_states.PENDING, #"pending",
+ instances = model.CloudInstance.filter( or_( model.CloudInstance.c.state==instance_states.RUNNING,
+ model.CloudInstance.c.state==instance_states.PENDING,
model.CloudInstance.c.state==instance_states.SHUTTING_DOWN ) ).all()
for inst in instances:
- if self.name == inst.uci.credentials.provider_name:
- log.debug( "[%s] Running general status update on instance '%s'" % ( inst.uci.credentials.provider_name, inst.instance_id ) )
+ if self.type == inst.uci.credentials.provider.type:
+ log.debug( "[%s] Running general status update on instance '%s'" % ( inst.uci.credentials.provider.type, inst.instance_id ) )
self.updateInstance( inst )
stores = model.CloudStore.filter( or_( model.CloudStore.c.status==store_states.IN_USE,
model.CloudStore.c.status==store_states.CREATING,
model.CloudStore.c.status==None ) ).all()
for store in stores:
- if self.name == store.uci.credentials.provider_name:
- log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider_name, store.volume_id ) )
+ if self.type == store.uci.credentials.provider.type:
+ log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider.type, store.volume_id ) )
self.updateStore( store )
def updateInstance( self, inst ):
@@ -431,8 +433,13 @@
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
- euca_region = RegionInfo( None, "eucalyptus", "mayhem9.cs.ucsb.edu" )
- conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key, is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
+ euca_region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
+ aws_secret_access_key=s_key,
+ is_secure=uci.credentials.provider.is_secure,
+ port=uci.credentials.provider.port,
+ region=euca_region,
+ path=uci.credentials.provider.path )
# Get reservations handle for given instance
rl= conn.get_all_instances( [inst.instance_id] )
# Because EPC deletes references to reservations after a short while after instances have terminated, getting an empty list as a response to a query
@@ -475,9 +482,13 @@
a_key = uci.credentials.access_key
s_key = uci.credentials.secret_key
# Get connection
- euca_region = RegionInfo( None, "eucalyptus", "mayhem9.cs.ucsb.edu" )
- conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key, is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
- # Get reservations handle for given store
+ euca_region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
+ aws_secret_access_key=s_key,
+ is_secure=uci.credentials.provider.is_secure,
+ port=uci.credentials.provider.port,
+ region=euca_region,
+ path=uci.credentials.provider.path )# Get reservations handle for given store
vl = conn.get_all_volumes( [store.volume_id] )
# log.debug( "Store '%s' vl: '%s'" % ( store.volume_id, vl ) )
# Update store status in local DB with info from cloud provider
diff -r 7f3054622e87 -r 676fae1a35f9 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Wed Oct 21 11:40:51 2009 -0400
+++ b/lib/galaxy/model/__init__.py Fri Oct 23 17:41:35 2009 -0400
@@ -963,6 +963,12 @@
self.user = None
self.size = None
self.availability_zone = None
+
+class CloudProvider( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+ self.type = None
class CloudUserCredentials( object ):
def __init__( self ):
diff -r 7f3054622e87 -r 676fae1a35f9 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Wed Oct 21 11:40:51 2009 -0400
+++ b/lib/galaxy/model/mapping.py Fri Oct 23 17:41:35 2009 -0400
@@ -395,14 +395,15 @@
Column( "state", TEXT ) )
""" UserConfiguredInstance (UCI) table """
-UCI.table = Table( "uci", metadata,
+UCI.table = Table( "cloud_uci", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
- Column( "credentials_id", Integer, ForeignKey( "cloud_user_credentials.id" ), index=True, nullable=False ),
+ Column( "credentials_id", Integer, ForeignKey( "cloud_user_credentials.id" ), index=True ),
Column( "name", TEXT ),
Column( "state", TEXT ),
+ Column( "error", TEXT ),
Column( "total_size", Integer ),
Column( "launch_time", DateTime ) )
@@ -413,12 +414,13 @@
Column( "launch_time", DateTime ),
Column( "stop_time", DateTime ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
- Column( "uci_id", Integer, ForeignKey( "uci.id" ), index=True ),
+ Column( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ),
Column( "type", TEXT ),
Column( "reservation_id", TEXT ),
Column( "instance_id", TEXT ),
Column( "mi_id", TEXT, ForeignKey( "cloud_image.image_id" ), index=True, nullable=False ),
Column( "state", TEXT ),
+ Column( "error", TEXT ),
Column( "public_dns", TEXT ),
Column( "private_dns", TEXT ),
Column( "keypair_name", TEXT ),
@@ -431,7 +433,7 @@
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "attach_time", DateTime ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
- Column( "uci_id", Integer, ForeignKey( "uci.id" ), index=True, nullable=False ),
+ Column( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True, nullable=False ),
Column( "volume_id", TEXT ),
Column( "size", Integer, nullable=False ),
Column( "availability_zone", TEXT ),
@@ -440,6 +442,27 @@
Column( "device", TEXT ),
Column( "space_consumed", Integer ) )
+CloudProvider.table = Table( "cloud_provider", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
+ Column( "type", TEXT, nullable=False ),
+ Column( "name", TEXT ),
+ Column( "region_connection", TEXT ),
+ Column( "region_name", TEXT ),
+ Column( "region_endpoint", TEXT ),
+ Column( "is_secure", Boolean ),
+ Column( "host", TEXT ),
+ Column( "port", Integer ),
+ Column( "proxy", TEXT ),
+ Column( "proxy_port", TEXT ),
+ Column( "proxy_user", TEXT ),
+ Column( "proxy_pass", TEXT ),
+ Column( "debug", Integer ),
+ Column( "https_connection_factory", TEXT ),
+ Column( "path", TEXT ) )
+
CloudUserCredentials.table = Table( "cloud_user_credentials", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
@@ -449,7 +472,8 @@
Column( "access_key", TEXT ),
Column( "secret_key", TEXT ),
Column( "defaultCred", Boolean, default=False ),
- Column( "provider_name", TEXT ) )
+ Column( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ) )
+
# ***************************************************************************
StoredWorkflow.table = Table( "stored_workflow", metadata,
@@ -978,8 +1002,13 @@
i=relation( CloudInstance )
) )
+assign_mapper( context, CloudProvider, CloudProvider.table,
+ properties=dict( user=relation( User )
+ ) )
+
assign_mapper( context, CloudUserCredentials, CloudUserCredentials.table,
- properties=dict( user=relation( User)
+ properties=dict( user=relation( User),
+ provider=relation( CloudProvider )
) )
# ^^^^^^^^^^^^^^^ End cloud table mappings ^^^^^^^^^^^^^^^^^^
diff -r 7f3054622e87 -r 676fae1a35f9 lib/galaxy/model/migrate/versions/0014_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Wed Oct 21 11:40:51 2009 -0400
+++ b/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Fri Oct 23 17:41:35 2009 -0400
@@ -20,14 +20,15 @@
Column( "manifest", TEXT ),
Column( "state", TEXT ) )
-UCI_table = Table( "uci", metadata,
+UCI_table = Table( "cloud_uci", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
- Column( "credentials_id", Integer, ForeignKey( "cloud_user_credentials.id" ), index=True, nullable=False ),
+ Column( "credentials_id", Integer, ForeignKey( "cloud_user_credentials.id" ), index=True ),
Column( "name", TEXT ),
Column( "state", TEXT ),
+ Column( "error", TEXT ),
Column( "total_size", Integer ),
Column( "launch_time", DateTime ) )
@@ -38,12 +39,13 @@
Column( "launch_time", DateTime ),
Column( "stop_time", DateTime ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
- Column( "uci_id", Integer, ForeignKey( "uci.id" ), index=True, nullable=False ),
+ Column( "uci_id", Integer, ForeignKey( "uci.id" ), index=True ),
Column( "type", TEXT ),
Column( "reservation_id", TEXT ),
Column( "instance_id", TEXT ),
- Column( "mi_id", TEXT, ForeignKey( "cloud_image.image_id" ), index=True ),
+ Column( "mi_id", TEXT, ForeignKey( "cloud_image.image_id" ), index=True, nullable=False ),
Column( "state", TEXT ),
+ Column( "error", TEXT ),
Column( "public_dns", TEXT ),
Column( "private_dns", TEXT ),
Column( "keypair_name", TEXT ),
@@ -74,7 +76,28 @@
Column( "access_key", TEXT ),
Column( "secret_key", TEXT ),
Column( "defaultCred", Boolean, default=False ),
- Column( "provider_name", TEXT ) )
+ Column( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ) )
+
+CloudProvider_table = Table( "cloud_provider", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
+ Column( "type", TEXT, nullable=False ),
+ Column( "name", TEXT ),
+ Column( "region_connection", TEXT ),
+ Column( "region_name", TEXT ),
+ Column( "region_endpoint", TEXT ),
+ Column( "is_secure", Boolean ),
+ Column( "host", TEXT ),
+ Column( "port", Integer ),
+ Column( "proxy", TEXT ),
+ Column( "proxy_port", TEXT ),
+ Column( "proxy_user", TEXT ),
+ Column( "proxy_pass", TEXT ),
+ Column( "debug", Integer ),
+ Column( "https_connection_factory", TEXT ),
+ Column( "path", TEXT ) )
def upgrade():
metadata.reflect()
@@ -86,12 +109,22 @@
UCI_table.create()
except Exception, e:
log.debug( "Creating UCI table failed. Table probably exists already." )
- CloudInstance_table.create()
- CloudStore_table.create()
+ try:
+ CloudInstance_table.create()
+ except Exception, e:
+ log.debug( "Creating cloud_instance table failed. Table probably exists already." )
+ try:
+ CloudStore_table.create()
+ except Exception:
+ log.debug( "Creating cloud_store table failed. Table probably exists already." )
try:
CloudUserCredentials_table.create()
except Exception, e:
log.debug( "Creating cloud_image table failed. Table probably exists already." )
+ try:
+ CloudProvider_table.create()
+ except Exception, e:
+ log.debug( "Creating cloud_provider table failed. Table probably exists already." )
def downgrade():
metadata.reflect()
@@ -102,12 +135,14 @@
log.debug( "Dropping cloud_image table failed: %s" % str( e ) )
try:
- CloudInstance_table.drop()
+ log.debug( "Would drop cloud_instance table." )
+# CloudInstance_table.drop()
except Exception, e:
log.debug( "Dropping cloud_instance table failed: %s" % str( e ) )
try:
- CloudStore_table.drop()
+ log.debug( "Would drop cloud_store table." )
+# CloudStore_table.drop()
except Exception, e:
log.debug( "Dropping cloud_store table failed: %s" % str( e ) )
@@ -122,6 +157,12 @@
# UCI_table.drop()
except Exception, e:
log.debug( "Dropping UCI table failed: %s" % str( e ) )
+
+ try:
+# log.debug( "Would drop cloud_provider table." )
+ CloudProvider_table.drop()
+ except Exception, e:
+ log.debug( "Dropping cloud_provider table failed: %s" % str( e ) )
diff -r 7f3054622e87 -r 676fae1a35f9 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Wed Oct 21 11:40:51 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Fri Oct 23 17:41:35 2009 -0400
@@ -77,7 +77,7 @@
cloudCredentials = trans.sa_session.query( model.CloudUserCredentials ) \
.filter_by( user=user ) \
- .order_by( desc( model.CloudUserCredentials.c.update_time ) ) \
+ .order_by( desc( model.CloudUserCredentials.c.name ) ) \
.all()
liveInstances = trans.sa_session.query( model.UCI ) \
@@ -115,6 +115,13 @@
"start up and then refresh this page. A button to connect to the instance will then appear alongside "
"instance description." )
+# log.debug( "provider.is_secure: '%s'" % trans.sa_session.query( model.CloudProvider).filter_by(id=1).first().is_secure )
+# trans.sa_session.query( model.CloudProvider).filter_by(id=1).first().is_secure=False
+# trans.sa_session.flush()
+# log.debug( "provider.is_secure: '%s'" % trans.sa_session.query( model.CloudProvider).filter_by(id=1).first().is_secure )
+
+# log.debug( "image: '%s'" % model.CloudImage.is_secure )
+
return trans.fill_template( "cloud/configure_cloud.mako",
cloudCredentials = cloudCredentials,
liveInstances = liveInstances,
@@ -146,13 +153,12 @@
"""
Start a new cloud resource instance
"""
- # TODO: Add choice of instance type before starting one
- #if type:
user = trans.get_user()
- mi = get_mi( trans, type )
uci = get_uci( trans, id )
+ mi = get_mi( trans, uci, type )
stores = get_stores( trans, uci )
-# log.debug(self.app.config.job_working_directory)
+ # Ensure instance is not already running (or related state) and store relevant data
+ # into DB to initiate instance startup by cloud manager
if ( len(stores) is not 0 ) and \
( uci.state != uci_states.SUBMITTED ) and \
( uci.state != uci_states.SUBMITTED_UCI ) and \
@@ -166,116 +172,45 @@
instance.uci = uci
instance.availability_zone = stores[0].availability_zone # Bc. all EBS volumes need to be in the same avail. zone, just check 1st
instance.type = type
-# instance.keypair_name = get_keypair_name( trans )
-# conn = get_connection( trans )
-# log.debug( '***** Setting up security group' )
- # If not existent, setup galaxy security group
-# try:
-# gSecurityGroup = conn.create_security_group('galaxy', 'Security group for Galaxy.')
-# gSecurityGroup.authorize( 'tcp', 80, 80, '0.0.0.0/0' ) # Open HTTP port
-# gSecurityGroup.authorize( 'tcp', 22, 22, '0.0.0.0/0' ) # Open SSH port
-# except:
-# pass
-# sgs = conn.get_all_security_groups()
-# for i in range( len( sgs ) ):
-# if sgs[i].name == "galaxy":
-# sg.append( sgs[i] )
-# break # only 1 security group w/ this name can exist, so continue
-
-# log.debug( '***** Starting an instance' )
-# log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( instance.image.image_id, instance.keypair_name ) )
-# reservation = conn.run_instances( image_id=instance.image.image_id, key_name=instance.keypair_name )
- #reservation = conn.run_instances( image_id=instance.image, key_name=instance.keypair_name, security_groups=['galaxy'], instance_type=instance.type, placement=instance.availability_zone )
-# instance.launch_time = datetime.utcnow()
-# uci.launch_time = instance.launch_time
-# instance.reservation_id = str( reservation ).split(":")[1]
-# instance.instance_id = str( reservation.instances[0]).split(":")[1]
-# instance.state = "pending"
-# instance.state = reservation.instances[0].state
uci.state = uci_states.SUBMITTED_UCI
-
# Persist
session = trans.sa_session
session.save_or_update( instance )
session.save_or_update( uci )
session.flush()
-
+ # Log
trans.log_event ("User initiated starting of cloud instance '%s'." % uci.name )
trans.set_message( "Galaxy instance started. NOTE: Please wait about 3-5 minutes for the instance to "
"start up and then refresh this page. A button to connect to the instance will then appear alongside "
"instance description." )
- time.sleep(1) # Wait for initial update to occur to avoid immediate page reload
return self.list( trans )
trans.show_error_message( "Cannot start instance that is in state '%s'." % uci.state )
return self.list( trans )
-
-# return trans.show_form(
-# web.FormBuilder( web.url_for(), "Start instance size", submit_text="Start" )
-# .add_input( "radio","Small","size", value='small' )
-# .add_input( "radio","Medium","size", value='medium' ) )
-
@web.expose
@web.require_login( "stop Galaxy cloud instance" )
def stop( self, trans, id ):
"""
- Stop a cloud UCI instance. This implies stopping Galaxy server and disconnecting/unmounting relevant file system(s).
+ Stop a cloud UCI instance.
"""
uci = get_uci( trans, id )
- uci.state = uci_states.SHUTTING_DOWN_UCI
- session = trans.sa_session
-# session.save_or_update( stores )
- session.save_or_update( uci )
- session.flush()
- trans.log_event( "User stopped cloud instance '%s'" % uci.name )
- trans.set_message( "Galaxy instance '%s' stopped." % uci.name )
+ if ( uci.state != uci_states.DELETING ) and \
+ ( uci.state != uci_states.DELETING_UCI ) and \
+ ( uci.state != uci_states.ERROR ) and \
+ ( uci.state != uci_states.SHUTTING_DOWN_UCI ) and \
+ ( uci.state != uci_states.SHUTTING_DOWN ) and \
+ ( uci.state != uci_states.AVAILABLE ):
+ uci.state = uci_states.SHUTTING_DOWN_UCI
+ session = trans.sa_session
+ session.save_or_update( uci )
+ session.flush()
+ trans.log_event( "User stopped cloud instance '%s' (id: %s)" % ( uci.name, uci.id ) )
+ trans.set_message( "Stopping of Galaxy instance '%s' initiated." % uci.name )
+
+ return self.list( trans )
-# dbInstances = get_instances( trans, uci ) #TODO: handle list!
-#
-# conn = get_connection( trans )
-# # Get actual cloud instance object
-# cloudInstance = get_cloud_instance( conn, dbInstances.instance_id )
-#
-# # TODO: Detach persistent storage volume(s) from instance and update volume data in local database
-# stores = get_stores( trans, uci )
-# for i, store in enumerate( stores ):
-# log.debug( "Detaching volume '%s' to instance '%s'." % ( store.volume_id, dbInstances.instance_id ) )
-# mntDevice = store.device
-# volStat = None
-## Detaching volume does not work with Eucalyptus Public Cloud, so comment it out
-## try:
-## volStat = conn.detach_volume( store.volume_id, dbInstances.instance_id, mntDevice )
-## except:
-## log.debug ( 'Error detaching volume; still going to try and stop instance %s.' % dbInstances.instance_id )
-# store.attach_time = None
-# store.device = None
-# store.i_id = None
-# store.status = volStat
-# log.debug ( '***** volume status: %s' % volStat )
-#
-#
-# # Stop the instance and update status in local database
-# cloudInstance.stop()
-# dbInstances.stop_time = datetime.utcnow()
-# while cloudInstance.state != 'terminated':
-# log.debug( "Stopping instance %s state; current state: %s" % ( str( cloudInstance ).split(":")[1], cloudInstance.state ) )
-# time.sleep(3)
-# cloudInstance.update()
-# dbInstances.state = cloudInstance.state
-#
-# # Reset relevant UCI fields
-# uci.state = 'available'
-# uci.launch_time = None
-#
-# # Persist
-# session = trans.sa_session
-## session.save_or_update( stores )
-# session.save_or_update( dbInstances ) # TODO: Is this going to work w/ multiple instances stored in dbInstances variable?
-# session.save_or_update( uci )
-# session.flush()
-# trans.log_event( "User stopped cloud instance '%s'" % uci.name )
-# trans.set_message( "Galaxy instance '%s' stopped." % uci.name )
+ trans.show_error_message( "Cannot stop instance that is in state '%s'." % uci.state )
return self.list( trans )
@web.expose
@@ -290,30 +225,10 @@
if ( uci.state != uci_states.DELETING_UCI ) and ( uci.state != uci_states.DELETING ) and ( uci.state != uci_states.ERROR ):
name = uci.name
uci.state = uci_states.DELETING_UCI
- # dbInstances = get_instances( trans, uci ) #TODO: handle list!
- #
- # conn = get_connection( trans )
session = trans.sa_session
- #
- # # Delete volume(s) associated with given uci
- # stores = get_stores( trans, uci )
- # for i, store in enumerate( stores ):
- # log.debug( "Deleting volume '%s' that is associated with UCI '%s'." % ( store.volume_id, uci.name ) )
- # volStat = None
- # try:
- # volStat = conn.delete_volume( store.volume_id )
- # except:
- # log.debug ( 'Error deleting volume %s' % store.volume_id )
- #
- # if volStat:
- # session.delete( store )
- #
- # # Delete UCI from table
- # uciName = uci.name # Store name for logging
- # session.delete( uci )
-
+ session.save_or_update( uci )
session.flush()
- trans.log_event( "User deleted cloud instance '%s'" % name )
+ trans.log_event( "User marked cloud instance '%s' for deletion." % name )
trans.set_message( "Galaxy instance '%s' marked for deletion." % name )
return self.list( trans )
@@ -332,7 +247,19 @@
@web.expose
@web.require_login( "use Galaxy cloud" )
- def configureNew( self, trans, instanceName='', credName='', volSize='', zone=''):
+ def usageReport( self, trans ):
+ user = trans.get_user()
+
+ prevInstances = trans.sa_session.query( model.CloudInstance ) \
+ .filter_by( user=user, state=instance_states.TERMINATED ) \
+ .order_by( desc( model.CloudInstance.c.update_time ) ) \
+ .all()
+
+ return trans.fill_template( "cloud/view_usage.mako", prevInstances = prevInstances )
+
+ @web.expose
+ @web.require_login( "use Galaxy cloud" )
+ def configureNew( self, trans, instanceName='', credName='', volSize='', zone='' ):
"""
Configure and add new cloud instance to user's instance pool
"""
@@ -346,16 +273,19 @@
providersToZones = {}
for storedCred in storedCreds:
- if storedCred.provider_name == 'ec2':
+ if storedCred.provider.type == 'ec2':
ec2_zones = ['us-east-1a', 'us-east-1b', 'us-east-1c', 'us-east-1d']
providersToZones[storedCred.name] = ec2_zones
- elif storedCred.provider_name == 'eucalyptus':
+ elif storedCred.provider.type == 'eucalyptus':
providersToZones[storedCred.name] = ['epc']
if instanceName:
# Create new user configured instance
try:
- if trans.app.model.UCI.filter( and_( trans.app.model.UCI.table.c.name==instanceName, trans.app.model.UCI.table.c.state!=uci_states.DELETED ) ).first():
+ if trans.app.model.UCI \
+ .filter_by (user=user) \
+ .filter( and_( trans.app.model.UCI.table.c.name==instanceName, trans.app.model.UCI.table.c.state!=uci_states.DELETED ) ) \
+ .first():
error['inst_error'] = "An instance with that name already exist."
elif instanceName=='' or len( instanceName ) > 255:
error['inst_error'] = "Instance name must be between 1 and 255 characters long."
@@ -393,7 +323,6 @@
# Log and display the management page
trans.log_event( "User configured new cloud instance" )
trans.set_message( "New Galaxy instance '%s' configured. Once instance status shows 'available' you will be able to start the instance." % instanceName )
- time.sleep(1) # Wait for initial update to occur to avoid immediate page reload
return self.list( trans )
except ValueError:
vol_error = "Volume size must be specified as an integer value only, between 1 and 1000."
@@ -460,8 +389,9 @@
trans.set_message( "Credentials renamed to '%s'." % new_name )
return self.list( trans )
else:
- return form( url_for( id=trans.security.encode_id(stored.id) ), "Rename credentials", submit_text="Rename" ) \
- .add_text( "new_name", "Credentials Name", value=stored.name )
+ return trans.show_form(
+ web.FormBuilder( url_for( id=trans.security.encode_id(stored.id) ), "Rename credentials", submit_text="Rename" )
+ .add_text( "new_name", "Credentials Name", value=stored.name ) )
@web.expose
@web.require_login( "use Galaxy cloud" )
@@ -473,8 +403,9 @@
trans.set_message( "Instance renamed to '%s'." % new_name )
return self.list( trans )
else:
- return form( url_for( id=trans.security.encode_id(instance.id) ), "Rename instance", submit_text="Rename" ) \
- .add_text( "new_name", "Instance name", value=instance.name )
+ return trans.show_form(
+ web.FormBuilder( url_for( id=trans.security.encode_id(instance.id) ), "Rename instance", submit_text="Rename" )
+ .add_text( "new_name", "Instance name", value=instance.name ) )
@web.expose
@web.require_login( "add credentials" )
@@ -485,14 +416,14 @@
user = trans.get_user()
error = {}
- if credName:
- if len( credName ) > 255:
- error['cred_error'] = "Credentials name exceeds maximum allowable length."
- elif trans.app.model.CloudUserCredentials.filter(
+ if credName or providerName or accessKey or secretKey:
+ if credName=='' or len( credName ) > 255:
+ error['cred_error'] = "Credentials name must be between 1 and 255 characters in length."
+ elif trans.app.model.CloudUserCredentials.filter_by( user=user ).filter(
trans.app.model.CloudUserCredentials.table.c.name==credName ).first():
error['cred_error'] = "Credentials with that name already exist."
- elif ( ( providerName.lower()!='ec2' ) and ( providerName.lower()!='eucalyptus' ) ):
- error['provider_error'] = "You specified an unsupported cloud provider."
+ elif providerName=='':
+ error['provider_error'] = "You must select cloud provider associated with these credentials."
elif accessKey=='' or len( accessKey ) > 255:
error['access_key_error'] = "Access key must be between 1 and 255 characters long."
elif secretKey=='' or len( secretKey ) > 255:
@@ -504,7 +435,8 @@
credentials.user = user
credentials.access_key = accessKey
credentials.secret_key = secretKey
- credentials.provider_name = providerName.lower()
+ provider = get_provider( trans, providerName )
+ credentials.provider = provider
# Persist
session = trans.sa_session
session.save_or_update( credentials )
@@ -516,12 +448,14 @@
# self.makeDefault( trans, credentials.id)
return self.list( trans )
- return trans.fill_template( "cloud/add_credentials.mako", \
- credName = credName, \
- providerName = providerName, \
- accessKey = accessKey, \
- secretKey = secretKey, \
- error = error
+ providers = trans.sa_session.query( model.CloudProvider ).filter_by( user=user ).all()
+ return trans.fill_template( "cloud/add_credentials.mako",
+ credName = credName,
+ providerName = providerName,
+ accessKey = accessKey,
+ secretKey = secretKey,
+ error = error,
+ providers = providers
)
# return trans.show_form(
@@ -544,6 +478,13 @@
credDetails = stored )
@web.expose
+ @web.require_login( "test cloud credentials" )
+ def test_cred( self, trans, id=None ):
+ """
+ Tests credentials provided by user with selected cloud provider
+ """
+
+ @web.expose
@web.require_login( "view instance details" )
def viewInstance( self, trans, id=None ):
"""
@@ -574,6 +515,97 @@
return self.list( trans )
@web.expose
+ @web.require_login( "add provider" )
+ def add_provider( self, trans, name='', type='', region_name='', region_endpoint='', is_secure='', host='', port='', proxy='', proxy_port='',
+ proxy_user='', proxy_pass='', debug='', https_connection_factory='', path='' ):
+ user = trans.get_user()
+ error = {}
+
+ # Check if Amazon EC2 has already been registered by this user
+ ec2_registered = trans.sa_session.query( model.CloudProvider ).filter_by( user=user, type='ec2' ).first()
+
+ if region_name or region_endpoint or name or is_secure or port or proxy or debug or path:
+ if trans.app.model.CloudProvider \
+ .filter_by (user=user, name=name) \
+ .first():
+ error['name_error'] = "A provider with that name already exist."
+ elif name=='' or len( name ) > 255:
+ error['name_error'] = "Provider name must be between 1 and 255 characters long."
+ elif type=='':
+ error['type_error'] = "Provider type must be selected."
+ elif ec2_registered:
+ error['type_error'] = "Amazon EC2 has already been registered as a provider."
+ elif is_secure!=0 or is_secure!=1:
+ error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'."
+ else:
+ provider = model.CloudProvider()
+ provider.user = user
+ provider.type = type
+ provider.name = name
+ provider.region_name = region_name
+ provider.region_endpoint = region_endpoint
+ if is_secure=='0':
+ provider.is_secure = False
+ else:
+ provider.is_secure = True
+ provider.host = host
+ provider.port = port
+ provider.proxy = proxy
+ provider.proxy_port = proxy_port
+ provider.proxy_user = proxy_user
+ provider.proxy_pass = proxy_pass
+ provider.debug = debug
+ provider.https_connection_factory = https_connection_factory
+ provider.path = path
+ # Persist
+ session = trans.sa_session
+ session.save_or_update( provider )
+ session.flush()
+ # Log and display the management page
+ trans.log_event( "User configured new cloud provider: '%s'" % name )
+ trans.set_message( "New cloud provider '%s' added." % name )
+ return self.list( trans )
+
+ return trans.fill_template( "cloud/add_provider.mako",
+ name = name,
+ type = type,
+ region_name = region_name,
+ region_endpoint = region_endpoint,
+ is_secure = is_secure,
+ host = host,
+ port = port,
+ proxy = proxy,
+ proxy_port = proxy_port,
+ proxy_user = proxy_user,
+ proxy_pass = proxy_pass,
+ debug = debug,
+ https_connection_factory = https_connection_factory,
+ path = path,
+ error = error
+ )
+
+ @web.expose
+ @web.require_login( "add Amazon EC2 provider" )
+ def add_ec2( self, trans ):
+ """ Default provider setup for Amazon's EC2. """
+ user = trans.get_user()
+ # Check if EC2 has already been registered by this user.
+ exists = trans.sa_session.query( model.CloudProvider ) \
+ .filter_by( user=user, type='ec2' ).first()
+
+ if not exists:
+ provider = model.CloudProvider()
+ provider.user = user
+ provider.type = 'ec2'
+ provider.name = 'EC2'
+ # Persist
+ session = trans.sa_session
+ session.save_or_update( provider )
+ session.flush()
+
+ return self.list( trans )
+
+ @web.expose
@web.require_login( "edit workflows" )
def editor( self, trans, id=None ):
"""
@@ -1012,25 +1044,32 @@
workflows=workflows,
shared_by_others=shared_by_others,
ids_in_menu=ids_in_menu )
+ @web.json
+ def json_update( self, trans ):
+ user = trans.get_user()
+ UCIs = trans.sa_session.query( model.UCI ).filter_by( user=user ).filter( model.UCI.c.state != uci_states.DELETED ).all()
+ insd = {} # instance name-state dict
+ for uci in UCIs:
+ dict = {}
+ dict['id'] = uci.id
+ dict['state'] = uci.state
+ if uci.launch_time != None:
+ dict['launch_time'] = str(uci.launch_time)
+ dict['time_ago'] = str(date.distance_of_time_in_words(uci.launch_time, date.datetime.utcnow() ) )
+ else:
+ dict['launch_time'] = None
+ dict['time_ago'] = None
+ insd[uci.name] = dict
+ return insd
## ---- Utility methods -------------------------------------------------------
-def get_UCIs_state( trans ):
+def get_provider( trans, name ):
user = trans.get_user()
- instances = trans.sa_session.query( model.UCI ).filter_by( user=user ).filter( model.UCI.c.state != uci_states.DELETED ).all()
- insd = {} # instance name-state dict
- for inst in instances:
- insd[inst.name] = inst.state
+ return trans.app.model.CloudProvider \
+ .filter_by (user=user, name=name) \
+ .first()
-
-def get_UCIs_time_ago( trans ):
- user = trans.get_user()
- instances = trans.sa_session.query( model.UCI ).filter_by( user=user ).all()
- intad = {} # instance name-time-ago dict
- for inst in instances:
- if inst.launch_time != None:
- intad[inst.name] = str(date.distance_of_time_in_words (inst.launch_time, date.datetime.utcnow() ) )
-
def get_stored_credentials( trans, id, check_ownership=True ):
"""
Get StoredUserCredentials from the database by id, verifying ownership.
@@ -1085,14 +1124,18 @@
# Looks good
return live
-def get_mi( trans, size='m1.small' ):
+def get_mi( trans, uci, size='m1.small' ):
"""
Get appropriate machine image (mi) based on instance size.
TODO: Dummy method - need to implement logic
For valid sizes, see http://aws.amazon.com/ec2/instance-types/
"""
- return trans.app.model.CloudImage.filter(
- trans.app.model.CloudImage.table.c.id==2).first()
+ if uci.credentials.provider.type == 'ec2':
+ return trans.app.model.CloudImage.filter(
+ trans.app.model.CloudImage.table.c.id==2).first()
+ else:
+ return trans.app.model.CloudImage.filter(
+ trans.app.model.CloudImage.table.c.id==1).first()
def get_stores( trans, uci ):
"""
diff -r 7f3054622e87 -r 676fae1a35f9 templates/cloud/add_credentials.mako
--- a/templates/cloud/add_credentials.mako Wed Oct 21 11:40:51 2009 -0400
+++ b/templates/cloud/add_credentials.mako Fri Oct 23 17:41:35 2009 -0400
@@ -15,11 +15,13 @@
${header}
%endif
+%if providers:
+
<div class="form">
<div class="form-title">Add credentials</div>
<div class="form-body">
<form name="Add credentials" action="${h.url_for( action='add' )}" method="post" >
-
+
<%
cls = "form-row"
if error.has_key('cred_error'):
@@ -45,11 +47,15 @@
<div class="${cls}">
<label>Cloud provider name:</label>
<div class="form-row-input">
- <select name="providerName" style="width:40em">
- <option value="ec2">Amazon EC2</option>
- <option value="eucalyptus">Eucalpytus Public Cloud (EPC)</option>
- </select>
- </div>
+ <select name="providerName" style="width:40em">
+ <option value="">Select Provider...</option>
+ %for provider in providers:
+ <option value="${provider.name}">${provider.name}</option>
+ %endfor
+ </select>
+ <br/>or <a href="${h.url_for( action='add_provider' )}">
+ <span>register additional cloud provider</span></a>
+ </div>
%if error.has_key('provider_error'):
<div class="form-row-error-message">${error['provider_error']}</div>
%endif
@@ -95,3 +101,12 @@
</form>
</div>
</div>
+
+%else:
+ In order to add credentials, desired cloud provider needs to be registered.<p/>
+ Register <a href="${h.url_for( action='add_ec2' )}">
+ <span>Amazon EC2 automatically</span></a>
+ or add
+ <a href="${h.url_for( action='add_provider' )}">
+ <span>custom cloud provider</span></a>.
+%endif
diff -r 7f3054622e87 -r 676fae1a35f9 templates/cloud/add_provider.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/cloud/add_provider.mako Fri Oct 23 17:41:35 2009 -0400
@@ -0,0 +1,225 @@
+<% _=n_ %>
+<%inherit file="/base.mako"/>
+<%def name="title()">Add provider</%def>
+
+<%def name="javascripts()">
+${parent.javascripts()}
+<script type="text/javascript">
+$(function(){
+
+ $("input:text:first").focus();
+
+ $("#type").change(function() {
+ if ($(this).val() == 'ec2') {
+ $("#name").val("EC2");
+ }
+ });
+})
+
+
+function af(){
+
+ if ( $("#autofill").attr('checked') ) {
+ $("#region_name").val("eucalyptus");
+ $("#region_endpoint").val("mayhem9.cs.ucsb.edu");
+ $("#is_secure").val("0");
+ $("#port").val("8773");
+ $("#path").val("/services/Eucalyptus");
+ }
+ else {
+ $("#region_name").val("");
+ $("#region_endpoint").val("");
+ $("#is_secure").val("");
+ $("#port").val("");
+ $("#path").val("");
+ }
+
+}
+
+</script>
+</%def>
+
+%if header:
+ ${header}
+%endif
+
+<div class="form">
+ <div class="form-title">Add cloud provider</div>
+ <div class="form-body">
+ <form name="add_provider_form" action="${h.url_for( action='add_provider' )}" method="post" >
+ <%
+ cls = "form-row"
+ if error.has_key('type_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Provider type:</label>
+ <div class="form-row-input">
+ <select id="type" name="type" style="width:40em">
+ <option value="">Select Provider...</option>
+ <option value="eucalyptus">Eucalyptus</option>
+ <option value="ec2">Amazon EC2</option>
+ </select>
+ <br/><input type="checkbox" id="autofill" onclick="javascript:af()">
+ auto fill using Eucalyptus Public Cloud values
+ </div>
+ %if error.has_key('type_error'):
+ <div class="form-row-error-message">${error['type_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ if error.has_key('name_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Provider name:</label>
+ <div class="form-row-input">
+ <input type="text" id="name" name="name" value="${name}" size="40">
+ </div>
+ %if error.has_key('name_error'):
+ <div class="form-row-error-message">${error['name_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Region name:</label>
+ <div class="form-row-input">
+ <input type="text" name="region_name" id="region_name" value="${region_name}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Region endpoint:</label>
+ <div class="form-row-input">
+ <input type="text" name="region_endpoint" id="region_endpoint" value="${region_endpoint}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Is secure ('O' for False or '1' for True):</label>
+ <div class="form-row-input">
+ <input type="text" name="is_secure" id="is_secure" value="${is_secure}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Host:</label>
+ <div class="form-row-input">
+ <input type="text" name="host" value="${host}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Port:</label>
+ <div class="form-row-input">
+ <input type="text" name="port" id="port" value="${port}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Proxy:</label>
+ <div class="form-row-input">
+ <input type="text" name="proxy" value="${proxy}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Proxy port:</label>
+ <div class="form-row-input">
+ <input type="text" name="proxy_port" value="${proxy_port}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Proxy user:</label>
+ <div class="form-row-input">
+ <input type="text" name="proxy_user" value="${proxy_user}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Proxy pass:</label>
+ <div class="form-row-input">
+ <input type="text" name="proxy_pass" value="${proxy_pass}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Debug:</label>
+ <div class="form-row-input">
+ <input type="text" name="debug" value="${debug}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>HTTPS connection factory:</label>
+ <div class="form-row-input">
+ <input type="text" name="https_connection_factory" value="${https_connection_factory}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ %>
+ <div class="${cls}">
+ <label>Path:</label>
+ <div class="form-row-input">
+ <input type="text" name="path" id="path" value="${path}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <div class="form-row"><input type="submit" value="Add"></div>
+
+ </form>
+
+ </div>
+</div>
diff -r 7f3054622e87 -r 676fae1a35f9 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Wed Oct 21 11:40:51 2009 -0400
+++ b/templates/cloud/configure_cloud.mako Fri Oct 23 17:41:35 2009 -0400
@@ -10,14 +10,38 @@
messagetype = "done"
%>
-
-
-<p />
<div class="${messagetype}message">
${message}
</div>
%endif
+<%def name="javascripts()">
+${parent.javascripts()}
+${h.js( "jquery" )}
+
+<script type="text/javascript">
+ function update_state() {
+ $.getJSON( "${h.url_for( action='json_update' )}", {}, function ( data ) {
+ for (var i in data) {
+ var elem = '#' + data[i].id;
+ $(elem + "-state").text( data[i].state );
+ if (data[i].launch_time) {
+ $(elem + "-launch_time").text( data[i].launch_time.substring(0, 16 ) + " (" + data[i].time_ago + ")" );
+ }
+ else {
+ $(elem + "-launch_time").text( "N/A" );
+ }
+ }
+ });
+ setTimeout("update_state()", 10000);
+ }
+
+ $(function() {
+ update_state();
+ });
+</script>
+</%def>
+
<h2>Galaxy in the clouds</h2>
%if cloudCredentials:
@@ -34,22 +58,23 @@
<table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr class="header">
<th>Credentials name</th>
- <th>Provider</th>
+ <th>Provider name (type)</th>
<th></th>
</tr>
%for i, cloudCredential in enumerate( cloudCredentials ):
<tr>
<td>
${cloudCredential.name}
- <a id="cr-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
+ <a id="cr-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
</td>
- <td>${cloudCredential.provider_name}</td>
-
+ <td>
+ ${cloudCredential.provider.name}
+ (${cloudCredential.provider.type})
+ </td>
<td>
<div popupmenu="cr-${i}-popup">
<a class="action-button" href="${h.url_for( action='view', id=trans.security.encode_id(cloudCredential.id) )}">View</a>
- <a class="action-button" href="${h.url_for( action='rename', id=trans.security.encode_id(cloudCredential.id) )}">Rename</a>
- <a class="action-button" href="${h.url_for( action='makeDefault', id=trans.security.encode_id(cloudCredential.id) )}" target="_parent">Make default</a>
+ <a class="action-button" href="${h.url_for( action='rename', id=trans.security.encode_id(cloudCredential.id) )}">Rename</a>
<a class="action-button" confirm="Are you sure you want to delete credentials '${cloudCredential.name}'?" href="${h.url_for( action='delete', id=trans.security.encode_id(cloudCredential.id) )}">Delete</a>
</div>
</td>
@@ -60,7 +85,7 @@
## *****************************************************
## Manage live instances
<p />
- <h2>Manage cloud instances</h2>
+ <h2>Manage your cloud instances</h2>
<ul class="manage-table-actions">
<li>
<a class="action-button" href="${h.url_for( action='configureNew' )}">
@@ -77,7 +102,7 @@
<colgroup width="25%"></colgroup>
<colgroup width="10%"></colgroup>
<tr class="header">
- <th>Your instances</th>
+ <th>Instance name (credentials)</th>
<th>Storage size (GB)</th>
<th>State</th>
<th>Alive since</th>
@@ -88,12 +113,12 @@
%for i, liveInstance in enumerate( liveInstances ):
<tr>
<td>
- ${liveInstance.name}
+ ${liveInstance.name} (${liveInstance.credentials.name})
<a id="li-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
</td>
<td>${str(liveInstance.total_size)}</td>
- <td>${str(liveInstance.state)}</td>
- <td>
+ <td id="${ liveInstance.id }-state">${str(liveInstance.state)}</td>
+ <td id="${ liveInstance.id }-launch_time">
##${str(liveInstance.launch_time)[:16]}
<%
#from datetime import datetime
@@ -120,9 +145,18 @@
%for j, instance in enumerate( liveInstance.instance ):
## TODO: Once more instances will be running under the same liveInstance, additional logic will need to be added to account for that
%if instance.state == "running":
- <a class="action-button" href="http://${instance.public_dns}" target="_blank">
- <span>Access Galaxy</span>
- <img src="${h.url_for('/static/images/silk/resultset_next.png')}" /></a></div>
+ ## Wait until Galaxy server starts to show 'Access Galaxy' button
+ <%
+ import urllib2
+ try:
+ urllib2.urlopen("http://"+instance.public_dns)
+ context.write( '<a class="action-button" href="http://'+instance.public_dns+'" target="_blank">' )
+ context.write( '<span>Access Galaxy</span>' )
+ context.write( '<img src="'+h.url_for('/static/images/silk/resultset_next.png')+'" /></a></div>' )
+ except urllib2.URLError:
+ context.write( '<span>Galaxy starting...</span>' )
+ %>
+
%endif
%endfor
</td>
@@ -164,17 +198,39 @@
%for i, prevInstance in enumerate( prevInstances ):
<tr>
<td>
- ${prevInstance.name}
+ ${prevInstance.name} (${prevInstance.credentials.name})
<a id="pi-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
</td>
<td>${str(prevInstance.total_size)}</td> <!-- TODO: Change to show vol size once available-->
- <td>${str(prevInstance.state)}</td>
+ <td>
+ <%state = str(prevInstance.state)%>
+ %if state =='error':
+ <div id="short">
+ <a onclick="document.getElementById('full').style.display = 'block';
+ document.getElementById('short').style.display = 'none'; return 0"
+ href="javascript:void(0)">
+ error
+ </a>
+ </div>
+ <div id="full" style="DISPLAY: none">
+ <a onclick="document.getElementById('short').style.display = 'block';
+ document.getElementById('full').style.display = 'none'; return 0;"
+ href="javascript:void(0)">
+ ${str(prevInstance.error)}
+ </a>
+ </div>
+ %else:
+ ${str(prevInstance.state)}
+ %endif
+ </td>
<td>N/A</td>
<td>
<div popupmenu="pi-${i}-popup">
- <a class="action-button" href="${h.url_for( action='start', id=trans.security.encode_id(prevInstance.id) )}">Start</a>
- <a class="action-button" href="${h.url_for( action='renameInstance', id=trans.security.encode_id(prevInstance.id) )}">Rename</a>
+ <a class="action-button" href="${h.url_for( action='start', id=trans.security.encode_id(prevInstance.id), type='m1.small' )}"> Start m1.small</a>
+ <a class="action-button" href="${h.url_for( action='start', id=trans.security.encode_id(prevInstance.id), type='c1.medium' )}"> Start c1.medium</a>
+ <a class="action-button" href="${h.url_for( action='renameInstance', id=trans.security.encode_id(prevInstance.id) )}">Rename</a>
<a class="action-button" href="${h.url_for( action='addStorage', id=trans.security.encode_id(prevInstance.id) )}" target="_parent">Add storage</a>
+ <a class="action-button" href="${h.url_for( action='usageReport' )}">Usage report</a>
<a class="action-button" confirm="Are you sure you want to delete instance '${prevInstance.name}'? This will delete all of your data assocaiated with this instance!" href="${h.url_for( action='deleteInstance', id=trans.security.encode_id(prevInstance.id) )}">Delete</a>
</div>
</td>
diff -r 7f3054622e87 -r 676fae1a35f9 templates/cloud/view.mako
--- a/templates/cloud/view.mako Wed Oct 21 11:40:51 2009 -0400
+++ b/templates/cloud/view.mako Fri Oct 23 17:41:35 2009 -0400
@@ -38,8 +38,12 @@
</td>
</tr>
<tr>
- <td> Cloud provider: </td>
- <td> ${str(credDetails.provider_name)[:16]}</td>
+ <td> Cloud provider type: </td>
+ <td> ${str(credDetails.provider.type)[:16]}</td>
+ </tr>
+ <tr>
+ <td> Cloud provider name: </td>
+ <td> ${str(credDetails.provider.name)[:16]}</td>
</tr>
<tr>
<td> Access key: </td>
@@ -67,6 +71,79 @@
</div>
</td>
</tr>
+ <tr><td><b>Additional cloud provider information (if available):</b></td></tr>
+ %if credDetails.provider.region_connection != None:
+ <tr>
+ <td> Region connection: </td>
+ <td> ${credDetails.provider.region_connection} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.region_name != None:
+ <tr>
+ <td> Region name: </td>
+ <td> ${credDetails.provider.region_name} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.region_endpoint != None:
+ <tr>
+ <td> Region endpoint: </td>
+ <td> ${credDetails.provider.region_endpoint} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.is_secure != None:
+ <tr>
+ <td> Is secure: </td>
+ <td> ${credDetails.provider.is_secure} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.host != None:
+ <tr>
+ <td> Host: </td>
+ <td> ${credDetails.provider.host} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.port != None:
+ <tr>
+ <td> Port: </td>
+ <td> ${credDetails.provider.port} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.proxy != None:
+ <tr>
+ <td> Proxy: </td>
+ <td> ${credDetails.provider.proxy} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.proxy_port != None:
+ <tr>
+ <td> Proxy port: </td>
+ <td> ${credDetails.provider.proxy_port} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.proxy_pass != None:
+ <tr>
+ <td> Proxy pass: </td>
+ <td> ${credDetails.provider.proxy_pass} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.debug != None:
+ <tr>
+ <td> Debug: </td>
+ <td> ${credDetails.provider.debug} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.https_connection_factory != None:
+ <tr>
+ <td> HTTPS connection factory: </td>
+ <td> ${credDetails.provider.https_connection_factory} </td>
+ </tr>
+ %endif
+ %if credDetails.provider.path != None:
+ <tr>
+ <td> Path: </td>
+ <td> ${credDetails.provider.path} </td>
+ </tr>
+ %endif
</table>
%else:
There are no credentials under that name.
diff -r 7f3054622e87 -r 676fae1a35f9 templates/cloud/view_usage.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/cloud/view_usage.mako Fri Oct 23 17:41:35 2009 -0400
@@ -0,0 +1,47 @@
+<%inherit file="/base.mako"/>
+
+<%def name="title()">Cloud home</%def>
+
+%if message:
+<%
+ try:
+ messagetype
+ except:
+ messagetype = "done"
+%>
+
+
+
+<p />
+<div class="${messagetype}message">
+ ${message}
+</div>
+%endif
+
+<h2>Instance usage report</h2>
+
+%if prevInstances:
+ <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <colgroup width="2%"></colgroup>
+ <colgroup width="20%"></colgroup>
+ <colgroup width="20%"></colgroup>
+ <colgroup width="5%"></colgroup>
+ <tr class="header">
+ <th>#</th>
+ <th>Launch time</th>
+ <th>Termination time</th>
+ <th>Type</th>
+ <th></th>
+ </tr>
+ %for i, prevInstance in enumerate( prevInstances ):
+ <tr>
+ <td>${i+1}</td>
+ <td>${str(prevInstance.launch_time)[:16]} UCT</td>
+ <td>${str(prevInstance.stop_time)[:16]} UCT</td>
+ <td>${prevInstance.type}</td>
+ </tr>
+ %endfor
+ </table>
+%else:
+ Selected instance has no record of being used.
+%endif
\ No newline at end of file
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/6aab50510e43
changeset: 3068:6aab50510e43
user: Enis Afgan <afgane(a)gmail.com>
date: Wed Oct 14 19:20:11 2009 -0400
description:
Decoupled cloud controller from cloud providers. Added cloud manager that runs as a daemon and pools database for change of ststus (i.e., user action). Implemented code to support 'eucalyptus' provider (tested with Eucalyptus Public Cloud).
diffstat:
lib/galaxy/cloud/__init__.py | 367 ++++++++++++++++-
lib/galaxy/cloud/providers/eucalyptus.py | 476 +++++++++++++++++-----
lib/galaxy/model/mapping.py | 2 +-
lib/galaxy/model/migrate/versions/0014_cloud_tables.py | 8 +-
lib/galaxy/web/controllers/cloud.py | 137 ++++--
templates/cloud/add_credentials.mako | 97 ++++
6 files changed, 895 insertions(+), 192 deletions(-)
diffs (1401 lines):
diff -r 7c438fd3cf4a -r 6aab50510e43 lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Fri Oct 02 18:31:32 2009 -0400
+++ b/lib/galaxy/cloud/__init__.py Wed Oct 14 19:20:11 2009 -0400
@@ -7,6 +7,7 @@
from galaxy.datatypes.interval import *
from galaxy.datatypes import metadata
from galaxy.util.bunch import Bunch
+from sqlalchemy import or_
import pkg_resources
pkg_resources.require( "PasteDeploy" )
@@ -147,13 +148,18 @@
# HACK: Delay until after forking, we need a way to do post fork notification!!!
time.sleep( 10 )
+ cnt = 0 # Run global update only periodically so keep counter variable
while self.running:
try:
# log.debug( "Calling monitor_step" )
self.__monitor_step()
+ if cnt%30 == 0: # Run global update every 30 seconds
+ self.provider.update()
+ cnt = 0
except:
log.exception( "Exception in cloud manager monitor_step" )
# Sleep
+ cnt += 1
self.sleeper.sleep( 2 )
def __monitor_step( self ):
@@ -167,31 +173,53 @@
it is marked as having errors and removed from the queue. Otherwise,
the job is dispatched.
"""
- # Get an orm session
+ # Get an orm (object relational mapping) session
session = mapping.Session()
# Pull all new jobs from the queue at once
- new_jobs = []
- new_instances = []
- new_UCIs = []
- stop_UCIs = []
+ new_requests = []
+# new_instances = []
+# new_UCIs = []
+# stop_UCIs = []
+# delete_UCIs = []
# for r in session.query( model.cloud_instance ).filter( model.cloud_instance.s.state == model.cloud_instance.states.NEW ).all():
# new_instances
- for r in session.query( model.UCI ).filter( model.UCI.c.state == "new" ).all():
- new_UCIs.append( r )
- for r in new_UCIs:
- self.provider.createUCI( r )
+ for r in session.query( model.UCI ) \
+ .filter( or_( model.UCI.c.state=="newUCI",
+ model.UCI.c.state=="submittedUCI",
+ model.UCI.c.state=="shutting-downUCI",
+ model.UCI.c.state=="deletingUCI" ) ) \
+ .all():
+ uci = UCIwrapper( r )
+ new_requests.append( uci )
+# log.debug( 'new_requests: %s' % new_requests )
+ for uci in new_requests:
+ session.clear()
+# log.debug( 'r.name: %s, state: %s' % ( r.name, r.state ) )
+# session.save_or_update( r )
+# session.flush()
+ self.provider.put( uci )
- for r in session.query( model.UCI ).filter( model.UCI.c.state == "submitted" ).all():
- new_instances.append( r )
- for r in new_instances:
- self.provider.startUCI( r )
+ # Done with the session
+ mapping.Session.remove()
- for r in session.query( model.UCI ).filter( model.UCI.c.state == "terminating" ).all():
- stop_UCIs.append( r )
- for r in stop_UCIs:
- self.provider.stopUCI( r )
+# for r in session.query( model.UCI ).filter( model.UCI.c.state == "submitted" ).all():
+# new_instances.append( r )
+# for r in new_instances:
+# self.provider.startUCI( r )
+#
+# for r in session.query( model.UCI ).filter( model.UCI.c.state == "shutting-down" ).all():
+# stop_UCIs.append( r )
+# for r in stop_UCIs:
+# self.provider.stopUCI( r )
+#
+# for r in session.query( model.UCI ).filter( model.UCI.c.state == "deleting" ).all():
+# delete_UCIs.append( r )
+# for r in delete_UCIs:
+# self.provider.deleteUCI( r )
+
+
# if self.track_jobs_in_database:
# for j in session.query( model.Job ).options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ).filter( model.Job.c.state == model.Job.states.NEW ).all():
@@ -327,6 +355,281 @@
log.info( "cloud manager stopped" )
self.dispatcher.shutdown()
+class UCIwrapper( object ):
+ """
+ Wraps 'model.UCI' with convenience methods for state management
+ """
+ def __init__( self, uci ):
+ self.uci_id = uci.id
+
+ # --------- Setter methods -----------------
+
+ def change_state( self, uci_state=None, instance_id=None, i_state=None ):
+ """
+ Sets state for UCI and/or UCI's instance with instance_id as provided by cloud provider and stored in local
+ Galaxy database.
+ Need to provide either state for the UCI or instance_id and it's state or all arguments.
+ """
+# log.debug( "Changing state - new uci_state: %s, instance_id: %s, i_state: %s" % ( uci_state, instance_id, i_state ) )
+ if uci_state is not None:
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.state = uci_state
+ uci.flush()
+ if ( instance_id is not None ) and ( i_state is not None ):
+ instance = model.CloudInstance.filter_by( uci_id=self.uci_id, instance_id=instance_id).first()
+ instance.state = i_state
+ instance.flush()
+
+ def set_mi( self, i_index, mi_id ):
+ """
+ Sets Machine Image (MI), e.g., 'ami-66fa190f', for UCI's instance with given index as it
+ is stored in local Galaxy database.
+ """
+ mi = model.CloudImage.filter( model.CloudImage.c.image_id==mi_id ).first()
+ instance = model.CloudInstance.get( i_index )
+ instance.image = mi
+ instance.flush()
+
+ def set_key_pair( self, i_index, key_name, key_material=None ):
+ """
+ Single UCI may instantiate many instances, i_index refers to the numeric index
+ of instance controlled by this UCI as it is stored in local DB (see get_instances_ids()).
+ """
+ instance = model.CloudInstance.get( i_index )
+ instance.keypair_name = key_name
+ if key_material is not None:
+ instance.keypair_material = key_material
+ instance.flush()
+
+ def set_launch_time( self, launch_time, i_index=None, i_id=None ):
+ """
+ Stores launch time in local database for instance with specified index (as it is stored in local
+ Galaxy database) or with specified instance ID (as obtained from the cloud provider AND stored
+ in local Galaxy Database). Only one of i_index or i_id needs to be provided.
+ """
+ if i_index != None:
+ instance = model.CloudInstance.get( i_index )
+ instance.launch_time = launch_time
+ instance.flush()
+ elif i_id != None:
+ instance = model.CloudInstance.filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ instance.launch_time = launch_time
+ instance.flush()
+
+ def set_uci_launch_time( self, launch_time ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.launch_time = launch_time
+ uci.flush()
+
+ def set_stop_time( self, stop_time, i_index=None, i_id=None ):
+ if i_index != None:
+ instance = model.CloudInstance.get( i_index )
+ instance.stop_time = stop_time
+ instance.flush()
+ elif i_id != None:
+ instance = model.CloudInstance.filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ instance.stop_time = stop_time
+ instance.flush()
+
+ def reset_uci_launch_time( self ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.launch_time = None
+ uci.flush()
+
+ def set_reservation_id( self, i_index, reservation_id ):
+ instance = model.CloudInstance.get( i_index )
+ instance.reservation_id = reservation_id
+ instance.flush()
+
+ def set_instance_id( self, i_index, instance_id ):
+ """
+ i_index refers to UCI's instance ID as stored in local database
+ instance_id refers to real-world, cloud resource ID (e.g., 'i-78hd823a')
+ """
+ instance = model.CloudInstance.get( i_index )
+ instance.instance_id = instance_id
+ instance.flush()
+
+ def set_public_dns( self, instance_id, public_dns ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.instance[instance_id].public_dns = public_dns
+ uci.instance[instance_id].flush()
+
+ def set_private_dns( self, instance_id, private_dns ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.instance[instance_id].private_dns = private_dns
+ uci.instance[instance_id].flush()
+
+ def set_store_device( self, store_id, device ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.store[store_id].device = device
+ uci.store[store_id].flush()
+
+ def set_store_status( self, store_id, status ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.store[store_id].status = status
+ uci.store[store_id].flush()
+
+ def set_store_availability_zone( self, store_id, availability_zone ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.store[store_id].availability_zone = availability_zone
+ uci.store[store_id].flush()
+
+ def set_store_volume_id( self, store_id, volume_id ):
+ """
+ Given store ID associated with this UCI, set volume ID as it is registered
+ on the cloud provider (e.g., vol-39890501)
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.store[store_id].volume_id = volume_id
+ uci.store[store_id].flush()
+
+ def set_store_instance( self, store_id, instance_id ):
+ """ Stores instance ID that given store volume is attached to. """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.store[store_id].i_id = instance_id
+ uci.store[store_id].flush()
+
+ # --------- Getter methods -----------------
+
+ def get_instances_indexes( self, state=None ):
+ """
+ Returns indexes of instances associated with given UCI as they are stored in local Galaxy database and
+ whose state corresponds to passed argument. Returned values enable indexing instances from local Galaxy database.
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ instances = model.CloudInstance.filter_by( uci=uci ).filter( model.CloudInstance.c.state==state ).all()
+ il = []
+ for i in instances:
+ il.append( i.id )
+
+ return il
+
+ def get_type( self, i_index ):
+ instance = model.CloudInstance.get( i_index )
+ return instance.type
+
+ def get_state( self ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.state
+
+ def get_instance_state( self, instance_id ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.instance[instance_id].state
+
+ def get_instances_ids( self ):
+ """
+ Returns list IDs of all instances' associated with this UCI that are not in 'terminated' state
+ (e.g., ['i-402906D2', 'i-q0290dsD2'] ).
+ """
+ il = model.CloudInstance.filter_by( uci_id=self.uci_id ).filter( model.CloudInstance.c.state != 'terminated' ).all()
+ instanceList = []
+ for i in il:
+ instanceList.append( i.instance_id )
+ return instanceList
+
+ def get_name( self ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.name
+
+ def get_key_pair_name( self, i_index=None, i_id=None ):
+ """
+ Given EITHER instance index as it is stored in local Galaxy database OR instance ID as it is
+ obtained from cloud provider and stored in local Galaxy database, return keypair name assocaited
+ with given instance.
+ """
+ if i_index != None:
+ instance = model.CloudInstance.get( i_index )
+ return instance.keypair_name
+ elif i_id != None:
+ instance = model.CloudInstance.filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ return instance.keypair_name
+
+ def get_access_key( self ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.credentials.access_key
+
+ def get_secret_key( self ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.credentials.secret_key
+
+ def get_mi_id( self, instance_id=0 ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.instance[instance_id].mi_id
+
+ def get_public_dns( self, instance_id=0 ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.instance[instance_id].public_dns
+
+ def get_private_dns( self, instance_id=0 ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.instance[instance_id].private_dns
+
+ def get_store_availability_zone( self, store_id ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.store[store_id].availability_zone
+
+ def get_store_size( self, store_id ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.store[store_id].size
+
+ def get_store_volume_id( self, store_id ):
+ """
+ Given store ID associated with this UCI, get volume ID as it is registered
+ on the cloud provider (e.g., 'vol-39890501')
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.store[store_id].volume_id
+
+ def get_all_stores( self ):
+ """ Returns all storage volumes' database objects associated with this UCI. """
+ return model.CloudStore.filter( model.CloudStore.c.uci_id == self.uci_id ).all()
+# svs = model.CloudStore.filter( model.CloudStore.c.uci_id == self.uci_id ).all()
+# svl = [] # storage volume list
+# for sv in svs:
+# svl.append( sv.volume_id )
+# return svl
+
+ def get_uci( self ):
+ """ Returns database object for given UCI. """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci
+
+ def uci_launch_time_set( self ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.launch_time
+
+ def delete( self ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+# uci.delete()
+ uci.state = 'deleted' # for bookkeeping reasons, mark as deleted but don't actually delete.
+ uci.flush()
+
class JobWrapper( object ):
"""
Wraps a 'model.Job' with convience methods for running processes and
@@ -675,6 +978,11 @@
else:
log.error( "Unable to start unknown cloud provider: %s" %self.provider_name )
+ def put( self, uci_wrapper ):
+ """ Put given request for UCI manipulation into provider's request queue."""
+# log.debug( "Adding UCI '%s' manipulation request into cloud manager's queue." % uci_wrapper.name )
+ self.cloud_provider[self.provider_name].put( uci_wrapper )
+
def createUCI( self, uci ):
"""
Createse User Configured Instance (UCI). Essentially, creates storage volume.
@@ -682,13 +990,15 @@
log.debug( "Creating UCI '%s'" % uci.name )
self.cloud_provider[self.provider_name].createUCI( uci )
- def deleteUCI( self, uciName ):
+ def deleteUCI( self, uci ):
"""
Deletes UCI. NOTE that this implies deletion of any and all data associated
with this UCI from the cloud. All data will be deleted.
"""
+ log.debug( "Deleting UCI '%s'" % uci.name )
+ self.cloud_provider[self.provider_name].deleteUCI( uci )
- def addStorageToUCI( self, uciName ):
+ def addStorageToUCI( self, uci ):
""" Adds more storage to specified UCI """
def startUCI( self, uci ):
@@ -706,17 +1016,16 @@
"""
log.debug( "Stopping UCI '%s'" % uci.name )
self.cloud_provider[self.provider_name].stopUCI( uci )
+
+ def update( self ):
+ """
+ Runs a global status update on all storage volumes and all instances whose UCI is
+ 'running' state.
+ Reason behind this method is to sync state of local DB and real world resources
+ """
+# log.debug( "Running global update" )
+ self.cloud_provider[self.provider_name].update()
- def put( self, job_wrapper ):
- runner_name = ( job_wrapper.tool.job_runner.split(":", 1) )[0]
- log.debug( "dispatching job %d to %s runner" %( job_wrapper.job_id, runner_name ) )
- self.cloud_provider[runner_name].put( job_wrapper )
-
- def stop( self, job ):
- runner_name = ( job.job_runner_name.split(":", 1) )[0]
- log.debug( "stopping job %d in %s runner" %( job.id, runner_name ) )
- self.cloud_provider[runner_name].stop_job( job )
-
def recover( self, job, job_wrapper ):
runner_name = ( job.job_runner_name.split(":", 1) )[0]
log.debug( "recovering job %d in %s runner" %( job.id, runner_name ) )
diff -r 7c438fd3cf4a -r 6aab50510e43 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Fri Oct 02 18:31:32 2009 -0400
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Wed Oct 14 19:20:11 2009 -0400
@@ -3,7 +3,10 @@
from datetime import datetime
from galaxy import model # Database interaction class
+from galaxy.model import mapping
from galaxy.datatypes.data import nice_size
+from Queue import Queue
+from sqlalchemy import or_
import galaxy.eggs
galaxy.eggs.require("boto")
@@ -22,56 +25,86 @@
log.debug( "Using eucalyptus as default cloud provider." )
self.zone = "epc"
self.key_pair = "galaxy-keypair"
+ self.queue = Queue()
#TODO: Use multiple threads to process requests?
+ self.threads = []
+ nworkers = 5
+ log.info( "Starting eucalyptus cloud controller workers" )
+ for i in range( nworkers ):
+ worker = threading.Thread( target=self.run_next )
+ worker.start()
+ self.threads.append( worker )
+ log.debug( "%d cloud workers ready", nworkers )
-
- def get_connection( self, uci ):
+ def run_next( self ):
+ """Run the next job, waiting until one is available if necessary"""
+ cnt = 0
+ while 1:
+
+ uci_wrapper = self.queue.get()
+# uci = uci_wrapper.get_uci()
+ log.debug( '[%d] uci name: %s' % ( cnt, uci_wrapper.get_name() ) )
+ uci_state = uci_wrapper.get_state()
+ if uci_state is self.STOP_SIGNAL:
+ return
+ try:
+ if uci_state=="new":
+ log.debug( "Calling create UCI" )
+ self.createUCI( uci_wrapper )
+ elif uci_state=="deleting":
+ self.deleteUCI( uci_wrapper )
+ elif uci_state=="submitted":
+ log.debug( "Calling start UCI" )
+ self.startUCI( uci_wrapper )
+ elif uci_state=="shutting-down":
+ self.stopUCI( uci_wrapper )
+ except:
+ log.exception( "Uncaught exception executing request." )
+ cnt += 1
+
+ def get_connection( self, uci_wrapper ):
"""
Establishes EC2 connection using user's default credentials
"""
log.debug( '##### Establishing cloud connection' )
-# creds = model.CloudUserCredentials.filter_by( user=user, defaultCred=True ).first()
- a_key = uci.credentials.access_key
- s_key = uci.credentials.secret_key
# Amazon EC2
- #conn = EC2Connection( a_key, s_key )
+ #conn = EC2Connection( uci_wrapper.get_access_key(), uci_wrapper.get_secret_key() )
+
# Eucalyptus Public Cloud
- # TODO: Add option in Galaxy config file to specify these values (i.e., for locally manages Eucalyptus deployments)
+ # TODO: Add option in Galaxy config file to specify these values (i.e., for locally managed Eucalyptus deployments)
euca_region = RegionInfo( None, "eucalyptus", "mayhem9.cs.ucsb.edu" )
- conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key, is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
+ conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(), aws_secret_access_key=uci_wrapper.get_secret_key(), is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
return conn
- def get_keypair_name( self, uci, conn ):
+ def set_keypair( self, uci_wrapper, conn ):
"""
Generate keypair using user's default credentials
"""
log.debug( "Getting user's keypair" )
kp = conn.get_key_pair( self.key_pair )
+ instances = uci_wrapper.get_instances_indexes()
try:
- for i, inst in enumerate( uci.instance ):
- uci.instance[i].keypair_name = kp.name
+ for inst in instances:
+ log.debug("inst: '%s'" % inst )
+ uci_wrapper.set_key_pair( inst, kp.name )
return kp.name
except AttributeError: # No keypair under this name exists so create it
- log.debug( "No keypair found, creating keypair '%s'" % self.key_pair )
+ log.info( "No keypair found, creating keypair '%s'" % self.key_pair )
kp = conn.create_key_pair( self.key_pair )
- for i, inst in enumerate( uci.instance ):
- uci.instance[i].keypair_name = kp.name
- uci.instance[i].keypair_material = kp.material
- uci.flush()
- # TODO: Store key_pair.material into instance table - this is the only time private key can be retrieved
- # Actually, probably return key_pair to calling method and store name & key from there...
-
+ for inst in instances:
+ uci_wrapper.set_key_pair( inst, kp.name, kp.material )
+
return kp.name
- def get_mi( self, type='small' ):
+ def get_mi_id( self, type ):
"""
Get appropriate machine image (mi) based on instance size.
TODO: Dummy method - need to implement logic
For valid sizes, see http://aws.amazon.com/ec2/instance-types/
"""
- return model.CloudImage.filter( model.CloudImage.table.c.id==1 ).first()
+ return model.CloudImage.filter( model.CloudImage.table.c.id==1 ).first().image_id
# def get_instances( self, uci ):
# """
@@ -91,25 +124,31 @@
def shutdown( self ):
"""Attempts to gracefully shut down the monitor thread"""
log.info( "sending stop signal to worker threads in eucalyptus cloud manager" )
- self.queue.put( self.STOP_SIGNAL )
+ for i in range( len( self.threads ) ):
+ self.queue.put( self.STOP_SIGNAL )
log.info( "eucalyptus cloud manager stopped" )
+
+ def put( self, uci_wrapper ):
+ # Get rid of UCI from state description
+ state = uci_wrapper.get_state()
+ uci_wrapper.change_state( state.split('U')[0] ) # remove 'UCI' from end of state description (i.e., mark as accepted and ready for processing)
+ self.queue.put( uci_wrapper )
- def createUCI( self, uci ):
+ def createUCI( self, uci_wrapper ):
"""
Creates User Configured Instance (UCI). Essentially, creates storage volume on cloud provider
and registers relevant information in Galaxy database.
"""
- conn = self.get_connection( uci )
+ conn = self.get_connection( uci_wrapper )
# Temporary code - need to ensure user selects zone at UCI creation time!
- if uci.store[0].availability_zone=='':
+ if uci_wrapper.get_store_availability_zone( 0 )=='':
log.info( "Availability zone for storage volume was not selected, using default zone: %s" % self.zone )
- uci.store[0].availability_zone = self.zone
- uci.store[0].flush()
+ uci_wrapper.set_store_availability_zone( 0, self.zone )
#TODO: check if volume associated with UCI already exists (if server crashed for example) and don't recreate it
- log.debug( "Creating volume in zone '%s'..." % uci.store[0].availability_zone )
- vol = conn.create_volume( uci.store[0].size, uci.store[0].availability_zone, snapshot=None )
- uci.store[0].volume_id = vol.id
+ log.info( "Creating volume in zone '%s'..." % uci_wrapper.get_store_availability_zone( 0 ) )
+ vol = conn.create_volume( uci_wrapper.get_store_size( 0 ), uci_wrapper.get_store_availability_zone( 0 ), snapshot=None )
+ uci_wrapper.set_store_volume_id( 0, vol.id )
# Wait for a while to ensure volume was created
# vol_status = vol.status
@@ -125,111 +164,180 @@
# uci.flush()
# return
- uci.state = 'available'
- uci.store[0].status = vol.status
- uci.store[0].flush()
- uci.flush()
+ # EPC does not allow creation of storage volumes (it deletes one as soon as it is created, so manually set uci_state here)
+ uci_wrapper.change_state( uci_state='available' )
+ uci_wrapper.set_store_status( 0, vol.status )
- def deleteUCI( self, name ):
+ def deleteUCI( self, uci_wrapper ):
"""
Deletes UCI. NOTE that this implies deletion of any and all data associated
with this UCI from the cloud. All data will be deleted.
"""
-
+ conn = self.get_connection( uci_wrapper )
+ vl = [] # volume list
+ count = 0 # counter for checking if all volumes assoc. w/ UCI were deleted
+
+ # Get all volumes assoc. w/ UCI, delete them from cloud as well as in local DB
+ vl = uci_wrapper.get_all_stores()
+ deletedList = []
+ failedList = []
+ for v in vl:
+ log.debug( "Deleting volume with id='%s'" % v.volume_id )
+ if conn.delete_volume( v.volume_id ):
+ deletedList.append( v.volume_id )
+ v.delete()
+ v.flush()
+ count += 1
+ else:
+ failedList.append( v.volume_id )
+
+ # Delete UCI if all of associated
+ log.debug( "count=%s, len(vl)=%s" % (count, len( vl ) ) )
+ if count == len( vl ):
+ uci_wrapper.delete()
+ else:
+ log.error( "Deleting following volume(s) failed: %s. However, these volumes were successfully deleted: %s. \
+ MANUAL intervention and processing needed." % ( failedList, deletedList ) )
+ uci_wrapper.change_state( uci_state="error" )
+
def addStorageToUCI( self, name ):
""" Adds more storage to specified UCI """
+
+ def dummyStartUCI( self, uci_wrapper ):
- def startUCI( self, uci ):
+ uci = uci_wrapper.get_uci()
+ log.debug( "Would be starting instance '%s'" % uci.name )
+ uci_wrapper.change_state( 'pending' )
+# log.debug( "Sleeping a bit... (%s)" % uci.name )
+# time.sleep(20)
+# log.debug( "Woke up! (%s)" % uci.name )
+
+ def startUCI( self, uci_wrapper ):
"""
- Starts an instance of named UCI on the cloud. This implies, mounting of
- storage and starting Galaxy instance.
+ Starts an instance of named UCI on the cloud.
"""
- conn = self.get_connection( uci )
+ conn = self.get_connection( uci_wrapper )
+#
+ self.set_keypair( uci_wrapper, conn )
- uci.instance[0].keypair_name = self.get_keypair_name( uci, conn )
- mi = self.get_mi( uci.instance[0].type )
+ i_indexes = uci_wrapper.get_instances_indexes() # Get indexes of i_indexes associated with this UCI
-# log.debug( "mi: %s, mi.image_id: %s, uci.instance[0].keypair_name: %s" % ( mi, mi.image_id, uci.instance[0].keypair_name ) )
- uci.instance[0].image = mi
+ for i_index in i_indexes:
+ mi_id = self.get_mi_id( uci_wrapper.get_type( i_index ) )
+ log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name( i_index ): %s" % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
+ uci_wrapper.set_mi( i_index, mi_id )
+
+ # log.debug( '***** Setting up security group' )
+ # If not existent, setup galaxy security group
+ # try:
+ # gSecurityGroup = conn.create_security_group('galaxy', 'Security group for Galaxy.')
+ # gSecurityGroup.authorize( 'tcp', 80, 80, '0.0.0.0/0' ) # Open HTTP port
+ # gSecurityGroup.authorize( 'tcp', 22, 22, '0.0.0.0/0' ) # Open SSH port
+ # except:
+ # pass
+ # sgs = conn.get_all_security_groups()
+ # for i in range( len( sgs ) ):
+ # if sgs[i].name == "galaxy":
+ # sg.append( sgs[i] )
+ # break # only 1 security group w/ this name can exist, so continue
+
+ log.debug( "***** Starting UCI instance '%s'" % uci_wrapper.get_name() )
+ log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( mi_id, uci_wrapper.get_key_pair_name( i_index ) ) )
+ reservation = conn.run_instances( image_id=mi_id, key_name=uci_wrapper.get_key_pair_name( i_index ) )
+ #reservation = conn.run_instances( image_id=instance.image, key_name=instance.keypair_name, security_groups=['galaxy'], instance_type=instance.type, placement=instance.availability_zone )
+ l_time = datetime.utcnow()
+ uci_wrapper.set_launch_time( l_time, i_index=i_index ) # format_time( reservation.i_indexes[0].launch_time ) )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( l_time )
+ uci_wrapper.set_reservation_id( i_index, str( reservation ).split(":")[1] )
+ i_id = str( reservation.instances[0]).split(":")[1]
+ uci_wrapper.set_instance_id( i_index, i_id )
+ s = reservation.instances[0].state # TODO: once more than a single instance will be started through single reservation, change this
+ uci_wrapper.change_state( s, i_id, s )
+ log.debug( "UCI '%s' started, current state: %s" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
-# log.debug( '***** Setting up security group' )
- # If not existent, setup galaxy security group
-# try:
-# gSecurityGroup = conn.create_security_group('galaxy', 'Security group for Galaxy.')
-# gSecurityGroup.authorize( 'tcp', 80, 80, '0.0.0.0/0' ) # Open HTTP port
-# gSecurityGroup.authorize( 'tcp', 22, 22, '0.0.0.0/0' ) # Open SSH port
-# except:
-# pass
-# sgs = conn.get_all_security_groups()
-# for i in range( len( sgs ) ):
-# if sgs[i].name == "galaxy":
-# sg.append( sgs[i] )
-# break # only 1 security group w/ this name can exist, so continue
-
- log.debug( "***** Starting UCI instance '%s'" % uci.name )
-# log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( uci.instance[0].image.image_id, uci.instance[0].keypair_name ) )
- reservation = conn.run_instances( image_id=uci.instance[0].image.image_id, key_name=uci.instance[0].keypair_name )
- #reservation = conn.run_instances( image_id=instance.image, key_name=instance.keypair_name, security_groups=['galaxy'], instance_type=instance.type, placement=instance.availability_zone )
- uci.instance[0].launch_time = datetime.utcnow()
- uci.launch_time = uci.instance[0].launch_time
- uci.instance[0].reservation_id = str( reservation ).split(":")[1]
- uci.instance[0].instance_id = str( reservation.instances[0]).split(":")[1]
- s = reservation.instances[0].state
- uci.instance[0].state = s
- uci.state = s
- uci.instance[0].flush()
- uci.flush()
- # Wait until instance gets running and then update the DB
- while s!="running":
- log.debug( "Waiting on instance '%s' to start up (reservation ID: %s); current state: %s" % ( uci.instance[0].instance_id, uci.instance[0].reservation_id, s ) )
- time.sleep( 15 )
- s = reservation.instances[0].update()
-
- uci.instance[0].state = s
- uci.state = s
- uci.instance[0].public_dns = reservation.instances[0].dns_name
- uci.instance[0].private_dns = reservation.instances[0].private_dns_name
- uci.instance[0].flush()
- uci.flush()
+# # Wait until instance gets running and then update the DB
+# while s!="running":
+# log.debug( "Waiting on instance '%s' to start up (reservation ID: %s); current state: %s" % ( uci.instance[0].instance_id, uci.instance[0].reservation_id, s ) )
+# time.sleep( 15 )
+# s = reservation.i_indexes[0].update()
+#
+# # Update instance data in local DB
+# uci.instance[0].state = s
+# uci.instance[0].public_dns = reservation.i_indexes[0].dns_name
+# uci.instance[0].private_dns = reservation.i_indexes[0].private_dns_name
+# uci.instance[0].flush()
+# # Update storage data in local DB w/ volume state info. NOTE that this only captures current volume state
+# # and does not connect or wait on connection between instance and volume to be established
+# vl = model.CloudStore.filter( model.CloudStore.c.uci_id == uci.id ).all()
+# vols = []
+# for v in vl:
+# vols.append( v.volume_id )
+# try:
+# volumes = conn.get_all_volumes( vols )
+# for i, v in enumerate( volumes ):
+# uci.store[i].i_id = v.instance_id
+# uci.store[i].status = v.status
+# uci.store[i].device = v.device
+# uci.store[i].flush()
+# except BotoServerError:
+# log.debug( "Error getting volume(s) attached to instance. Volume status was not updated." )
+#
+# uci.state = s
+# uci.flush()
- def stopUCI( self, uci ):
+
+ def stopUCI( self, uci_wrapper):
"""
Stops all of cloud instances associated with named UCI.
"""
- conn = self.get_connection( uci )
- tl = [] # temination list
+ conn = self.get_connection( uci_wrapper )
- for i, inst in enumerate( uci.instance ):
- tl.append( uci.instance[i].instance_id )
+ # Get all instances associated with given UCI
+ il = uci_wrapper.get_instances_ids() # instance list
+# log.debug( 'List of instances being terminated: %s' % il )
+ rl = conn.get_all_instances( il ) # Reservation list associated with given instances
- instList = conn.get_all_instances( tl )
-# log.debug( 'instList: %s' % instList )
-
- for i, inst in enumerate( instList ):
-# log.debug( 'inst: %s' % inst )
- log.debug( 'Before stop - inst.instances[0].update(): %s' % inst.instances[0].update() )
- inst.instances[0].stop()
- log.debug( 'After stop - inst.instances[0].update(): %s' % inst.instances[0].update() )
- uci.instance[i].stop_time = datetime.utcnow()
+# tState = conn.terminate_instances( il )
+# # TODO: Need to update instance stop time (for all individual instances)
+# stop_time = datetime.utcnow()
+# uci_wrapper.set_stop_time( stop_time )
- terminated=0
- while terminated!=len( instList ):
- for i, inst in enumerate( instList ):
- log.debug( "inst state: %s" % inst.instances[0].state )
- state = inst.instances[0].update()
- if state=='terminated':
- uci.instance[i].state = state
- uci.instance[i].flush()
- terminated += 1
- time.sleep ( 5 )
-
- uci.state = 'available'
- uci.launch_time = None
- uci.flush()
-
- log.debug( "All instances for UCI '%s' were terminated." % uci.name )
+ # Initiate shutdown of all instances under given UCI
+ cnt = 0
+ stopped = []
+ notStopped = []
+ for r in rl:
+ for inst in r.instances:
+ log.debug( "Sending stop signal to instance '%s' associated with reservation '%s'." % ( inst, r ) )
+ inst.stop()
+ uci_wrapper.set_stop_time( datetime.utcnow(), i_id=inst.id )
+ uci_wrapper.change_state( instance_id=inst.id, i_state=inst.update() )
+ stopped.append( inst )
+
+# uci_wrapper.change_state( uci_state='available' )
+ uci_wrapper.reset_uci_launch_time()
+
+# # Wait for all instances to actually terminate and update local DB
+# terminated=0
+# while terminated!=len( rl ):
+# for i, r in enumerate( rl ):
+# log.debug( "r state: %s" % r.instances[0].state )
+# state = r.instances[0].update()
+# if state=='terminated':
+# uci.instance[i].state = state
+# uci.instance[i].flush()
+# terminated += 1
+# time.sleep ( 5 )
+#
+# # Reset UCI state
+# uci.state = 'available'
+# uci.launch_time = None
+# uci.flush()
+#
+ log.debug( "All instances for UCI '%s' were terminated." % uci_wrapper.get_name() )
@@ -277,4 +385,144 @@
# session.flush()
# trans.log_event( "User stopped cloud instance '%s'" % uci.name )
# trans.set_message( "Galaxy instance '%s' stopped." % uci.name )
-
\ No newline at end of file
+
+ def update( self ):
+ """
+ Runs a global status update on all storage volumes and all instances whose UCI is in
+ 'running', 'pending', or 'shutting-down' state.
+ Reason behind this method is to sync state of local DB and real-world resources
+ """
+ log.debug( "Running general status update for EPC UCIs." )
+ instances = model.CloudInstance.filter( or_( model.CloudInstance.c.state=="running", model.CloudInstance.c.state=="pending", model.CloudInstance.c.state=="shutting-down" ) ).all()
+ for inst in instances:
+ log.debug( "Running general status update on instance '%s'" % inst.instance_id )
+ self.updateInstance( inst )
+
+ stores = model.CloudStore.filter( or_( model.CloudStore.c.status=="in-use", model.CloudStore.c.status=="creating" ) ).all()
+ for store in stores:
+ log.debug( "Running general status update on store '%s'" % store.volume_id )
+ self.updateStore( store )
+
+ def updateInstance( self, inst ):
+
+ # Get credentials associated wit this instance
+ uci_id = inst.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ a_key = uci.credentials.access_key
+ s_key = uci.credentials.secret_key
+ # Get connection
+ euca_region = RegionInfo( None, "eucalyptus", "mayhem9.cs.ucsb.edu" )
+ conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key, is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
+ # Get reservations handle for given instance
+ rl= conn.get_all_instances( [inst.instance_id] )
+ # Because EPC deletes references to reservations after a short while after instances have terminated, getting an empty list as a response to a query
+ # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. As a result, below code simply
+ # marks given instance as having terminated. Note that an instance might have also crashed and this code will not catch the difference...
+ if len( rl ) == 0:
+ log.info( "Instance ID '%s' was not found by the cloud provider. Instance might have crashed or otherwise been terminated." % inst.instance_id )
+ inst.state = 'terminated'
+ uci.state = 'available'
+ uci.launch_time = None
+ inst.flush()
+ uci.flush()
+ # Update instance status in local DB with info from cloud provider
+ for r in rl:
+ for i, cInst in enumerate( r.instances ):
+ s = cInst.update()
+ log.debug( "Checking state of cloud instance '%s' associated with reservation '%s'. State='%s'" % ( cInst, r, s ) )
+ if s != inst.state:
+ inst.state = s
+ inst.flush()
+ if s == 'terminated': # After instance has shut down, ensure UCI is marked as 'available'
+ uci.state = 'available'
+ uci.flush()
+ if s != uci.state and s != 'terminated':
+ # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed.
+ uci.state = s
+ uci.flush()
+ if cInst.public_dns_name != inst.public_dns:
+ inst.public_dns = cInst.public_dns_name
+ inst.flush()
+ if cInst.private_dns_name != inst.private_dns:
+ inst.private_dns = cInst.private_dns_name
+ inst.flush()
+
+ def updateStore( self, store ):
+ # Get credentials associated wit this store
+ uci_id = store.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ a_key = uci.credentials.access_key
+ s_key = uci.credentials.secret_key
+ # Get connection
+ euca_region = RegionInfo( None, "eucalyptus", "mayhem9.cs.ucsb.edu" )
+ conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key, is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
+ # Get reservations handle for given store
+ vl = conn.get_all_volumes( [store.volume_id] )
+# log.debug( "Store '%s' vl: '%s'" % ( store.volume_id, vl ) )
+ # Update store status in local DB with info from cloud provider
+ if store.status != vl[0].status:
+ store.status = vl[0].status
+ store.flush()
+ if store.i_id != vl[0].instance_id:
+ store.i_id = vl[0].instance_id
+ store.flush()
+ if store.attach_time != vl[0].attach_time:
+ store.attach_time = vl[0].attach_time
+ store.flush()
+ if store.device != vl[0].device:
+ store.device = vl[0].device
+ store.flush()
+
+ def updateUCI( self, uci ):
+ """
+ Runs a global status update on all storage volumes and all instances that are
+ associated with specified UCI
+ """
+ conn = self.get_connection( uci )
+
+ # Update status of storage volumes
+ vl = model.CloudStore.filter( model.CloudInstance.c.uci_id == uci.id ).all()
+ vols = []
+ for v in vl:
+ vols.append( v.volume_id )
+ try:
+ volumes = conn.get_all_volumes( vols )
+ for i, v in enumerate( volumes ):
+ uci.store[i].i_id = v.instance_id
+ uci.store[i].status = v.status
+ uci.store[i].device = v.device
+ uci.store[i].flush()
+ except:
+ log.debug( "Error updating status of volume(s) associated with UCI '%s'. Status was not updated." % uci.name )
+ pass
+
+ # Update status of instances
+ il = model.CloudInstance.filter_by( uci_id=uci.id ).filter( model.CloudInstance.c.state != 'terminated' ).all()
+ instanceList = []
+ for i in il:
+ instanceList.append( i.instance_id )
+ log.debug( 'instanceList: %s' % instanceList )
+ try:
+ reservations = conn.get_all_instances( instanceList )
+ for i, r in enumerate( reservations ):
+ uci.instance[i].state = r.instances[0].update()
+ log.debug('updating instance %s; status: %s' % ( uci.instance[i].instance_id, uci.instance[i].state ) )
+ uci.state = uci.instance[i].state
+ uci.instance[i].public_dns = r.instances[0].dns_name
+ uci.instance[i].private_dns = r.instances[0].private_dns_name
+ uci.instance[i].flush()
+ uci.flush()
+ except:
+ log.debug( "Error updating status of instances associated with UCI '%s'. Instance status was not updated." % uci.name )
+ pass
+
+ # --------- Helper methods ------------
+
+ def format_time( time ):
+ dict = {'T':' ', 'Z':''}
+ for i, j in dict.iteritems():
+ time = time.replace(i, j)
+ return time
+
\ No newline at end of file
diff -r 7c438fd3cf4a -r 6aab50510e43 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Fri Oct 02 18:31:32 2009 -0400
+++ b/lib/galaxy/model/mapping.py Wed Oct 14 19:20:11 2009 -0400
@@ -965,7 +965,7 @@
properties=dict( user=relation( User ),
credentials=relation( CloudUserCredentials ),
instance=relation( CloudInstance, backref='uci' ),
- store=relation( CloudStore, backref='uci' )
+ store=relation( CloudStore, backref='uci', cascade='all, delete-orphan' )
) )
assign_mapper( context, CloudInstance, CloudInstance.table,
diff -r 7c438fd3cf4a -r 6aab50510e43 lib/galaxy/model/migrate/versions/0014_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Fri Oct 02 18:31:32 2009 -0400
+++ b/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Wed Oct 14 19:20:11 2009 -0400
@@ -82,7 +82,10 @@
CloudImage_table.create()
except Exception, e:
log.debug( "Creating cloud_image table failed. Table probably exists already." )
- UCI_table.create()
+ try:
+ UCI_table.create()
+ except Exception, e:
+ log.debug( "Creating UCI table failed. Table probably exists already." )
CloudInstance_table.create()
CloudStore_table.create()
try:
@@ -115,7 +118,8 @@
log.debug( "Dropping cloud_user_credentials table failed: %s" % str( e ) )
try:
- UCI_table.drop()
+ log.debug( "Would drop UCI table." )
+# UCI_table.drop()
except Exception, e:
log.debug( "Dropping UCI table failed: %s" % str( e ) )
diff -r 7c438fd3cf4a -r 6aab50510e43 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Fri Oct 02 18:31:32 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Wed Oct 14 19:20:11 2009 -0400
@@ -56,25 +56,39 @@
liveInstances = trans.sa_session.query( model.UCI ) \
.filter_by( user=user ) \
- .filter( or_( model.UCI.c.state=="running", model.UCI.c.state=="pending", model.UCI.c.state=="terminating" ) ) \
- .order_by( desc( model.UCI.c.launch_time ) ) \
+ .filter( or_( model.UCI.c.state=="running",
+ model.UCI.c.state=="pending",
+ model.UCI.c.state=="submitted",
+ model.UCI.c.state=="submittedUCI",
+ model.UCI.c.state=="shutting-down",
+ model.UCI.c.state=="shutting-downUCI" ) ) \
+ .order_by( desc( model.UCI.c.update_time ) ) \
.all()
prevInstances = trans.sa_session.query( model.UCI ) \
.filter_by( user=user ) \
- .filter( or_( model.UCI.c.state=="available", model.UCI.c.state=="new", model.UCI.c.state=="error", model.UCI.c.state=="submitted" ) ) \
+ .filter( or_( model.UCI.c.state=="available",
+ model.UCI.c.state=="new",
+ model.UCI.c.state=="newUCI",
+ model.UCI.c.state=="error",
+ model.UCI.c.state=="deleting",
+ model.UCI.c.state=="deletingUCI" ) ) \
.order_by( desc( model.UCI.c.update_time ) ) \
.all()
# Check after update there are instances in pending state; if so, display message
# TODO: Auto-refresh once instance is running
pendingInstances = trans.sa_session.query( model.UCI ) \
- .filter_by( user=user, state="pending" ) \
+ .filter_by( user=user ) \
+ .filter( or_( model.UCI.c.state=="pending" , \
+ model.UCI.c.state=="submitted" , \
+ model.UCI.c.state=="submittedUCI" ) ) \
.all()
if pendingInstances:
trans.set_message( "Galaxy instance started. NOTE: Please wait about 3-5 minutes for the instance to "
"start up and then refresh this page. A button to connect to the instance will then appear alongside "
"instance description." )
+
return trans.fill_template( "cloud/configure_cloud.mako",
cloudCredentials = cloudCredentials,
liveInstances = liveInstances,
@@ -113,7 +127,13 @@
uci = get_uci( trans, id )
stores = get_stores( trans, uci )
# log.debug(self.app.config.job_working_directory)
- if len(stores) is not 0:
+ if ( len(stores) is not 0 ) and \
+ ( uci.state != 'submitted' ) and \
+ ( uci.state != 'submittedUCI' ) and \
+ ( uci.state != 'pending' ) and \
+ ( uci.state != 'deleting' ) and \
+ ( uci.state != 'deletingUCI' ) and \
+ ( uci.state != 'error' ):
instance = model.CloudInstance()
instance.user = user
instance.image = mi
@@ -146,7 +166,7 @@
# instance.instance_id = str( reservation.instances[0]).split(":")[1]
# instance.state = "pending"
# instance.state = reservation.instances[0].state
- uci.state = 'submitted'
+ uci.state = 'submittedUCI'
# Persist
session = trans.sa_session
@@ -155,8 +175,14 @@
session.flush()
trans.log_event ("User initiated starting of cloud instance '%s'." % uci.name )
+ trans.set_message( "Galaxy instance started. NOTE: Please wait about 3-5 minutes for the instance to "
+ "start up and then refresh this page. A button to connect to the instance will then appear alongside "
+ "instance description." )
return self.list( trans )
+ trans.show_error_message( "Cannot start instance that is in state '%s'." % uci.state )
+ return self.list( trans )
+
# return trans.show_form(
# web.FormBuilder( web.url_for(), "Start instance size", submit_text="Start" )
# .add_input( "radio","Small","size", value='small' )
@@ -170,7 +196,7 @@
Stop a cloud UCI instance. This implies stopping Galaxy server and disconnecting/unmounting relevant file system(s).
"""
uci = get_uci( trans, id )
- uci.state = 'terminating'
+ uci.state = 'shutting-downUCI'
session = trans.sa_session
# session.save_or_update( stores )
session.save_or_update( uci )
@@ -234,32 +260,38 @@
any and all storage associated with this UCI!
"""
uci = get_uci( trans, id )
- dbInstances = get_instances( trans, uci ) #TODO: handle list!
- conn = get_connection( trans )
- session = trans.sa_session
+ if ( uci.state != 'deletingUCI' ) and ( uci.state != 'deleting' ) and ( uci.state != 'error' ):
+ name = uci.name
+ uci.state = "deletingUCI"
+ # dbInstances = get_instances( trans, uci ) #TODO: handle list!
+ #
+ # conn = get_connection( trans )
+ session = trans.sa_session
+ #
+ # # Delete volume(s) associated with given uci
+ # stores = get_stores( trans, uci )
+ # for i, store in enumerate( stores ):
+ # log.debug( "Deleting volume '%s' that is associated with UCI '%s'." % ( store.volume_id, uci.name ) )
+ # volStat = None
+ # try:
+ # volStat = conn.delete_volume( store.volume_id )
+ # except:
+ # log.debug ( 'Error deleting volume %s' % store.volume_id )
+ #
+ # if volStat:
+ # session.delete( store )
+ #
+ # # Delete UCI from table
+ # uciName = uci.name # Store name for logging
+ # session.delete( uci )
+
+ session.flush()
+ trans.log_event( "User deleted cloud instance '%s'" % name )
+ trans.set_message( "Galaxy instance '%s' marked for deletion." % name )
+ return self.list( trans )
- # Delete volume(s) associated with given uci
- stores = get_stores( trans, uci )
- for i, store in enumerate( stores ):
- log.debug( "Deleting volume '%s' that is associated with UCI '%s'." % ( store.volume_id, uci.name ) )
- volStat = None
- try:
- volStat = conn.delete_volume( store.volume_id )
- except:
- log.debug ( 'Error deleting volume %s' % store.volume_id )
-
- if volStat:
- session.delete( store )
-
- # Delete UCI from table
- uciName = uci.name # Store name for logging
- session.delete( uci )
-
- session.flush()
- trans.log_event( "User deleted cloud instance '%s'" % uciName )
- trans.set_message( "Galaxy instance '%s' deleted." % uciName )
-
+ trans.set_message( "Instance '%s' is already marked for deletion." % uci.name )
return self.list( trans )
@web.expose
@@ -280,7 +312,7 @@
"""
inst_error = vol_error = cred_error = None
user = trans.get_user()
- # TODO: Hack until present user w/ bullet list w/ registered credentials
+ # TODO: Hack until present user w/ bullet list w/ registered credentials
storedCreds = trans.sa_session.query( model.CloudUserCredentials ) \
.filter_by( user=user ).all()
credsMatch = False
@@ -293,8 +325,7 @@
try:
if len( instanceName ) > 255:
inst_error = "Instance name exceeds maximum allowable length."
- elif trans.app.model.UCI.filter(
- trans.app.model.UCI.table.c.name==instanceName ).first():
+ elif trans.app.model.UCI.filter( and_( trans.app.model.UCI.table.c.name==instanceName, trans.app.model.UCI.table.c.state!='deleted' ) ).first():
inst_error = "An instance with that name already exist."
elif int( volSize ) > 1000:
vol_error = "Volume size cannot exceed 1000GB. You must specify an integer between 1 and 1000."
@@ -310,7 +341,7 @@
trans.app.model.CloudUserCredentials.table.c.name==credName ).first()
uci.user= user
uci.total_size = volSize # This is OK now because new instance is being created.
- uci.state = "new" # Valid states include: "new, "available", "running", "pending", "submitted", "terminating", or "error"
+ uci.state = "newUCI"
storage = model.CloudStore()
storage.user = user
storage.uci = uci
@@ -403,15 +434,20 @@
Add user's cloud credentials stored under name `credName`.
"""
user = trans.get_user()
- cred_error = accessKey_error = secretKey_error = provider_error = None
+ error = {}
+
if credName:
if len( credName ) > 255:
- cred_error = "Credentials name exceeds maximum allowable length."
+ error['cred_error'] = "Credentials name exceeds maximum allowable length."
elif trans.app.model.CloudUserCredentials.filter(
trans.app.model.CloudUserCredentials.table.c.name==credName ).first():
- cred_error = "Credentials with that name already exist."
+ error['cred_error'] = "Credentials with that name already exist."
elif ( ( providerName.lower()!='ec2' ) and ( providerName.lower()!='eucalyptus' ) ):
- provider_error = "You specified an unsupported cloud provider."
+ error['provider_error'] = "You specified an unsupported cloud provider."
+ elif accessKey=='' or len( accessKey ) > 255:
+ error['access_key_error'] = "Access key much be between 1 and 255 characters long."
+ elif secretKey=='' or len( secretKey ) > 255:
+ error['secret_key_error'] = "Secret key much be between 1 and 255 characters long."
else:
# Create new user stored credentials
credentials = model.CloudUserCredentials()
@@ -430,12 +466,21 @@
# if defaultCred:
# self.makeDefault( trans, credentials.id)
return self.list( trans )
- return trans.show_form(
- web.FormBuilder( web.url_for(), "Add credentials", submit_text="Add" )
- .add_text( "credName", "Credentials name", value="Unnamed credentials", error=cred_error )
- .add_text( "providerName", "Cloud provider name", value="ec2 or eucalyptus", error=provider_error )
- .add_text( "accessKey", "Access key", value='', error=accessKey_error )
- .add_password( "secretKey", "Secret key", value='', error=secretKey_error ) )
+
+ return trans.fill_template( "cloud/add_credentials.mako", \
+ credName = credName, \
+ providerName = providerName, \
+ accessKey = accessKey, \
+ secretKey = secretKey, \
+ error = error
+ )
+
+# return trans.show_form(
+# web.FormBuilder( web.url_for(), "Add credentials", submit_text="Add" )
+# .add_text( "credName", "Credentials name", value="Unnamed credentials", error=cred_error )
+# .add_text( "providerName", "Cloud provider name", value="ec2 or eucalyptus", error=provider_error )
+# .add_text( "accessKey", "Access key", value='', error=accessKey_error )
+# .add_password( "secretKey", "Secret key", value='', error=secretKey_error ) )
@web.expose
@web.require_login( "view credentials" )
@@ -1018,7 +1063,7 @@
# Retrieve cloud instance based on passed instance id. get_all_instances( idLst ) method returns reservation ID. Because
# we are passing only 1 ID, we can retrieve only the first element of the returning list. Furthermore, because (for now!)
# only 1 instance corresponds each individual reservation, grab only the first element of the returned list of instances.
- cloudInstance = conn.get_all_instances( idLst )[0].instances[0]
+ cloudInstance = conn.get_all_instances( [instance_id] )[0].instances[0]
return cloudInstance
def get_connection( trans, credName ):
diff -r 7c438fd3cf4a -r 6aab50510e43 templates/cloud/add_credentials.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/cloud/add_credentials.mako Wed Oct 14 19:20:11 2009 -0400
@@ -0,0 +1,97 @@
+<% _=n_ %>
+<%inherit file="/base.mako"/>
+<%def name="title()">Add credentials</%def>
+
+<%def name="javascripts()">
+${parent.javascripts()}
+<script type="text/javascript">
+$(function(){
+ $("input:text:first").focus();
+})
+</script>
+</%def>
+
+%if header:
+ ${header}
+%endif
+
+<div class="form">
+ <div class="form-title">Add credentials</div>
+ <div class="form-body">
+ <form name="Add credentials" action="${h.url_for( action='add' )}" method="post" >
+
+ <%
+ cls = "form-row"
+ if error.has_key('cred_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Credentials name:</label>
+ <div class="form-row-input">
+ <input type="text" name="credName" value="Unnamed credentials" size="40">
+ </div>
+ %if error.has_key('cred_error'):
+ <div class="form-row-error-message">${error['cred_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+
+ <%
+ cls = "form-row"
+ if error.has_key('provider_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Cloud provider name:</label>
+ <div class="form-row-input">
+ <select name="providerName">
+ <option value="ec2">Amazon EC2</option>
+ <option value="eucalyptus">Eucalpytus Public Cloud (EPC)</option>
+ </select>
+ </div>
+ %if error.has_key('provider_error'):
+ <div class="form-row-error-message">${error['provider_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ if error.has_key('access_key_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Access key:</label>
+ <div class="form-row-input">
+ <input type="text" name="accessKey" value="" size="40">
+ </div>
+ %if error.has_key('access_key_error'):
+ <div class="form-row-error-message">${error['access_key_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+
+ <%
+ cls = "form-row"
+ if error.has_key('secret_key_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Secret key:</label>
+ <div class="form-row-input">
+ <input type="password" name="secretKey" value="" size="40">
+ </div>
+ %if error.has_key('secret_key_error'):
+ <div class="form-row-error-message">${error['secret_key_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+
+ <div class="form-row"><input type="submit" value="Add"></div>
+
+ </form>
+ </div>
+</div>
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/7c438fd3cf4a
changeset: 3067:7c438fd3cf4a
user: Enis Afgan <afgane(a)gmail.com>
date: Fri Oct 02 18:31:32 2009 -0400
description:
Added most of the functionality for interaction with Eucalyptus cloud
diffstat:
lib/galaxy/app.py | 4 +-
lib/galaxy/cloud/__init__.py | 262 ++++++++++++---------
lib/galaxy/cloud/providers/eucalyptus.py | 275 +++++++++++++++++++---
lib/galaxy/model/mapping.py | 4 +-
lib/galaxy/model/migrate/versions/0014_cloud_tables.py | 16 +-
lib/galaxy/web/controllers/cloud.py | 160 ++++++------
6 files changed, 465 insertions(+), 256 deletions(-)
diffs (986 lines):
diff -r c9c9adf06e9d -r 7c438fd3cf4a lib/galaxy/app.py
--- a/lib/galaxy/app.py Wed Sep 30 17:57:11 2009 -0400
+++ b/lib/galaxy/app.py Fri Oct 02 18:31:32 2009 -0400
@@ -1,6 +1,6 @@
import sys, os, atexit
-from galaxy import config, jobs, util, tools, web
+from galaxy import config, jobs, util, tools, web, cloud
from galaxy.tracks import store
from galaxy.web import security
import galaxy.model
@@ -68,6 +68,8 @@
# FIXME: These are exposed directly for backward compatibility
self.job_queue = self.job_manager.job_queue
self.job_stop_queue = self.job_manager.job_stop_queue
+ # Start the cloud manager
+ self.cloud_manager = cloud.CloudManager( self )
# Track Store
self.track_store = store.TrackStoreManager( self.config.track_store_path )
diff -r c9c9adf06e9d -r 7c438fd3cf4a lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Wed Sep 30 17:57:11 2009 -0400
+++ b/lib/galaxy/cloud/__init__.py Fri Oct 02 18:31:32 2009 -0400
@@ -6,7 +6,7 @@
from galaxy.datatypes.tabular import *
from galaxy.datatypes.interval import *
from galaxy.datatypes import metadata
-#from util import Bunch
+from galaxy.util.bunch import Bunch
import pkg_resources
pkg_resources.require( "PasteDeploy" )
@@ -34,7 +34,7 @@
# The dispatcher manager underlying cloud instances
self.provider = DefaultCloudProvider( app )
# Monitor for updating status of cloud instances
-# self.cloud_monitor = CloudMonitor( self.config, self.provider )
+ self.cloud_monitor = CloudMonitor( self.app, self.provider )
# self.job_stop_queue = JobStopQueue( app, self.dispatcher )
else:
self.job_queue = self.job_stop_queue = NoopCloudMonitor()
@@ -43,32 +43,32 @@
self.cloud_monitor.shutdown()
# self.job_stop_queue.shutdown()
- def createUCI( self, user, name, storage_size, zone=None):
- """
- Createse User Configured Instance (UCI). Essentially, creates storage volume.
- """
- self.provider.createUCI( user, name, storage_size, zone )
-
- def deleteUCI( self, name ):
- """
- Deletes UCI. NOTE that this implies deletion of any and all data associated
- with this UCI from the cloud. All data will be deleted.
- """
-
- def addStorageToUCI( self, name ):
- """ Adds more storage to specified UCI """
-
- def startUCI( self, name, type ):
- """
- Starts an instance of named UCI on the cloud. This implies, mounting of
- storage and starting Galaxy instance.
- """
-
- def stopUCI( self, name ):
- """
- Stops cloud instance associated with named UCI. This also implies
- stopping of Galaxy and unmounting of the file system.
- """
+# def createUCI( self, user, name, storage_size, zone=None):
+# """
+# Createse User Configured Instance (UCI). Essentially, creates storage volume.
+# """
+# self.provider.createUCI( user, name, storage_size, zone )
+#
+# def deleteUCI( self, name ):
+# """
+# Deletes UCI. NOTE that this implies deletion of any and all data associated
+# with this UCI from the cloud. All data will be deleted.
+# """
+#
+# def addStorageToUCI( self, name ):
+# """ Adds more storage to specified UCI """
+#
+# def startUCI( self, name, type ):
+# """
+# Starts an instance of named UCI on the cloud. This implies, mounting of
+# storage and starting Galaxy instance.
+# """
+#
+# def stopUCI( self, name ):
+# """
+# Stops cloud instance associated with named UCI. This also implies
+# stopping of Galaxy and unmounting of the file system.
+# """
class Sleeper( object ):
"""
@@ -149,8 +149,8 @@
while self.running:
try:
- #self.__monitor_step()
- log.debug ( "would be calling monitor_step" )
+# log.debug( "Calling monitor_step" )
+ self.__monitor_step()
except:
log.exception( "Exception in cloud manager monitor_step" )
# Sleep
@@ -171,88 +171,110 @@
session = mapping.Session()
# Pull all new jobs from the queue at once
new_jobs = []
- if self.track_jobs_in_database:
- for j in session.query( model.Job ).options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ).filter( model.Job.c.state == model.Job.states.NEW ).all():
- job = JobWrapper( j, self.app.toolbox.tools_by_id[ j.tool_id ], self )
- new_jobs.append( job )
- else:
- try:
- while 1:
- message = self.queue.get_nowait()
- if message is self.STOP_SIGNAL:
- return
- # Unpack the message
- job_id, tool_id = message
- # Create a job wrapper from it
- job_entity = session.query( model.Job ).get( job_id )
- job = JobWrapper( job_entity, self.app.toolbox.tools_by_id[ tool_id ], self )
- # Append to watch queue
- new_jobs.append( job )
- except Empty:
- pass
- # Iterate over new and waiting jobs and look for any that are
- # ready to run
- new_waiting = []
- for job in ( new_jobs + self.waiting ):
- try:
- # Clear the session for each job so we get fresh states for
- # job and all datasets
- session.clear()
- # Get the real job entity corresponding to the wrapper (if we
- # are tracking in the database this is probably cached in
- # the session from the origianl query above)
- job_entity = session.query( model.Job ).get( job.job_id )
- # Check the job's dependencies, requeue if they're not done
- job_state = self.__check_if_ready_to_run( job, job_entity )
- if job_state == JOB_WAIT:
- if not self.track_jobs_in_database:
- new_waiting.append( job )
- elif job_state == JOB_ERROR:
- log.info( "job %d ended with an error" % job.job_id )
- elif job_state == JOB_INPUT_ERROR:
- log.info( "job %d unable to run: one or more inputs in error state" % job.job_id )
- elif job_state == JOB_INPUT_DELETED:
- log.info( "job %d unable to run: one or more inputs deleted" % job.job_id )
- elif job_state == JOB_READY:
- # If special queuing is enabled, put the ready jobs in the special queue
- if self.use_policy :
- self.squeue.put( job )
- log.debug( "job %d put in policy queue" % job.job_id )
- else: # or dispatch the job directly
- self.dispatcher.put( job )
- log.debug( "job %d dispatched" % job.job_id)
- elif job_state == JOB_DELETED:
- msg = "job %d deleted by user while still queued" % job.job_id
- job.info = msg
- log.debug( msg )
- elif job_state == JOB_ADMIN_DELETED:
- job.fail( job_entity.info )
- log.info( "job %d deleted by admin while still queued" % job.job_id )
- else:
- msg = "unknown job state '%s' for job %d" % ( job_state, job.job_id )
- job.info = msg
- log.error( msg )
- except Exception, e:
- job.info = "failure running job %d: %s" % ( job.job_id, str( e ) )
- log.exception( "failure running job %d" % job.job_id )
- # Update the waiting list
- self.waiting = new_waiting
- # If special (e.g. fair) scheduling is enabled, dispatch all jobs
- # currently in the special queue
- if self.use_policy :
- while 1:
- try:
- sjob = self.squeue.get()
- self.dispatcher.put( sjob )
- log.debug( "job %d dispatched" % sjob.job_id )
- except Empty:
- # squeue is empty, so stop dispatching
- break
- except Exception, e: # if something else breaks while dispatching
- job.fail( "failure running job %d: %s" % ( sjob.job_id, str( e ) ) )
- log.exception( "failure running job %d" % sjob.job_id )
- # Done with the session
- mapping.Session.remove()
+ new_instances = []
+ new_UCIs = []
+ stop_UCIs = []
+
+# for r in session.query( model.cloud_instance ).filter( model.cloud_instance.s.state == model.cloud_instance.states.NEW ).all():
+# new_instances
+
+ for r in session.query( model.UCI ).filter( model.UCI.c.state == "new" ).all():
+ new_UCIs.append( r )
+ for r in new_UCIs:
+ self.provider.createUCI( r )
+
+ for r in session.query( model.UCI ).filter( model.UCI.c.state == "submitted" ).all():
+ new_instances.append( r )
+ for r in new_instances:
+ self.provider.startUCI( r )
+
+ for r in session.query( model.UCI ).filter( model.UCI.c.state == "terminating" ).all():
+ stop_UCIs.append( r )
+ for r in stop_UCIs:
+ self.provider.stopUCI( r )
+
+# if self.track_jobs_in_database:
+# for j in session.query( model.Job ).options( lazyload( "external_output_metadata" ), lazyload( "parameters" ) ).filter( model.Job.c.state == model.Job.states.NEW ).all():
+# job = JobWrapper( j, self.app.toolbox.tools_by_id[ j.tool_id ], self )
+# new_jobs.append( job )
+# else:
+# try:
+# while 1:
+# message = self.queue.get_nowait()
+# if message is self.STOP_SIGNAL:
+# return
+# # Unpack the message
+# job_id, tool_id = message
+# # Create a job wrapper from it
+# job_entity = session.query( model.Job ).get( job_id )
+# job = JobWrapper( job_entity, self.app.toolbox.tools_by_id[ tool_id ], self )
+# # Append to watch queue
+# new_jobs.append( job )
+# except Empty:
+# pass
+# # Iterate over new and waiting jobs and look for any that are
+# # ready to run
+# new_waiting = []
+# for job in ( new_jobs + self.waiting ):
+# try:
+# # Clear the session for each job so we get fresh states for
+# # job and all datasets
+# session.clear()
+# # Get the real job entity corresponding to the wrapper (if we
+# # are tracking in the database this is probably cached in
+# # the session from the origianl query above)
+# job_entity = session.query( model.Job ).get( job.job_id )
+# # Check the job's dependencies, requeue if they're not done
+# job_state = self.__check_if_ready_to_run( job, job_entity )
+# if job_state == JOB_WAIT:
+# if not self.track_jobs_in_database:
+# new_waiting.append( job )
+# elif job_state == JOB_ERROR:
+# log.info( "job %d ended with an error" % job.job_id )
+# elif job_state == JOB_INPUT_ERROR:
+# log.info( "job %d unable to run: one or more inputs in error state" % job.job_id )
+# elif job_state == JOB_INPUT_DELETED:
+# log.info( "job %d unable to run: one or more inputs deleted" % job.job_id )
+# elif job_state == JOB_READY:
+# # If special queuing is enabled, put the ready jobs in the special queue
+# if self.use_policy :
+# self.squeue.put( job )
+# log.debug( "job %d put in policy queue" % job.job_id )
+# else: # or dispatch the job directly
+# self.dispatcher.put( job )
+# log.debug( "job %d dispatched" % job.job_id)
+# elif job_state == JOB_DELETED:
+# msg = "job %d deleted by user while still queued" % job.job_id
+# job.info = msg
+# log.debug( msg )
+# elif job_state == JOB_ADMIN_DELETED:
+# job.fail( job_entity.info )
+# log.info( "job %d deleted by admin while still queued" % job.job_id )
+# else:
+# msg = "unknown job state '%s' for job %d" % ( job_state, job.job_id )
+# job.info = msg
+# log.error( msg )
+# except Exception, e:
+# job.info = "failure running job %d: %s" % ( job.job_id, str( e ) )
+# log.exception( "failure running job %d" % job.job_id )
+# # Update the waiting list
+# self.waiting = new_waiting
+# # If special (e.g. fair) scheduling is enabled, dispatch all jobs
+# # currently in the special queue
+# if self.use_policy :
+# while 1:
+# try:
+# sjob = self.squeue.get()
+# self.dispatcher.put( sjob )
+# log.debug( "job %d dispatched" % sjob.job_id )
+# except Empty:
+# # squeue is empty, so stop dispatching
+# break
+# except Exception, e: # if something else breaks while dispatching
+# job.fail( "failure running job %d: %s" % ( sjob.job_id, str( e ) ) )
+# log.exception( "failure running job %d" % sjob.job_id )
+# # Done with the session
+# mapping.Session.remove()
def __check_if_ready_to_run( self, job_wrapper, job ):
"""
@@ -653,12 +675,12 @@
else:
log.error( "Unable to start unknown cloud provider: %s" %self.provider_name )
- def createUCI( self, user, uciName, storage_size, zone=None):
+ def createUCI( self, uci ):
"""
Createse User Configured Instance (UCI). Essentially, creates storage volume.
"""
- log.debug( "Creating UCI %s" % uciName )
- self.cloud_provider[self.provider_name].createUCI( user, uciName, storage_size, zone )
+ log.debug( "Creating UCI '%s'" % uci.name )
+ self.cloud_provider[self.provider_name].createUCI( uci )
def deleteUCI( self, uciName ):
"""
@@ -669,18 +691,22 @@
def addStorageToUCI( self, uciName ):
""" Adds more storage to specified UCI """
- def startUCI( self, uciName, type ):
+ def startUCI( self, uci ):
"""
Starts an instance of named UCI on the cloud. This implies, mounting of
storage and starting Galaxy instance.
"""
+ log.debug( "Starting UCI '%s'" % uci.name )
+ self.cloud_provider[self.provider_name].startUCI( uci )
- def stopUCI( self, uciName ):
+ def stopUCI( self, uci ):
"""
Stops cloud instance associated with named UCI. This also implies
stopping of Galaxy and unmounting of the file system.
"""
-
+ log.debug( "Stopping UCI '%s'" % uci.name )
+ self.cloud_provider[self.provider_name].stopUCI( uci )
+
def put( self, job_wrapper ):
runner_name = ( job_wrapper.tool.job_runner.split(":", 1) )[0]
log.debug( "dispatching job %d to %s runner" %( job_wrapper.job_id, runner_name ) )
diff -r c9c9adf06e9d -r 7c438fd3cf4a lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Wed Sep 30 17:57:11 2009 -0400
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Fri Oct 02 18:31:32 2009 -0400
@@ -1,12 +1,10 @@
-import subprocess, threading, os, errno
+import subprocess, threading, os, errno, time, datetime
from Queue import Queue, Empty
from datetime import datetime
from galaxy import model # Database interaction class
from galaxy.datatypes.data import nice_size
-from time import sleep
-
import galaxy.eggs
galaxy.eggs.require("boto")
from boto.ec2.connection import EC2Connection
@@ -19,64 +17,119 @@
"""
Eucalyptus-based cloud provider implementation for managing instances.
"""
- def __init__( self, app, user ):
+ STOP_SIGNAL = object()
+ def __init__( self, app ):
log.debug( "Using eucalyptus as default cloud provider." )
- self.conn = get_connection( user )
+ self.zone = "epc"
+ self.key_pair = "galaxy-keypair"
+
+ #TODO: Use multiple threads to process requests?
- def get_connection( user ):
+ def get_connection( self, uci ):
"""
Establishes EC2 connection using user's default credentials
"""
log.debug( '##### Establishing cloud connection' )
- creds = model.CloudUserCredentials.filter_by( user=user, defaultCred=True ).first()
- if creds:
- a_key = creds.access_key
- s_key = creds.secret_key
- # Amazon EC2
- #conn = EC2Connection( a_key, s_key )
- # Eucalyptus Public Cloud
- # TODO: Add option in Galaxy config file to specify these values (i.e., for locally manages Eucalyptus deployments)
- euca_region = RegionInfo( None, "eucalyptus", "mayhem9.cs.ucsb.edu" )
- conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key, is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
- return conn
- else:
- log.debug( "User did not specify default credentials." )
- return 0
+# creds = model.CloudUserCredentials.filter_by( user=user, defaultCred=True ).first()
+ a_key = uci.credentials.access_key
+ s_key = uci.credentials.secret_key
+ # Amazon EC2
+ #conn = EC2Connection( a_key, s_key )
+ # Eucalyptus Public Cloud
+ # TODO: Add option in Galaxy config file to specify these values (i.e., for locally manages Eucalyptus deployments)
+ euca_region = RegionInfo( None, "eucalyptus", "mayhem9.cs.ucsb.edu" )
+ conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key, is_secure=False, port=8773, region=euca_region, path="/services/Eucalyptus" )
+ return conn
+
+ def get_keypair_name( self, uci, conn ):
+ """
+ Generate keypair using user's default credentials
+ """
+ log.debug( "Getting user's keypair" )
+ kp = conn.get_key_pair( self.key_pair )
+
+ try:
+ for i, inst in enumerate( uci.instance ):
+ uci.instance[i].keypair_name = kp.name
+ return kp.name
+ except AttributeError: # No keypair under this name exists so create it
+ log.debug( "No keypair found, creating keypair '%s'" % self.key_pair )
+ kp = conn.create_key_pair( self.key_pair )
+ for i, inst in enumerate( uci.instance ):
+ uci.instance[i].keypair_name = kp.name
+ uci.instance[i].keypair_material = kp.material
+ uci.flush()
+ # TODO: Store key_pair.material into instance table - this is the only time private key can be retrieved
+ # Actually, probably return key_pair to calling method and store name & key from there...
+
+ return kp.name
+
+ def get_mi( self, type='small' ):
+ """
+ Get appropriate machine image (mi) based on instance size.
+ TODO: Dummy method - need to implement logic
+ For valid sizes, see http://aws.amazon.com/ec2/instance-types/
+ """
+ return model.CloudImage.filter( model.CloudImage.table.c.id==1 ).first()
+
+# def get_instances( self, uci ):
+# """
+# Get objects of instances that are pending or running and are connected to uci object
+# """
+# instances = trans.sa_session.query( model.CloudInstance ) \
+# .filter_by( user=user, uci_id=uci.id ) \
+# .filter( or_(model.CloudInstance.table.c.state=="running", model.CloudInstance.table.c.state=="pending" ) ) \
+# .first()
+# #.all() #TODO: return all but need to edit calling method(s) to handle list
+#
+# instances = uci.instance
+#
+# return instances
-
+
def shutdown( self ):
"""Attempts to gracefully shut down the monitor thread"""
log.info( "sending stop signal to worker threads in eucalyptus cloud manager" )
self.queue.put( self.STOP_SIGNAL )
log.info( "eucalyptus cloud manager stopped" )
- def createUCI( self, user, name, storage_size, zone=None):
+ def createUCI( self, uci ):
"""
Creates User Configured Instance (UCI). Essentially, creates storage volume on cloud provider
and registers relevant information in Galaxy database.
"""
- conn = getConnection( user )
- # Capture user configured instance information
- uci = model.UCI()
- uci.name = name
- uci.user = user
- uci.state = "available" # Valid states include: "available", "running" or "pending"
- uci.total_size = storage_size # This is OK now because a new instance is being created.
- # Capture store related information
- storage = model.CloudStore()
- storage.user = user
- storage.uci = uci
- storage.size = storage_size
- storage.availability_zone = "us-east-1a" # TODO: Give user choice here. Also, enable region selection.
- #self.conn.create_volume( storage_size, storage.availability_zone, snapshot=None )
- # TODO: get correct value from Eucalyptus
- storage.volume_id = "made up"
- # Persist
+ conn = self.get_connection( uci )
+ # Temporary code - need to ensure user selects zone at UCI creation time!
+ if uci.store[0].availability_zone=='':
+ log.info( "Availability zone for storage volume was not selected, using default zone: %s" % self.zone )
+ uci.store[0].availability_zone = self.zone
+ uci.store[0].flush()
+
+ #TODO: check if volume associated with UCI already exists (if server crashed for example) and don't recreate it
+ log.debug( "Creating volume in zone '%s'..." % uci.store[0].availability_zone )
+ vol = conn.create_volume( uci.store[0].size, uci.store[0].availability_zone, snapshot=None )
+ uci.store[0].volume_id = vol.id
+
+ # Wait for a while to ensure volume was created
+# vol_status = vol.status
+# for i in range( 30 ):
+# if vol_status is not "u'available'":
+# log.debug( 'Updating volume status; current status: %s' % vol_status )
+# vol_status = vol.status
+# time.sleep(3)
+# if i is 29:
+# log.debug( "Error while creating volume '%s'; stuck in state '%s'; deleting volume." % ( vol.id, vol_status ) )
+# conn.delete_volume( vol.id )
+# uci.state = 'error'
+# uci.flush()
+# return
+
+ uci.state = 'available'
+ uci.store[0].status = vol.status
+ uci.store[0].flush()
uci.flush()
- storage.flush()
- session.flush()
-
+
def deleteUCI( self, name ):
"""
Deletes UCI. NOTE that this implies deletion of any and all data associated
@@ -86,14 +139,142 @@
def addStorageToUCI( self, name ):
""" Adds more storage to specified UCI """
- def startUCI( self, name, type ):
+ def startUCI( self, uci ):
"""
Starts an instance of named UCI on the cloud. This implies, mounting of
storage and starting Galaxy instance.
"""
+ conn = self.get_connection( uci )
- def stopUCI( self, name ):
+ uci.instance[0].keypair_name = self.get_keypair_name( uci, conn )
+ mi = self.get_mi( uci.instance[0].type )
+
+# log.debug( "mi: %s, mi.image_id: %s, uci.instance[0].keypair_name: %s" % ( mi, mi.image_id, uci.instance[0].keypair_name ) )
+ uci.instance[0].image = mi
+
+# log.debug( '***** Setting up security group' )
+ # If not existent, setup galaxy security group
+# try:
+# gSecurityGroup = conn.create_security_group('galaxy', 'Security group for Galaxy.')
+# gSecurityGroup.authorize( 'tcp', 80, 80, '0.0.0.0/0' ) # Open HTTP port
+# gSecurityGroup.authorize( 'tcp', 22, 22, '0.0.0.0/0' ) # Open SSH port
+# except:
+# pass
+# sgs = conn.get_all_security_groups()
+# for i in range( len( sgs ) ):
+# if sgs[i].name == "galaxy":
+# sg.append( sgs[i] )
+# break # only 1 security group w/ this name can exist, so continue
+
+ log.debug( "***** Starting UCI instance '%s'" % uci.name )
+# log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( uci.instance[0].image.image_id, uci.instance[0].keypair_name ) )
+ reservation = conn.run_instances( image_id=uci.instance[0].image.image_id, key_name=uci.instance[0].keypair_name )
+ #reservation = conn.run_instances( image_id=instance.image, key_name=instance.keypair_name, security_groups=['galaxy'], instance_type=instance.type, placement=instance.availability_zone )
+ uci.instance[0].launch_time = datetime.utcnow()
+ uci.launch_time = uci.instance[0].launch_time
+ uci.instance[0].reservation_id = str( reservation ).split(":")[1]
+ uci.instance[0].instance_id = str( reservation.instances[0]).split(":")[1]
+ s = reservation.instances[0].state
+ uci.instance[0].state = s
+ uci.state = s
+ uci.instance[0].flush()
+ uci.flush()
+
+ # Wait until instance gets running and then update the DB
+ while s!="running":
+ log.debug( "Waiting on instance '%s' to start up (reservation ID: %s); current state: %s" % ( uci.instance[0].instance_id, uci.instance[0].reservation_id, s ) )
+ time.sleep( 15 )
+ s = reservation.instances[0].update()
+
+ uci.instance[0].state = s
+ uci.state = s
+ uci.instance[0].public_dns = reservation.instances[0].dns_name
+ uci.instance[0].private_dns = reservation.instances[0].private_dns_name
+ uci.instance[0].flush()
+ uci.flush()
+
+
+ def stopUCI( self, uci ):
"""
- Stops cloud instance associated with named UCI. This also implies
- stopping of Galaxy and unmounting of the file system.
- """
\ No newline at end of file
+ Stops all of cloud instances associated with named UCI.
+ """
+ conn = self.get_connection( uci )
+ tl = [] # temination list
+
+ for i, inst in enumerate( uci.instance ):
+ tl.append( uci.instance[i].instance_id )
+
+ instList = conn.get_all_instances( tl )
+# log.debug( 'instList: %s' % instList )
+
+ for i, inst in enumerate( instList ):
+# log.debug( 'inst: %s' % inst )
+ log.debug( 'Before stop - inst.instances[0].update(): %s' % inst.instances[0].update() )
+ inst.instances[0].stop()
+ log.debug( 'After stop - inst.instances[0].update(): %s' % inst.instances[0].update() )
+ uci.instance[i].stop_time = datetime.utcnow()
+
+ terminated=0
+ while terminated!=len( instList ):
+ for i, inst in enumerate( instList ):
+ log.debug( "inst state: %s" % inst.instances[0].state )
+ state = inst.instances[0].update()
+ if state=='terminated':
+ uci.instance[i].state = state
+ uci.instance[i].flush()
+ terminated += 1
+ time.sleep ( 5 )
+
+ uci.state = 'available'
+ uci.launch_time = None
+ uci.flush()
+
+ log.debug( "All instances for UCI '%s' were terminated." % uci.name )
+
+
+
+# dbInstances = get_instances( trans, uci ) #TODO: handle list!
+#
+# # Get actual cloud instance object
+# cloudInstance = get_cloud_instance( conn, dbInstances.instance_id )
+#
+# # TODO: Detach persistent storage volume(s) from instance and update volume data in local database
+# stores = get_stores( trans, uci )
+# for i, store in enumerate( stores ):
+# log.debug( "Detaching volume '%s' to instance '%s'." % ( store.volume_id, dbInstances.instance_id ) )
+# mntDevice = store.device
+# volStat = None
+## Detaching volume does not work with Eucalyptus Public Cloud, so comment it out
+## try:
+## volStat = conn.detach_volume( store.volume_id, dbInstances.instance_id, mntDevice )
+## except:
+## log.debug ( 'Error detaching volume; still going to try and stop instance %s.' % dbInstances.instance_id )
+# store.attach_time = None
+# store.device = None
+# store.i_id = None
+# store.status = volStat
+# log.debug ( '***** volume status: %s' % volStat )
+#
+#
+# # Stop the instance and update status in local database
+# cloudInstance.stop()
+# dbInstances.stop_time = datetime.utcnow()
+# while cloudInstance.state != 'terminated':
+# log.debug( "Stopping instance %s state; current state: %s" % ( str( cloudInstance ).split(":")[1], cloudInstance.state ) )
+# time.sleep(3)
+# cloudInstance.update()
+# dbInstances.state = cloudInstance.state
+#
+# # Reset relevant UCI fields
+# uci.state = 'available'
+# uci.launch_time = None
+#
+# # Persist
+# session = trans.sa_session
+## session.save_or_update( stores )
+# session.save_or_update( dbInstances ) # TODO: Is this going to work w/ multiple instances stored in dbInstances variable?
+# session.save_or_update( uci )
+# session.flush()
+# trans.log_event( "User stopped cloud instance '%s'" % uci.name )
+# trans.set_message( "Galaxy instance '%s' stopped." % uci.name )
+
\ No newline at end of file
diff -r c9c9adf06e9d -r 7c438fd3cf4a lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Wed Sep 30 17:57:11 2009 -0400
+++ b/lib/galaxy/model/mapping.py Fri Oct 02 18:31:32 2009 -0400
@@ -432,9 +432,9 @@
Column( "attach_time", DateTime ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
Column( "uci_id", Integer, ForeignKey( "uci.id" ), index=True, nullable=False ),
- Column( "volume_id", TEXT, nullable=False ),
+ Column( "volume_id", TEXT ),
Column( "size", Integer, nullable=False ),
- Column( "availability_zone", TEXT, nullable=False ),
+ Column( "availability_zone", TEXT ),
Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ), index=True ),
Column( "status", TEXT ),
Column( "device", TEXT ),
diff -r c9c9adf06e9d -r 7c438fd3cf4a lib/galaxy/model/migrate/versions/0014_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Wed Sep 30 17:57:11 2009 -0400
+++ b/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Fri Oct 02 18:31:32 2009 -0400
@@ -38,11 +38,11 @@
Column( "launch_time", DateTime ),
Column( "stop_time", DateTime ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
- Column( "uci_id", Integer, ForeignKey( "uci.id" ), index=True ),
+ Column( "uci_id", Integer, ForeignKey( "uci.id" ), index=True, nullable=False ),
Column( "type", TEXT ),
Column( "reservation_id", TEXT ),
Column( "instance_id", TEXT ),
- Column( "mi_id", TEXT, ForeignKey( "cloud_image.image_id" ), index=True, nullable=False ),
+ Column( "mi_id", TEXT, ForeignKey( "cloud_image.image_id" ), index=True ),
Column( "state", TEXT ),
Column( "public_dns", TEXT ),
Column( "private_dns", TEXT ),
@@ -57,9 +57,9 @@
Column( "attach_time", DateTime ),
Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
Column( "uci_id", Integer, ForeignKey( "uci.id" ), index=True, nullable=False ),
- Column( "volume_id", TEXT, nullable=False ),
+ Column( "volume_id", TEXT ),
Column( "size", Integer, nullable=False ),
- Column( "availability_zone", TEXT, nullable=False ),
+ Column( "availability_zone", TEXT ),
Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ), index=True ),
Column( "status", TEXT ),
Column( "device", TEXT ),
@@ -93,8 +93,8 @@
def downgrade():
metadata.reflect()
try:
-# log.debug( "Would drop cloud_image table." )
- CloudImage_table.drop() #Enable before release
+ log.debug( "Would drop cloud_image table." )
+# CloudImage_table.drop() #Enable before release
except Exception, e:
log.debug( "Dropping cloud_image table failed: %s" % str( e ) )
@@ -109,8 +109,8 @@
log.debug( "Dropping cloud_store table failed: %s" % str( e ) )
try:
-# log.debug( "Would drop cloud_user_credentials table." )
- CloudUserCredentials_table.drop() #Enable before putting final version
+ log.debug( "Would drop cloud_user_credentials table." )
+# CloudUserCredentials_table.drop() #Enable before putting final version
except Exception, e:
log.debug( "Dropping cloud_user_credentials table failed: %s" % str( e ) )
diff -r c9c9adf06e9d -r 7c438fd3cf4a lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Wed Sep 30 17:57:11 2009 -0400
+++ b/lib/galaxy/web/controllers/cloud.py Fri Oct 02 18:31:32 2009 -0400
@@ -42,12 +42,12 @@
Render cloud main page (management of cloud resources)
"""
user = trans.get_user()
- pendingInstances = trans.sa_session.query( model.UCI ) \
- .filter_by( user=user, state="pending" ) \
- .all()
-
- for i in range( len ( pendingInstances ) ):
- update_instance_state( trans, pendingInstances[i].id )
+# pendingInstances = trans.sa_session.query( model.UCI ) \
+# .filter_by( user=user, state="pending" ) \
+# .all()
+#
+# for i inupdate_in range( len ( pendingInstances ) ):
+# stance_state( trans, pendingInstances[i].id )
cloudCredentials = trans.sa_session.query( model.CloudUserCredentials ) \
.filter_by( user=user ) \
@@ -56,12 +56,13 @@
liveInstances = trans.sa_session.query( model.UCI ) \
.filter_by( user=user ) \
- .filter( or_(model.UCI.c.state=="running", model.UCI.c.state=="pending") ) \
+ .filter( or_( model.UCI.c.state=="running", model.UCI.c.state=="pending", model.UCI.c.state=="terminating" ) ) \
.order_by( desc( model.UCI.c.launch_time ) ) \
.all()
prevInstances = trans.sa_session.query( model.UCI ) \
- .filter_by( user=user, state="available" ) \
+ .filter_by( user=user ) \
+ .filter( or_( model.UCI.c.state=="available", model.UCI.c.state=="new", model.UCI.c.state=="error", model.UCI.c.state=="submitted" ) ) \
.order_by( desc( model.UCI.c.update_time ) ) \
.all()
@@ -101,26 +102,26 @@
@web.expose
@web.require_login( "start Galaxy cloud instance" )
- def start( self, trans, id, size='small' ):
+ def start( self, trans, id, type='small' ):
"""
Start a new cloud resource instance
"""
- # TODO: Add choice of instance size before starting one
- #if size:
+ # TODO: Add choice of instance type before starting one
+ #if type:
user = trans.get_user()
- mi = get_mi( trans, size )
+ mi = get_mi( trans, type )
uci = get_uci( trans, id )
stores = get_stores( trans, uci )
- log.debug(self.app.config.job_working_directory)
+# log.debug(self.app.config.job_working_directory)
if len(stores) is not 0:
instance = model.CloudInstance()
instance.user = user
instance.image = mi
instance.uci = uci
- instance.keypair_name = get_keypair_name( trans )
instance.availability_zone = stores[0].availability_zone # Bc. all EBS volumes need to be in the same avail. zone, just check 1st
- instance.type = size
- conn = get_connection( trans )
+ instance.type = type
+# instance.keypair_name = get_keypair_name( trans )
+# conn = get_connection( trans )
# log.debug( '***** Setting up security group' )
# If not existent, setup galaxy security group
# try:
@@ -135,26 +136,25 @@
# sg.append( sgs[i] )
# break # only 1 security group w/ this name can exist, so continue
- log.debug( '***** Starting an instance' )
- log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( instance.image.image_id, instance.keypair_name ) )
- reservation = conn.run_instances( image_id=instance.image.image_id, key_name=instance.keypair_name )
+# log.debug( '***** Starting an instance' )
+# log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( instance.image.image_id, instance.keypair_name ) )
+# reservation = conn.run_instances( image_id=instance.image.image_id, key_name=instance.keypair_name )
#reservation = conn.run_instances( image_id=instance.image, key_name=instance.keypair_name, security_groups=['galaxy'], instance_type=instance.type, placement=instance.availability_zone )
- instance.launch_time = datetime.utcnow()
- uci.launch_time = instance.launch_time
- instance.reservation_id = str( reservation ).split(":")[1]
- instance.instance_id = str( reservation.instances[0]).split(":")[1]
+# instance.launch_time = datetime.utcnow()
+# uci.launch_time = instance.launch_time
+# instance.reservation_id = str( reservation ).split(":")[1]
+# instance.instance_id = str( reservation.instances[0]).split(":")[1]
# instance.state = "pending"
- instance.state = reservation.instances[0].state
- uci.state = instance.state
-
- # TODO: After instance boots up, need to update status, DNS and attach EBS
+# instance.state = reservation.instances[0].state
+ uci.state = 'submitted'
# Persist
session = trans.sa_session
session.save_or_update( instance )
+ session.save_or_update( uci )
session.flush()
- trans.log_event ("Started new instance. Reservation ID: '%s', Instance ID: '%s'" % (instance.reservation_id, instance.instance_id ) )
+ trans.log_event ("User initiated starting of cloud instance '%s'." % uci.name )
return self.list( trans )
# return trans.show_form(
@@ -167,55 +167,63 @@
@web.require_login( "stop Galaxy cloud instance" )
def stop( self, trans, id ):
"""
- Stop a cloud instance. This implies stopping Galaxy server and disconnecting/unmounting relevant file system(s).
+ Stop a cloud UCI instance. This implies stopping Galaxy server and disconnecting/unmounting relevant file system(s).
"""
uci = get_uci( trans, id )
- dbInstances = get_instances( trans, uci ) #TODO: handle list!
-
- conn = get_connection( trans )
- # Get actual cloud instance object
- cloudInstance = get_cloud_instance( conn, dbInstances.instance_id )
-
- # TODO: Detach persistent storage volume(s) from instance and update volume data in local database
- stores = get_stores( trans, uci )
- for i, store in enumerate( stores ):
- log.debug( "Detaching volume '%s' to instance '%s'." % ( store.volume_id, dbInstances.instance_id ) )
- mntDevice = store.device
- volStat = None
-# Detaching volume does not work with Eucalyptus Public Cloud, so comment it out
-# try:
-# volStat = conn.detach_volume( store.volume_id, dbInstances.instance_id, mntDevice )
-# except:
-# log.debug ( 'Error detaching volume; still going to try and stop instance %s.' % dbInstances.instance_id )
- store.attach_time = None
- store.device = None
- store.i_id = None
- store.status = volStat
- log.debug ( '***** volume status: %s' % volStat )
-
-
- # Stop the instance and update status in local database
- cloudInstance.stop()
- dbInstances.stop_time = datetime.utcnow()
- while cloudInstance.state != 'terminated':
- log.debug( "Stopping instance %s state; current state: %s" % ( str( cloudInstance ).split(":")[1], cloudInstance.state ) )
- time.sleep(3)
- cloudInstance.update()
- dbInstances.state = cloudInstance.state
-
- # Reset relevant UCI fields
- uci.state = 'available'
- uci.launch_time = None
-
- # Persist
+ uci.state = 'terminating'
session = trans.sa_session
# session.save_or_update( stores )
- session.save_or_update( dbInstances ) # TODO: Is this going to work w/ multiple instances stored in dbInstances variable?
session.save_or_update( uci )
session.flush()
trans.log_event( "User stopped cloud instance '%s'" % uci.name )
trans.set_message( "Galaxy instance '%s' stopped." % uci.name )
-
+
+# dbInstances = get_instances( trans, uci ) #TODO: handle list!
+#
+# conn = get_connection( trans )
+# # Get actual cloud instance object
+# cloudInstance = get_cloud_instance( conn, dbInstances.instance_id )
+#
+# # TODO: Detach persistent storage volume(s) from instance and update volume data in local database
+# stores = get_stores( trans, uci )
+# for i, store in enumerate( stores ):
+# log.debug( "Detaching volume '%s' to instance '%s'." % ( store.volume_id, dbInstances.instance_id ) )
+# mntDevice = store.device
+# volStat = None
+## Detaching volume does not work with Eucalyptus Public Cloud, so comment it out
+## try:
+## volStat = conn.detach_volume( store.volume_id, dbInstances.instance_id, mntDevice )
+## except:
+## log.debug ( 'Error detaching volume; still going to try and stop instance %s.' % dbInstances.instance_id )
+# store.attach_time = None
+# store.device = None
+# store.i_id = None
+# store.status = volStat
+# log.debug ( '***** volume status: %s' % volStat )
+#
+#
+# # Stop the instance and update status in local database
+# cloudInstance.stop()
+# dbInstances.stop_time = datetime.utcnow()
+# while cloudInstance.state != 'terminated':
+# log.debug( "Stopping instance %s state; current state: %s" % ( str( cloudInstance ).split(":")[1], cloudInstance.state ) )
+# time.sleep(3)
+# cloudInstance.update()
+# dbInstances.state = cloudInstance.state
+#
+# # Reset relevant UCI fields
+# uci.state = 'available'
+# uci.launch_time = None
+#
+# # Persist
+# session = trans.sa_session
+## session.save_or_update( stores )
+# session.save_or_update( dbInstances ) # TODO: Is this going to work w/ multiple instances stored in dbInstances variable?
+# session.save_or_update( uci )
+# session.flush()
+# trans.log_event( "User stopped cloud instance '%s'" % uci.name )
+# trans.set_message( "Galaxy instance '%s' stopped." % uci.name )
+#
return self.list( trans )
@web.expose
@@ -302,20 +310,12 @@
trans.app.model.CloudUserCredentials.table.c.name==credName ).first()
uci.user= user
uci.total_size = volSize # This is OK now because new instance is being created.
- # Need to flush because connection object accesses uci table
- uci.flush()
- # Capture store related information
+ uci.state = "new" # Valid states include: "new, "available", "running", "pending", "submitted", "terminating", or "error"
storage = model.CloudStore()
storage.user = user
storage.uci = uci
storage.size = volSize
- storage.availability_zone = "us-east-1a" # TODO: Give user choice here. Also, enable region selection.
- conn = get_connection( trans, credName )
- #conn.create_volume( volSize, storage.availability_zone, snapshot=None )
- # TODO: get correct value from AWS
- storage.volume_id = "made up"
- # TODO: If volume creation was successfull, set state to available
- uci.state = "available" # Valid states include: "available", "running" or "pending"
+ storage.availability_zone = zone # TODO: Give user choice here. Also, enable region selection.
# Persist
session = trans.sa_session
session.save_or_update( uci )
@@ -323,7 +323,7 @@
session.flush()
# Log and display the management page
trans.log_event( "User configured new cloud instance" )
- trans.set_message( "New Galaxy instance '%s' configured." % instanceName )
+ trans.set_message( "New Galaxy instance '%s' configured. Once instance status shows 'available' you will be able to start the instance." % instanceName )
return self.list( trans )
except ValueError:
vol_error = "Volume size must be specified as an integer value only, between 1 and 1000."
1
0