details: http://www.bx.psu.edu/hg/galaxy/rev/c9c9adf06e9d changeset: 3066:c9c9adf06e9d user: Enis Afgan <afgane@gmail.com> date: Wed Sep 30 17:57:11 2009 -0400 description: Modified couple of DB tables to accomodate per-user cloud provider support diffstat: lib/galaxy/app.py | 4 +- lib/galaxy/cloud/__init__.py | 47 ++++++----- lib/galaxy/cloud/providers/eucalyptus.py | 78 ++++++++++++++++++- lib/galaxy/model/mapping.py | 16 ++- lib/galaxy/model/migrate/versions/0014_cloud_tables.py | 23 +++-- lib/galaxy/web/controllers/cloud.py | 66 +++++++++++----- templates/cloud/configure_cloud.mako | 14 +-- templates/cloud/view.mako | 4 + templates/cloud/viewInstance.mako | 20 +++++ 9 files changed, 196 insertions(+), 76 deletions(-) diffs (660 lines): diff -r cff66a171623 -r c9c9adf06e9d lib/galaxy/app.py --- a/lib/galaxy/app.py Thu Sep 17 13:45:01 2009 -0400 +++ b/lib/galaxy/app.py Wed Sep 30 17:57:11 2009 -0400 @@ -1,6 +1,6 @@ import sys, os, atexit -from galaxy import config, jobs, util, tools, web, cloud +from galaxy import config, jobs, util, tools, web from galaxy.tracks import store from galaxy.web import security import galaxy.model @@ -68,8 +68,6 @@ # 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 cff66a171623 -r c9c9adf06e9d lib/galaxy/cloud/__init__.py --- a/lib/galaxy/cloud/__init__.py Thu Sep 17 13:45:01 2009 -0400 +++ b/lib/galaxy/cloud/__init__.py Wed Sep 30 17:57:11 2009 -0400 @@ -1,11 +1,12 @@ import logging, threading, sys, os, time, subprocess, string, tempfile, re, traceback, shutil -from galaxy import util, model +from galaxy import util, model, config from galaxy.model import mapping from galaxy.model.orm import lazyload from galaxy.datatypes.tabular import * from galaxy.datatypes.interval import * from galaxy.datatypes import metadata +#from util import Bunch import pkg_resources pkg_resources.require( "PasteDeploy" ) @@ -17,6 +18,10 @@ log = logging.getLogger( __name__ ) # States for running a job. These are NOT the same as data states +#messages = { +# JOB_WAIT +# +# } 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 ): @@ -29,19 +34,20 @@ # The dispatcher manager underlying cloud instances self.provider = DefaultCloudProvider( app ) # Monitor for updating status of cloud instances - self.cloud_monitor = CloudMonitor( app, self.provider ) +# self.cloud_monitor = CloudMonitor( self.config, 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, name, storage_size, zone=None): + def createUCI( self, user, name, storage_size, zone=None): """ Createse User Configured Instance (UCI). Essentially, creates storage volume. """ - self.provider.createUCI( name, storage_size, zone ) + self.provider.createUCI( user, name, storage_size, zone ) def deleteUCI( self, name ): """ @@ -104,7 +110,7 @@ self.provider = provider self.monitor_thread = threading.Thread( target=self.__monitor ) self.monitor_thread.start() - log.info( "cloud manager started" ) + log.info( "Cloud manager started" ) # if app.config.get_bool( 'enable_job_recovery', True ): # self.__check_jobs_at_startup() @@ -143,11 +149,12 @@ while self.running: try: - self.__monitor_step() + #self.__monitor_step() + log.debug ( "would be calling monitor_step" ) except: log.exception( "Exception in cloud manager monitor_step" ) # Sleep - self.sleeper.sleep( 1 ) + self.sleeper.sleep( 2 ) def __monitor_step( self ): """ @@ -636,39 +643,39 @@ # 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: - provider_name = app.config.cloud_provider - if provider_name == "eucalyptus": + self.provider_name = app.config.cloud_provider + if self.provider_name == "eucalyptus": import providers.eucalyptus - self.cloud_provider[provider_name] = providers.eucalyptus.EucalyptusCloudProvider( app ) - elif provider_name == "ec2": + self.cloud_provider[self.provider_name] = providers.eucalyptus.EucalyptusCloudProvider( app ) + elif self.provider_name == "ec2": import providers.ec2 - self.cloud_provider[provider_name] = providers.ec2.EC2CloudProvider( app ) + self.cloud_provider[self.provider_name] = providers.ec2.EC2CloudProvider( app ) else: - log.error( "Unable to start unknown cloud provider: %s" %provider_name ) + log.error( "Unable to start unknown cloud provider: %s" %self.provider_name ) - def createUCI( self, name, storage_size, zone=None): + def createUCI( self, user, uciName, storage_size, zone=None): """ Createse User Configured Instance (UCI). Essentially, creates storage volume. """ - log.debug( "Creating UCI %s" % name ) - self.cloud_provider[name].createUCI( name, storage_size, zone ) + log.debug( "Creating UCI %s" % uciName ) + self.cloud_provider[self.provider_name].createUCI( user, uciName, storage_size, zone ) - def deleteUCI( self, name ): + def deleteUCI( self, uciName ): """ 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 ): + def addStorageToUCI( self, uciName ): """ Adds more storage to specified UCI """ - def startUCI( self, name, type ): + def startUCI( self, uciName, type ): """ Starts an instance of named UCI on the cloud. This implies, mounting of storage and starting Galaxy instance. """ - def stopUCI( self, name ): + def stopUCI( self, uciName ): """ Stops cloud instance associated with named UCI. This also implies stopping of Galaxy and unmounting of the file system. diff -r cff66a171623 -r c9c9adf06e9d lib/galaxy/cloud/providers/eucalyptus.py --- a/lib/galaxy/cloud/providers/eucalyptus.py Thu Sep 17 13:45:01 2009 -0400 +++ b/lib/galaxy/cloud/providers/eucalyptus.py Wed Sep 30 17:57:11 2009 -0400 @@ -2,7 +2,7 @@ from Queue import Queue, Empty from datetime import datetime -from galaxy import model +from galaxy import model # Database interaction class from galaxy.datatypes.data import nice_size from time import sleep @@ -19,11 +19,81 @@ """ Eucalyptus-based cloud provider implementation for managing instances. """ - def __init__( self, app ): - log.debug( "In eucalyptus cloud provider." ) + def __init__( self, app, user ): + log.debug( "Using eucalyptus as default cloud provider." ) + self.conn = get_connection( user ) + + + def get_connection( user ): + """ + 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 + 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" ) \ No newline at end of file + log.info( "eucalyptus cloud manager stopped" ) + + def createUCI( self, user, name, storage_size, zone=None): + """ + 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 + uci.flush() + storage.flush() + session.flush() + + 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. + """ \ No newline at end of file diff -r cff66a171623 -r c9c9adf06e9d lib/galaxy/model/mapping.py --- a/lib/galaxy/model/mapping.py Thu Sep 17 13:45:01 2009 -0400 +++ b/lib/galaxy/model/mapping.py Wed Sep 30 17:57:11 2009 -0400 @@ -400,6 +400,7 @@ 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( "name", TEXT ), Column( "state", TEXT ), Column( "total_size", Integer ), @@ -421,6 +422,7 @@ Column( "public_dns", TEXT ), Column( "private_dns", TEXT ), Column( "keypair_name", TEXT ), + Column( "keypair_material", TEXT ), Column( "availability_zone", TEXT ) ) CloudStore.table = Table( "cloud_store", metadata, @@ -436,19 +438,18 @@ Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ), index=True ), Column( "status", TEXT ), Column( "device", TEXT ), - Column( "space_consumed", Integer ) - ) + Column( "space_consumed", Integer ) ) CloudUserCredentials.table = Table( "cloud_user_credentials", 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( "name", TEXT), - Column( "access_key", TEXT), - Column( "secret_key", TEXT), - Column( "defaultCred", Boolean, default=False) - ) + Column( "name", TEXT ), + Column( "access_key", TEXT ), + Column( "secret_key", TEXT ), + Column( "defaultCred", Boolean, default=False ), + Column( "provider_name", TEXT ) ) # *************************************************************************** StoredWorkflow.table = Table( "stored_workflow", metadata, @@ -962,6 +963,7 @@ assign_mapper( context, UCI, UCI.table, properties=dict( user=relation( User ), + credentials=relation( CloudUserCredentials ), instance=relation( CloudInstance, backref='uci' ), store=relation( CloudStore, backref='uci' ) ) ) diff -r cff66a171623 -r c9c9adf06e9d lib/galaxy/model/migrate/versions/0014_cloud_tables.py --- a/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Thu Sep 17 13:45:01 2009 -0400 +++ b/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Wed Sep 30 17:57:11 2009 -0400 @@ -25,6 +25,7 @@ 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( "name", TEXT ), Column( "state", TEXT ), Column( "total_size", Integer ), @@ -46,6 +47,7 @@ Column( "public_dns", TEXT ), Column( "private_dns", TEXT ), Column( "keypair_name", TEXT ), + Column( "keypair_material", TEXT ), Column( "availability_zone", TEXT ) ) CloudStore_table = Table( "cloud_store", metadata, @@ -61,19 +63,18 @@ Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ), index=True ), Column( "status", TEXT ), Column( "device", TEXT ), - Column( "space_consumed", Integer ) - ) + Column( "space_consumed", Integer ) ) CloudUserCredentials_table = Table( "cloud_user_credentials", 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( "name", TEXT), - Column( "access_key", TEXT), - Column( "secret_key", TEXT), - Column( "defaultCred", Boolean, default=False) - ) + Column( "name", TEXT ), + Column( "access_key", TEXT ), + Column( "secret_key", TEXT ), + Column( "defaultCred", Boolean, default=False ), + Column( "provider_name", TEXT ) ) def upgrade(): metadata.reflect() @@ -92,8 +93,8 @@ def downgrade(): metadata.reflect() try: - log.debug( "Would drop cloud_image table." ) - CloudImage_table.drop() #Enable before putting final version +# 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 ) ) @@ -108,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 cff66a171623 -r c9c9adf06e9d lib/galaxy/web/controllers/cloud.py --- a/lib/galaxy/web/controllers/cloud.py Thu Sep 17 13:45:01 2009 -0400 +++ b/lib/galaxy/web/controllers/cloud.py Wed Sep 30 17:57:11 2009 -0400 @@ -21,12 +21,16 @@ galaxy.eggs.require("boto") from boto.ec2.connection import EC2Connection from boto.ec2.regioninfo import RegionInfo +from galaxy.cloud import CloudManager import logging log = logging.getLogger( __name__ ) class CloudController( BaseController ): +# def __init__( self ): +# self.cloudManager = CloudManager() + @web.expose def index( self, trans ): return trans.fill_template( "cloud/index.mako" ) @@ -262,12 +266,20 @@ @web.expose @web.require_login( "use Galaxy cloud" ) - def configureNew( self, trans, instanceName='', volSize=''): + def configureNew( self, trans, instanceName='', credName='', volSize='', zone=''): """ Configure and add new cloud instance to user's instance pool """ + inst_error = vol_error = cred_error = None user = trans.get_user() - inst_error = vol_error = None + # 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 + for cred in storedCreds: + if cred.name == credName: + credsMatch = True + if instanceName: # Create new user configured instance try: @@ -280,23 +292,30 @@ vol_error = "Volume size cannot exceed 1000GB. You must specify an integer between 1 and 1000." elif int( volSize ) < 1: vol_error = "Volume size cannot be less than 1GB. You must specify an integer between 1 and 1000." + elif not credsMatch: + cred_error = "You specified unknown credentials." else: # Capture user configured instance information uci = model.UCI() uci.name = instanceName + uci.credentials = trans.app.model.CloudUserCredentials.filter( + trans.app.model.CloudUserCredentials.table.c.name==credName ).first() uci.user= user - uci.state = "available" # Valid states include: "available", "running" or "pending" 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 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 ) + 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" # Persist session = trans.sa_session session.save_or_update( uci ) @@ -304,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." % uci.name ) + trans.set_message( "New Galaxy instance '%s' configured." % instanceName ) return self.list( trans ) except ValueError: vol_error = "Volume size must be specified as an integer value only, between 1 and 1000." @@ -315,6 +334,7 @@ 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 ) ) @@ -347,7 +367,7 @@ return trans.show_form( web.FormBuilder( web.url_for(), "Add new cloud image", submit_text="Add" ) - .add_text( "image_id", "Image ID", value='', error=error ) + .add_text( "image_id", "Machine Image ID (AMI or EMI)", value='', error=error ) .add_text( "manifest", "Manifest", value='', error=error ) ) @web.expose @@ -378,18 +398,20 @@ @web.expose @web.require_login( "add credentials" ) - def add( self, trans, credName='', accessKey='', secretKey='', defaultCred=True ): + def add( self, trans, credName='', accessKey='', secretKey='', providerName='' ): """ Add user's cloud credentials stored under name `credName`. """ user = trans.get_user() - cred_error = accessKey_error = secretKey_error = None + cred_error = accessKey_error = secretKey_error = provider_error = None if credName: if len( credName ) > 255: 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." + elif ( ( providerName.lower()!='ec2' ) and ( providerName.lower()!='eucalyptus' ) ): + provider_error = "You specified an unsupported cloud provider." else: # Create new user stored credentials credentials = model.CloudUserCredentials() @@ -397,6 +419,7 @@ credentials.user = user credentials.access_key = accessKey credentials.secret_key = secretKey + credentials.provider_name = providerName.lower() # Persist session = trans.sa_session session.save_or_update( credentials ) @@ -404,15 +427,15 @@ # 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) +# 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 ) - .add_input( "checkbox","Make default credentials","defaultCred", value='defaultCred' ) ) + .add_password( "secretKey", "Secret key", value='', error=secretKey_error ) ) @web.expose @web.require_login( "view credentials" ) @@ -443,8 +466,9 @@ @web.require_login( "delete credentials" ) def delete( self, trans, id=None ): """ - Delete user's cloud credentials - """ + 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 + """ # Load credentials from database stored = get_stored_credentials( trans, id ) # Delete and save @@ -899,7 +923,7 @@ def get_stored_credentials( trans, id, check_ownership=True ): """ - Get a StoredUserCredntials from the database by id, verifying ownership. + Get StoredUserCredentials from the database by id, verifying ownership. """ # Check if 'id' is in int (i.e., it was called from this program) or # it was passed from the web (in which case decode it) @@ -997,21 +1021,21 @@ cloudInstance = conn.get_all_instances( idLst )[0].instances[0] return cloudInstance -def get_connection( trans ): +def get_connection( trans, credName ): """ - Establishes EC2 conncection using user's default credentials + Establishes EC2 connection using user's default credentials """ log.debug( '##### Establishing cloud connection.' ) user = trans.get_user() - creds = trans.sa_session.query(model.CloudUserCredentials).filter_by(user=user, defaultCred=True).first() + creds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user, name=credName ).first() if creds: a_key = creds.access_key s_key = creds.secret_key # Amazon EC2 #conn = EC2Connection( a_key, s_key ) # Eucalyptus Public Cloud - 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, "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: error( "You must specify default credentials before starting an instance." ) @@ -1031,6 +1055,8 @@ 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 cff66a171623 -r c9c9adf06e9d templates/cloud/configure_cloud.mako --- a/templates/cloud/configure_cloud.mako Thu Sep 17 13:45:01 2009 -0400 +++ b/templates/cloud/configure_cloud.mako Wed Sep 30 17:57:11 2009 -0400 @@ -34,7 +34,7 @@ <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%"> <tr class="header"> <th>Credentials name</th> - <th>Default</th> + <th>Provider</th> <th></th> </tr> %for i, cloudCredential in enumerate( cloudCredentials ): @@ -43,15 +43,7 @@ ${cloudCredential.name} <a id="cr-${i}-popup" class="popup-arrow" style="display: none;">▼</a> </td> - ## Comment <td>${len(workflow.latest_workflow.steps)}</td> - ##<td>${str(cloudCredential.update_time)[:19]}</td> - <td> - ##${str(cloudCredential.defaultCred)} - <% - if cloudCredential.defaultCred: - context.write('*') - %> - </td> + <td>${cloudCredential.provider_name}</td> <td> <div popupmenu="cr-${i}-popup"> @@ -85,7 +77,7 @@ <colgroup width="25%"></colgroup> <colgroup width="10%"></colgroup> <tr class="header"> - <th>Live instances</th> + <th>Your instances</th> <th>Storage size (GB)</th> <th>State</th> <th>Alive since</th> diff -r cff66a171623 -r c9c9adf06e9d templates/cloud/view.mako --- a/templates/cloud/view.mako Thu Sep 17 13:45:01 2009 -0400 +++ b/templates/cloud/view.mako Wed Sep 30 17:57:11 2009 -0400 @@ -38,6 +38,10 @@ </td> </tr> <tr> + <td> Cloud provider: </td> + <td> ${str(credDetails.provider_name)[:16]}</td> + </tr> + <tr> <td> Access key: </td> <td> ${credDetails.access_key} diff -r cff66a171623 -r c9c9adf06e9d templates/cloud/viewInstance.mako --- a/templates/cloud/viewInstance.mako Thu Sep 17 13:45:01 2009 -0400 +++ b/templates/cloud/viewInstance.mako Wed Sep 30 17:57:11 2009 -0400 @@ -103,6 +103,26 @@ <td> ${liveInstance.keypair_name} </td> </tr> %endif + %if liveInstance.keypair_material != None: + <tr> + <td> Keypair material:</td> + <div id="shortComment2"> + <a onclick="document.getElementById('fullComment2').style.display = 'block'; + document.getElementById('shortComment2').style.display = 'none'; return 0" + href="javascript:void(0)"> + + Show + </a> + </div> + <div id="fullComment2" style="DISPLAY: none"> + <nobr><b>${liveInstance.keypair_material}</b></nobr><br/> + <a onclick="document.getElementById('shortComment2').style.display = 'block'; + document.getElementById('fullComment2').style.display = 'none'; return 0;" + href="javascript:void(0)"> + - Hide + </a> + </div> + </tr> + %endif </table> %else: