[hg] galaxy 3072: A lot of usability and UI enhancements. Curren...
details: http://www.bx.psu.edu/hg/galaxy/rev/676fae1a35f9 changeset: 3072:676fae1a35f9 user: Enis Afgan <afgane@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
participants (1)
-
Greg Von Kuster