galaxy-dev
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 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
- 10008 discussions

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/83551d2b3144
changeset: 3093:83551d2b3144
user: Enis Afgan <afgane(a)gmail.com>
date: Thu Nov 12 15:26:10 2009 -0500
description:
Cleaned up AJAXed UCI state changes on main UI. UCI still does not get automatically removed after from displayed list being deleted by user.
diffstat:
templates/cloud/configure_cloud.mako | 28 ++++++++++++++++++----------
1 files changed, 18 insertions(+), 10 deletions(-)
diffs (61 lines):
diff -r 118dc385752b -r 83551d2b3144 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Thu Nov 12 14:24:43 2009 -0500
+++ b/templates/cloud/configure_cloud.mako Thu Nov 12 15:26:10 2009 -0500
@@ -36,24 +36,21 @@
old_state = $(elem + "-state").text();
prev_old_state = trim19( $(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 );
+ //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 );
//console.log( trim19(prev_old_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' ) || ( prev_old_state.match('newUCI') && new_state=='available' ) || \
- ( prev_old_state.match('new') && new_state=='available' ) || ( prev_old_state.match('deletingUCI') && new_state=='deleted' ) || \
- ( prev_old_state.match('deleting') && new_state=='deleted' ) ) {
+ ( old_state=='pending' && new_state=='available' ) || ( old_state=='submitted' && new_state=='available' ) || \
+ ( prev_old_state.match('creating') && 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' ) || ( prev_old_state.match('newUCI') && new_state=='error' ) || \
- ( prev_old_state.match('new') && new_state=='error' ) || \
- ( prev_old_state.match('deletingUCI') && new_state=='error' ) ) {
+ ( prev_old_state.match('new') && new_state=='error' ) || ( prev_old_state.match('deletingUCI') && new_state=='error' ) ) {
// TODO: Following clause causes constant page refresh for an exception thrown as a result of instance not starting correctly - need alternative method!
//( prev_old_state.match('available') && new_state=='error' ) || ( prev_old_state.match('deleting') && new_state=='error' ) \
@@ -61,6 +58,10 @@
location.replace( url );
}
+ if ( prev_old_state.match('deletingUCI') || prev_old_state.match('deleting') ) {
+ setTimeout("update_state()", 3000);
+ }
+
if ( new_state=='shutting-down' || new_state=='shutting-downUCI' ) {
$(elem + "-link").text( "" );
}
@@ -89,7 +90,14 @@
// Update 'state' and 'time alive' fields
$(elem + "-state").text( data[i].state );
- //$(elem + "-state-p").text( data[i].state );
+ if ( ( prev_old_state.match('newUCI') && new_state=='new' ) || \
+ ( prev_old_state.match('newUCI') && new_state=='creating' ) || ( prev_old_state.match('new') && new_state=='creating' ) || \
+ ( prev_old_state.match('deletingUCI') && new_state=='deleted' ) || ( prev_old_state.match('deleting') && new_state=='deleted' ) || \
+ ( prev_old_state.match('available') && new_state=='error' ) || ( prev_old_state.match('deleting') && new_state=='error' ) ) {
+ // TODO: on state change from available->error and deleting->error page should be refreshed but that causes problems with
+ // constant refreshings depending on what error message is so at least do it here...
+ $(elem + "-state-p").text( data[i].state );
+ }
if (data[i].launch_time) {
$(elem + "-launch_time").text( data[i].launch_time.substring(0, 16 ) + " UTC (" + data[i].time_ago + ")" );
}
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/7d013eb98022
changeset: 3094:7d013eb98022
user: Kanwei Li <kanwei(a)gmail.com>
date: Thu Nov 12 16:36:07 2009 -0500
description:
Import cloud management module
diffstat:
eggs.ini | 2 +
lib/galaxy/app.py | 4 +-
lib/galaxy/cloud/__init__.py | 620 +++++++++++
lib/galaxy/cloud/providers/ec2.py | 940 +++++++++++++++++
lib/galaxy/cloud/providers/eucalyptus.py | 923 ++++++++++++++++
lib/galaxy/config.py | 7 +
lib/galaxy/model/__init__.py | 54 +
lib/galaxy/model/mapping.py | 147 ++
lib/galaxy/model/migrate/versions/0026_cloud_tables.py | 152 ++
lib/galaxy/web/controllers/cloud.py | 1193 +++++++++++++++++++++
static/images/silk/resultset_previous.png |
templates/admin/index.mako | 9 +
templates/base_panels.mako | 14 +-
templates/cloud/add_credentials.mako | 110 ++
templates/cloud/add_image.mako | 98 +
templates/cloud/add_provider.mako | 252 ++++
templates/cloud/configure_cloud.mako | 367 ++++++
templates/cloud/configure_uci.mako | 116 ++
templates/cloud/edit_credentials.mako | 91 +
templates/cloud/edit_image.mako | 92 +
templates/cloud/edit_provider.mako | 261 ++++
templates/cloud/index.mako | 16 +
templates/cloud/list_images.mako | 90 +
templates/cloud/view_credentials.mako | 157 ++
templates/cloud/view_instance.mako | 140 ++
templates/cloud/view_provider.mako | 126 ++
templates/cloud/view_snapshots.mako | 90 +
templates/cloud/view_usage.mako | 117 ++
templates/root/index.mako | 13 +-
universe_wsgi.ini.sample | 5 +
30 files changed, 6197 insertions(+), 9 deletions(-)
diffs (truncated from 6417 to 3000 lines):
diff -r 0984c3800775 -r 7d013eb98022 eggs.ini
--- a/eggs.ini Thu Nov 12 15:25:48 2009 -0500
+++ b/eggs.ini Thu Nov 12 16:36:07 2009 -0500
@@ -52,6 +52,7 @@
wsgiref = 0.1.2
Babel = 0.9.4
wchartype = 0.1
+boto = 1.8d
; extra version information
[tags]
@@ -102,3 +103,4 @@
wsgiref = http://pypi.python.org/packages/source/w/wsgiref/wsgiref-0.1.2.zip
Babel = http://ftp.edgewall.com/pub/babel/Babel-0.9.4.zip
wchartype = http://ginstrom.com/code/wchartype-0.1.zip
+boto = http://boto.googlecode.com/files/boto-1.8d.tar.gz
\ No newline at end of file
diff -r 0984c3800775 -r 7d013eb98022 lib/galaxy/app.py
--- a/lib/galaxy/app.py Thu Nov 12 15:25:48 2009 -0500
+++ b/lib/galaxy/app.py Thu Nov 12 16:36:07 2009 -0500
@@ -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 0984c3800775 -r 7d013eb98022 lib/galaxy/cloud/__init__.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/cloud/__init__.py Thu Nov 12 16:36:07 2009 -0500
@@ -0,0 +1,620 @@
+import logging, threading, sys, os, time, subprocess, string, tempfile, re, traceback, shutil
+
+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 galaxy.util.bunch import Bunch
+from sqlalchemy import or_
+
+import pkg_resources
+pkg_resources.require( "PasteDeploy" )
+
+from paste.deploy.converters import asbool
+
+from Queue import Queue, Empty
+
+log = logging.getLogger( __name__ )
+
+uci_states = Bunch(
+ NEW_UCI = "newUCI",
+ NEW = "new",
+ CREATING = "creating",
+ DELETING_UCI = "deletingUCI",
+ DELETING = "deleting",
+ DELETED = "deleted",
+ SUBMITTED_UCI = "submittedUCI",
+ SUBMITTED = "submitted",
+ SHUTTING_DOWN_UCI = "shutting-downUCI",
+ SHUTTING_DOWN = "shutting-down",
+ AVAILABLE = "available",
+ RUNNING = "running",
+ PENDING = "pending",
+ ERROR = "error",
+ SNAPSHOT_UCI = "snapshotUCI",
+ SNAPSHOT = "snapshot"
+)
+instance_states = Bunch(
+ TERMINATED = "terminated",
+ SUBMITTED = "submitted",
+ RUNNING = "running",
+ PENDING = "pending",
+ SHUTTING_DOWN = "shutting-down",
+ ERROR = "error"
+)
+
+snapshot_status = Bunch(
+ SUBMITTED = 'submitted',
+ PENDING = 'pending',
+ COMPLETED = 'completed',
+ DELETE = 'delete',
+ DELETED= 'deleted',
+ ERROR = "error"
+)
+
+class CloudManager( object ):
+ """
+ Highest level interface to cloud management.
+ """
+ def __init__( self, app ):
+ self.app = app
+ if self.app.config.get_bool( "enable_cloud_execution", True ):
+ # The dispatcher manager for underlying cloud instances - implements and contacts individual cloud providers
+ self.provider = CloudProvider( app )
+ # Monitor for updating status of cloud instances
+ self.cloud_monitor = CloudMonitor( self.app, self.provider )
+ else:
+ self.job_queue = self.job_stop_queue = NoopCloudMonitor()
+
+ def shutdown( self ):
+ self.cloud_monitor.shutdown()
+
+class Sleeper( object ):
+ """
+ Provides a 'sleep' method that sleeps for a number of seconds *unless*
+ the notify method is called (from a different thread).
+ """
+ def __init__( self ):
+ self.condition = threading.Condition()
+ def sleep( self, seconds ):
+ self.condition.acquire()
+ self.condition.wait( seconds )
+ self.condition.release()
+ def wake( self ):
+ self.condition.acquire()
+ self.condition.notify()
+ self.condition.release()
+
+class CloudMonitor( object ):
+ """
+ Cloud manager, waits for user to instantiate a cloud instance and then invokes a
+ CloudProvider.
+ """
+ STOP_SIGNAL = object()
+ def __init__( self, app, provider ):
+ """Start the cloud manager"""
+ self.app = app
+ # Keep track of the pid that started the cloud manager, only it
+ # has valid threads
+ self.parent_pid = os.getpid()
+
+ # Contains requests that are waiting (only use from monitor thread)
+ self.waiting = []
+
+ # Helper for interruptable sleep
+ self.sleeper = Sleeper()
+ self.running = True
+ self.provider = provider
+ self.monitor_thread = threading.Thread( target=self.__monitor )
+ self.monitor_thread.start()
+ log.info( "Cloud manager started" )
+
+ def __monitor( self ):
+ """
+ Daemon that continuously monitors cloud instance requests as well as state
+ of running instances.
+ """
+ # 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 iterations (1 minute)
+ 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 ):
+ """
+ Called repeatedly by `monitor` to process cloud instance requests.
+ TODO: Update following description to match the code
+ Gets any new cloud instance requests from the database, then iterates
+ over all new and waiting jobs to check the state of the jobs each
+ depends on. If the job has dependencies that have not finished, it
+ it goes to the waiting queue. If the job has dependencies with errors,
+ it is marked as having errors and removed from the queue. Otherwise,
+ the job is dispatched.
+ """
+ # Get an orm (object relational mapping) session
+ session = mapping.Session()
+ new_requests = []
+
+ for r in session.query( model.UCI ) \
+ .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,
+ model.UCI.c.state==uci_states.SNAPSHOT_UCI ) ) \
+ .all():
+ uci_wrapper = UCIwrapper( r )
+ new_requests.append( uci_wrapper )
+
+ for uci_wrapper in new_requests:
+ session.clear()
+ self.put( uci_wrapper )
+
+ # Done with the session
+ mapping.Session.remove()
+
+ def put( self, uci_wrapper ):
+ """Add a request to the queue."""
+ self.provider.put( uci_wrapper )
+ self.sleeper.wake()
+
+ def shutdown( self ):
+ """Attempts to gracefully shut down the worker thread"""
+ if self.parent_pid != os.getpid():
+ # We're not the real queue, do nothing
+ return
+ else:
+ log.info( "Sending stop signal to worker thread" )
+ self.running = False
+ self.sleeper.wake()
+ 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: (1) state for the UCI, or (2) instance_id and it's state, or (3) 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, key_name, key_material=None ):
+ """
+ Sets key pair value for current UCI.
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.key_pair_name = key_name
+ if key_material is not None:
+ uci.key_pair_material = key_material
+ uci.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 - i_index (as it is stored in local
+ Galaxy database) or with specified instance ID - i_id (as obtained from the cloud provider AND stored
+ in local Galaxy Database). Either '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 ):
+ """
+ Stores stop time in local database for instance with specified index - i_index (as it is stored in local
+ Galaxy database) or with specified instance ID - i_id (as obtained from the cloud provider AND stored
+ in local Galaxy Database). Either 'i_index' or 'i_id' needs to be provided.
+ """
+ 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_security_group_name( self, security_group_name, i_index=None, i_id=None ):
+ """
+ Stores security group name in local database for instance with specified index - i_index (as it is stored in local
+ Galaxy database) or with specified instance ID - i_id (as obtained from the cloud provider AND stored
+ in local Galaxy Database). Either 'i_index' or 'i_id' needs to be provided.
+ """
+ if i_index != None:
+ instance = model.CloudInstance.get( i_index )
+ instance.security_group = security_group_name
+ instance.flush()
+ elif i_id != None:
+ instance = model.CloudInstance.filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ instance.security_group = security_group_name
+ instance.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_error( self, error, store_index=None, store_id=None ):
+ if store_index != None:
+ store = model.CloudStore.get( store_index )
+ elif store_id != None:
+ store = model.CloudStore.filter_by( volume_id = store_id ).first()
+ else:
+ return None
+
+ store.error = error
+ store.flush()
+
+ def set_store_status( self, vol_id, status ):
+ vol = model.CloudStore.filter( model.CloudStore.c.volume_id == vol_id ).first()
+ vol.status = status
+ vol.flush()
+
+ def set_snapshot_id( self, snap_index, id ):
+ snap = model.CloudSnapshot.get( snap_index )
+ snap.snapshot_id = id
+ snap.flush()
+
+ def set_snapshot_status( self, status, snap_index=None, snap_id=None ):
+ if snap_index != None:
+ snap = model.CloudSnapshot.get( snap_index )
+ elif snap_id != None:
+ snap = model.CloudSnapshot.filter_by( snapshot_id = snap_id).first()
+ else:
+ return
+ snap.status = status
+ snap.flush()
+
+ def set_snapshot_error( self, error, snap_index=None, snap_id=None, set_status=False ):
+ if snap_index != None:
+ snap = model.CloudSnapshot.get( snap_index )
+ elif snap_id != None:
+ snap = model.CloudSnapshot.filter_by( snapshot_id = snap_id).first()
+ else:
+ return
+ snap.error = error
+
+ if set_status:
+ snap.status = snapshot_status.ERROR
+
+ snap.flush()
+
+ def set_store_availability_zone( self, availability_zone, vol_id=None ):
+ """
+ Sets availability zone of storage volumes for either ALL volumes associated with current
+ UCI or for the volume whose volume ID (e.g., 'vol-39F80512') is provided as argument.
+ """
+ if vol_id is not None:
+ vol = model.CloudStore.filter( model.CloudStore.c.volume_id == vol_id ).all()
+ else:
+ vol = model.CloudStore.filter( model.CloudStore.c.uci_id == self.uci_id ).all()
+
+ for v in vol:
+ v.availability_zone = availability_zone
+ v.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, vol_id, instance_id ):
+ """
+ Stores instance ID that given store volume is attached to. Store volume ID should
+ be given in following format: 'vol-78943248'
+ """
+ vol = model.CloudStore.filter( model.CloudStore.c.volume_id == vol_id ).first()
+ vol.i_id = instance_id
+ vol.flush()
+
+ def set_error( self, error, set_state=False ):
+ """
+ Sets error field of given UCI in local Galaxy database as well as any instances associated with
+ this UCI whose state is 'None' or 'SUBMITTED'. If set_state is set to 'true',
+ method also sets state of give UCI and corresponding instances to 'error'
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.error = error
+ if set_state:
+ uci.state = uci_states.ERROR
+ instances = model.CloudInstance \
+ .filter_by( uci=uci ) \
+ .filter( or_( model.CloudInstance.c.state==None, model.CloudInstance.c.state==instance_states.SUBMITTED ) ) \
+ .all()
+ for i in instances:
+ i.error = error
+ i.state = instance_states.ERROR
+ i.flush()
+ uci.flush()
+
+ def set_deleted( self ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.state = uci_states.DELETED # for bookkeeping reasons, mark as deleted but don't actually delete.
+ uci.deleted = True
+ uci.flush()
+
+ # --------- Getter methods -----------------
+
+ 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 uci.credentials.provider.type
+
+ 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_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_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 ):
+ """
+ Returns keypair name associated with given UCI.
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.key_pair_name
+
+ def get_key_pair_material( self ):
+ """
+ Returns keypair material (i.e., private key) associated with given UCI.
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.key_pair_material
+
+ def get_security_group_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 security group name associated
+ with given instance.
+ """
+ if i_index != None:
+ instance = model.CloudInstance.get( i_index )
+ return instance.security_group
+ elif i_id != None:
+ instance = model.CloudInstance.filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ return instance.security_group
+
+ 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_uci_availability_zone( self ):
+ """
+ Returns UCI's availability zone.
+ Because all of storage volumes associated with a given UCI must be in the same
+ availability zone, availability of a UCI is determined by availability zone of
+ any one storage volume.
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.store[0].availability_zone
+
+ def get_store_size( self, store_id=0 ):
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.store[store_id].size
+
+ def get_store_volume_id( self, store_id=0 ):
+ """
+ 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()
+
+ def get_snapshots( self, status=None ):
+ """ Returns database objects for all snapshots associated with this UCI and in given status."""
+ return model.CloudSnapshot.filter_by( uci_id=self.uci_id, status=status ).all()
+
+ def get_uci( self ):
+ """ Returns database object for given UCI. """
+ uci = model.UCI.get( self.uci_id )
+ 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()
+ return uci.launch_time
+
+class CloudProvider( object ):
+ def __init__( self, app ):
+ import providers.eucalyptus
+ import providers.ec2
+
+ self.app = app
+ self.cloud_provider = {}
+ self.cloud_provider["eucalyptus"] = providers.eucalyptus.EucalyptusCloudProvider( app )
+ self.cloud_provider["ec2"] = providers.ec2.EC2CloudProvider( app )
+
+ def put( self, uci_wrapper ):
+ """ Put given request for UCI manipulation into provider's request queue."""
+ self.cloud_provider[uci_wrapper.get_provider_type()].put( uci_wrapper )
+
+ def update( self ):
+ """
+ Runs a global status update across all providers for all UCIs in state other than 'terminated' and 'available'.
+ Reason behind this method is to sync state of local DB and real world resources.
+ """
+ for provider in self.cloud_provider.keys():
+# log.debug( "Running global update for provider: '%s'" % provider )
+ self.cloud_provider[provider].update()
+
+ def shutdown( self ):
+ for runner in self.cloud_provider.itervalues():
+ runner.shutdown()
+
+class NoopCloudMonitor( object ):
+ """
+ Implements the CloudMonitor interface but does nothing
+ """
+ def put( self, *args ):
+ return
+ def shutdown( self ):
+ return
+
diff -r 0984c3800775 -r 7d013eb98022 lib/galaxy/cloud/providers/ec2.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/cloud/providers/ec2.py Thu Nov 12 16:36:07 2009 -0500
@@ -0,0 +1,940 @@
+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.model import mapping
+from galaxy.datatypes.data import nice_size
+from galaxy.util.bunch import Bunch
+from galaxy.cloud import UCIwrapper
+from Queue import Queue
+from sqlalchemy import or_, and_
+
+import galaxy.eggs
+galaxy.eggs.require("boto")
+from boto.ec2.connection import EC2Connection
+from boto.ec2.regioninfo import RegionInfo
+import boto.exception
+import boto
+
+import logging
+log = logging.getLogger( __name__ )
+
+uci_states = Bunch(
+ NEW_UCI = "newUCI",
+ NEW = "new",
+ CREATING = "creating",
+ DELETING_UCI = "deletingUCI",
+ DELETING = "deleting",
+ SUBMITTED_UCI = "submittedUCI",
+ SUBMITTED = "submitted",
+ SHUTTING_DOWN_UCI = "shutting-downUCI",
+ SHUTTING_DOWN = "shutting-down",
+ AVAILABLE = "available",
+ RUNNING = "running",
+ PENDING = "pending",
+ ERROR = "error",
+ DELETED = "deleted",
+ SNAPSHOT_UCI = "snapshotUCI",
+ SNAPSHOT = "snapshot"
+)
+
+instance_states = Bunch(
+ TERMINATED = "terminated",
+ SUBMITTED = "submitted",
+ RUNNING = "running",
+ PENDING = "pending",
+ SHUTTING_DOWN = "shutting-down",
+ ERROR = "error"
+)
+
+store_status = Bunch(
+ IN_USE = "in-use",
+ CREATING = "creating",
+ DELETED = 'deleted',
+ ERROR = "error"
+)
+
+snapshot_status = Bunch(
+ SUBMITTED = 'submitted',
+ PENDING = 'pending',
+ COMPLETED = 'completed',
+ DELETE = 'delete',
+ DELETED= 'deleted',
+ ERROR = "error"
+)
+
+class EC2CloudProvider( object ):
+ """
+ Amazon EC2-based cloud provider implementation for managing instances.
+ """
+ STOP_SIGNAL = object()
+ def __init__( self, app ):
+ self.type = "ec2" # cloud provider type (e.g., ec2, eucalyptus, opennebula)
+ self.zone = "us-east-1a"
+ self.security_group = "galaxyWeb"
+ self.queue = Queue()
+
+ self.threads = []
+ nworkers = 5
+ log.info( "Starting EC2 cloud controller workers..." )
+ for i in range( nworkers ):
+ worker = threading.Thread( target=self.run_next )
+ worker.start()
+ self.threads.append( worker )
+ log.debug( "%d EC2 cloud workers ready", nworkers )
+
+ 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_state = uci_wrapper.get_state()
+ if uci_state is self.STOP_SIGNAL:
+ return
+ try:
+ if uci_state==uci_states.NEW:
+ self.createUCI( uci_wrapper )
+ elif uci_state==uci_states.DELETING:
+ self.deleteUCI( uci_wrapper )
+ elif uci_state==uci_states.SUBMITTED:
+ self.startUCI( uci_wrapper )
+ elif uci_state==uci_states.SHUTTING_DOWN:
+ self.stopUCI( uci_wrapper )
+ elif uci_state==uci_states.SNAPSHOT:
+ self.snapshotUCI( uci_wrapper )
+ except:
+ log.exception( "Uncaught exception executing cloud request." )
+ cnt += 1
+
+ def get_connection( self, uci_wrapper ):
+ """
+ Establishes EC2 cloud connection using user's credentials associated with given UCI
+ """
+ log.debug( 'Establishing %s cloud connection.' % self.type )
+ provider = uci_wrapper.get_provider()
+ try:
+ region = RegionInfo( None, provider.region_name, provider.region_endpoint )
+ except Exception, ex:
+ err = "Selecting region with cloud provider failed: " + str( ex )
+ log.error( err )
+ uci_wrapper.set_error( err, 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 boto.exception.EC2ResponseError, e:
+ err = "Establishing connection with cloud failed: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ return None
+
+ return conn
+
+ def check_key_pair( self, uci_wrapper, conn ):
+ """
+ Generate key pair using user's credentials
+ """
+ kp = None
+ kp_name = uci_wrapper.get_name().replace(' ','_') + "_kp"
+ log.debug( "Checking user's key pair: '%s'" % kp_name )
+ try:
+ kp = conn.get_key_pair( kp_name )
+ uci_kp_name = uci_wrapper.get_key_pair_name()
+ uci_material = uci_wrapper.get_key_pair_material()
+ if kp != None:
+ if kp.name != uci_kp_name or uci_material == None:
+ # key pair exists on the cloud but not in local database, so re-generate it (i.e., delete and then create)
+ try:
+ conn.delete_key_pair( kp_name )
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while deleting key pair: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ else:
+ try:
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while creating key pair: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ except Exception, ex:
+ err = "Exception while creating key pair: " + str( ex )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ 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'" % kp_name )
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ else:
+ err = "EC2 response error while retrieving key pair: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+ if kp != None:
+ return kp.name
+ else:
+ return None
+
+ def create_key_pair( self, conn, kp_name ):
+ try:
+ return conn.create_key_pair( kp_name )
+ except boto.exception.EC2ResponseError, e:
+ return None
+
+ def get_mi_id( self, uci_wrapper, i_index ):
+ """
+ Get appropriate machine image (mi) based on instance size.
+ """
+ i_type = uci_wrapper.get_type( i_index )
+ if i_type=='m1.small' or i_type=='c1.medium':
+ arch = 'i386'
+ else:
+ arch = 'x86_64'
+
+ mi = model.CloudImage.filter_by( deleted=False, provider_type=self.type, architecture=arch ).first()
+ if mi:
+ return mi.image_id
+ else:
+ err = "Machine image could not be retrieved"
+ log.error( "%s for UCI '%s'." % (err, uci_wrapper.get_name() ) )
+ uci_wrapper.set_error( err+". Contact site administrator to ensure needed machine image is registered.", True )
+ return None
+
+ def shutdown( self ):
+ """Attempts to gracefully shut down the monitor thread"""
+ log.info( "sending stop signal to worker threads in EC2 cloud manager" )
+ for i in range( len( self.threads ) ):
+ self.queue.put( self.STOP_SIGNAL )
+ log.info( "EC2 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_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_wrapper )
+ 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 )
+
+ 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 )
+
+ # Wait for a while to ensure volume was created
+# vol_status = vol.status
+# for i in range( 30 ):
+# if vol_status is not "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_wrapper.change_state( uci_state='error' )
+# return
+
+ # Retrieve created volume again to get updated status
+ try:
+ vl = conn.get_all_volumes( [vol.id] )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while retrieving (i.e., updating status) of just created storage volume '" + vol.id + "': " + str( e )
+ log.error( err )
+ uci_wrapper.set_store_status( vol.id, uci_states.ERROR )
+ uci_wrapper.set_error( err, True )
+ return
+ except Exception, ex:
+ err = "Error while retrieving (i.e., updating status) of just created storage volume '" + vol.id + "': " + str( ex )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ return
+
+ if len( vl ) > 0:
+ uci_wrapper.change_state( uci_state=vl[0].status )
+ uci_wrapper.set_store_status( vol.id, vl[0].status )
+ else:
+ err = "Volume '" + vol.id +"' not found by EC2 after being created."
+ log.error( err )
+ uci_wrapper.set_store_status( vol.id, uci_states.ERROR )
+ uci_wrapper.set_error( err, True )
+
+ 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 )
+ try:
+ if conn.delete_volume( v.volume_id ):
+ deletedList.append( v.volume_id )
+ v.deleted = True
+ v.flush()
+ count += 1
+ else:
+ failedList.append( v.volume_id )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while deleting storage volume '" + v.volume_id + "': " + str( e )
+ log.error( err )
+ uci_wrapper.set_store_error( err, store_id = v.volume_id )
+ uci_wrapper.set_error( err, True )
+
+ # Delete UCI if all of associated
+ if count == len( vl ):
+ uci_wrapper.set_deleted()
+ else:
+ err = "Deleting following volume(s) failed: "+failedList+". However, these volumes were successfully deleted: "+deletedList+". \
+ MANUAL intervention and processing needed."
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+ def snapshotUCI( self, uci_wrapper ):
+ """
+ Creates snapshot of all storage volumes associated with this UCI.
+ """
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ conn = self.get_connection( uci_wrapper )
+
+ snapshots = uci_wrapper.get_snapshots( status = snapshot_status.SUBMITTED )
+ for snapshot in snapshots:
+ log.debug( "Snapshot DB id: '%s', volume id: '%s'" % ( snapshot.id, snapshot.store.volume_id ) )
+ try:
+ snap = conn.create_snapshot( volume_id=snapshot.store.volume_id )
+ snap_id = str( snap ).split(':')[1]
+ uci_wrapper.set_snapshot_id( snapshot.id, snap_id )
+ sh = conn.get_all_snapshots( snap_id ) # get updated status
+ uci_wrapper.set_snapshot_status( status=sh[0].status, snap_id=snap_id )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while creating snapshot: " + str( e )
+ log.error( err )
+ uci_wrapper.set_snapshot_error( error=err, snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( err, True )
+ return
+ except Exception, ex:
+ err = "Error while creating snapshot: " + str( ex )
+ log.error( err )
+ uci_wrapper.set_snapshot_error( error=err, snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( err, True )
+ return
+
+ uci_wrapper.change_state( uci_state=uci_states.AVAILABLE )
+
+ def addStorageToUCI( self, name ):
+ """ Adds more storage to specified UCI
+ TODO"""
+
+ def dummyStartUCI( self, uci_wrapper ):
+
+ uci = uci_wrapper.get_uci()
+ log.debug( "Would be starting instance '%s'" % uci.name )
+ uci_wrapper.change_state( uci_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 instance(s) of given UCI on the cloud.
+ """
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ conn = self.get_connection( uci_wrapper )
+ self.check_key_pair( uci_wrapper, conn )
+ if uci_wrapper.get_key_pair_name() == None:
+ err = "Key pair not found"
+ log.error( "%s for UCI '%s'." % ( err, uci_wrapper.get_name() ) )
+ uci_wrapper.set_error( err + ". Try resetting the state and starting the instance again.", True )
+ return
+
+ i_indexes = uci_wrapper.get_instances_indexes( state=instance_states.SUBMITTED ) # Get indexes of i_indexes associated with this UCI that are in 'submitted' state
+ log.debug( "Starting instances with IDs: '%s' associated with UCI '%s' " % ( i_indexes, uci_wrapper.get_name(), ) )
+ if len( i_indexes ) > 0:
+ for i_index in i_indexes:
+ # Get machine image for current instance
+ mi_id = self.get_mi_id( uci_wrapper, i_index )
+ log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name(): %s" % ( mi_id, uci_wrapper.get_key_pair_name() ) )
+ 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 '%s' security group." % self.security_group )
+ try:
+ conn.get_all_security_groups( [self.security_group] ) # security groups
+ except boto.exception.EC2ResponseError, e:
+ if e.code == 'InvalidGroup.NotFound':
+ log.info( "No security group found, creating security group '%s'" % self.security_group )
+ try:
+ gSecurityGroup = conn.create_security_group(self.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
+ except boto.exception.EC2ResponseError, ee:
+ err = "EC2 response error while creating security group: " + str( ee )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ else:
+ err = "EC2 response error while retrieving security group: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+
+ 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(), self.security_group, uci_wrapper.get_type( i_index ), uci_wrapper.get_uci_availability_zone() ) )
+ reservation = None
+ try:
+ reservation = conn.run_instances( image_id=mi_id,
+ key_name=uci_wrapper.get_key_pair_name(),
+ security_groups=[self.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:
+ err = "EC2 response error when starting UCI '"+ uci_wrapper.get_name() +"': " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ except Exception, ex:
+ err = "Error when starting UCI '" + uci_wrapper.get_name() + "': " + str( ex )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ # Record newly available instance data into local Galaxy database
+ if reservation:
+ uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( self.format_time( reservation.instances[0].launch_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 )
+ uci_wrapper.set_security_group_name( self.security_group, i_id=i_id )
+ log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_state() ) )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error when retrieving instance information for UCI '" + uci_wrapper.get_name() + "': " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ else:
+ log.error( "UCI '%s' is in 'error' state, starting instance was aborted." % uci_wrapper.get_name() )
+ else:
+ err = "No instances in state '"+ instance_states.SUBMITTED +"' found for UCI '" + uci_wrapper.get_name() + \
+ "'. Nothing to start."
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ else:
+ log.error( "UCI '%s' is in 'error' state, starting instance was aborted." % uci_wrapper.get_name() )
+
+ def stopUCI( self, uci_wrapper):
+ """
+ Stops all of cloud instances associated with given UCI.
+ """
+ conn = self.get_connection( uci_wrapper )
+
+ # Get all instances associated with given UCI
+ il = uci_wrapper.get_instances_ids() # instance list
+ rl = conn.get_all_instances( il ) # Reservation list associated with given instances
+
+ # Initiate shutdown of all instances under given UCI
+ cnt = 0
+ stopped = []
+ not_stopped = []
+ for r in rl:
+ for inst in r.instances:
+ log.debug( "Sending stop signal to instance '%s' associated with reservation '%s'." % ( inst, r ) )
+ try:
+ 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 )
+ except boto.exception.EC2ResponseError, e:
+ not_stopped.append( inst )
+ err = "EC2 response error when stopping instance '" + inst.instance_id + "': " + str(e)
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+ uci_wrapper.reset_uci_launch_time()
+ 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
+# 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 )
+
+ def update( self ):
+ """
+ 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..." )
+ # 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()
+ for inst in instances:
+ 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_status.IN_USE,
+ model.CloudStore.c.status==store_status.CREATING,
+ model.CloudStore.c.status==None ) ).all()
+ for store in stores:
+ if self.type == store.uci.credentials.provider.type: # and store.volume_id != None:
+ log.debug( "[%s] Running general status update on store with local database ID: '%s'" % ( store.uci.credentials.provider.type, store.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 entry for given " \
+# "storage volume should be updated."
+# store.status = store_status.ERROR
+# store.uci.state = uci_states.ERROR
+# store.uci.flush()
+# store.flush()
+
+ # Update pending snapshots or delete ones marked for deletion
+ snapshots = model.CloudSnapshot.filter_by( status=snapshot_status.PENDING, status=snapshot_status.DELETE ).all()
+ for snapshot in snapshots:
+ if self.type == snapshot.uci.credentials.provider.type and snapshot.status == snapshot_status.PENDING:
+ log.debug( "[%s] Running general status update on snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
+ self.update_snapshot( snapshot )
+ elif self.type == snapshot.uci.credentials.provider.type and snapshot.status == snapshot_status.DELETE:
+ log.debug( "[%s] Initiating deletion of snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
+ self.delete_snapshot( snapshot )
+
+ # 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() - z_inst.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 with DB id '%s'" % ( z_inst.uci.credentials.provider.type, z_inst.id ) )
+ self.processZombie( z_inst )
+
+ def updateInstance( self, inst ):
+
+ # Get credentials associated wit this instance
+ uci_id = inst.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ # Get reservations handle for given instance
+ try:
+ rl= conn.get_all_instances( [inst.instance_id] )
+ except boto.exception.EC2ResponseError, e:
+ err = "Retrieving instance(s) from cloud failed for UCI '"+ uci.name +"' during general status update: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ return None
+
+ # Because references to reservations are deleted shortly after instances have been 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 an 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:
+ err = "Instance ID '"+inst.instance_id+"' was not found by the cloud provider. Instance might have crashed or otherwise been terminated."+ \
+ "Manual check is recommended."
+ log.error( err )
+ inst.error = err
+ uci.error = err
+ inst.state = instance_states.TERMINATED
+ uci.state = uci_states.ERROR
+ 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 ):
+ 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:
+ err = "Updating instance status from cloud failed for UCI '"+ uci.name + "' during general status update: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ return None
+
+ def updateStore( self, store ):
+ # Get credentials associated wit this store
+ uci_id = store.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ # Get reservations handle for given store
+ try:
+ vl = conn.get_all_volumes( [store.volume_id] )
+ except boto.exception.EC2ResponseError, e:
+ err = "Retrieving volume(s) from cloud failed for UCI '"+ uci.name + "' during general status update: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ return None
+
+ # Update store status in local DB with info from cloud provider
+ if len(vl) > 0:
+ 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()
+ # If UCI was marked in state 'CREATING', update its status to reflect new status
+ elif ( uci.state == uci_states.CREATING ):
+ 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:
+ err = "Updating status of volume(s) from cloud failed for UCI '"+ uci.name + "' during general status update: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ return None
+ else:
+ err = "No storage volumes returned by cloud provider on general update"
+ log.error( "%s for UCI '%s'" % ( err, uci.name ) )
+ store.status = store_status.ERROR
+ store.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ store.flush()
+
+ def updateSnapshot( self, snapshot ):
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Updating status of snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.get_all_snapshots( [snapshot.snapshot_id] )
+ if len( snap ) > 0:
+ log.debug( "Snapshot '%s' status: %s" % ( snapshot.snapshot_id, snap[0].status ) )
+ snapshot.status = snap[0].status
+ snapshot.flush()
+ else:
+ err = "No snapshots returned by EC2 on general update"
+ log.error( "%s for UCI '%s'" % ( err, uci.name ) )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while updating snapshot status: " + str( e )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ err = "Error while updating snapshot status: " + str( ex )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+
+ def delete_snapshot( self, snapshot ):
+ if snapshot.status == snapshot_status.DELETE:
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Deleting snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.delete_snapshot( snapshot.snapshot_id )
+ if snap == True:
+ snapshot.deleted = True
+ snapshot.status = snapshot_status.DELETED
+ snapshot.flush()
+ return snap
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while deleting snapshot: " + str( e )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ err = "Error while deleting snapshot: " + str( ex )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ else:
+ err = "Cannot delete snapshot '"+snapshot.snapshot_id+"' because its status is '"+snapshot.status+"'. Only snapshots with '" + \
+ snapshot_status.COMPLETED+"' status can be deleted."
+ log.error( err )
+ snapshot.error = err
+ snapshot.flush()
+
+ def processZombie( self, inst ):
+ """
+ Attempt at discovering if starting an instance was successful but local database was not updated
+ accordingly or if something else failed and instance was never started. Currently, no automatic
+ repairs are being attempted; instead, appropriate error messages are set.
+ """
+ # Check if any instance-specific information was written to local DB; if 'yes', set instance and UCI's error message
+ # suggesting manual check.
+ if inst.launch_time != None or inst.reservation_id != None or inst.instance_id != None:
+ # Try to recover state - this is best-case effort, so if something does not work immediately, not
+ # recovery steps are attempted. Recovery is based on hope that instance_id is available in local DB; if not,
+ # report as error.
+ # Fields attempting to be recovered are: reservation_id, instance status, and launch_time
+ if inst.instance_id != None:
+ conn = self.get_connection_from_uci( uci )
+ rl = conn.get_all_instances( [inst.instance_id] ) # reservation list
+ # Update local DB with relevant data from instance
+ if inst.reservation_id == None:
+ try:
+ inst.reservation_id = str(rl[0]).split(":")[1]
+ except: # something failed, so skip
+ pass
+
+ try:
+ state = rl[0].instances[0].update()
+ inst.state = state
+ inst.uci.state = state
+ inst.flush()
+ inst.uci.flush()
+ except: # something failed, so skip
+ pass
+
+ if inst.launch_time == None:
+ try:
+ launch_time = self.format_time( rl[0].instances[0].launch_time )
+ inst.launch_time = launch_time
+ inst.flush()
+ if inst.uci.launch_time == None:
+ inst.uci.launch_time = launch_time
+ inst.uci.flush()
+ except: # something failed, so skip
+ pass
+ else:
+ err = "Starting a machine instance (DB id: '"+str(inst.id)+"') associated with this UCI '" + str(inst.uci.name) + \
+ "' seems to have failed. Because it appears that cloud instance might have gotten started, manual check is recommended."
+ inst.error = err
+ inst.state = instance_states.ERROR
+ inst.uci.error = err
+ inst.uci.state = uci_states.ERROR
+ log.error( err )
+ inst.flush()
+ inst.uci.flush()
+
+ else: #Instance most likely never got processed, so set error message suggesting user to try starting instance again.
+ err = "Starting a machine instance (DB id: '"+str(inst.id)+"') associated with this UCI '" + str(inst.uci.name) + \
+ "' seems to have failed. Because it appears that cloud instance never got started, it should be safe to reset state and try " \
+ "starting the instance again."
+ inst.error = err
+ inst.state = instance_states.ERROR
+ inst.uci.error = err
+ inst.uci.state = uci_states.ERROR
+ log.error( err )
+ inst.flush()
+ inst.uci.flush()
+# uw = UCIwrapper( inst.uci )
+# log.debug( "Try automatically re-submitting UCI '%s'." % uw.get_name() )
+
+ def get_connection_from_uci( self, uci ):
+ """
+ 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
+ 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:
+ err = "Establishing connection with cloud failed: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ return None
+
+ return conn
+
+# 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( self, 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 0984c3800775 -r 7d013eb98022 lib/galaxy/cloud/providers/eucalyptus.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Thu Nov 12 16:36:07 2009 -0500
@@ -0,0 +1,923 @@
+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.model import mapping
+from galaxy.datatypes.data import nice_size
+from galaxy.util.bunch import Bunch
+from galaxy.cloud import UCIwrapper
+from Queue import Queue
+from sqlalchemy import or_, and_
+
+import galaxy.eggs
+galaxy.eggs.require("boto")
+from boto.ec2.connection import EC2Connection
+from boto.ec2.regioninfo import RegionInfo
+import boto.exception
+import boto
+
+import logging
+log = logging.getLogger( __name__ )
+
+uci_states = Bunch(
+ NEW_UCI = "newUCI",
+ NEW = "new",
+ CREATING = "creating",
+ DELETING_UCI = "deletingUCI",
+ DELETING = "deleting",
+ SUBMITTED_UCI = "submittedUCI",
+ SUBMITTED = "submitted",
+ SHUTTING_DOWN_UCI = "shutting-downUCI",
+ SHUTTING_DOWN = "shutting-down",
+ AVAILABLE = "available",
+ RUNNING = "running",
+ PENDING = "pending",
+ ERROR = "error",
+ DELETED = "deleted",
+ SNAPSHOT_UCI = "snapshotUCI",
+ SNAPSHOT = "snapshot"
+)
+
+instance_states = Bunch(
+ TERMINATED = "terminated",
+ SUBMITTED = "submitted",
+ RUNNING = "running",
+ PENDING = "pending",
+ SHUTTING_DOWN = "shutting-down",
+ ERROR = "error"
+)
+
+store_status = Bunch(
+ IN_USE = "in-use",
+ CREATING = "creating",
+ DELETED = 'deleted',
+ ERROR = "error"
+)
+
+snapshot_status = Bunch(
+ SUBMITTED = 'submitted',
+ PENDING = 'pending',
+ COMPLETED = 'completed',
+ DELETE = 'delete',
+ DELETED= 'deleted',
+ ERROR = "error"
+)
+
+class EucalyptusCloudProvider( object ):
+ """
+ Eucalyptus-based cloud provider implementation for managing instances.
+ """
+ STOP_SIGNAL = object()
+ def __init__( self, app ):
+ self.type = "eucalyptus" # cloud provider type (e.g., ec2, eucalyptus, opennebula)
+ self.zone = "epc"
+ self.queue = Queue()
+
+ 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 eucalyptus cloud workers ready", nworkers )
+
+ 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_state = uci_wrapper.get_state()
+ if uci_state is self.STOP_SIGNAL:
+ return
+ try:
+ if uci_state==uci_states.NEW:
+ self.createUCI( uci_wrapper )
+ elif uci_state==uci_states.DELETING:
+ 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 )
+ elif uci_state==uci_states.SNAPSHOT:
+ self.snapshotUCI( uci_wrapper )
+ except:
+ log.exception( "Uncaught exception executing cloud request." )
+ cnt += 1
+
+ def get_connection( self, uci_wrapper ):
+ """
+ Establishes eucalyptus cloud connection using user's credentials associated with given UCI
+ """
+ log.debug( 'Establishing %s cloud connection.' % self.type )
+ provider = uci_wrapper.get_provider()
+ try:
+ region = RegionInfo( None, provider.region_name, provider.region_endpoint )
+ except Exception, ex:
+ err = "Selecting region with cloud provider failed: " + str( ex )
+ log.error( err )
+ uci_wrapper.set_error( err, 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=region,
+ path=provider.path )
+ except boto.exception.EC2ResponseError, e:
+ err = "Establishing connection with cloud failed: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ return None
+
+ return conn
+
+ def check_key_pair( self, uci_wrapper, conn ):
+ """
+ Generate key pair using user's credentials
+ """
+ kp = None
+ kp_name = uci_wrapper.get_name().replace(' ','_') + "_kp"
+ log.debug( "Checking user's key pair: '%s'" % kp_name )
+ try:
+ kp = conn.get_key_pair( kp_name )
+ uci_kp_name = uci_wrapper.get_key_pair_name()
+ uci_material = uci_wrapper.get_key_pair_material()
+ if kp != None:
+ if kp.name != uci_kp_name or uci_material == None:
+ # key pair exists on the cloud but not in local database, so re-generate it (i.e., delete and then create)
+ try:
+ conn.delete_key_pair( kp_name )
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while deleting key pair: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ else:
+ try:
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while creating key pair: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ except Exception, ex:
+ err = "Exception while creating key pair: " + str( ex )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ 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'" % kp_name )
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ else:
+ err = "EC2 response error while retrieving key pair: " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+ if kp != None:
+ return kp.name
+ else:
+ return None
+
+ def create_key_pair( self, conn, kp_name ):
+ try:
+ return conn.create_key_pair( kp_name )
+ except boto.exception.EC2ResponseError, e:
+ return None
+
+ def get_mi_id( self, uci_wrapper, i_index ):
+ """
+ Get appropriate machine image (mi) based on instance size.
+ """
+ i_type = uci_wrapper.get_type( i_index )
+ if i_type=='m1.small' or i_type=='c1.medium':
+ arch = 'i386'
+ else:
+ arch = 'x86_64'
+
+ mi = model.CloudImage.filter_by( deleted=False, provider_type=self.type, architecture=arch ).first()
+ if mi:
+ return mi.image_id
+ else:
+ err = "Machine image could not be retrieved"
+ log.error( "%s for UCI '%s'." % (err, uci_wrapper.get_name() ) )
+ uci_wrapper.set_error( err+". Contact site administrator to ensure needed machine image is registered.", True )
+ return None
+
+ def shutdown( self ):
+ """Attempts to gracefully shut down the monitor thread"""
+ log.info( "sending stop signal to worker threads in eucalyptus cloud manager" )
+ 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_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_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 )
+
+ log.debug( "Creating volume; using command: conn.create_volume( %s, '%s', snapshot=None )" % ( uci_wrapper.get_store_size( 0 ), uci_wrapper.get_uci_availability_zone() ))
+ 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 )
+
+ # Retrieve created volume again to get updated status
+ try:
+ vl = conn.get_all_volumes( [vol.id] )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while retrieving (i.e., updating status) of just created storage volume '" + vol.id + "': " + str( e )
+ log.error( err )
+ uci_wrapper.set_store_status( vol.id, uci_states.ERROR )
+ uci_wrapper.set_error( err, True )
+ return
+ except Exception, ex:
+ err = "Error while retrieving (i.e., updating status) of just created storage volume '" + vol.id + "': " + str( ex )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ return
+
+ if len( vl ) > 0:
+ # EPC does not allow creation of storage volumes (it deletes one as soon as it is created, so manually set uci_state here)
+ if vl[0].status == store_status.DELETED:
+ uci_wrapper.change_state( uci_state=uci_states.AVAILABLE )
+ else:
+ uci_wrapper.change_state( uci_state=vl[0].status )
+ uci_wrapper.set_store_status( vol.id, vl[0].status )
+ else:
+ err = "Volume '" + vol.id +"' not found by EC2 after being created."
+ log.error( err )
+ uci_wrapper.set_store_status( vol.id, uci_states.ERROR )
+ uci_wrapper.set_error( err, True )
+
+ 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 )
+ try:
+ if conn.delete_volume( v.volume_id ):
+ deletedList.append( v.volume_id )
+ v.deleted = True
+ v.flush()
+ count += 1
+ else:
+ failedList.append( v.volume_id )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while deleting storage volume '" + v.volume_id + "': " + str( e )
+ log.error( err )
+ uci_wrapper.set_store_error( err, store_id = v.volume_id )
+ uci_wrapper.set_error( err, True )
+
+ # Delete UCI if all of associated
+ if count == len( vl ):
+ uci_wrapper.set_deleted()
+ else:
+ err = "Deleting following volume(s) failed: "+failedList+". However, these volumes were successfully deleted: "+deletedList+". \
+ MANUAL intervention and processing needed."
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+ def snapshotUCI( self, uci_wrapper ):
+ """
+ Creates snapshot of all storage volumes associated with this UCI.
+ """
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ conn = self.get_connection( uci_wrapper )
+
+ snapshots = uci_wrapper.get_snapshots( status = snapshot_status.SUBMITTED )
+ for snapshot in snapshots:
+ log.debug( "Snapshot DB id: '%s', volume id: '%s'" % ( snapshot.id, snapshot.store.volume_id ) )
+ try:
+ snap = conn.create_snapshot( volume_id=snapshot.store.volume_id )
+ snap_id = str( snap ).split(':')[1]
+ uci_wrapper.set_snapshot_id( snapshot.id, snap_id )
+ sh = conn.get_all_snapshots( snap_id ) # get updated status
+ uci_wrapper.set_snapshot_status( status=sh[0].status, snap_id=snap_id )
+ except boto.exception.EC2ResponseError, e:
+ err = "Cloud provider response error while creating snapshot: " + str( e )
+ log.error( err )
+ uci_wrapper.set_snapshot_error( error=err, snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( err, True )
+ return
+ except Exception, ex:
+ err = "Error while creating snapshot: " + str( ex )
+ log.error( err )
+ uci_wrapper.set_snapshot_error( error=err, snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( err, True )
+ return
+
+ uci_wrapper.change_state( uci_state=uci_states.AVAILABLE )
+
+# if uci_wrapper.get_state() != uci_states.ERROR:
+#
+# snapshots = uci_wrapper.get_snapshots( status = 'submitted' )
+# for snapshot in snapshots:
+# uci_wrapper.set_snapshot_id( snapshot.id, None, 'euca_error' )
+#
+# log.debug( "Eucalyptus snapshot attempted by user for UCI '%s'" % uci_wrapper.get_name() )
+# uci_wrapper.set_error( "Eucalyptus does not support creation of snapshots at this moment. No snapshot or other changes were performed. \
+# Feel free to resent state of this instance and use it normally.", True )
+
+
+ def addStorageToUCI( self, uci_wrapper ):
+ """ Adds more storage to specified UCI """
+
+ def dummyStartUCI( self, uci_wrapper ):
+
+ uci = uci_wrapper.get_uci()
+ log.debug( "Would be starting instance '%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 ):
+ """
+ Starts instance(s) of given UCI on the cloud.
+ """
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ conn = self.get_connection( uci_wrapper )
+ self.check_key_pair( uci_wrapper, conn )
+ if uci_wrapper.get_key_pair_name() == None:
+ err = "Key pair not found"
+ log.error( "%s for UCI '%s'." % ( err, uci_wrapper.get_name() ) )
+ uci_wrapper.set_error( err + ". Try resetting the state and starting the instance again.", True )
+ return
+
+ i_indexes = uci_wrapper.get_instances_indexes( state=instance_states.SUBMITTED ) # Get indexes of i_indexes associated with this UCI that are in 'submitted' state
+ log.debug( "Starting instances with IDs: '%s' associated with UCI '%s' " % ( i_indexes, uci_wrapper.get_name(), ) )
+ if len( i_indexes ) > 0:
+ for i_index in i_indexes:
+ # Get machine image for current instance
+ mi_id = self.get_mi_id( uci_wrapper, i_index )
+ log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name(): %s" % ( mi_id, uci_wrapper.get_key_pair_name() ) )
+ uci_wrapper.set_mi( i_index, mi_id )
+
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ # Start an instance
+ log.debug( "Starting UCI instance '%s'" % uci_wrapper.get_name() )
+ log.debug( "Using following command: conn.run_instances( image_id='%s', key_name='%s', instance_type='%s' )"
+ % ( mi_id, uci_wrapper.get_key_pair_name(), uci_wrapper.get_type( i_index ) ) )
+ reservation = None
+ try:
+ reservation = conn.run_instances( image_id=mi_id,
+ key_name=uci_wrapper.get_key_pair_name(),
+ instance_type=uci_wrapper.get_type( i_index ) )
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error when starting UCI '"+ uci_wrapper.get_name() +"': " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ except Exception, ex:
+ err = "Error when starting UCI '" + uci_wrapper.get_name() + "': " + str( ex )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ # Record newly available instance data into local Galaxy database
+ if reservation:
+ uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( self.format_time( reservation.instances[0].launch_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 from 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:
+ err = "EC2 response error when retrieving instance information for UCI '" + uci_wrapper.get_name() + "': " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ else:
+ log.error( "UCI '%s' is in 'error' state, starting instance was aborted." % uci_wrapper.get_name() )
+ else:
+ err = "No instances in state '"+ instance_states.SUBMITTED +"' found for UCI '" + uci_wrapper.get_name() + \
+ "'. Nothing to start."
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+ else:
+ log.error( "UCI '%s' is in 'error' state, starting instance was aborted." % uci_wrapper.get_name() )
+
+ def stopUCI( self, uci_wrapper):
+ """
+ Stops all of cloud instances associated with given UCI.
+ """
+ conn = self.get_connection( uci_wrapper )
+
+ # 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
+
+ # Initiate shutdown of all instances under given UCI
+ cnt = 0
+ stopped = []
+ not_stopped = []
+ for r in rl:
+ for inst in r.instances:
+ log.debug( "Sending stop signal to instance '%s' associated with reservation '%s' (UCI: %s)." % ( inst, r, uci_wrapper.get_name() ) )
+ try:
+ 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 )
+ except boto.exception.EC2ResponseError, e:
+ not_stopped.append( inst )
+ err = "EC2 response error when stopping instance '" + inst.instance_id + "': " + str( e )
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+ uci_wrapper.reset_uci_launch_time()
+ 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
+# 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 )
+
+ def update( self ):
+ """
+ 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()
+ for inst in instances:
+ 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_status.IN_USE,
+ model.CloudStore.c.status==store_status.CREATING,
+ model.CloudStore.c.status==None ) ).all()
+ for store in stores:
+ if self.type == store.uci.credentials.provider.type: # and store.volume_id != None:
+ log.debug( "[%s] Running general status update on store with local database ID: '%s'" % ( store.uci.credentials.provider.type, store.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 entry for given " \
+# "storage volume should be updated."
+# store.status = store_status.ERROR
+# store.uci.state = uci_states.ERROR
+# store.uci.flush()
+# store.flush()
+
+ # Update pending snapshots or delete ones marked for deletion
+ snapshots = model.CloudSnapshot.filter_by( status=snapshot_status.PENDING, status=snapshot_status.DELETE ).all()
+ for snapshot in snapshots:
+ if self.type == snapshot.uci.credentials.provider.type and snapshot.status == snapshot_status.PENDING:
+ log.debug( "[%s] Running general status update on snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
+ self.update_snapshot( snapshot )
+ elif self.type == snapshot.uci.credentials.provider.type and snapshot.status == snapshot_status.DELETE:
+ log.debug( "[%s] Initiating deletion of snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
+ self.delete_snapshot( snapshot )
+
+ # 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:
+ log.debug( "zombie UCI: %s" % zombie.name )
+ z_instances = model.CloudInstance \
+ .filter_by( uci_id=zombie.id, 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() - z_inst.update_time
+ log.debug( "z_inst.id: %s, time delta is %s sec" % ( z_inst.id, td.seconds ) )
+ if td.seconds > 180: # if instance has been in SUBMITTED state for more than 3 minutes
+ log.debug( "[%s](td=%s) Running zombie repair update on instance with DB id '%s'" % ( z_inst.uci.credentials.provider.type, td.seconds, z_inst.id ) )
+ self.processZombie( z_inst )
+
+ def updateInstance( self, inst ):
+
+ # Get credentials associated wit this instance
+ uci_id = inst.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ # Get reservations handle for given instance
+ try:
+ rl= conn.get_all_instances( [inst.instance_id] )
+ except boto.exception.EC2ResponseError, e:
+ err = "Retrieving instance(s) from cloud failed for UCI '"+ uci.name +"' during general status update: " + str( e )
+ log.error( err )
+ uci.error = err
+ 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.
+ if len( rl ) == 0:
+ err = "Instance ID '"+inst.instance_id+"' was not found by the cloud provider. Instance might have crashed or otherwise been terminated."+ \
+ "Manual check is recommended."
+ log.error( err )
+ inst.error = err
+ uci.error = err
+ inst.state = instance_states.TERMINATED
+ uci.state = uci_states.ERROR
+ 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 ):
+ 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()
+ except boto.exception.EC2ResponseError, e:
+ err = "Updating instance status from cloud failed for UCI '"+ uci.name + "' during general status update: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ return None
+
+ def updateStore( self, store ):
+ # Get credentials associated wit this store
+ uci_id = store.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ vl = conn.get_all_volumes( [store.volume_id] )
+ except boto.exception.EC2ResponseError, e:
+ err = "Retrieving volume(s) from cloud failed for UCI '"+ uci.name + "' during general status update: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ return None
+
+ # Update store status in local DB with info from cloud provider
+ if len(vl) > 0:
+ 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()
+ # If UCI was marked in state 'CREATING', update its status to reflect new status
+ elif ( uci.state == uci_states.CREATING ):
+ # Because Eucalyptus Public Cloud (EPC) deletes volumes immediately after they are created, artificially
+ # set status of given UCI to 'available' based on storage volume's availability zone (i.e., it's residing
+ # in EPC as opposed to some other Eucalyptus based cloud that allows creation of storage volumes.
+ if store.availability_zone == 'epc':
+ uci.state = uci_states.AVAILABLE
+ else:
+ 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:
+ err = "Updating status of volume(s) from cloud failed for UCI '"+ uci.name + "' during general status update: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ return None
+ else:
+ err = "No storage volumes returned by cloud provider on general update"
+ log.error( "%s for UCI '%s'" % ( err, uci.name ) )
+ store.status = store_status.ERROR
+ store.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ store.flush()
+
+ def updateSnapshot( self, snapshot ):
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Updating status of snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.get_all_snapshots( [snapshot.snapshot_id] )
+ if len( snap ) > 0:
+ log.debug( "Snapshot '%s' status: %s" % ( snapshot.snapshot_id, snap[0].status ) )
+ snapshot.status = snap[0].status
+ snapshot.flush()
+ else:
+ err = "No snapshots returned by cloud provider on general update"
+ log.error( "%s for UCI '%s'" % ( err, uci.name ) )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except boto.exception.EC2ResponseError, e:
+ err = "Cloud provider response error while updating snapshot status: " + str( e )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ err = "Error while updating snapshot status: " + str( ex )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+
+ def delete_snapshot( self, snapshot ):
+ if snapshot.status == snapshot_status.DELETE:
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Deleting snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.delete_snapshot( snapshot.snapshot_id )
+ if snap == True:
+ snapshot.deleted = True
+ snapshot.status = snapshot_status.DELETED
+ snapshot.flush()
+ return snap
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while deleting snapshot: " + str( e )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ err = "Error while deleting snapshot: " + str( ex )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ else:
+ err = "Cannot delete snapshot '"+snapshot.snapshot_id+"' because its status is '"+snapshot.status+"'. Only snapshots with '" + \
+ snapshot_status.COMPLETED+"' status can be deleted."
+ log.error( err )
+ snapshot.error = err
+ snapshot.flush()
+
+ def processZombie( self, inst ):
+ """
+ Attempt at discovering if starting an instance was successful but local database was not updated
+ accordingly or if something else failed and instance was never started. Currently, no automatic
+ repairs are being attempted; instead, appropriate error messages are set.
+ """
+ # Check if any instance-specific information was written to local DB; if 'yes', set instance and UCI's error message
+ # suggesting manual check.
+ if inst.launch_time != None or inst.reservation_id != None or inst.instance_id != None:
+ # Try to recover state - this is best-case effort, so if something does not work immediately, not
+ # recovery steps are attempted. Recovery is based on hope that instance_id is available in local DB; if not,
+ # report as error.
+ # Fields attempting to be recovered are: reservation_id, instance status, and launch_time
+ if inst.instance_id != None:
+ conn = self.get_connection_from_uci( inst.uci )
+ rl = conn.get_all_instances( [inst.instance_id] ) # reservation list
+ # Update local DB with relevant data from instance
+ if inst.reservation_id == None:
+ try:
+ inst.reservation_id = str(rl[0]).split(":")[1]
+ except: # something failed, so skip
+ pass
+
+ try:
+ state = rl[0].instances[0].update()
+ inst.state = state
+ inst.uci.state = state
+ inst.flush()
+ inst.uci.flush()
+ except: # something failed, so skip
+ pass
+
+ if inst.launch_time == None:
+ try:
+ launch_time = self.format_time( rl[0].instances[0].launch_time )
+ inst.launch_time = launch_time
+ inst.flush()
+ if inst.uci.launch_time == None:
+ inst.uci.launch_time = launch_time
+ inst.uci.flush()
+ except: # something failed, so skip
+ pass
+ else:
+ err = "Starting a machine instance (DB id: '"+str(inst.id)+"') associated with this UCI '" + str(inst.uci.name) + \
+ "' seems to have failed. Because it appears that cloud instance might have gotten started, manual check is recommended."
+ inst.error = err
+ inst.state = instance_states.ERROR
+ inst.uci.error = err
+ inst.uci.state = uci_states.ERROR
+ log.error( err )
+ inst.flush()
+ inst.uci.flush()
+
+ else: #Instance most likely never got processed, so set error message suggesting user to try starting instance again.
+ err = "Starting a machine instance (DB id: '"+str(inst.id)+"') associated with this UCI '" + str(inst.uci.name) + \
+ "' seems to have failed. Because it appears that cloud instance never got started, it should be safe to reset state and try " \
+ "starting the instance again."
+ inst.error = err
+ inst.state = instance_states.ERROR
+ inst.uci.error = err
+ inst.uci.state = uci_states.ERROR
+ log.error( err )
+ inst.flush()
+ inst.uci.flush()
+# uw = UCIwrapper( inst.uci )
+# log.debug( "Try automatically re-submitting UCI '%s'." % uw.get_name() )
+
+ def get_connection_from_uci( self, uci ):
+ """
+ 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
+ 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,
+ port=uci.credentials.provider.port,
+ region=region,
+ path=uci.credentials.provider.path )
+ except boto.exception.EC2ResponseError, e:
+ err = "Establishing connection with cloud failed: " + str( e )
+ log.error( err )
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ return None
+
+ return conn
+
+# 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( self, 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 0984c3800775 -r 7d013eb98022 lib/galaxy/config.py
--- a/lib/galaxy/config.py Thu Nov 12 15:25:48 2009 -0500
+++ b/lib/galaxy/config.py Thu Nov 12 16:36:07 2009 -0500
@@ -113,6 +113,13 @@
except ConfigParser.NoSectionError:
self.tool_runners = []
self.datatypes_config = kwargs.get( 'datatypes_config_file', 'datatypes_conf.xml' )
+ # Cloud configuration options
+ self.cloud_controller_instance = string_as_bool( kwargs.get( 'cloud_controller_instance', 'False' ) )
+ self.cloud_provider = kwargs.get( 'cloud_provider', None )
+ if self.cloud_controller_instance:
+ self.enable_cloud_execution = string_as_bool( kwargs.get( 'enable_cloud_execution', 'True' ) )
+ else:
+ self.enable_cloud_execution = string_as_bool( kwargs.get( 'enable_cloud_execution', 'False' ) )
def get( self, key, default ):
return self.config_dict.get( key, default )
def get_bool( self, key, default ):
diff -r 0984c3800775 -r 7d013eb98022 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Thu Nov 12 15:25:48 2009 -0500
+++ b/lib/galaxy/model/__init__.py Thu Nov 12 16:36:07 2009 -0500
@@ -38,6 +38,7 @@
self.username = None
# Relationships
self.histories = []
+ self.credentials = []
def set_password_cleartext( self, cleartext ):
"""Set 'self.password' to the digest of 'cleartext'."""
@@ -1049,7 +1050,60 @@
def __init__( self, galaxy_session, history ):
self.galaxy_session = galaxy_session
self.history = history
+
+class CloudImage( object ):
+ def __init__( self ):
+ self.id = None
+ self.instance_id = None
+ self.state = None
+class UCI( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+
+class CloudInstance( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+ self.name = None
+ self.instance_id = None
+ self.mi = None
+ self.state = None
+ self.public_dns = None
+ self.availability_zone = None
+
+class CloudStore( object ):
+ def __init__( self ):
+ self.id = None
+ self.volume_id = None
+ self.i_id = None
+ self.user = None
+ self.size = None
+ self.availability_zone = None
+
+class CloudSnapshot( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+ self.store_id = None
+ self.snapshot_id = None
+
+class CloudProvider( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+ self.type = None
+
+class CloudUserCredentials( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+ self.name = None
+ self.accessKey = None
+ self.secretKey = None
+ self.credentials = []
+
class StoredWorkflow( object ):
def __init__( self ):
self.id = None
diff -r 0984c3800775 -r 7d013eb98022 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Thu Nov 12 15:25:48 2009 -0500
+++ b/lib/galaxy/model/mapping.py Thu Nov 12 16:36:07 2009 -0500
@@ -390,6 +390,117 @@
Column( "session_id", Integer, ForeignKey( "galaxy_session.id" ), index=True ),
Column( "history_id", Integer, ForeignKey( "history.id" ), index=True ) )
+# *************************** Start cloud tables***********************************
+CloudImage.table = Table( "cloud_image", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "provider_type", TEXT ),
+ Column( "image_id", TEXT, nullable=False ),
+ Column( "manifest", TEXT ),
+ Column( "state", TEXT ),
+ Column( "architecture", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+
+""" UserConfiguredInstance (UCI) table """
+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 ),
+ Column( "key_pair_name", TEXT ),
+ Column( "key_pair_material", TEXT ),
+ Column( "name", TEXT ),
+ Column( "state", TEXT ),
+ Column( "error", TEXT ),
+ Column( "total_size", Integer ),
+ Column( "launch_time", DateTime ),
+ Column( "deleted", Boolean, default=False ) )
+
+CloudInstance.table = Table( "cloud_instance", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ 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( "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 ),
+ Column( "state", TEXT ),
+ Column( "error", TEXT ),
+ Column( "public_dns", TEXT ),
+ Column( "private_dns", TEXT ),
+ Column( "security_group", TEXT ),
+ Column( "availability_zone", TEXT ) )
+
+CloudStore.table = Table( "cloud_store", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ 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( "cloud_uci.id" ), index=True, nullable=False ),
+ Column( "volume_id", TEXT ),
+ Column( "size", Integer, nullable=False ),
+ Column( "availability_zone", TEXT ),
+ Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ) ),
+ Column( "status", TEXT ),
+ Column( "device", TEXT ),
+ Column( "space_consumed", Integer ),
+ Column( "error", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+
+CloudSnapshot.table = Table( "cloud_snapshot", 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( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ),
+ Column( "store_id", Integer, ForeignKey( "cloud_store.id" ), index=True, nullable=False ),
+ Column( "snapshot_id", TEXT ),
+ Column( "status", TEXT ),
+ Column( "description", TEXT ),
+ Column( "error", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+
+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( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ),
+ Column( "name", TEXT ),
+ Column( "access_key", TEXT ),
+ Column( "secret_key", TEXT ),
+ Column( "deleted", Boolean, default=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 ),
+ Column( "deleted", Boolean, default=False ) )
+# *************************** End cloud tables***********************************
+
StoredWorkflow.table = Table( "stored_workflow", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
@@ -1004,6 +1115,42 @@
output_step=relation( WorkflowStep, backref="output_connections", cascade="all",
primaryjoin=( WorkflowStepConnection.table.c.output_step_id == WorkflowStep.table.c.id ) ) ) )
+# vvvvvvvvvvvvvvvv Start cloud table mappings vvvvvvvvvvvvvvvv
+assign_mapper( context, CloudImage, CloudImage.table )
+
+assign_mapper( context, UCI, UCI.table,
+ properties=dict( user=relation( User ),
+ credentials=relation( CloudUserCredentials ),
+ instance=relation( CloudInstance, backref='uci' ),
+ store=relation( CloudStore, backref='uci', cascade='all, delete-orphan' ),
+ snapshot=relation( CloudSnapshot, backref='uci' )
+ ) )
+
+assign_mapper( context, CloudInstance, CloudInstance.table,
+ properties=dict( user=relation( User ),
+ image=relation( CloudImage )
+ ) )
+
+assign_mapper( context, CloudStore, CloudStore.table,
+ properties=dict( user=relation( User ),
+ i=relation( CloudInstance ),
+ snapshot=relation( CloudSnapshot, backref="store" )
+ ) )
+
+assign_mapper( context, CloudSnapshot, CloudSnapshot.table,
+ properties=dict( user=relation( User )
+ ) )
+
+assign_mapper( context, CloudProvider, CloudProvider.table,
+ properties=dict( user=relation( User )
+ ) )
+
+assign_mapper( context, CloudUserCredentials, CloudUserCredentials.table,
+ properties=dict( user=relation( User),
+ provider=relation( CloudProvider )
+ ) )
+# ^^^^^^^^^^^^^^^ End cloud table mappings ^^^^^^^^^^^^^^^^^^
+
assign_mapper( context, StoredWorkflow, StoredWorkflow.table,
properties=dict( user=relation( User ),
workflows=relation( Workflow, backref='stored_workflow',
diff -r 0984c3800775 -r 7d013eb98022 lib/galaxy/model/migrate/versions/0026_cloud_tables.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Thu Nov 12 16:36:07 2009 -0500
@@ -0,0 +1,152 @@
+from sqlalchemy import *
+from migrate import *
+
+import datetime
+now = datetime.datetime.utcnow
+
+# Need our custom types, but don't import anything else from model
+from galaxy.model.custom_types import *
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+
+def display_migration_details():
+ print
+ print "========================================"
+ print "This script adds tables needed for Galaxy cloud functionality."
+ print "========================================"
+
+CloudImage_table = Table( "cloud_image", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "provider_type", TEXT ),
+ Column( "image_id", TEXT, nullable=False ),
+ Column( "manifest", TEXT ),
+ Column( "state", TEXT ),
+ Column( "architecture", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+
+""" UserConfiguredInstance (UCI) table """
+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 ),
+ Column( "key_pair_name", TEXT ),
+ Column( "key_pair_material", TEXT ),
+ Column( "name", TEXT ),
+ Column( "state", TEXT ),
+ Column( "error", TEXT ),
+ Column( "total_size", Integer ),
+ Column( "launch_time", DateTime ),
+ Column( "deleted", Boolean, default=False ) )
+
+CloudInstance_table = Table( "cloud_instance", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ 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( "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 ),
+ Column( "state", TEXT ),
+ Column( "error", TEXT ),
+ Column( "public_dns", TEXT ),
+ Column( "private_dns", TEXT ),
+ Column( "security_group", TEXT ),
+ Column( "availability_zone", TEXT ) )
+
+CloudStore_table = Table( "cloud_store", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ 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( "cloud_uci.id" ), index=True, nullable=False ),
+ Column( "volume_id", TEXT ),
+ Column( "size", Integer, nullable=False ),
+ Column( "availability_zone", TEXT ),
+ Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ) ),
+ Column( "status", TEXT ),
+ Column( "device", TEXT ),
+ Column( "space_consumed", Integer ),
+ Column( "error", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+
+CloudSnapshot_table = Table( "cloud_snapshot", 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( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ),
+ Column( "store_id", Integer, ForeignKey( "cloud_store.id" ), index=True, nullable=False ),
+ Column( "snapshot_id", TEXT ),
+ Column( "status", TEXT ),
+ Column( "description", TEXT ),
+ Column( "error", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+
+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( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ),
+ Column( "name", TEXT ),
+ Column( "access_key", TEXT ),
+ Column( "secret_key", TEXT ),
+ Column( "deleted", Boolean, default=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 ),
+ Column( "deleted", Boolean, default=False ) )
+
+def upgrade():
+ display_migration_details()
+ # Load existing tables
+ metadata.reflect()
+
+ CloudImage_table.create()
+ UCI_table.create()
+ CloudUserCredentials_table.create()
+ CloudStore_table.create()
+ CloudSnapshot_table.create()
+ CloudInstance_table.create()
+ CloudProvider_table.create()
+
+def downgrade():
+ metadata.reflect()
+
+ CloudImage_table.drop()
+ CloudInstance_table.drop()
+ CloudStore_table.drop()
+ CloudSnapshot_table.drop()
+ CloudUserCredentials_table.drop()
+ UCI_table.drop()
+ CloudProvider_table.drop()
\ No newline at end of file
diff -r 0984c3800775 -r 7d013eb98022 lib/galaxy/web/controllers/cloud.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/web/controllers/cloud.py Thu Nov 12 16:36:07 2009 -0500
@@ -0,0 +1,1193 @@
+from galaxy.web.base.controller import *
+
+import pkg_resources
+pkg_resources.require( "simplejson" )
+import simplejson
+import urllib2
+
+from galaxy.tools.parameters import *
+from galaxy.tools import DefaultToolState
+from galaxy.tools.parameters.grouping import Repeat, Conditional
+from galaxy.datatypes.data import Data
+from galaxy.util.odict import odict
+from galaxy.util.bunch import Bunch
+from galaxy.util.topsort import topsort, topsort_levels, CycleError
+from galaxy.model.mapping import desc
+from galaxy.model.orm import *
+from datetime import datetime, timedelta
+
+pkg_resources.require( "WebHelpers" )
+from webhelpers import *
+
+# Required for Cloud tab
+import galaxy.eggs
+galaxy.eggs.require("boto")
+from boto.ec2.connection import EC2Connection
+from boto.ec2.regioninfo import RegionInfo
+from galaxy.cloud import CloudManager
+import boto.exception
+import boto
+
+import logging
+log = logging.getLogger( __name__ )
+
+uci_states = Bunch(
+ NEW_UCI = "newUCI",
+ NEW = "new",
+ CREATING = "creating",
+ DELETING_UCI = "deletingUCI",
+ DELETING = "deleting",
+ SUBMITTED_UCI = "submittedUCI",
+ SUBMITTED = "submitted",
+ SHUTTING_DOWN_UCI = "shutting-downUCI",
+ SHUTTING_DOWN = "shutting-down",
+ AVAILABLE = "available",
+ RUNNING = "running",
+ PENDING = "pending",
+ ERROR = "error",
+ DELETED = "deleted",
+ SNAPSHOT_UCI = "snapshotUCI",
+ SNAPSHOT = "snapshot"
+)
+
1
0

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/b90db227df6d
changeset: 3090:b90db227df6d
user: Enis Afgan <afgane(a)gmail.com>
date: Thu Nov 12 10:28:54 2009 -0500
description:
Fixed a bug in EC2 controller regarding error reporting during snapshot update. Cleaned up code dealing with snapshots error reporting a bit.
diffstat:
lib/galaxy/cloud/__init__.py | 11 ++-
lib/galaxy/cloud/providers/ec2.py | 131 ++++++++++++++++++--------------
lib/galaxy/cloud/providers/eucalyptus.py | 83 +++++++++++---------
lib/galaxy/web/controllers/cloud.py | 15 +++-
4 files changed, 140 insertions(+), 100 deletions(-)
diffs (426 lines):
diff -r c1dc30106721 -r b90db227df6d lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Wed Nov 11 20:11:58 2009 -0500
+++ b/lib/galaxy/cloud/__init__.py Thu Nov 12 10:28:54 2009 -0500
@@ -44,6 +44,15 @@
ERROR = "error"
)
+snapshot_status = Bunch(
+ SUBMITTED = 'submitted',
+ PENDING = 'pending',
+ COMPLETED = 'completed',
+ DELETE = 'delete',
+ DELETED= 'deleted',
+ ERROR = "error"
+)
+
class CloudManager( object ):
"""
Highest level interface to cloud management.
@@ -338,7 +347,7 @@
snap.error = error
if set_status:
- snap.status = 'error'
+ snap.status = snapshot_status.ERROR
snap.flush()
diff -r c1dc30106721 -r b90db227df6d lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Wed Nov 11 20:11:58 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Thu Nov 12 10:28:54 2009 -0500
@@ -52,6 +52,15 @@
ERROR = "error"
)
+snapshot_status = Bunch(
+ SUBMITTED = 'submitted',
+ PENDING = 'pending',
+ COMPLETED = 'completed',
+ DELETE = 'delete',
+ DELETED= 'deleted',
+ ERROR = "error"
+)
+
class EC2CloudProvider( object ):
"""
Amazon EC2-based cloud provider implementation for managing instances.
@@ -282,7 +291,7 @@
if uci_wrapper.get_state() != uci_states.ERROR:
conn = self.get_connection( uci_wrapper )
- snapshots = uci_wrapper.get_snapshots( status = 'submitted' )
+ snapshots = uci_wrapper.get_snapshots( status = snapshot_status.SUBMITTED )
for snapshot in snapshots:
log.debug( "Snapshot DB id: '%s', volume id: '%s'" % ( snapshot.id, snapshot.store.volume_id ) )
try:
@@ -292,14 +301,16 @@
sh = conn.get_all_snapshots( snap_id ) # get updated status
uci_wrapper.set_snapshot_status( status=sh[0].status, snap_id=snap_id )
except boto.exception.EC2ResponseError, ex:
- log.error( "EC2 response error while creating snapshot: '%s'" % e )
- uci_wrapper.set_snapshot_error( error="EC2 response error while creating snapshot: " + str( e ), snap_index=snapshot.id, set_status=True )
- uci_wrapper.set_error( "EC2 response error while creating snapshot: " + str( e ), True )
+ err = "Cloud provider response error while creating snapshot: " + str( e )
+ log.error( err )
+ uci_wrapper.set_snapshot_error( error=err, snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( error=err, True )
return
except Exception, ex:
- log.error( "Error while creating snapshot: '%s'" % ex )
- uci_wrapper.set_snapshot_error( error="Error while creating snapshot: "+str( ex ), snap_index=snapshot.id, set_status=True )
- uci_wrapper.set_error( "Error while creating snapshot: " + str( ex ), True )
+ err = "Error while creating snapshot: " + str( ex )
+ log.error( err )
+ uci_wrapper.set_snapshot_error( error=err, snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( error=err, True )
return
uci_wrapper.change_state( uci_state=uci_states.AVAILABLE )
@@ -509,12 +520,12 @@
# store.flush()
# Update pending snapshots or delete ones marked for deletion
- snapshots = model.CloudSnapshot.filter_by( status='pending', status='delete' ).all()
+ snapshots = model.CloudSnapshot.filter_by( status=snapshot_status.PENDING, status=snapshot_status.DELETE ).all()
for snapshot in snapshots:
- if self.type == snapshot.uci.credentials.provider.type and snapshot.status == 'pending':
+ if self.type == snapshot.uci.credentials.provider.type and snapshot.status == snapshot_status.PENDING:
log.debug( "[%s] Running general status update on snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
self.update_snapshot( snapshot )
- elif self.type == snapshot.uci.credentials.provider.type and snapshot.status == 'delete':
+ elif self.type == snapshot.uci.credentials.provider.type and snapshot.status == snapshot_status.DELETE:
log.debug( "[%s] Initiating deletion of snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
self.delete_snapshot( snapshot )
@@ -640,49 +651,48 @@
def updateSnapshot( self, snapshot ):
# Get credentials associated wit this store
- if snapshot.status == 'completed':
- uci_id = snapshot.uci_id
- uci = model.UCI.get( uci_id )
- uci.refresh()
- conn = self.get_connection_from_uci( uci )
-
- try:
- log.debug( "Updating status of snapshot '%s'" % snapshot.snapshot_id )
- snap = conn.get_all_snapshots( [snapshot.snapshot_id] )
- if len( snap ) > 0:
- log.debug( "Snapshot '%s' status: %s" % ( snapshot.snapshot_id, snap[0].status ) )
- snapshot.status = snap[0].status
- snapshot.flush()
- else:
- log.error( "No snapshots returned by EC2 for UCI '%s'" % uci.name )
- snapshot.status = 'No snapshots returned by EC2.'
- uci.error = "No snapshots returned by EC2."
- uci.state = uci_states.ERROR
- uci.flush()
- snapshot.flush()
- except boto.exception.EC2ResponseError, e:
- log.error( "EC2 response error while updating snapshot: '%s'" % e )
- snapshot.status = 'error'
- snapshot.error = "EC2 response error while updating snapshot status: " + str( e )
- uci.error = "EC2 response error while updating snapshot status: " + str( e )
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Updating status of snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.get_all_snapshots( [snapshot.snapshot_id] )
+ if len( snap ) > 0:
+ log.debug( "Snapshot '%s' status: %s" % ( snapshot.snapshot_id, snap[0].status ) )
+ snapshot.status = snap[0].status
+ snapshot.flush()
+ else:
+ err = "No snapshots returned by EC2"
+ log.error( "%s for UCI '%s'" % ( err, uci.name ) )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
uci.state = uci_states.ERROR
uci.flush()
snapshot.flush()
- except Exception, ex:
- log.error( "Error while updating snapshot: '%s'" % ex )
- snapshot.status = 'error'
- snapshot.error = "Error while updating snapshot status: " + str( e )
- uci.error = "Error while updating snapshot status: " + str( ex )
- uci.state = uci_states.ERROR
- uci.flush()
- snapshot.flush()
- else:
- log.error( "Cannot delete snapshot '%s' because its status is '%s'. Only snapshots with 'completed' status can be deleted." % ( snapshot.snapshot_id, snapshot.status ) )
- snapshot.error = "Cannot delete snapshot because its status is '"+snapshot.status+"'. Only snapshots with 'completed' status can be deleted."
+ except boto.exception.EC2ResponseError, e:
+ err = "EC2 response error while updating snapshot status: " + str( e )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ err = "Error while updating snapshot status: " + str( ex )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
+ uci.state = uci_states.ERROR
+ uci.flush()
snapshot.flush()
def delete_snapshot( self, snapshot ):
- if snapshot.status == 'delete':
+ if snapshot.status == snapshot_status.DELETE:
# Get credentials associated wit this store
uci_id = snapshot.uci_id
uci = model.UCI.get( uci_id )
@@ -694,29 +704,32 @@
snap = conn.delete_snapshot( snapshot.snapshot_id )
if snap == True:
snapshot.deleted = True
- snapshot.status = 'deleted'
+ snapshot.status = snapshot_status.DELETED
snapshot.flush()
return snap
except boto.exception.EC2ResponseError, e:
- log.error( "EC2 response error while deleting snapshot: '%s'" % e )
- snapshot.status = 'error'
- snapshot.error = "EC2 response error while deleting snapshot: " + str( e )
- uci.error = "EC2 response error while deleting snapshot: " + str( e )
+ err = "EC2 response error while deleting snapshot: " + str( e )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
uci.state = uci_states.ERROR
uci.flush()
snapshot.flush()
except Exception, ex:
- log.error( "Error while deleting snapshot: '%s'" % ex )
- snapshot.status = 'error'
- snapshot.error = "Cloud provider error while deleting snapshot: " + str( ex )
- uci.error = "Cloud provider error while deleting snapshot: " + str( ex )
+ err = "Error while deleting snapshot: " + str( ex )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
uci.state = uci_states.ERROR
uci.flush()
snapshot.flush()
else:
- log.error( "Cannot delete snapshot '%s' because its status is '%s'. Only snapshots with 'completed' status can be deleted." % ( snapshot.snapshot_id, snapshot.status ) )
- snapshot.error = "Cannot delete snapshot because its status is '"+snapshot.status+"'. Only snapshots with 'completed' status can be deleted."
- snapshot.status = 'error'
+ err = "Cannot delete snapshot '"+snapshot.snapshot_id+"' because its status is '"+snapshot.status+"'. Only snapshots with '" + \
+ snapshot_status.COMPLETED+"' status can be deleted."
+ log.error( err )
+ snapshot.error = err
snapshot.flush()
def processZombie( self, inst ):
diff -r c1dc30106721 -r b90db227df6d lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Wed Nov 11 20:11:58 2009 -0500
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Thu Nov 12 10:28:54 2009 -0500
@@ -267,7 +267,7 @@
if uci_wrapper.get_state() != uci_states.ERROR:
conn = self.get_connection( uci_wrapper )
- snapshots = uci_wrapper.get_snapshots( status = 'submitted' )
+ snapshots = uci_wrapper.get_snapshots( status = snapshot_status.SUBMITTED )
for snapshot in snapshots:
log.debug( "Snapshot DB id: '%s', volume id: '%s'" % ( snapshot.id, snapshot.store.volume_id ) )
try:
@@ -277,16 +277,18 @@
sh = conn.get_all_snapshots( snap_id ) # get updated status
uci_wrapper.set_snapshot_status( status=sh[0].status, snap_id=snap_id )
except boto.exception.EC2ResponseError, ex:
- log.error( "EC2 response error while creating snapshot: '%s'" % e )
- uci_wrapper.set_snapshot_error( error="EC2 response error while creating snapshot: " + str( e ), snap_index=snapshot.id, set_status=True )
- uci_wrapper.set_error( "Cloud provider response error while creating snapshot: " + str( e ), True )
+ err = "Cloud provider response error while creating snapshot: " + str( e )
+ log.error( err )
+ uci_wrapper.set_snapshot_error( error=err, snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( error=err, True )
return
except Exception, ex:
- log.error( "Error while creating snapshot: '%s'" % ex )
- uci_wrapper.set_snapshot_error( error="Error while creating snapshot: "+str( ex ), snap_index=snapshot.id, set_status=True )
- uci_wrapper.set_error( "Error while creating snapshot: " + str( ex ), True )
+ err = "Error while creating snapshot: " + str( ex )
+ log.error( err )
+ uci_wrapper.set_snapshot_error( error=err, snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( error=err, True )
return
-
+
uci_wrapper.change_state( uci_state=uci_states.AVAILABLE )
# if uci_wrapper.get_state() != uci_states.ERROR:
@@ -477,12 +479,12 @@
# store.flush()
# Update pending snapshots or delete ones marked for deletion
- snapshots = model.CloudSnapshot.filter_by( status='pending', status='delete' ).all()
+ snapshots = model.CloudSnapshot.filter_by( status=snapshot_status.PENDING, status=snapshot_status.DELETE ).all()
for snapshot in snapshots:
- if self.type == snapshot.uci.credentials.provider.type and snapshot.status == 'pending':
+ if self.type == snapshot.uci.credentials.provider.type and snapshot.status == snapshot_status.PENDING:
log.debug( "[%s] Running general status update on snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
self.update_snapshot( snapshot )
- elif self.type == snapshot.uci.credentials.provider.type and snapshot.status == 'delete':
+ elif self.type == snapshot.uci.credentials.provider.type and snapshot.status == snapshot_status.DELETE:
log.debug( "[%s] Initiating deletion of snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
self.delete_snapshot( snapshot )
@@ -606,7 +608,7 @@
uci.flush()
return None
- def update_snapshot( self, snapshot ):
+ def updateSnapshot( self, snapshot ):
# Get credentials associated wit this store
uci_id = snapshot.uci_id
uci = model.UCI.get( uci_id )
@@ -617,35 +619,39 @@
log.debug( "Updating status of snapshot '%s'" % snapshot.snapshot_id )
snap = conn.get_all_snapshots( [snapshot.snapshot_id] )
if len( snap ) > 0:
+ log.debug( "Snapshot '%s' status: %s" % ( snapshot.snapshot_id, snap[0].status ) )
snapshot.status = snap[0].status
- log.debug( "Snapshot '%s' status: %s" % ( snapshot.snapshot_id, snapshot.status ) )
snapshot.flush()
else:
- log.error( "No snapshots returned by cloud provider for UCI '%s'" % uci.name )
- snapshot.status = 'No snapshots returned by cloud provider.'
- uci.error = "No snapshots returned by cloud provider."
+ err = "No snapshots returned by cloud provider"
+ log.error( "%s for UCI '%s'" % ( err, uci.name ) )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
uci.state = uci_states.ERROR
uci.flush()
snapshot.flush()
except boto.exception.EC2ResponseError, e:
- log.error( "Cloud provider response error while updating snapshot: '%s'" % e )
- snapshot.status = 'error'
- snapshot.error = "Cloud provider response error while updating snapshot status: " + str( e )
- uci.error = "Cloud provider response error while updating snapshot status: " + str( e )
+ err = "Cloud provider response error while updating snapshot status: " + str( e )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
uci.state = uci_states.ERROR
uci.flush()
snapshot.flush()
except Exception, ex:
- log.error( "Error while updating snapshot: '%s'" % ex )
- snapshot.status = 'error'
- snapshot.error = "Error while updating snapshot status: " + str( e )
- uci.error = "Error while updating snapshot status: " + str( ex )
+ err = "Error while updating snapshot status: " + str( ex )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
uci.state = uci_states.ERROR
uci.flush()
snapshot.flush()
def delete_snapshot( self, snapshot ):
- if snapshot.status == 'delete':
+ if snapshot.status == snapshot_status.DELETE:
# Get credentials associated wit this store
uci_id = snapshot.uci_id
uci = model.UCI.get( uci_id )
@@ -657,29 +663,32 @@
snap = conn.delete_snapshot( snapshot.snapshot_id )
if snap == True:
snapshot.deleted = True
- snapshot.status = 'deleted'
+ snapshot.status = snapshot_status.DELETED
snapshot.flush()
return snap
except boto.exception.EC2ResponseError, e:
- log.error( "EC2 response error while deleting snapshot: '%s'" % e )
- snapshot.status = 'error'
- snapshot.error = "Cloud provider response error while deleting snapshot: " + str( e )
- uci.error = "Cloud provider response error while deleting snapshot: " + str( e )
+ err = "EC2 response error while deleting snapshot: " + str( e )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
uci.state = uci_states.ERROR
uci.flush()
snapshot.flush()
except Exception, ex:
- log.error( "Error while deleting snapshot: '%s'" % ex )
- snapshot.status = 'error'
- snapshot.error = "Cloud provider error while deleting snapshot: " + str( ex )
- uci.error = "Cloud provider error while deleting snapshot: " + str( ex )
+ err = "Error while deleting snapshot: " + str( ex )
+ log.error( err )
+ snapshot.status = snapshot_status.ERROR
+ snapshot.error = err
+ uci.error = err
uci.state = uci_states.ERROR
uci.flush()
snapshot.flush()
else:
- log.error( "Cannot delete snapshot '%s' because its status is '%s'. Only snapshots with 'completed' status can be deleted." % ( snapshot.snapshot_id, snapshot.status ) )
- snapshot.error = "Cannot delete snapshot because its status is '"+snapshot.status+"'. Only snapshots with 'completed' status can be deleted."
- snapshot.status = 'error'
+ err = "Cannot delete snapshot '"+snapshot.snapshot_id+"' because its status is '"+snapshot.status+"'. Only snapshots with '" + \
+ snapshot_status.COMPLETED+"' status can be deleted."
+ log.error( err )
+ snapshot.error = err
snapshot.flush()
def processZombie( self, inst ):
diff -r c1dc30106721 -r b90db227df6d lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Wed Nov 11 20:11:58 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Thu Nov 12 10:28:54 2009 -0500
@@ -64,6 +64,15 @@
ERROR = "error"
)
+snapshot_status = Bunch(
+ SUBMITTED = 'submitted',
+ PENDING = 'pending',
+ COMPLETED = 'completed',
+ DELETE = 'delete',
+ DELETED= 'deleted',
+ ERROR = "error"
+)
+
class CloudController( BaseController ):
@web.expose
@@ -243,7 +252,7 @@
snapshot.user = user
snapshot.uci = uci
snapshot.store = store
- snapshot.status = 'submitted'
+ snapshot.status = snapshot_status.SUBMITTED
uci.state = uci_states.SNAPSHOT_UCI
# Persist
session = trans.sa_session
@@ -288,8 +297,8 @@
# Set snapshot as 'ready for deletion' to be picked up by general updater
snap = trans.sa_session.query( model.CloudSnapshot ).get( snap_id )
- if snap.status == 'completed':
- snap.status = 'delete'
+ if snap.status == snapshot_status.COMPLETED:
+ snap.status = snapshot_status.DELETE
snap.flush()
trans.set_message( "Snapshot '%s' is marked for deletion. Once the deletion is complete, it will no longer be visible in this list. "
"Please note that this process may take up to a minute." % snap.snapshot_id )
1
0

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/c1dc30106721
changeset: 3089:c1dc30106721
user: Enis Afgan <afgane(a)gmail.com>
date: Wed Nov 11 20:11:58 2009 -0500
description:
Added ability for users to take snapshots of EBS storage volumes for configured UCIs. Note that, currently, this is supported only for UCIs that are not running at the time of snapshot creation.
diffstat:
lib/galaxy/cloud/__init__.py | 39 ++++++-
lib/galaxy/cloud/providers/ec2.py | 136 +++++++++++++++++++++-
lib/galaxy/cloud/providers/eucalyptus.py | 138 ++++++++++++++++++++++-
lib/galaxy/model/__init__.py | 7 +
lib/galaxy/model/mapping.py | 23 +++-
lib/galaxy/model/migrate/versions/0014_cloud_tables.py | 39 ++++--
lib/galaxy/web/controllers/cloud.py | 89 ++++++++++++++-
templates/cloud/configure_cloud.mako | 18 ++-
templates/cloud/view_snapshots.mako | 90 +++++++++++++++
9 files changed, 548 insertions(+), 31 deletions(-)
diffs (841 lines):
diff -r c78e8e1514e4 -r c1dc30106721 lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Tue Nov 10 18:36:21 2009 -0500
+++ b/lib/galaxy/cloud/__init__.py Wed Nov 11 20:11:58 2009 -0500
@@ -32,7 +32,8 @@
RUNNING = "running",
PENDING = "pending",
ERROR = "error",
- CREATING = "creating"
+ SNAPSHOT_UCI = "snapshotUCI",
+ SNAPSHOT = "snapshot"
)
instance_states = Bunch(
TERMINATED = "terminated",
@@ -141,7 +142,8 @@
.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 ) ) \
+ model.UCI.c.state==uci_states.DELETING_UCI,
+ model.UCI.c.state==uci_states.SNAPSHOT_UCI ) ) \
.all():
uci_wrapper = UCIwrapper( r )
new_requests.append( uci_wrapper )
@@ -310,6 +312,35 @@
vol = model.CloudStore.filter( model.CloudStore.c.volume_id == vol_id ).first()
vol.status = status
vol.flush()
+
+ def set_snapshot_id( self, snap_index, id ):
+ snap = model.CloudSnapshot.get( snap_index )
+ snap.snapshot_id = id
+ snap.flush()
+
+ def set_snapshot_status( self, status, snap_index=None, snap_id=None ):
+ if snap_index != None:
+ snap = model.CloudSnapshot.get( snap_index )
+ elif snap_id != None:
+ snap = model.CloudSnapshot.filter_by( snapshot_id = snap_id).first()
+ else:
+ return
+ snap.status = status
+ snap.flush()
+
+ def set_snapshot_error( self, error, snap_index=None, snap_id=None, set_status=False ):
+ if snap_index != None:
+ snap = model.CloudSnapshot.get( snap_index )
+ elif snap_id != None:
+ snap = model.CloudSnapshot.filter_by( snapshot_id = snap_id).first()
+ else:
+ return
+ snap.error = error
+
+ if set_status:
+ snap.status = 'error'
+
+ snap.flush()
def set_store_availability_zone( self, availability_zone, vol_id=None ):
"""
@@ -501,6 +532,10 @@
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()
+
+ def get_snapshots( self, status=None ):
+ """ Returns database objects for all snapshots associated with this UCI and in given status."""
+ return model.CloudSnapshot.filter_by( uci_id=self.uci_id, status=status ).all()
def get_uci( self ):
""" Returns database object for given UCI. """
diff -r c78e8e1514e4 -r c1dc30106721 lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Tue Nov 10 18:36:21 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Wed Nov 11 20:11:58 2009 -0500
@@ -32,7 +32,9 @@
RUNNING = "running",
PENDING = "pending",
ERROR = "error",
- DELETED = "deleted"
+ DELETED = "deleted",
+ SNAPSHOT_UCI = "snapshotUCI",
+ SNAPSHOT = "snapshot"
)
instance_states = Bunch(
@@ -89,6 +91,8 @@
self.startUCI( uci_wrapper )
elif uci_state==uci_states.SHUTTING_DOWN:
self.stopUCI( uci_wrapper )
+ elif uci_state==uci_states.SNAPSHOT:
+ self.snapshotUCI( uci_wrapper )
except:
log.exception( "Uncaught exception executing request." )
cnt += 1
@@ -271,6 +275,35 @@
uci_wrapper.set_error( "Deleting following volume(s) failed: "+failedList+". However, these volumes were successfully deleted: "+deletedList+". \
MANUAL intervention and processing needed." )
+ def snapshotUCI( self, uci_wrapper ):
+ """
+ Creates snapshot of all storage volumes associated with this UCI.
+ """
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ conn = self.get_connection( uci_wrapper )
+
+ snapshots = uci_wrapper.get_snapshots( status = 'submitted' )
+ for snapshot in snapshots:
+ log.debug( "Snapshot DB id: '%s', volume id: '%s'" % ( snapshot.id, snapshot.store.volume_id ) )
+ try:
+ snap = conn.create_snapshot( volume_id=snapshot.store.volume_id )
+ snap_id = str( snap ).split(':')[1]
+ uci_wrapper.set_snapshot_id( snapshot.id, snap_id )
+ sh = conn.get_all_snapshots( snap_id ) # get updated status
+ uci_wrapper.set_snapshot_status( status=sh[0].status, snap_id=snap_id )
+ except boto.exception.EC2ResponseError, ex:
+ log.error( "EC2 response error while creating snapshot: '%s'" % e )
+ uci_wrapper.set_snapshot_error( error="EC2 response error while creating snapshot: " + str( e ), snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( "EC2 response error while creating snapshot: " + str( e ), True )
+ return
+ except Exception, ex:
+ log.error( "Error while creating snapshot: '%s'" % ex )
+ uci_wrapper.set_snapshot_error( error="Error while creating snapshot: "+str( ex ), snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( "Error while creating snapshot: " + str( ex ), True )
+ return
+
+ uci_wrapper.change_state( uci_state=uci_states.AVAILABLE )
+
def addStorageToUCI( self, name ):
""" Adds more storage to specified UCI
TODO"""
@@ -328,7 +361,7 @@
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' )"
+ 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(), self.security_group, uci_wrapper.get_type( i_index ), uci_wrapper.get_uci_availability_zone() ) )
reservation = None
try:
@@ -474,7 +507,17 @@
# store.uci.state = uci_states.ERROR
# store.uci.flush()
# store.flush()
-
+
+ # Update pending snapshots or delete ones marked for deletion
+ snapshots = model.CloudSnapshot.filter_by( status='pending', status='delete' ).all()
+ for snapshot in snapshots:
+ if self.type == snapshot.uci.credentials.provider.type and snapshot.status == 'pending':
+ log.debug( "[%s] Running general status update on snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
+ self.update_snapshot( snapshot )
+ elif self.type == snapshot.uci.credentials.provider.type and snapshot.status == 'delete':
+ log.debug( "[%s] Initiating deletion of snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
+ self.delete_snapshot( snapshot )
+
# 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:
@@ -496,7 +539,7 @@
uci_id = inst.uci_id
uci = model.UCI.get( uci_id )
uci.refresh()
- conn = self.get_connection_from_uci( inst.uci )
+ conn = self.get_connection_from_uci( uci )
# Get reservations handle for given instance
try:
@@ -555,7 +598,7 @@
uci_id = store.uci_id
uci = model.UCI.get( uci_id )
uci.refresh()
- conn = self.get_connection_from_uci( inst.uci )
+ conn = self.get_connection_from_uci( uci )
# Get reservations handle for given store
try:
@@ -595,6 +638,87 @@
uci.state( uci_states.ERROR )
return None
+ def updateSnapshot( self, snapshot ):
+ # Get credentials associated wit this store
+ if snapshot.status == 'completed':
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Updating status of snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.get_all_snapshots( [snapshot.snapshot_id] )
+ if len( snap ) > 0:
+ log.debug( "Snapshot '%s' status: %s" % ( snapshot.snapshot_id, snap[0].status ) )
+ snapshot.status = snap[0].status
+ snapshot.flush()
+ else:
+ log.error( "No snapshots returned by EC2 for UCI '%s'" % uci.name )
+ snapshot.status = 'No snapshots returned by EC2.'
+ uci.error = "No snapshots returned by EC2."
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error while updating snapshot: '%s'" % e )
+ snapshot.status = 'error'
+ snapshot.error = "EC2 response error while updating snapshot status: " + str( e )
+ uci.error = "EC2 response error while updating snapshot status: " + str( e )
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ log.error( "Error while updating snapshot: '%s'" % ex )
+ snapshot.status = 'error'
+ snapshot.error = "Error while updating snapshot status: " + str( e )
+ uci.error = "Error while updating snapshot status: " + str( ex )
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ else:
+ log.error( "Cannot delete snapshot '%s' because its status is '%s'. Only snapshots with 'completed' status can be deleted." % ( snapshot.snapshot_id, snapshot.status ) )
+ snapshot.error = "Cannot delete snapshot because its status is '"+snapshot.status+"'. Only snapshots with 'completed' status can be deleted."
+ snapshot.flush()
+
+ def delete_snapshot( self, snapshot ):
+ if snapshot.status == 'delete':
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Deleting snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.delete_snapshot( snapshot.snapshot_id )
+ if snap == True:
+ snapshot.deleted = True
+ snapshot.status = 'deleted'
+ snapshot.flush()
+ return snap
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error while deleting snapshot: '%s'" % e )
+ snapshot.status = 'error'
+ snapshot.error = "EC2 response error while deleting snapshot: " + str( e )
+ uci.error = "EC2 response error while deleting snapshot: " + str( e )
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ log.error( "Error while deleting snapshot: '%s'" % ex )
+ snapshot.status = 'error'
+ snapshot.error = "Cloud provider error while deleting snapshot: " + str( ex )
+ uci.error = "Cloud provider error while deleting snapshot: " + str( ex )
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ else:
+ log.error( "Cannot delete snapshot '%s' because its status is '%s'. Only snapshots with 'completed' status can be deleted." % ( snapshot.snapshot_id, snapshot.status ) )
+ snapshot.error = "Cannot delete snapshot because its status is '"+snapshot.status+"'. Only snapshots with 'completed' status can be deleted."
+ snapshot.status = 'error'
+ snapshot.flush()
+
def processZombie( self, inst ):
"""
Attempt at discovering if starting an instance was successful but local database was not updated
@@ -609,7 +733,7 @@
# report as error.
# Fields attempting to be recovered are: reservation_id, instance status, and launch_time
if inst.instance_id != None:
- conn = self.get_connection_from_uci( inst.uci )
+ conn = self.get_connection_from_uci( uci )
rl = conn.get_all_instances( [inst.instance_id] ) # reservation list
# Update local DB with relevant data from instance
if inst.reservation_id == None:
diff -r c78e8e1514e4 -r c1dc30106721 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Tue Nov 10 18:36:21 2009 -0500
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Wed Nov 11 20:11:58 2009 -0500
@@ -33,7 +33,9 @@
RUNNING = "running",
PENDING = "pending",
ERROR = "error",
- DELETED = "deleted"
+ DELETED = "deleted",
+ SNAPSHOT_UCI = "snapshotUCI",
+ SNAPSHOT = "snapshot"
)
instance_states = Bunch(
@@ -88,6 +90,8 @@
#self.dummyStartUCI( uci_wrapper )
elif uci_state==uci_states.SHUTTING_DOWN:
self.stopUCI( uci_wrapper )
+ elif uci_state==uci_states.SNAPSHOT:
+ self.snapshotUCI( uci_wrapper )
except:
log.exception( "Uncaught exception executing cloud request." )
cnt += 1
@@ -255,8 +259,48 @@
uci_wrapper.change_state( uci_state=uci_states.ERROR )
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 snapshotUCI( self, uci_wrapper ):
+ """
+ Creates snapshot of all storage volumes associated with this UCI.
+ """
+ if uci_wrapper.get_state() != uci_states.ERROR:
+ conn = self.get_connection( uci_wrapper )
- def addStorageToUCI( self, name ):
+ snapshots = uci_wrapper.get_snapshots( status = 'submitted' )
+ for snapshot in snapshots:
+ log.debug( "Snapshot DB id: '%s', volume id: '%s'" % ( snapshot.id, snapshot.store.volume_id ) )
+ try:
+ snap = conn.create_snapshot( volume_id=snapshot.store.volume_id )
+ snap_id = str( snap ).split(':')[1]
+ uci_wrapper.set_snapshot_id( snapshot.id, snap_id )
+ sh = conn.get_all_snapshots( snap_id ) # get updated status
+ uci_wrapper.set_snapshot_status( status=sh[0].status, snap_id=snap_id )
+ except boto.exception.EC2ResponseError, ex:
+ log.error( "EC2 response error while creating snapshot: '%s'" % e )
+ uci_wrapper.set_snapshot_error( error="EC2 response error while creating snapshot: " + str( e ), snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( "Cloud provider response error while creating snapshot: " + str( e ), True )
+ return
+ except Exception, ex:
+ log.error( "Error while creating snapshot: '%s'" % ex )
+ uci_wrapper.set_snapshot_error( error="Error while creating snapshot: "+str( ex ), snap_index=snapshot.id, set_status=True )
+ uci_wrapper.set_error( "Error while creating snapshot: " + str( ex ), True )
+ return
+
+ uci_wrapper.change_state( uci_state=uci_states.AVAILABLE )
+
+# if uci_wrapper.get_state() != uci_states.ERROR:
+#
+# snapshots = uci_wrapper.get_snapshots( status = 'submitted' )
+# for snapshot in snapshots:
+# uci_wrapper.set_snapshot_id( snapshot.id, None, 'euca_error' )
+#
+# log.debug( "Eucalyptus snapshot attempted by user for UCI '%s'" % uci_wrapper.get_name() )
+# uci_wrapper.set_error( "Eucalyptus does not support creation of snapshots at this moment. No snapshot or other changes were performed. \
+# Feel free to resent state of this instance and use it normally.", True )
+
+
+ def addStorageToUCI( self, uci_wrapper ):
""" Adds more storage to specified UCI """
def dummyStartUCI( self, uci_wrapper ):
@@ -432,6 +476,16 @@
# store.uci.flush()
# store.flush()
+ # Update pending snapshots or delete ones marked for deletion
+ snapshots = model.CloudSnapshot.filter_by( status='pending', status='delete' ).all()
+ for snapshot in snapshots:
+ if self.type == snapshot.uci.credentials.provider.type and snapshot.status == 'pending':
+ log.debug( "[%s] Running general status update on snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
+ self.update_snapshot( snapshot )
+ elif self.type == snapshot.uci.credentials.provider.type and snapshot.status == 'delete':
+ log.debug( "[%s] Initiating deletion of snapshot '%s'" % ( snapshot.uci.credentials.provider.type, snapshot.snapshot_id ) )
+ self.delete_snapshot( snapshot )
+
# 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:
@@ -548,10 +602,86 @@
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 )
+ uci.state = uci_states.ERROR
uci.flush()
return None
-
+
+ def update_snapshot( self, snapshot ):
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Updating status of snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.get_all_snapshots( [snapshot.snapshot_id] )
+ if len( snap ) > 0:
+ snapshot.status = snap[0].status
+ log.debug( "Snapshot '%s' status: %s" % ( snapshot.snapshot_id, snapshot.status ) )
+ snapshot.flush()
+ else:
+ log.error( "No snapshots returned by cloud provider for UCI '%s'" % uci.name )
+ snapshot.status = 'No snapshots returned by cloud provider.'
+ uci.error = "No snapshots returned by cloud provider."
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Cloud provider response error while updating snapshot: '%s'" % e )
+ snapshot.status = 'error'
+ snapshot.error = "Cloud provider response error while updating snapshot status: " + str( e )
+ uci.error = "Cloud provider response error while updating snapshot status: " + str( e )
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ log.error( "Error while updating snapshot: '%s'" % ex )
+ snapshot.status = 'error'
+ snapshot.error = "Error while updating snapshot status: " + str( e )
+ uci.error = "Error while updating snapshot status: " + str( ex )
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+
+ def delete_snapshot( self, snapshot ):
+ if snapshot.status == 'delete':
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = model.UCI.get( uci_id )
+ uci.refresh()
+ conn = self.get_connection_from_uci( uci )
+
+ try:
+ log.debug( "Deleting snapshot '%s'" % snapshot.snapshot_id )
+ snap = conn.delete_snapshot( snapshot.snapshot_id )
+ if snap == True:
+ snapshot.deleted = True
+ snapshot.status = 'deleted'
+ snapshot.flush()
+ return snap
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error while deleting snapshot: '%s'" % e )
+ snapshot.status = 'error'
+ snapshot.error = "Cloud provider response error while deleting snapshot: " + str( e )
+ uci.error = "Cloud provider response error while deleting snapshot: " + str( e )
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ except Exception, ex:
+ log.error( "Error while deleting snapshot: '%s'" % ex )
+ snapshot.status = 'error'
+ snapshot.error = "Cloud provider error while deleting snapshot: " + str( ex )
+ uci.error = "Cloud provider error while deleting snapshot: " + str( ex )
+ uci.state = uci_states.ERROR
+ uci.flush()
+ snapshot.flush()
+ else:
+ log.error( "Cannot delete snapshot '%s' because its status is '%s'. Only snapshots with 'completed' status can be deleted." % ( snapshot.snapshot_id, snapshot.status ) )
+ snapshot.error = "Cannot delete snapshot because its status is '"+snapshot.status+"'. Only snapshots with 'completed' status can be deleted."
+ snapshot.status = 'error'
+ snapshot.flush()
+
def processZombie( self, inst ):
"""
Attempt at discovering if starting an instance was successful but local database was not updated
diff -r c78e8e1514e4 -r c1dc30106721 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Tue Nov 10 18:36:21 2009 -0500
+++ b/lib/galaxy/model/__init__.py Wed Nov 11 20:11:58 2009 -0500
@@ -963,6 +963,13 @@
self.size = None
self.availability_zone = None
+class CloudSnapshot( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+ self.store_id = None
+ self.snapshot_id = None
+
class CloudProvider( object ):
def __init__( self ):
self.id = None
diff -r c78e8e1514e4 -r c1dc30106721 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Tue Nov 10 18:36:21 2009 -0500
+++ b/lib/galaxy/model/mapping.py Wed Nov 11 20:11:58 2009 -0500
@@ -448,6 +448,19 @@
Column( "space_consumed", Integer ),
Column( "deleted", Boolean, default=False ) )
+CloudSnapshot.table = Table( "cloud_snapshot", 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( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ),
+ Column( "store_id", Integer, ForeignKey( "cloud_store.id" ), index=True, nullable=False ),
+ Column( "snapshot_id", TEXT ),
+ Column( "status", TEXT ),
+ Column( "description", TEXT ),
+ Column( "error", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+
CloudUserCredentials.table = Table( "cloud_user_credentials", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
@@ -995,7 +1008,8 @@
properties=dict( user=relation( User ),
credentials=relation( CloudUserCredentials ),
instance=relation( CloudInstance, backref='uci' ),
- store=relation( CloudStore, backref='uci', cascade='all, delete-orphan' )
+ store=relation( CloudStore, backref='uci', cascade='all, delete-orphan' ),
+ snapshot=relation( CloudSnapshot, backref='uci' )
) )
assign_mapper( context, CloudInstance, CloudInstance.table,
@@ -1005,7 +1019,12 @@
assign_mapper( context, CloudStore, CloudStore.table,
properties=dict( user=relation( User ),
- i=relation( CloudInstance )
+ i=relation( CloudInstance ),
+ snapshot=relation( CloudSnapshot, backref="store" )
+ ) )
+
+assign_mapper( context, CloudSnapshot, CloudSnapshot.table,
+ properties=dict( user=relation( User )
) )
assign_mapper( context, CloudProvider, CloudProvider.table,
diff -r c78e8e1514e4 -r c1dc30106721 lib/galaxy/model/migrate/versions/0014_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Tue Nov 10 18:36:21 2009 -0500
+++ b/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Wed Nov 11 20:11:58 2009 -0500
@@ -80,6 +80,19 @@
Column( "space_consumed", Integer ),
Column( "deleted", Boolean, default=False ) )
+CloudSnapshot_table = Table( "cloud_snapshot", 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( "uci_id", Integer, ForeignKey( "cloud_uci.id" ), index=True ),
+ Column( "store_id", Integer, ForeignKey( "cloud_store.id" ), index=True, nullable=False ),
+ Column( "snapshot_id", TEXT ),
+ Column( "status", TEXT ),
+ Column( "description", TEXT ),
+ Column( "error", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+
CloudUserCredentials_table = Table( "cloud_user_credentials", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
@@ -118,19 +131,21 @@
# Load existing tables
metadata.reflect()
- CloudImage_table.create()
- UCI_table.create()
- CloudUserCredentials_table.create()
- CloudStore_table.create()
- CloudInstance_table.create()
- CloudProvider_table.create()
+# CloudImage_table.create()
+# UCI_table.create()
+# CloudUserCredentials_table.create()
+# CloudStore_table.create()
+ CloudSnapshot_table.create()
+# CloudInstance_table.create()
+# CloudProvider_table.create()
def downgrade():
metadata.reflect()
- CloudImage_table.drop()
- CloudInstance_table.drop()
- CloudStore_table.drop()
- CloudUserCredentials_table.drop()
- UCI_table.drop()
- CloudProvider_table.drop()
\ No newline at end of file
+# CloudImage_table.drop()
+# CloudInstance_table.drop()
+# CloudStore_table.drop()
+ CloudSnapshot_table.drop()
+# CloudUserCredentials_table.drop()
+# UCI_table.drop()
+# CloudProvider_table.drop()
\ No newline at end of file
diff -r c78e8e1514e4 -r c1dc30106721 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Tue Nov 10 18:36:21 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Wed Nov 11 20:11:58 2009 -0500
@@ -44,7 +44,9 @@
RUNNING = "running",
PENDING = "pending",
ERROR = "error",
- DELETED = "deleted"
+ DELETED = "deleted",
+ SNAPSHOT_UCI = "snapshotUCI",
+ SNAPSHOT = "snapshot"
)
instance_states = Bunch(
@@ -106,7 +108,9 @@
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 ) ) \
+ model.UCI.c.state==uci_states.DELETING_UCI,
+ model.UCI.c.state==uci_states.SNAPSHOT,
+ model.UCI.c.state==uci_states.SNAPSHOT_UCI ) ) \
.order_by( desc( model.UCI.c.update_time ) ) \
.all()
@@ -223,6 +227,87 @@
return self.list( trans )
@web.expose
+ @web.require_login( "use Galaxy cloud" )
+ def create_snapshot( self, trans, id ):
+ user = trans.get_user()
+ id = trans.security.decode_id( id )
+ uci = get_uci( trans, id )
+
+ stores = trans.sa_session.query( model.CloudStore ) \
+ .filter_by( user=user, deleted=False, uci_id=id ) \
+ .all()
+
+ if ( len( stores ) > 0 ) and ( uci.state == uci_states.AVAILABLE ):
+ for store in stores:
+ snapshot = model.CloudSnapshot()
+ snapshot.user = user
+ snapshot.uci = uci
+ snapshot.store = store
+ snapshot.status = 'submitted'
+ uci.state = uci_states.SNAPSHOT_UCI
+ # Persist
+ session = trans.sa_session
+ session.save_or_update( snapshot )
+ session.save_or_update( uci )
+ session.flush()
+ elif len( stores ) == 0:
+ error( "No storage volumes found that are associated with this instance." )
+ else:
+ error( "Snapshot can be created only for an instance that is in 'available' state." )
+
+ # Log and display the management page
+ trans.log_event( "User initiated creation of new snapshot." )
+ trans.set_message( "Creation of new snapshot initiated. " )
+ return self.list( trans )
+
+ @web.expose
+ @web.require_login( "use Galaxy cloud" )
+ def view_snapshots( self, trans, id=None ):
+ """
+ View details about any snapshots associated with given UCI
+ """
+ user = trans.get_user()
+ id = trans.security.decode_id( id )
+
+ snaps = trans.sa_session.query( model.CloudSnapshot ) \
+ .filter_by( user=user, uci_id=id, deleted=False ) \
+ .order_by( desc( model.CloudSnapshot.c.update_time ) ) \
+ .all()
+
+ return trans.fill_template( "cloud/view_snapshots.mako",
+ snaps = snaps )
+
+ @web.expose
+ @web.require_login( "use Galaxy cloud" )
+ def delete_snapshot( self, trans, uci_id=None, snap_id=None ):
+ """
+ Initiates deletion of a snapshot
+ """
+ user = trans.get_user()
+ snap_id = trans.security.decode_id( snap_id )
+ # Set snapshot as 'ready for deletion' to be picked up by general updater
+ snap = trans.sa_session.query( model.CloudSnapshot ).get( snap_id )
+
+ if snap.status == 'completed':
+ snap.status = 'delete'
+ snap.flush()
+ trans.set_message( "Snapshot '%s' is marked for deletion. Once the deletion is complete, it will no longer be visible in this list. "
+ "Please note that this process may take up to a minute." % snap.snapshot_id )
+ else:
+ error( "Only snapshots in state 'completed' can be deleted. See the cloud provider directly "
+ "if you believe the snapshot is available and can be deleted." )
+
+ # Display new list of snapshots
+ uci_id = trans.security.decode_id( uci_id )
+ snaps = trans.sa_session.query( model.CloudSnapshot ) \
+ .filter_by( user=user, uci_id=uci_id, deleted=False ) \
+ .order_by( desc( model.CloudSnapshot.c.update_time ) ) \
+ .all()
+
+ return trans.fill_template( "cloud/view_snapshots.mako",
+ snaps = snaps )
+
+ @web.expose
@web.require_login( "add instance storage" )
def addStorage( self, trans, id ):
instance = get_uci( trans, id )
diff -r c78e8e1514e4 -r c1dc30106721 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Tue Nov 10 18:36:21 2009 -0500
+++ b/templates/cloud/configure_cloud.mako Wed Nov 11 20:11:58 2009 -0500
@@ -20,17 +20,26 @@
${h.js( "jquery" )}
<script type="text/javascript">
+function trim19(str){
+ var str = str.replace(/^\s\s*/, ''),
+ ws = /\s/,
+ i = str.length;
+ while (ws.test(str.charAt(--i)));
+ return str.slice(0, i + 1);
+}
+
function update_state() {
$.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, reload url on various state changes
old_state = $(elem + "-state").text();
- prev_old_state = $(elem + "-state-p").text();
+ prev_old_state = trim19( $(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 );
+ //console.log( trim19(prev_old_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' ) || \
@@ -44,9 +53,9 @@
( old_state=='submitted' && new_state=='error' ) || ( old_state=='submittedUCI' && 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('deleting') && new_state=='error' ) || ( prev_old_state.match('deletingUCI') && new_state=='error' ) ) {
+ ( prev_old_state.match('deletingUCI') && new_state=='error' ) ) {
// TODO: Following clause causes constant page refresh for an exception thrown as a result of instance not starting correctly - need alternative method!
- //( prev_old_state.match('available') && new_state=='error' ) || \
+ //( prev_old_state.match('available') && new_state=='error' ) || ( prev_old_state.match('deleting') && new_state=='error' ) \
var url = "${h.url_for( controller='cloud', action='list')}";
location.replace( url );
@@ -80,6 +89,7 @@
// Update 'state' and 'time alive' fields
$(elem + "-state").text( data[i].state );
+ //$(elem + "-state-p").text( data[i].state );
if (data[i].launch_time) {
$(elem + "-launch_time").text( data[i].launch_time.substring(0, 16 ) + " UTC (" + data[i].time_ago + ")" );
}
@@ -313,6 +323,8 @@
<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='create_snapshot', id=trans.security.encode_id(prevInstance.id) )}">Create snapshot</a>
+ <a class="action-button" href="${h.url_for( action='view_snapshots', id=trans.security.encode_id(prevInstance.id) )}">View snapshots</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', 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>
diff -r c78e8e1514e4 -r c1dc30106721 templates/cloud/view_snapshots.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/cloud/view_snapshots.mako Wed Nov 11 20:11:58 2009 -0500
@@ -0,0 +1,90 @@
+<%inherit file="/base.mako"/>
+
+<%def name="title()">Snapshots</%def>
+
+%if message:
+<%
+ try:
+ messagetype
+ except:
+ messagetype = "done"
+%>
+
+
+
+<p />
+<div class="${messagetype}message">
+ ${message}
+</div>
+%endif
+
+%if snaps:
+ <h2>Snapshots for instance ${snaps[0].uci.name}</h2>
+%else:
+ <h2>Selected instance has no recorded or associated snapshots.</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 snaps:
+ <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <colgroup width="2%"></colgroup>
+ <colgroup width="16%"></colgroup>
+ <colgroup width="16%"></colgroup>
+ <colgroup width="10%"></colgroup>
+ <colgroup width="5%"></colgroup>
+ <tr class="header">
+ <th>#</th>
+ <th>Create time</th>
+ <th>Snapshot ID</th>
+ <th>Status</th>
+ <th>Delete?</th>
+ <th></th>
+ </tr>
+ <%
+ total_hours = 0
+ %>
+ %for i, snap in enumerate( snaps ):
+ <tr>
+ <td>${i+1}</td>
+ <td>
+ %if snap.create_time:
+ ${str(snap.create_time)[:16]} UCT
+ %else:
+ N/A
+ %endif
+ </td>
+ <td>
+ %if snap.snapshot_id:
+ ${snap.snapshot_id}
+ %else:
+ N/A
+ %endif
+ </td>
+ <td>
+ %if snap.status:
+ ${snap.status}
+ %else:
+ N/A
+ %endif
+ </td>
+ <td>
+ <a confirm="Are you sure you want to delete snapshot '${snap.snapshot_id}'?"
+ href="${h.url_for( controller='cloud', action='delete_snapshot', uci_id=trans.security.encode_id(snap.uci.id), snap_id=trans.security.encode_id(snap.id) )}">x</a>
+ </td>
+ </tr>
+ %endfor
+ </table>
+%endif
+
+
+
+
+
1
0

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/5963dd169715
changeset: 3086:5963dd169715
user: Enis Afgan <afgane(a)gmail.com>
date: Fri Nov 06 13:45:57 2009 -0500
description:
Implemented on-line checking of zone availability when registering a UCI. This introduces provider-specific ties in cloud.py controller that will need to be modified if non-EC2 cloud providers are added.
diffstat:
lib/galaxy/cloud/providers/ec2.py | 1 +
lib/galaxy/cloud/providers/eucalyptus.py | 1 +
lib/galaxy/web/controllers/cloud.py | 72 ++++++++++++++++++++++++-----------
3 files changed, 51 insertions(+), 23 deletions(-)
diffs (127 lines):
diff -r dc1a6f3a2c08 -r 5963dd169715 lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Fri Nov 06 12:27:52 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Fri Nov 06 13:45:57 2009 -0500
@@ -633,6 +633,7 @@
log.error( "Establishing connection with cloud failed: %s" % str(e) )
uci.error( "Establishing connection with cloud failed: " + str(e) )
uci.state( uci_states.ERROR )
+ uci.flush()
return None
return conn
diff -r dc1a6f3a2c08 -r 5963dd169715 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Fri Nov 06 12:27:52 2009 -0500
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Fri Nov 06 13:45:57 2009 -0500
@@ -599,6 +599,7 @@
log.error( "Establishing connection with cloud failed: %s" % str(e) )
uci.error( "Establishing connection with cloud failed: " + str(e) )
uci.state( uci_states.ERROR )
+ uci.flush()
return None
return conn
diff -r dc1a6f3a2c08 -r 5963dd169715 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Fri Nov 06 12:27:52 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Fri Nov 06 13:45:57 2009 -0500
@@ -25,6 +25,8 @@
from boto.ec2.connection import EC2Connection
from boto.ec2.regioninfo import RegionInfo
from galaxy.cloud import CloudManager
+import boto.exception
+import boto
import logging
log = logging.getLogger( __name__ )
@@ -270,22 +272,38 @@
user = trans.get_user()
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...
+ return trans.show_error_message( "You must register credentials before configuring a Galaxy cloud instance." )
# Create dict mapping of cloud providers to zones available by those providers
providersToZones = {}
for storedCred in storedCreds:
- 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']
+ zones = None
+ conn = get_connection( trans, storedCred )
+ if conn != None:
+ avail_zones = []
+ try:
+ zones = conn.get_all_zones()
+ for zone in zones:
+ zone = str( zone ).split(':')[1]
+ avail_zones.append( zone )
+ providersToZones[storedCred.name] = avail_zones
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Retrieving zones for credentials '%s' failed." % storedCred.name )
+ providersToZones[storedCred.name] = ['Unknown provider zone']
else:
providersToZones[storedCred.name] = ['Unknown provider zone']
+ # Hard-coded solution
+# if storedCred.provider.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:
# Check if volume size is entered as an integer
try:
@@ -1030,22 +1048,30 @@
return instances
-def get_connection( trans, credName ):
+def get_connection( trans, creds ):
"""
- Establishes EC2 connection using user's default credentials
+ Establishes cloud connection using user's credentials
"""
- log.debug( '##### Establishing cloud connection.' )
- user = trans.get_user()
- creds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user, name=credName ).first()
+ log.debug( 'Establishing cloud connection.' )
+# user = trans.get_user()
+# creds = trans.sa_session.query( model.CloudUserCredentials ) \
+# .filter_by( user=user, name=credName ) \
+# .first()
+ #.filter( model.CloudUserCredentials.c.deleted != True ) \ MOVE TO LINE ABOVE ONCE DELETE COLUMS ARE IMPLEMENTED
+
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" )
+ try:
+ euca_region = RegionInfo( None, creds.provider.region_name, creds.provider.region_endpoint )
+ conn = EC2Connection( aws_access_key_id=a_key,
+ aws_secret_access_key=s_key,
+ is_secure=creds.provider.is_secure,
+ port=creds.provider.port,
+ region=euca_region,
+ path=creds.provider.path )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Establishing connection with cloud failed: %s" % str(e) )
+ return None
+
return conn
- else:
- error( "You must specify default credentials before starting an instance." )
- return 0
1
0

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/c78e8e1514e4
changeset: 3088:c78e8e1514e4
user: Enis Afgan <afgane(a)gmail.com>
date: Tue Nov 10 18:36:21 2009 -0500
description:
Fixed bugs leading to cloud provder code for EC2 and EPC to be very functional.
diffstat:
lib/galaxy/cloud/__init__.py | 39 ++++-
lib/galaxy/cloud/providers/ec2.py | 210 ++++++++++++++++++++--------------
lib/galaxy/cloud/providers/eucalyptus.py | 162 ++++++++++++++++----------
lib/galaxy/web/controllers/cloud.py | 88 +++++++-------
templates/cloud/configure_cloud.mako | 42 +++---
templates/cloud/viewInstance.mako | 21 +-
6 files changed, 325 insertions(+), 237 deletions(-)
diffs (798 lines):
diff -r 7c4cfc243cc3 -r c78e8e1514e4 lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Mon Nov 09 18:55:14 2009 -0500
+++ b/lib/galaxy/cloud/__init__.py Tue Nov 10 18:36:21 2009 -0500
@@ -34,6 +34,14 @@
ERROR = "error",
CREATING = "creating"
)
+instance_states = Bunch(
+ TERMINATED = "terminated",
+ SUBMITTED = "submitted",
+ RUNNING = "running",
+ PENDING = "pending",
+ SHUTTING_DOWN = "shutting-down",
+ ERROR = "error"
+)
class CloudManager( object ):
"""
@@ -338,14 +346,23 @@
def set_error( self, error, set_state=False ):
"""
- 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'
+ Sets error field of given UCI in local Galaxy database as well as any instances associated with
+ this UCI whose state is 'None' or 'SUBMITTED'. If set_state is set to 'true',
+ method also sets state of give UCI and corresponding instances to 'error'
"""
uci = model.UCI.get( self.uci_id )
uci.refresh()
uci.error = error
if set_state:
uci.state = uci_states.ERROR
+ instances = model.CloudInstance \
+ .filter_by( uci=uci ) \
+ .filter( or_( model.CloudInstance.c.state==None, model.CloudInstance.c.state==instance_states.SUBMITTED ) ) \
+ .all()
+ for i in instances:
+ i.error = error
+ i.state = instance_states.ERROR
+ i.flush()
uci.flush()
# --------- Getter methods -----------------
@@ -358,6 +375,15 @@
# cred = model.CloudUserCredentials.get( cred_id )
return uci.credentials.provider.type
+ 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_instances_indexes( self, state=None ):
"""
Returns indexes of instances associated with given UCI as they are stored in local Galaxy database and
@@ -372,15 +398,6 @@
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()
diff -r 7c4cfc243cc3 -r c78e8e1514e4 lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Mon Nov 09 18:55:14 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Tue Nov 10 18:36:21 2009 -0500
@@ -37,6 +37,7 @@
instance_states = Bunch(
TERMINATED = "terminated",
+ SUBMITTED = "submitted",
RUNNING = "running",
PENDING = "pending",
SHUTTING_DOWN = "shutting-down",
@@ -119,49 +120,73 @@
def check_key_pair( self, uci_wrapper, conn ):
"""
- Generate keypair using user's default credentials
+ Generate key pair using user's credentials
"""
- log.debug( "Getting user's key pair: '%s'" % self.key_pair )
+ kp = None
+ kp_name = uci_wrapper.get_name().replace(' ','_') + "_kp"
+ log.debug( "Checking user's key pair: '%s'" % kp_name )
try:
- kp = conn.get_key_pair( self.key_pair )
- uci_kp = uci_wrapper.get_key_pair_name()
+ kp = conn.get_key_pair( kp_name )
+ uci_kp_name = uci_wrapper.get_key_pair_name()
uci_material = uci_wrapper.get_key_pair_material()
- if kp.name != uci_kp or uci_material == None:
- try: # key pair exists on the cloud but not in local database, so re-generate it (i.e., delete and then create)
- conn.delete_key_pair( self.key_pair )
- except boto.exception.EC2ResponseError:
- pass
- kp = self.create_key_pair( conn )
+ if kp != None:
+ if kp.name != uci_kp_name or uci_material == None:
+ # key pair exists on the cloud but not in local database, so re-generate it (i.e., delete and then create)
+ try:
+ conn.delete_key_pair( kp_name )
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error when deleting key pair: '%s'" % e )
+ uci_wrapper.set_error( "EC2 response error while deleting key pair: " + str( e ), True )
+ else:
+ try:
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error when creating key pair: '%s'" % e )
+ uci_wrapper.set_error( "EC2 response error while creating key pair: " + str( e ), True )
+ except Exception, ex:
+ log.error( "Exception when creating key pair: '%s'" % e )
+ uci_wrapper.set_error( "Error while creating key pair: " + str( e ), True )
+
+ 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'" % kp_name )
+ kp = self.create_key_pair( conn, kp_name )
uci_wrapper.set_key_pair( kp.name, kp.material )
else:
- return kp.name
- 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 = self.create_key_pair( conn )
- uci_wrapper.set_key_pair( 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 )
+ log.error( "EC2 response error while retrieving key pair: '%s'" % e )
+ uci_wrapper.set_error( "Cloud provider response error while retrieving key pair: " + str( e ), True )
if kp != None:
return kp.name
else:
return None
- def create_key_pair( self, conn ):
+ def create_key_pair( self, conn, kp_name ):
try:
- return conn.create_key_pair( self.key_pair )
+ return conn.create_key_pair( kp_name )
except boto.exception.EC2ResponseError, e:
return None
- def get_mi_id( self, type ):
+ def get_mi_id( self, uci_wrapper, i_index ):
"""
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==2 ).first().image_id
+ i_type = uci_wrapper.get_type( i_index )
+ if i_type=='m1.small' or i_type=='c1.medium':
+ arch = 'i386'
+ else:
+ arch = 'x86_64'
+
+ mi = model.CloudImage.filter_by( deleted=False, provider_type=self.type, architecture=arch ).first()
+ if mi:
+ return mi.image_id
+ else:
+ log.error( "Machine image could not be retrieved for UCI '%s'." % uci_wrapper.get_name() )
+ uci_wrapper.set_error( "Machine image could not be retrieved. Contact site administrator to ensure needed machine image is registered.", True )
+ return None
def shutdown( self ):
"""Attempts to gracefully shut down the monitor thread"""
@@ -267,71 +292,82 @@
conn = self.get_connection( uci_wrapper )
self.check_key_pair( uci_wrapper, conn )
- i_indexes = uci_wrapper.get_instances_indexes( state=None ) # 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 )
+ i_indexes = uci_wrapper.get_instances_indexes( state=instance_states.SUBMITTED ) # Get indexes of i_indexes associated with this UCI that are in 'submitted' state
+ log.debug( "Starting instances with IDs: '%s' associated with UCI '%s' " % ( i_indexes, uci_wrapper.get_name(), ) )
+ if len( i_indexes ) > 0:
+ for i_index in i_indexes:
+ # Get machine image for current instance
+ mi_id = self.get_mi_id( uci_wrapper, i_index )
+ uci_wrapper.set_mi( i_index, mi_id )
+ if uci_wrapper.get_key_pair_name() == None:
+ log.error( "Key pair for UCI '%s' is None." % uci_wrapper.get_name() )
+ uci_wrapper.set_error( "Key pair not found. Try resetting the state and starting the instance again.", True )
+ return
- # Check if galaxy security group exists (and create it if it does not)
- log.debug( "Setting up '%s' security group." % self.security_group )
- try:
- conn.get_all_security_groups( [self.security_group] ) # security groups
- except boto.exception.EC2ResponseError, e:
- if e.code == 'InvalidGroup.NotFound':
- log.info( "No security group found, creating security group '%s'" % self.security_group )
+ # Check if galaxy security group exists (and create it if it does not)
+ log.debug( "Setting up '%s' security group." % self.security_group )
+ try:
+ conn.get_all_security_groups( [self.security_group] ) # security groups
+ except boto.exception.EC2ResponseError, e:
+ if e.code == 'InvalidGroup.NotFound':
+ log.info( "No security group found, creating security group '%s'" % self.security_group )
+ try:
+ gSecurityGroup = conn.create_security_group(self.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
+ except boto.exception.EC2ResponseError, ex:
+ log.error( "EC2 response error while creating security group: '%s'" % e )
+ uci_wrapper.set_error( "EC2 response error while creating security group: " + str( e ), True )
+ else:
+ log.error( "EC2 response error while retrieving security group: '%s'" % e )
+ uci_wrapper.set_error( "EC2 response error while retrieving security group: " + str( e ), True )
+
+
+ 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(), self.security_group, uci_wrapper.get_type( i_index ), uci_wrapper.get_uci_availability_zone() ) )
+ reservation = None
try:
- gSecurityGroup = conn.create_security_group(self.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
- except boto.exception.EC2ResponseError, ex:
- log.error( "EC2 response error while creating security group: '%s'" % e )
- uci_wrapper.set_error( "EC2 response error while creating security group: " + str( e ), True )
+ reservation = conn.run_instances( image_id=mi_id,
+ key_name=uci_wrapper.get_key_pair_name(),
+ security_groups=[self.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 )
+ except Exception, ex:
+ log.error( "Error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str( ex ) ) )
+ uci_wrapper.set_error( "Cloud provider error when starting: " + str( ex ), True )
+ # Record newly available instance data into local Galaxy database
+ if reservation:
+ uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( self.format_time( reservation.instances[0].launch_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 )
+ uci_wrapper.set_security_group_name( self.security_group, i_id=i_id )
+ 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 )
else:
- log.error( "EC2 response error while retrieving security group: '%s'" % e )
- uci_wrapper.set_error( "EC2 response error while retrieving security group: " + str( e ), True )
-
-
- 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 ), self.security_group, uci_wrapper.get_type( i_index ), uci_wrapper.get_uci_availability_zone() ) )
- reservation = None
- try:
- reservation = conn.run_instances( image_id=mi_id,
- key_name=uci_wrapper.get_key_pair_name( i_index ),
- security_groups=[self.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 )
- except Exception, ex:
- log.error( "Error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str( ex ) ) )
- uci_wrapper.set_error( "Cloud provider error when starting: " + str( ex ), 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 reservation:
- uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
- if not uci_wrapper.uci_launch_time_set():
- uci_wrapper.set_uci_launch_time( self.format_time( reservation.instances[0].launch_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 )
- uci_wrapper.set_security_group_name( self.security_group, i_id=i_id )
- 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 )
+ log.error( "UCI '%s' is in 'error' state, starting instance was aborted." % uci_wrapper.get_name() )
+ else:
+ log.error( "No instances were found for UCI '%s'" % uci_wrapper.get_name() )
+ uci_wrapper.set_error( "EC2 response error when retrieving instance information: " + str(e), True )
+ else:
+ log.error( "UCI '%s' is in 'error' state, starting instance was aborted." % uci_wrapper.get_name() )
def stopUCI( self, uci_wrapper):
"""
diff -r 7c4cfc243cc3 -r c78e8e1514e4 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Mon Nov 09 18:55:14 2009 -0500
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Tue Nov 10 18:36:21 2009 -0500
@@ -38,6 +38,7 @@
instance_states = Bunch(
TERMINATED = "terminated",
+ SUBMITTED = "submitted",
RUNNING = "running",
PENDING = "pending",
SHUTTING_DOWN = "shutting-down",
@@ -58,7 +59,6 @@
def __init__( self, app ):
self.type = "eucalyptus" # cloud provider type (e.g., ec2, eucalyptus, opennebula)
self.zone = "epc"
- self.key_pair = "galaxy-keypair"
self.queue = Queue()
self.threads = []
@@ -123,49 +123,72 @@
"""
Generate key pair using user's credentials
"""
- log.debug( "Getting user's key pair: '%s'" % self.key_pair )
+ kp = None
+ kp_name = uci_wrapper.get_name().replace(' ','_') + "_kp"
+ log.debug( "Checking user's key pair: '%s'" % kp_name )
try:
- kp = conn.get_key_pair( self.key_pair )
- uci_kp = uci_wrapper.get_key_pair_name()
+ kp = conn.get_key_pair( kp_name )
+ uci_kp_name = uci_wrapper.get_key_pair_name()
uci_material = uci_wrapper.get_key_pair_material()
- if kp.name != uci_kp or uci_material == None:
- try: # key pair exists on the cloud but not in local database, so re-generate it (i.e., delete and then create)
- conn.delete_key_pair( self.key_pair )
- except boto.exception.EC2ResponseError:
- pass
- kp = self.create_key_pair( conn )
+ if kp != None:
+ if kp.name != uci_kp_name or uci_material == None:
+ # key pair exists on the cloud but not in local database, so re-generate it (i.e., delete and then create)
+ try:
+ conn.delete_key_pair( kp_name )
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error when deleting key pair: '%s'" % e )
+ uci_wrapper.set_error( "Cloud provider response error while deleting key pair: " + str( e ), True )
+ else:
+ try:
+ kp = self.create_key_pair( conn, kp_name )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ except boto.exception.EC2ResponseError, e:
+ log.error( "EC2 response error when creating key pair: '%s'" % e )
+ uci_wrapper.set_error( "Cloud provider response error while creating key pair: " + str( e ), True )
+ except Exception, ex:
+ log.error( "Exception when creating key pair: '%s'" % e )
+ uci_wrapper.set_error( "Error while creating key pair: " + str( e ), True )
+
+ 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'" % kp_name )
+ kp = self.create_key_pair( conn, kp_name )
uci_wrapper.set_key_pair( kp.name, kp.material )
else:
- return kp.name
- 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 = self.create_key_pair( conn )
- uci_wrapper.set_key_pair( kp.name, kp.material )
- else:
- log.error( "EC2 response error: '%s'" % e )
- uci_wrapper.set_error( "Cloud provider response error while creating key pair: " + str( e ), True )
+ log.error( "EC2 response error while retrieving key pair: '%s'" % e )
+ uci_wrapper.set_error( "Cloud provider response error while retrieving key pair: " + str( e ), True )
if kp != None:
return kp.name
else:
return None
- def create_key_pair( self, conn ):
+ def create_key_pair( self, conn, kp_name ):
try:
- return conn.create_key_pair( self.key_pair )
+ return conn.create_key_pair( kp_name )
except boto.exception.EC2ResponseError, e:
return None
- def get_mi_id( self, type ):
+ def get_mi_id( self, uci_wrapper, i_index ):
"""
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/
"""
- log.debug( "image id: '%s'" % model.CloudImage.get( 1 ).image_id )
- return model.CloudImage.get( 1 ).image_id
-
+ i_type = uci_wrapper.get_type( i_index )
+ if i_type=='m1.small' or i_type=='c1.medium':
+ arch = 'i386'
+ else:
+ arch = 'x86_64'
+
+ mi = model.CloudImage.filter_by( deleted=False, provider_type=self.type, architecture=arch ).first()
+ if mi:
+ return mi.image_id
+ else:
+ log.error( "Machine image could not be retrieved for UCI '%s'." % uci_wrapper.get_name() )
+ uci_wrapper.set_error( "Machine image could not be retrieved. Contact site administrator to ensure needed machine image is registered.", True )
+ return None
+
def shutdown( self ):
"""Attempts to gracefully shut down the monitor thread"""
log.info( "sending stop signal to worker threads in eucalyptus cloud manager" )
@@ -253,47 +276,56 @@
if uci_wrapper.get_state() != uci_states.ERROR:
conn = self.get_connection( uci_wrapper )
self.check_key_pair( uci_wrapper, conn )
+ if uci_wrapper.get_key_pair_name() == None:
+ log.error( "Key pair for UCI '%s' is NULL." % uci_wrapper.get_name() )
+ uci_wrapper.set_error( "Key pair not found. Try resetting the state and starting the instance again.", True )
+ return
- i_indexes = uci_wrapper.get_instances_indexes( state=None ) # 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(): %s" % ( mi_id, uci_wrapper.get_key_pair_name() ) )
- uci_wrapper.set_mi( i_index, mi_id )
-
- if uci_wrapper.get_state() != uci_states.ERROR and uci_wrapper.get_key_pair_name() != None:
- 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() ) )
- reservation = None
- try:
- reservation = conn.run_instances( image_id=mi_id, key_name=uci_wrapper.get_key_pair_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 )
- 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( "Cloud provider response error when starting: " + str( e ), True )
- except Exception, ex:
- log.error( "Error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str( ex ) ) )
- uci_wrapper.set_error( "Cloud provider error when starting: " + str( ex ), True )
-# l_time = datetime.utcnow()
-# uci_wrapper.set_launch_time( l_time, i_index=i_index )
- if reservation:
- uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
- if not uci_wrapper.uci_launch_time_set():
- uci_wrapper.set_uci_launch_time( self.format_time( reservation.instances[0].launch_time ) )
+ i_indexes = uci_wrapper.get_instances_indexes( state=instance_states.SUBMITTED ) # Get indexes of i_indexes associated with this UCI that are in 'submitted' state
+ if len( i_indexes ) > 0:
+ for i_index in i_indexes:
+ mi_id = self.get_mi_id( uci_wrapper, i_index )
+ log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name(): %s" % ( mi_id, uci_wrapper.get_key_pair_name() ) )
+ uci_wrapper.set_mi( i_index, mi_id )
+
+ if uci_wrapper.get_state() != uci_states.ERROR and uci_wrapper.get_key_pair_name() != None:
+ log.debug( "Starting UCI instance '%s'" % uci_wrapper.get_name() )
+ log.debug( "Using following command: conn.run_instances( image_id='%s', key_name='%s', instance_type='%s' )"
+ % ( mi_id, uci_wrapper.get_key_pair_name(), uci_wrapper.get_type( i_index ) ) )
+ reservation = None
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 from 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() ) )
+ reservation = conn.run_instances( image_id=mi_id,
+ key_name=uci_wrapper.get_key_pair_name(),
+ instance_type=uci_wrapper.get_type( i_index ) )
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( "Cloud provider response error when retrieving instance information: " + str(e), True )
-
- if uci_wrapper.get_key_pair_name() == None:
- log.debug( "Key pair for UCI '%s' is NULL." % uci_wrapper.get_name() )
- uci_wrapper.set_error( "Key pair not found. Try resetting the state and starting the instance again.", True )
+ log.error( "EC2 response error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str( e ) ) )
+ uci_wrapper.set_error( "Cloud provider response error when starting: " + str( e ), True )
+ except Exception, ex:
+ log.error( "Error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str( ex ) ) )
+ uci_wrapper.set_error( "Cloud provider error when starting: " + str( ex ), True )
+ # Record newly available instance data into local Galaxy database
+ if reservation:
+ uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( self.format_time( reservation.instances[0].launch_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 from 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( "Cloud provider response error when retrieving instance information: " + str(e), True )
+ else:
+ log.error( "UCI '%s' is in 'error' state, starting instance was aborted." % uci_wrapper.get_name() )
+ else:
+ log.error( "No instances were found for UCI '%s'" % uci_wrapper.get_name() )
+ uci_wrapper.set_error( "EC2 response error when retrieving instance information: " + str(e), True )
+ else:
+ log.error( "UCI '%s' is in 'error' state, starting instance was aborted." % uci_wrapper.get_name() )
def stopUCI( self, uci_wrapper):
"""
diff -r 7c4cfc243cc3 -r c78e8e1514e4 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Mon Nov 09 18:55:14 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Tue Nov 10 18:36:21 2009 -0500
@@ -49,9 +49,11 @@
instance_states = Bunch(
TERMINATED = "terminated",
+ SUBMITTED = "submitted",
RUNNING = "running",
PENDING = "pending",
- SHUTTING_DOWN = "shutting-down"
+ SHUTTING_DOWN = "shutting-down",
+ ERROR = "error"
)
store_states = Bunch(
@@ -141,15 +143,14 @@
"""
user = trans.get_user()
uci = get_uci( trans, id )
-# mi = get_mi( trans, uci, type )
stores = get_stores( trans, uci )
# 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.AVAILABLE ):
instance = model.CloudInstance()
instance.user = user
-# instance.image = mi
instance.uci = uci
+ instance.state = instance_states.SUBMITTED
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
uci.state = uci_states.SUBMITTED_UCI
@@ -253,39 +254,6 @@
inst_error = vol_error = cred_error = None
error = {}
user = trans.get_user()
- storedCreds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user, deleted=False ).all()
- if len( storedCreds ) == 0:
- return trans.show_error_message( "You must register credentials before configuring a Galaxy cloud instance." )
- # Create dict mapping of cloud-providers-to-zones available by those providers
- providersToZones = {}
- for storedCred in storedCreds:
- zones = None
- conn = get_connection( trans, storedCred )
- if conn != None:
- avail_zones = []
- try:
- zones = conn.get_all_zones()
- for zone in zones:
- zone = str( zone ).split(':')[1]
- avail_zones.append( zone )
- providersToZones[storedCred.name] = avail_zones
- except boto.exception.EC2ResponseError, e:
- log.error( "Retrieving zones for credentials '%s' failed." % storedCred.name )
- providersToZones[storedCred.name] = ['Unknown provider zone']
- else:
- providersToZones[storedCred.name] = ['Unknown provider zone']
-
- # Hard-coded solution
-# if storedCred.provider.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:
# Check if volume size is entered as an integer
@@ -337,14 +305,48 @@
except AttributeError, ae:
inst_error = "No registered cloud images. You must contact administrator to add some before proceeding."
log.debug("AttributeError when registering new UCI '%s': %s " % ( instanceName, str( ae ) ) )
+ else:
+ storedCreds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user, deleted=False ).all()
+ if len( storedCreds ) == 0:
+ return trans.show_error_message( "You must register credentials before configuring a Galaxy cloud instance." )
+ # Create dict mapping of cloud-providers-to-zones available by those providers
+ providersToZones = {}
+ for storedCred in storedCreds:
+ zones = None
+ conn = get_connection( trans, storedCred )
+ if conn != None:
+ avail_zones = []
+ try:
+ zones = conn.get_all_zones()
+ for z in zones:
+ z = str( z ).split(':')[1]
+ avail_zones.append( z )
+ providersToZones[storedCred.name] = avail_zones
+ except boto.exception.EC2ResponseError, e:
+ log.error( "Retrieving zones for credentials '%s' failed: %s" % ( storedCred.name, e ) )
+ providersToZones[storedCred.name] = [ "Retrieving zones failed: " + str( e ) ]
+ else:
+ providersToZones[storedCred.name] = ['Connection with cloud provider could not be established.']
- return trans.fill_template( "cloud/configure_uci.mako",
- instanceName = instanceName,
- credName = storedCreds,
- volSize = volSize,
- zone = zone,
- error = error,
- providersToZones = providersToZones )
+ # Hard-coded solution
+ # if storedCred.provider.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']
+
+ return trans.fill_template( "cloud/configure_uci.mako",
+ instanceName = instanceName,
+ credName = storedCreds,
+ volSize = volSize,
+ zone = zone,
+ error = error,
+ providersToZones = providersToZones )
@web.expose
@web.require_admin
diff -r 7c4cfc243cc3 -r c78e8e1514e4 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Mon Nov 09 18:55:14 2009 -0500
+++ b/templates/cloud/configure_cloud.mako Tue Nov 10 18:36:21 2009 -0500
@@ -28,14 +28,15 @@
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 );
+ 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' ) || ( prev_old_state.match('newUCI') && new_state=='available' ) || \
- ( prev_old_state.match('new') && new_state=='available' ) ) {
+ ( prev_old_state.match('new') && new_state=='available' ) || ( prev_old_state.match('deletingUCI') && new_state=='deleted' ) || \
+ ( prev_old_state.match('deleting') && new_state=='deleted' ) ) {
var url = "${h.url_for( controller='cloud', action='list')}";
location.replace( url );
}
@@ -50,7 +51,8 @@
var url = "${h.url_for( controller='cloud', action='list')}";
location.replace( url );
}
- else if ( new_state=='shutting-down' || new_state=='shutting-downUCI' ) {
+
+ if ( new_state=='shutting-down' || new_state=='shutting-downUCI' ) {
$(elem + "-link").text( "" );
}
@@ -198,7 +200,7 @@
<colgroup width="25%"></colgroup>
<colgroup width="10%"></colgroup>
<tr class="header">
- <th>Instance name (credentials)</th>
+ <th>Live instance name (credentials)</th>
<th>Storage size (GB)</th>
<th>State</th>
<th>Alive since</th>
@@ -255,24 +257,23 @@
</tr>
%endif
</table>
-
+ <p />
## *****************************************************
## Manage previously configured instances
- <table class="mange-table noHR" border="0" cellspacing="0" cellpadding="0" width="100%">
+ ## <table class="mange-table noHR" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<colgroup width="40%"></colgroup>
<colgroup width="15%"></colgroup>
<colgroup width="10%"></colgroup>
- <colgroup width="35%"></colgroup>
- ##<tr class="header">
- ##<th>Previously configured instances</th>
- ##<th>Storage size (GB)</th>
- ##<th>State</th>
- ##<th>Alive since</th>
- ##<th></th>
- ##<th></th>
- ##<th></th>
- ##<th></th>
- ##</tr>
+ <colgroup width="25%"></colgroup>
+ <colgroup width="10%"></colgroup>
+ <tr class="header">
+ <th>Configured instance name (credentials)</th>
+ <th>Storage size (GB)</th>
+ <th>State</th>
+ <th></th>
+ <th></th>
+ </tr>
%if prevInstances:
%for i, prevInstance in enumerate( prevInstances ):
@@ -307,8 +308,7 @@
${str(prevInstance.state)}
%endif
</td>
- <td>N/A</td>
- <td>
+ <td>
<div popupmenu="pi-${i}-popup">
<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>
diff -r 7c4cfc243cc3 -r c78e8e1514e4 templates/cloud/viewInstance.mako
--- a/templates/cloud/viewInstance.mako Mon Nov 09 18:55:14 2009 -0500
+++ b/templates/cloud/viewInstance.mako Tue Nov 10 18:36:21 2009 -0500
@@ -115,21 +115,22 @@
%if liveInstance.uci.key_pair_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"
+ <td>
+ <div id="short">
+ <a onclick="document.getElementById('full').style.display = 'block';
+ document.getElementById('short').style.display = 'none'; return 0"
href="javascript:void(0)">
+ Show
</a>
</div>
- <div id="fullComment2" style="DISPLAY: none">
- <nobr><b>${liveInstance.uci.key_pair_material}</b></nobr><br/>
- <a onclick="document.getElementById('shortComment2').style.display = 'block';
- document.getElementById('fullComment2').style.display = 'none'; return 0;"
+ <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)">
- - Hide
- </a>
- </div>
+ - Hide</a>
+ ${liveInstance.uci.key_pair_material}<br/>
+ </div>
+ </td>
</tr>
%endif
1
0

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/7c4cfc243cc3
changeset: 3087:7c4cfc243cc3
user: Enis Afgan <afgane(a)gmail.com>
date: Mon Nov 09 18:55:14 2009 -0500
description:
Changed DB tables to support ghost deletion. Changed keypair saving location to UCI table. Adjusted code accordingly. *Have not tested with cloud providers yet thoughtadd templates/cloud/add_image.mako
diffstat:
lib/galaxy/cloud/__init__.py | 86 ++++++++---
lib/galaxy/cloud/providers/ec2.py | 146 +++++++++++---------
lib/galaxy/cloud/providers/eucalyptus.py | 130 ++++++++++--------
lib/galaxy/model/__init__.py | 1 -
lib/galaxy/model/mapping.py | 46 +++--
lib/galaxy/model/migrate/versions/0014_cloud_tables.py | 44 ++++--
lib/galaxy/web/controllers/cloud.py | 126 +++++++++--------
templates/cloud/add_image.mako | 98 ++++++++++++++
templates/cloud/configure_cloud.mako | 5 +-
templates/cloud/edit_image.mako | 34 ++++-
templates/cloud/list_images.mako | 22 ++-
templates/cloud/view.mako | 6 +-
templates/cloud/viewInstance.mako | 14 +-
13 files changed, 501 insertions(+), 257 deletions(-)
diffs (1468 lines):
diff -r 5963dd169715 -r 7c4cfc243cc3 lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Fri Nov 06 13:45:57 2009 -0500
+++ b/lib/galaxy/cloud/__init__.py Mon Nov 09 18:55:14 2009 -0500
@@ -23,6 +23,7 @@
NEW = "new",
DELETING_UCI = "deletingUCI",
DELETING = "deleting",
+ DELETED = "deleted",
SUBMITTED_UCI = "submittedUCI",
SUBMITTED = "submitted",
SHUTTING_DOWN_UCI = "shutting-downUCI",
@@ -139,16 +140,15 @@
for uci_wrapper in new_requests:
session.clear()
- self.provider.put( uci_wrapper )
+ self.put( uci_wrapper )
# Done with the session
mapping.Session.remove()
- def put( self, job_id, tool ):
- """Add a job to the queue (by job identifier)"""
- if not self.track_jobs_in_database:
- self.queue.put( ( job_id, tool.id ) )
- self.sleeper.wake()
+ def put( self, uci_wrapper ):
+ """Add a request to the queue."""
+ self.provider.put( uci_wrapper )
+ self.sleeper.wake()
def shutdown( self ):
"""Attempts to gracefully shut down the worker thread"""
@@ -156,7 +156,7 @@
# We're not the real queue, do nothing
return
else:
- log.info( "sending stop signal to worker thread" )
+ log.info( "Sending stop signal to worker thread" )
self.running = False
self.sleeper.wake()
log.info( "cloud manager stopped" )
@@ -175,7 +175,7 @@
"""
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.
+ Need to provide either: (1) state for the UCI, or (2) instance_id and it's state, or (3) 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:
@@ -198,22 +198,22 @@
instance.image = mi
instance.flush()
- def set_key_pair( self, i_index, key_name, key_material=None ):
+ def set_key_pair( self, 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()).
+ Sets key pair value for current UCI.
"""
- instance = model.CloudInstance.get( i_index )
- instance.keypair_name = key_name
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ uci.key_pair_name = key_name
if key_material is not None:
- instance.keypair_material = key_material
- instance.flush()
+ uci.key_pair_material = key_material
+ uci.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.
+ Stores launch time in local database for instance with specified index - i_index (as it is stored in local
+ Galaxy database) or with specified instance ID - i_id (as obtained from the cloud provider AND stored
+ in local Galaxy Database). Either 'i_index' or 'i_id' needs to be provided.
"""
if i_index != None:
instance = model.CloudInstance.get( i_index )
@@ -231,6 +231,11 @@
uci.flush()
def set_stop_time( self, stop_time, i_index=None, i_id=None ):
+ """
+ Stores stop time in local database for instance with specified index - i_index (as it is stored in local
+ Galaxy database) or with specified instance ID - i_id (as obtained from the cloud provider AND stored
+ in local Galaxy Database). Either 'i_index' or 'i_id' needs to be provided.
+ """
if i_index != None:
instance = model.CloudInstance.get( i_index )
instance.stop_time = stop_time
@@ -245,6 +250,21 @@
uci.refresh()
uci.launch_time = None
uci.flush()
+
+ def set_security_group_name( self, security_group_name, i_index=None, i_id=None ):
+ """
+ Stores security group name in local database for instance with specified index - i_index (as it is stored in local
+ Galaxy database) or with specified instance ID - i_id (as obtained from the cloud provider AND stored
+ in local Galaxy Database). Either 'i_index' or 'i_id' needs to be provided.
+ """
+ if i_index != None:
+ instance = model.CloudInstance.get( i_index )
+ instance.security_group = security_group_name
+ instance.flush()
+ elif i_id != None:
+ instance = model.CloudInstance.filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ instance.security_group = security_group_name
+ instance.flush()
def set_reservation_id( self, i_index, reservation_id ):
instance = model.CloudInstance.get( i_index )
@@ -382,19 +402,35 @@
uci.refresh()
return uci.name
- def get_key_pair_name( self, i_index=None, i_id=None ):
+ def get_key_pair_name( self ):
+ """
+ Returns keypair name associated with given UCI.
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.key_pair_name
+
+ def get_key_pair_material( self ):
+ """
+ Returns keypair material (i.e., private key) associated with given UCI.
+ """
+ uci = model.UCI.get( self.uci_id )
+ uci.refresh()
+ return uci.key_pair_material
+
+ def get_security_group_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
+ obtained from cloud provider and stored in local Galaxy database, return security group name associated
with given instance.
"""
if i_index != None:
instance = model.CloudInstance.get( i_index )
- return instance.keypair_name
+ return instance.security_group
elif i_id != None:
instance = model.CloudInstance.filter_by( uci_id=self.uci_id, instance_id=i_id).first()
- return instance.keypair_name
-
+ return instance.security_group
+
def get_access_key( self ):
uci = model.UCI.get( self.uci_id )
uci.refresh()
@@ -469,8 +505,8 @@
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.state = uci_states.DELETED # for bookkeeping reasons, mark as deleted but don't actually delete.
+ uci.deleted = True
uci.flush()
class CloudProvider( object ):
diff -r 5963dd169715 -r 7c4cfc243cc3 lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Fri Nov 06 13:45:57 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Mon Nov 09 18:55:14 2009 -0500
@@ -58,11 +58,12 @@
self.type = "ec2" # cloud provider type (e.g., ec2, eucalyptus, opennebula)
self.zone = "us-east-1a"
self.key_pair = "galaxy-keypair"
+ self.security_group = "galaxyWeb"
self.queue = Queue()
self.threads = []
nworkers = 5
- log.info( "Starting EC2 cloud controller workers" )
+ log.info( "Starting EC2 cloud controller workers..." )
for i in range( nworkers ):
worker = threading.Thread( target=self.run_next )
worker.start()
@@ -116,28 +117,43 @@
return conn
- def set_keypair( self, uci_wrapper, conn ):
+ def check_key_pair( self, uci_wrapper, conn ):
"""
Generate keypair using user's default credentials
"""
- log.debug( "Getting user's keypair" )
- instances = uci_wrapper.get_instances_indexes()
+ log.debug( "Getting user's key pair: '%s'" % self.key_pair )
try:
kp = conn.get_key_pair( self.key_pair )
- for inst in instances:
- uci_wrapper.set_key_pair( inst, kp.name )
- return kp.name
+ uci_kp = uci_wrapper.get_key_pair_name()
+ uci_material = uci_wrapper.get_key_pair_material()
+ if kp.name != uci_kp or uci_material == None:
+ try: # key pair exists on the cloud but not in local database, so re-generate it (i.e., delete and then create)
+ conn.delete_key_pair( self.key_pair )
+ except boto.exception.EC2ResponseError:
+ pass
+ kp = self.create_key_pair( conn )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ else:
+ return kp.name
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 )
+ kp = self.create_key_pair( conn )
+ uci_wrapper.set_key_pair( 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
+ uci_wrapper.set_error( "EC2 response error while creating key pair: " + str( e ), True )
+
+ if kp != None:
+ return kp.name
+ else:
+ return None
+
+ def create_key_pair( self, conn ):
+ try:
+ return conn.create_key_pair( self.key_pair )
+ except boto.exception.EC2ResponseError, e:
+ return None
def get_mi_id( self, type ):
"""
@@ -170,7 +186,6 @@
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
@@ -196,7 +211,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 )
+ uci_wrapper.set_error( "Volume '%s' not found by EC2 after being created" % vol.id )
def deleteUCI( self, uci_wrapper ):
"""
@@ -215,14 +230,13 @@
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.deleted = True
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:
@@ -249,31 +263,34 @@
"""
Starts instance(s) of given UCI on the cloud.
"""
- conn = self.get_connection( uci_wrapper )
-
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 ) )
-
- if uci_wrapper.get_state() != uci_states.ERROR:
+ conn = self.get_connection( uci_wrapper )
+ self.check_key_pair( uci_wrapper, conn )
+
+ i_indexes = uci_wrapper.get_instances_indexes( state=None ) # 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 )
# 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
+ log.debug( "Setting up '%s' security group." % self.security_group )
+ try:
+ conn.get_all_security_groups( [self.security_group] ) # security groups
+ except boto.exception.EC2ResponseError, e:
+ if e.code == 'InvalidGroup.NotFound':
+ log.info( "No security group found, creating security group '%s'" % self.security_group )
+ try:
+ gSecurityGroup = conn.create_security_group(self.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
+ except boto.exception.EC2ResponseError, ex:
+ log.error( "EC2 response error while creating security group: '%s'" % e )
+ uci_wrapper.set_error( "EC2 response error while creating security group: " + str( e ), True )
+ else:
+ log.error( "EC2 response error while retrieving security group: '%s'" % e )
+ uci_wrapper.set_error( "EC2 response error while retrieving security group: " + str( e ), True )
+
if uci_wrapper.get_state() != uci_states.ERROR:
# Start an instance
@@ -281,34 +298,40 @@
#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() ) )
+ % ( mi_id, uci_wrapper.get_key_pair_name( i_index ), self.security_group, uci_wrapper.get_type( i_index ), uci_wrapper.get_uci_availability_zone() ) )
+ reservation = None
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() )
+ key_name=uci_wrapper.get_key_pair_name( i_index ),
+ security_groups=[self.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 )
+ except Exception, ex:
+ log.error( "Error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str( ex ) ) )
+ uci_wrapper.set_error( "Cloud provider error when starting: " + str( ex ), True )
# Record newly available instance data into local Galaxy database
- l_time = datetime.utcnow()
+# l_time = datetime.utcnow()
# uci_wrapper.set_launch_time( l_time, i_index=i_index ) # format_time( reservation.i_indexes[0].launch_time ) )
- uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
- 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 )
+ if reservation:
+ uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( self.format_time( reservation.instances[0].launch_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 )
+ uci_wrapper.set_security_group_name( self.security_group, i_id=i_id )
+ 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):
"""
@@ -544,11 +567,11 @@
"""
# Check if any instance-specific information was written to local DB; if 'yes', set instance and UCI's error message
# suggesting manual check.
- if inst.launch_time != None or inst.reservation_id != None or inst.instance_id != None or inst.keypair_name != None:
+ if inst.launch_time != None or inst.reservation_id != None or inst.instance_id != None:
# Try to recover state - this is best-case effort, so if something does not work immediately, not
# recovery steps are attempted. Recovery is based on hope that instance_id is available in local DB; if not,
# report as error.
- # Fields attempting to be recovered are: reservation_id, keypair_name, instance status, and launch_time
+ # Fields attempting to be recovered are: reservation_id, instance status, and launch_time
if inst.instance_id != None:
conn = self.get_connection_from_uci( inst.uci )
rl = conn.get_all_instances( [inst.instance_id] ) # reservation list
@@ -559,11 +582,6 @@
except: # something failed, so skip
pass
- if inst.keypair_name == None:
- try:
- inst.keypair_name = rl[0].instances[0].key_name
- except: # something failed, so skip
- pass
try:
state = rl[0].instances[0].update()
inst.state = state
diff -r 5963dd169715 -r 7c4cfc243cc3 lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Fri Nov 06 13:45:57 2009 -0500
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Mon Nov 09 18:55:14 2009 -0500
@@ -63,7 +63,7 @@
self.threads = []
nworkers = 5
- log.info( "Starting eucalyptus cloud controller workers" )
+ log.info( "Starting eucalyptus cloud controller workers..." )
for i in range( nworkers ):
worker = threading.Thread( target=self.run_next )
worker.start()
@@ -74,7 +74,6 @@
"""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()
uci_state = uci_wrapper.get_state()
if uci_state is self.STOP_SIGNAL:
@@ -90,7 +89,7 @@
elif uci_state==uci_states.SHUTTING_DOWN:
self.stopUCI( uci_wrapper )
except:
- log.exception( "Uncaught exception executing request." )
+ log.exception( "Uncaught exception executing cloud request." )
cnt += 1
def get_connection( self, uci_wrapper ):
@@ -120,28 +119,43 @@
return conn
- def set_keypair( self, uci_wrapper, conn ):
+ def check_key_pair( self, uci_wrapper, conn ):
"""
- Generate keypair using user's default credentials
+ Generate key pair using user's credentials
"""
- log.debug( "Getting user's keypair: '%s'" % self.key_pair )
- instances = uci_wrapper.get_instances_indexes()
+ log.debug( "Getting user's key pair: '%s'" % self.key_pair )
try:
kp = conn.get_key_pair( self.key_pair )
- for inst in instances:
- uci_wrapper.set_key_pair( inst, kp.name )
- return kp.name
+ uci_kp = uci_wrapper.get_key_pair_name()
+ uci_material = uci_wrapper.get_key_pair_material()
+ if kp.name != uci_kp or uci_material == None:
+ try: # key pair exists on the cloud but not in local database, so re-generate it (i.e., delete and then create)
+ conn.delete_key_pair( self.key_pair )
+ except boto.exception.EC2ResponseError:
+ pass
+ kp = self.create_key_pair( conn )
+ uci_wrapper.set_key_pair( kp.name, kp.material )
+ else:
+ return kp.name
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 )
+ kp = self.create_key_pair( conn )
+ uci_wrapper.set_key_pair( 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 )
+ uci_wrapper.set_error( "Cloud provider response error while creating key pair: " + str( e ), True )
- return kp.name
+ if kp != None:
+ return kp.name
+ else:
+ return None
+
+ def create_key_pair( self, conn ):
+ try:
+ return conn.create_key_pair( self.key_pair )
+ except boto.exception.EC2ResponseError, e:
+ return None
def get_mi_id( self, type ):
"""
@@ -203,7 +217,7 @@
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.deleted = True
v.flush()
count += 1
else:
@@ -236,46 +250,50 @@
"""
Starts instance(s) of given UCI on the cloud.
"""
- conn = self.get_connection( uci_wrapper )
-#
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
-
- if uci_wrapper.get_state() != uci_states.ERROR:
+ conn = self.get_connection( uci_wrapper )
+ self.check_key_pair( uci_wrapper, conn )
+
+ i_indexes = uci_wrapper.get_instances_indexes( state=None ) # 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 ) ) )
+ log.debug( "mi_id: %s, uci_wrapper.get_key_pair_name(): %s" % ( mi_id, uci_wrapper.get_key_pair_name() ) )
uci_wrapper.set_mi( i_index, mi_id )
- if uci_wrapper.get_state() != uci_states.ERROR:
+ if uci_wrapper.get_state() != uci_states.ERROR and uci_wrapper.get_key_pair_name() != None:
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 ) ) )
+ log.debug( 'Using following command: conn.run_instances( image_id=%s, key_name=%s )' % ( mi_id, uci_wrapper.get_key_pair_name() ) )
+ reservation = None
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=mi_id, key_name=uci_wrapper.get_key_pair_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 )
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()
+ log.error( "EC2 response error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str( e ) ) )
+ uci_wrapper.set_error( "Cloud provider response error when starting: " + str( e ), True )
+ except Exception, ex:
+ log.error( "Error when starting UCI '%s': '%s'" % ( uci_wrapper.get_name(), str( ex ) ) )
+ uci_wrapper.set_error( "Cloud provider error when starting: " + str( ex ), True )
+# l_time = datetime.utcnow()
# uci_wrapper.set_launch_time( l_time, i_index=i_index )
- uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
- 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 from 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 )
-
+ if reservation:
+ uci_wrapper.set_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
+ if not uci_wrapper.uci_launch_time_set():
+ uci_wrapper.set_uci_launch_time( self.format_time( reservation.instances[0].launch_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 from 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( "Cloud provider response error when retrieving instance information: " + str(e), True )
+
+ if uci_wrapper.get_key_pair_name() == None:
+ log.debug( "Key pair for UCI '%s' is NULL." % uci_wrapper.get_name() )
+ uci_wrapper.set_error( "Key pair not found. Try resetting the state and starting the instance again.", True )
def stopUCI( self, uci_wrapper):
"""
@@ -294,7 +312,7 @@
notStopped = []
for r in rl:
for inst in r.instances:
- log.debug( "Sending stop signal to instance '%s' associated with reservation '%s'." % ( inst, r ) )
+ log.debug( "Sending stop signal to instance '%s' associated with reservation '%s' (UCI: %s)." % ( inst, r, uci_wrapper.get_name() ) )
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() )
@@ -385,16 +403,17 @@
# 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 ) ) \
+ log.debug( "zombie UCI: %s" % zombie.name )
+ z_instances = model.CloudInstance \
+ .filter_by( uci_id=zombie.id, 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() - z_inst.update_time
+ log.debug( "z_inst.id: %s, time delta is %s sec" % ( z_inst.id, td.seconds ) )
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 with DB id '%s'" % ( z_inst.uci.credentials.provider.type, z_inst.id ) )
+ log.debug( "[%s](td=%s) Running zombie repair update on instance with DB id '%s'" % ( z_inst.uci.credentials.provider.type, td.seconds, z_inst.id ) )
self.processZombie( z_inst )
def updateInstance( self, inst ):
@@ -509,11 +528,11 @@
"""
# Check if any instance-specific information was written to local DB; if 'yes', set instance and UCI's error message
# suggesting manual check.
- if inst.launch_time != None or inst.reservation_id != None or inst.instance_id != None or inst.keypair_name != None:
+ if inst.launch_time != None or inst.reservation_id != None or inst.instance_id != None:
# Try to recover state - this is best-case effort, so if something does not work immediately, not
# recovery steps are attempted. Recovery is based on hope that instance_id is available in local DB; if not,
# report as error.
- # Fields attempting to be recovered are: reservation_id, keypair_name, instance status, and launch_time
+ # Fields attempting to be recovered are: reservation_id, instance status, and launch_time
if inst.instance_id != None:
conn = self.get_connection_from_uci( inst.uci )
rl = conn.get_all_instances( [inst.instance_id] ) # reservation list
@@ -524,11 +543,6 @@
except: # something failed, so skip
pass
- if inst.keypair_name == None:
- try:
- inst.keypair_name = rl[0].instances[0].key_name
- except: # something failed, so skip
- pass
try:
state = rl[0].instances[0].update()
inst.state = state
diff -r 5963dd169715 -r 7c4cfc243cc3 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Fri Nov 06 13:45:57 2009 -0500
+++ b/lib/galaxy/model/__init__.py Mon Nov 09 18:55:14 2009 -0500
@@ -951,7 +951,6 @@
self.instance_id = None
self.mi = None
self.state = None
- self.keypair_name = None
self.public_dns = None
self.availability_zone = None
diff -r 5963dd169715 -r 7c4cfc243cc3 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Fri Nov 06 13:45:57 2009 -0500
+++ b/lib/galaxy/model/mapping.py Mon Nov 09 18:55:14 2009 -0500
@@ -390,9 +390,12 @@
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "provider_type", TEXT ),
Column( "image_id", TEXT, nullable=False ),
Column( "manifest", TEXT ),
- Column( "state", TEXT ) )
+ Column( "state", TEXT ),
+ Column( "architecture", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
""" UserConfiguredInstance (UCI) table """
UCI.table = Table( "cloud_uci", metadata,
@@ -401,11 +404,14 @@
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 ),
+ Column( "key_pair_name", TEXT ),
+ Column( "key_pair_material", TEXT ),
Column( "name", TEXT ),
Column( "state", TEXT ),
Column( "error", TEXT ),
Column( "total_size", Integer ),
- Column( "launch_time", DateTime ) )
+ Column( "launch_time", DateTime ),
+ Column( "deleted", Boolean, default=False ) )
CloudInstance.table = Table( "cloud_instance", metadata,
Column( "id", Integer, primary_key=True ),
@@ -418,13 +424,12 @@
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( "error", TEXT ),
Column( "public_dns", TEXT ),
Column( "private_dns", TEXT ),
- Column( "keypair_name", TEXT ),
- Column( "keypair_material", TEXT ),
+ Column( "security_group", TEXT ),
Column( "availability_zone", TEXT ) )
CloudStore.table = Table( "cloud_store", metadata,
@@ -437,10 +442,22 @@
Column( "volume_id", TEXT ),
Column( "size", Integer, nullable=False ),
Column( "availability_zone", TEXT ),
- Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ), index=True ),
+ Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ) ),
Column( "status", TEXT ),
Column( "device", TEXT ),
- Column( "space_consumed", Integer ) )
+ Column( "space_consumed", Integer ),
+ Column( "deleted", Boolean, default=False ) )
+
+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( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ),
+ Column( "name", TEXT ),
+ Column( "access_key", TEXT ),
+ Column( "secret_key", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
CloudProvider.table = Table( "cloud_provider", metadata,
Column( "id", Integer, primary_key=True ),
@@ -461,19 +478,8 @@
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 ),
- 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( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ) )
-
+ Column( "path", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
# ***************************************************************************
StoredWorkflow.table = Table( "stored_workflow", metadata,
diff -r 5963dd169715 -r 7c4cfc243cc3 lib/galaxy/model/migrate/versions/0014_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Fri Nov 06 13:45:57 2009 -0500
+++ b/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Mon Nov 09 18:55:14 2009 -0500
@@ -12,25 +12,38 @@
metadata = MetaData( migrate_engine )
+def display_migration_details():
+ print
+ print "========================================"
+ print "This script adds tables needed for Galaxy cloud functionality."
+ print "========================================"
+
CloudImage_table = Table( "cloud_image", metadata,
Column( "id", Integer, primary_key=True ),
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "provider_type", TEXT ),
Column( "image_id", TEXT, nullable=False ),
Column( "manifest", TEXT ),
- Column( "state", TEXT ) )
+ Column( "state", TEXT ),
+ Column( "architecture", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
+""" UserConfiguredInstance (UCI) table """
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 ),
+ Column( "key_pair_name", TEXT ),
+ Column( "key_pair_material", TEXT ),
Column( "name", TEXT ),
Column( "state", TEXT ),
Column( "error", TEXT ),
Column( "total_size", Integer ),
- Column( "launch_time", DateTime ) )
+ Column( "launch_time", DateTime ),
+ Column( "deleted", Boolean, default=False ) )
CloudInstance_table = Table( "cloud_instance", metadata,
Column( "id", Integer, primary_key=True ),
@@ -39,17 +52,16 @@
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( "mi_id", TEXT, ForeignKey( "cloud_image.image_id" ), index=True ),
Column( "state", TEXT ),
Column( "error", TEXT ),
Column( "public_dns", TEXT ),
Column( "private_dns", TEXT ),
- Column( "keypair_name", TEXT ),
- Column( "keypair_material", TEXT ),
+ Column( "security_group", TEXT ),
Column( "availability_zone", TEXT ) )
CloudStore_table = Table( "cloud_store", metadata,
@@ -58,25 +70,26 @@
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 ),
- Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ), index=True ),
+ Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ) ),
Column( "status", TEXT ),
Column( "device", TEXT ),
- Column( "space_consumed", Integer ) )
+ Column( "space_consumed", Integer ),
+ Column( "deleted", Boolean, default=False ) )
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( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ),
Column( "name", TEXT ),
Column( "access_key", TEXT ),
Column( "secret_key", TEXT ),
- Column( "defaultCred", Boolean, default=False ),
- Column( "provider_id", Integer, ForeignKey( "cloud_provider.id" ), index=True, nullable=False ) )
+ Column( "deleted", Boolean, default=False ) )
CloudProvider_table = Table( "cloud_provider", metadata,
Column( "id", Integer, primary_key=True ),
@@ -97,17 +110,20 @@
Column( "proxy_pass", TEXT ),
Column( "debug", Integer ),
Column( "https_connection_factory", TEXT ),
- Column( "path", TEXT ) )
+ Column( "path", TEXT ),
+ Column( "deleted", Boolean, default=False ) )
def upgrade():
+ display_migration_details()
+ # Load existing tables
metadata.reflect()
CloudImage_table.create()
UCI_table.create()
CloudUserCredentials_table.create()
+ CloudStore_table.create()
+ CloudInstance_table.create()
CloudProvider_table.create()
- CloudInstance_table.create()
- CloudStore_table.create()
def downgrade():
metadata.reflect()
diff -r 5963dd169715 -r 7c4cfc243cc3 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Fri Nov 06 13:45:57 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Mon Nov 09 18:55:14 2009 -0500
@@ -76,11 +76,13 @@
cloudCredentials = trans.sa_session.query( model.CloudUserCredentials ) \
.filter_by( user=user ) \
+ .filter( model.CloudUserCredentials.c.deleted != True ) \
.order_by( model.CloudUserCredentials.c.name ) \
.all()
cloudProviders = trans.sa_session.query( model.CloudProvider ) \
.filter_by( user=user ) \
+ .filter( model.CloudProvider.c.deleted != True ) \
.order_by( model.CloudProvider.c.name ) \
.all()
@@ -131,25 +133,6 @@
prevInstances = prevInstances,
cloudProviders = cloudProviders )
- @web.require_login( "use Galaxy cloud" )
- def makeDefault( self, trans, id=None ):
- """
- Set current credentials as default.
- *NOT USED*
- """
- currentDefault = get_default_credentials (trans)
- if currentDefault:
- currentDefault.defaultCred = False
-
- newDefault = get_stored_credentials( trans, id )
- newDefault.defaultCred = True
- trans.sa_session.flush()
- trans.set_message( "Credentials '%s' set as default." % newDefault.name )
-
- # 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' ):
@@ -158,14 +141,14 @@
"""
user = trans.get_user()
uci = get_uci( trans, id )
- mi = get_mi( trans, uci, type )
+# mi = get_mi( trans, uci, type )
stores = get_stores( trans, uci )
# 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.AVAILABLE ):
instance = model.CloudInstance()
instance.user = user
- instance.image = mi
+# instance.image = mi
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
@@ -177,9 +160,9 @@
session.flush()
# Log
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." )
+ trans.set_message( "Galaxy instance started. NOTE: Please wait about 5 minutes for the instance to "
+ "start up. A button to connect to the instance will appear alongside "
+ "instance description once cloud instance of Galaxy is ready." )
return self.list( trans )
if len(stores) == 0:
@@ -270,10 +253,10 @@
inst_error = vol_error = cred_error = None
error = {}
user = trans.get_user()
- storedCreds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user ).all()
+ storedCreds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user, deleted=False ).all()
if len( storedCreds ) == 0:
return trans.show_error_message( "You must register credentials before configuring a Galaxy cloud instance." )
- # Create dict mapping of cloud providers to zones available by those providers
+ # Create dict mapping of cloud-providers-to-zones available by those providers
providersToZones = {}
for storedCred in storedCreds:
zones = None
@@ -314,8 +297,7 @@
# Create new user configured instance
try:
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 ) ) \
+ .filter_by (user=user, deleted=False, name=instanceName ) \
.first():
error['inst_error'] = "An instance with that name already exist."
elif instanceName=='' or len( instanceName ) > 255:
@@ -327,7 +309,7 @@
elif ( int( volSize ) < 1 ) or ( int( volSize ) > 1000 ):
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."
+ error['zone_error'] = "You must select a zone where this UCI will be registered."
else:
# Capture user configured instance information
uci = model.UCI()
@@ -349,14 +331,12 @@
session.save_or_update( storage )
session.flush()
# Log and display the management page
- trans.log_event( "User configured new cloud instance" )
+ trans.log_event( "User configured new cloud instance: '%s'" % 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."
except AttributeError, ae:
inst_error = "No registered cloud images. You must contact administrator to add some before proceeding."
- log.debug("AttributeError: %s " % str( ae ) )
+ log.debug("AttributeError when registering new UCI '%s': %s " % ( instanceName, str( ae ) ) )
return trans.fill_template( "cloud/configure_uci.mako",
instanceName = instanceName,
@@ -368,21 +348,29 @@
@web.expose
@web.require_admin
- def addNewImage( self, trans, image_id='', manifest='', state=None ):
- id_error = None
- manifest_error = None
- if image_id:
- if image_id=='' or len( image_id ) > 255:
- id_error = "Image ID must be between 1 and 255 characters long."
- elif trans.app.model.CloudUserCredentials.filter(
- trans.app.model.CloudImage.table.c.image_id==image_id ).first():
- id_error = "Image with ID '" + image_id + "' is already registered. \
- Please choose another ID.ga"
+ def addNewImage( self, trans, provider_type='', image_id='', manifest='', architecture='', state=None ):
+ #id_error = arch_error = provider_error = manifest_error = None
+ error = {}
+ if provider_type or image_id or manifest or architecture:
+ if provider_type=='':
+ error['provider_error'] = "You must select cloud provider type for this machine image."
+ elif image_id=='' or len( image_id ) > 255:
+ error['id_error'] = "Image ID must be between 1 and 255 characters long."
+ elif trans.app.model.CloudUserCredentials \
+ .filter_by( deleted=False ) \
+ .filter( trans.app.model.CloudImage.table.c.image_id == image_id ) \
+ .first():
+ error['id_error'] = "Image with ID '" + image_id + "' is already registered. \
+ Please choose another ID."
+ elif architecture=='':
+ error['arch_error'] = "You must select architecture type for this machine image."
else:
# Create new image
image = model.CloudImage()
+ image.provider_type = provider_type
image.image_id = image_id
image.manifest = manifest
+ image.architecture = architecture
# Persist
session = trans.sa_session
session.save_or_update( image )
@@ -394,16 +382,24 @@
image.state = state
images = trans.sa_session.query( model.CloudImage ).all()
return trans.fill_template( '/cloud/list_images.mako', images=images )
-
- return trans.show_form(
- web.FormBuilder( web.url_for(), "Add new cloud image", submit_text="Add" )
- .add_text( "image_id", "Machine Image ID (AMI or EMI)", value='', error=id_error )
- .add_text( "manifest", "Manifest", value='', error=manifest_error ) )
+
+ return trans.fill_template( "cloud/add_image.mako",
+ provider_type = provider_type,
+ image_id = image_id,
+ manifest = manifest,
+ architecture = architecture,
+ error = error )
+# return trans.show_form(
+# web.FormBuilder( web.url_for(), "Add new cloud image", submit_text="Add" )
+# .add_text( "provider_type", "Provider type", value='ec2 or eucalyptus', error=provider_error )
+# .add_text( "image_id", "Machine Image ID (AMI or EMI)", value='', error=id_error )
+# .add_text( "manifest", "Manifest", value='', error=manifest_error )
+# .add_text( "architecture", "Architecture", value='i386 or x86_64', error=arch_error ) )
@web.expose
@web.require_login( "use Galaxy cloud" )
def listMachineImages( self, trans ):
- images = trans.sa_session.query( model.CloudImage ).all()
+ images = trans.sa_session.query( model.CloudImage ).filter( trans.app.model.CloudImage.table.c.deleted != True ).all()
return trans.fill_template( '/cloud/list_images.mako', images=images )
@web.expose
@@ -413,13 +409,13 @@
id = trans.security.decode_id( id )
image = trans.sa_session.query( model.CloudImage ).get( id )
- image.delete()
+ image.deleted = True
image.flush()
return self.listMachineImages( trans )
@web.expose
@web.require_admin
- def editImage( self, trans, image_id, manifest, id=None, edited=False ):
+ def editImage( self, trans, provider_type='', image_id='', manifest='', architecture='', id='', edited=False ):
error = {}
if not isinstance( id, int ):
id = trans.security.decode_id( id )
@@ -435,9 +431,12 @@
if image_id=='' or len( image_id ) > 255:
error['id_error'] = "Image ID must be between 1 and 255 characters in length."
elif trans.app.model.CloudImage \
+ .filter_by( deleted=False ) \
.filter( and_( trans.app.model.CloudImage.table.c.id != image.id, trans.app.model.CloudImage.table.c.image_id==image_id ) ) \
.first():
error['id_error'] = "Image with ID '" + image_id + "' already exist. Please choose an alternative name."
+ elif architecture=='' or len( architecture ) > 255:
+ error['arch_error'] = "Architecture type must be between 1 and 255 characters long."
if error:
return trans.fill_template( "cloud/edit_image.mako",
image = image,
@@ -446,12 +445,13 @@
else:
image.image_id = image_id
image.manifest = manifest
+ image.architecture = architecture
# Persist
session = trans.sa_session
session.save_or_update( image )
session.flush()
# Log and display the management page
- trans.set_message( "Image '%s' edited." % image.image_id )
+ trans.set_message( "Machine image '%s' edited." % image.image_id )
return self.listMachineImages( trans )
@web.expose
@@ -548,8 +548,10 @@
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():
+ elif trans.app.model.CloudUserCredentials \
+ .filter_by( user=user, deleted=False ) \
+ .filter( trans.app.model.CloudUserCredentials.table.c.name == credName ) \
+ .first():
error['cred_error'] = "Credentials with that name already exist."
elif providerName=='':
error['provider_error'] = "You must select cloud provider associated with these credentials."
@@ -627,13 +629,12 @@
stored = get_stored_credentials( trans, id )
UCIs = trans.sa_session.query( model.UCI ) \
.filter_by( user=user, credentials_id=stored.id ) \
- .filter( model.UCI.c.state!=uci_states.DELETED ) \
+ .filter( model.UCI.c.deleted != True ) \
.all()
if len(UCIs) == 0:
# Delete and save
- sess = trans.sa_session
- sess.delete( stored )
+ stored.deleted = True
stored.flush()
# Display the management page
trans.set_message( "Credentials '%s' deleted." % stored.name )
@@ -658,6 +659,7 @@
if trans.app.model.CloudProvider \
.filter_by (user=user, name=name) \
+ .filter( model.CloudProvider.c.deleted != True ) \
.first():
error['name_error'] = "A provider with that name already exist."
elif name=='' or len( name ) > 255:
@@ -889,13 +891,13 @@
provider = get_provider_by_id( trans, id )
creds = trans.sa_session.query( model.CloudUserCredentials ) \
.filter_by( user=user, provider_id=provider.id ) \
- .filter( model.UCI.c.state!=uci_states.DELETED ) \
+ .filter( model.CloudUserCredentials.c.deleted != True ) \
.all()
-
+
if len( creds ) == 0:
# Delete and save
- sess = trans.sa_session
- sess.delete( provider )
+ #sess = trans.sa_session
+ provider.deleted = True
provider.flush()
# Display the management page
trans.set_message( "Cloud provider '%s' deleted." % provider.name )
@@ -908,7 +910,7 @@
@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()
+ UCIs = trans.sa_session.query( model.UCI ).filter_by( user=user ).filter( model.UCI.c.deleted != True ).all()
insd = {} # instance name-state dict
for uci in UCIs:
dict = {}
diff -r 5963dd169715 -r 7c4cfc243cc3 templates/cloud/add_image.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/cloud/add_image.mako Mon Nov 09 18:55:14 2009 -0500
@@ -0,0 +1,98 @@
+<% _=n_ %>
+<%inherit file="/base.mako"/>
+<%def name="title()">Add machine image</%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 machine image</div>
+ <div class="form-body">
+ <form name="add_image" action="${h.url_for( action='addNewImage' )}" method="post" >
+ <%
+ cls = "form-row"
+ if error.has_key('provider_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Cloud provider type:</label>
+ <div class="form-row-input">
+ <select name="provider_type" style="width:40em">
+ <option value="">Select Provider Type...</option>
+ <option value="eucalyptus">Eucalyptus</option>
+ <option value="ec2">Amazon EC2</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('id_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Machine Image ID (AMI or EMI):</label>
+ <div class="form-row-input">
+ <input type="text" name="image_id" value="${image_id}" size="40">
+ </div>
+ %if error.has_key('id_error'):
+ <div class="form-row-error-message">${error['id_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+ <%
+ cls = "form-row"
+ if error.has_key('manifest_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Manifest:</label>
+ <div class="form-row-input">
+ <input type="text" name="manifest" value="${manifest}" size="40">
+ </div>
+ %if error.has_key('manifest_error'):
+ <div class="form-row-error-message">${error['manifest_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+
+ <%
+ cls = "form-row"
+ if error.has_key('arch_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Image architecture:</label>
+ <div class="form-row-input">
+ <select name="architecture" style="width:40em">
+ <option value="">Select Architecture Type...</option>
+ <option value="i386">i386 (32 bit)</option>
+ <option value="x86_64">x86_64 (64 bit)</option>
+ </select>
+ </div>
+ %if error.has_key('arch_error'):
+ <div class="form-row-error-message">${error['arch_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+
+ <div class="form-row"><input type="submit" value="Add"></div>
+ </form>
+ </div>
+</div>
\ No newline at end of file
diff -r 5963dd169715 -r 7c4cfc243cc3 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Fri Nov 06 13:45:57 2009 -0500
+++ b/templates/cloud/configure_cloud.mako Mon Nov 09 18:55:14 2009 -0500
@@ -42,8 +42,11 @@
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' ) || ( 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('new') && new_state=='error' ) || \
( prev_old_state.match('deleting') && new_state=='error' ) || ( prev_old_state.match('deletingUCI') && new_state=='error' ) ) {
+ // TODO: Following clause causes constant page refresh for an exception thrown as a result of instance not starting correctly - need alternative method!
+ //( prev_old_state.match('available') && new_state=='error' ) || \
+
var url = "${h.url_for( controller='cloud', action='list')}";
location.replace( url );
}
diff -r 5963dd169715 -r 7c4cfc243cc3 templates/cloud/edit_image.mako
--- a/templates/cloud/edit_image.mako Fri Nov 06 13:45:57 2009 -0500
+++ b/templates/cloud/edit_image.mako Mon Nov 09 18:55:14 2009 -0500
@@ -21,8 +21,22 @@
<div class="form-title">Edit image</div>
<div class="form-body">
<form name="edit_image" action="${h.url_for( action='editImage', id=trans.security.encode_id(image.id), edited="true" )}" method="post" >
-
- <%
+ <%
+ cls = "form-row"
+ if error.has_key('provider_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Provider type:</label>
+ <div class="form-row-input">
+ ${image.provider_type}
+ </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('id_error'):
cls += " form-row-error"
@@ -52,7 +66,21 @@
%endif
<div style="clear: both"></div>
</div>
-
+ <%
+ cls = "form-row"
+ if error.has_key('arch_error'):
+ cls += " form-row-error"
+ %>
+ <div class="${cls}">
+ <label>Architecture:</label>
+ <div class="form-row-input">
+ <input type="text" name="architecture" value="${image.architecture}" size="40">
+ </div>
+ %if error.has_key('arch_error'):
+ <div class="form-row-error-message">${error['arch_error']}</div>
+ %endif
+ <div style="clear: both"></div>
+ </div>
<div class="form-row"><input type="submit" value="Save"></div>
</form>
diff -r 5963dd169715 -r 7c4cfc243cc3 templates/cloud/list_images.mako
--- a/templates/cloud/list_images.mako Fri Nov 06 13:45:57 2009 -0500
+++ b/templates/cloud/list_images.mako Mon Nov 09 18:55:14 2009 -0500
@@ -21,14 +21,18 @@
<table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<colgroup width="2%"></colgroup>
+ <colgroup width="10%"></colgroup>
<colgroup width="13%"></colgroup>
- <colgroup width="70%"></colgroup>
+ <colgroup width="55%"></colgroup>
+ <colgroup width="10%"></colgroup>
<colgroup width="5%"></colgroup>
<colgroup width="5%"></colgroup>
<tr class="header">
<th>#</th>
- <th>Machime image ID</th>
+ <th>Provider type</th>
+ <th>Machime image ID</th>
<th>Manifest</th>
+ <th>Architecture</th>
<th>Edit</th>
<th>Delete</th>
<th></th>
@@ -37,6 +41,13 @@
<tr>
<td>${i+1}</td>
<td>
+ %if image.provider_type:
+ ${image.provider_type}
+ %else:
+ N/A
+ %endif
+ </td>
+ <td>
%if image.image_id:
${image.image_id}
%else:
@@ -51,6 +62,13 @@
%endif
</td>
<td>
+ %if image.architecture:
+ ${image.architecture}
+ %else:
+ N/A
+ %endif
+ </td>
+ <td>
<a href="${h.url_for( controller='cloud', action='editImage', image_id=image.image_id, manifest=image.manifest, id=trans.security.encode_id(image.id) )}">e</a>
</td>
<td>
diff -r 5963dd169715 -r 7c4cfc243cc3 templates/cloud/view.mako
--- a/templates/cloud/view.mako Fri Nov 06 13:45:57 2009 -0500
+++ b/templates/cloud/view.mako Mon Nov 09 18:55:14 2009 -0500
@@ -48,11 +48,11 @@
</tr>
<tr>
<td> Cloud provider type: </td>
- <td> ${str(credDetails.provider.type)[:16]}</td>
+ <td> ${str(credDetails.provider.type)}</td>
</tr>
<tr>
<td> Cloud provider name: </td>
- <td> ${str(credDetails.provider.name)[:16]}</td>
+ <td> ${str(credDetails.provider.name)}</td>
</tr>
<tr>
<td> Access key: </td>
@@ -76,7 +76,7 @@
href="javascript:void(0)">
- Hide
</a><br />
- <nobr><b>${credDetails.secret_key}</b></nobr><br/>
+ <nobr>${credDetails.secret_key}</nobr><br/>
</div>
</td>
</tr>
diff -r 5963dd169715 -r 7c4cfc243cc3 templates/cloud/viewInstance.mako
--- a/templates/cloud/viewInstance.mako Fri Nov 06 13:45:57 2009 -0500
+++ b/templates/cloud/viewInstance.mako Mon Nov 09 18:55:14 2009 -0500
@@ -94,19 +94,25 @@
<td> ${liveInstance.private_dns}</td>
</tr>
%endif
+ %if liveInstance.security_group != None:
+ <tr>
+ <td> Security group zone:</td>
+ <td> ${liveInstance.security_group} </td>
+ </tr>
+ %endif
%if liveInstance.availability_zone != None:
<tr>
<td> Availabilty zone:</td>
<td> ${liveInstance.availability_zone} </td>
</tr>
%endif
- %if liveInstance.keypair_name != None:
+ %if liveInstance.uci.key_pair_name != None:
<tr>
<td> Keypair file name:</td>
- <td> ${liveInstance.keypair_name} </td>
+ <td> ${liveInstance.uci.key_pair_name} </td>
</tr>
%endif
- %if liveInstance.keypair_material != None:
+ %if liveInstance.uci.key_pair_material != None:
<tr>
<td> Keypair material:</td>
<div id="shortComment2">
@@ -117,7 +123,7 @@
</a>
</div>
<div id="fullComment2" style="DISPLAY: none">
- <nobr><b>${liveInstance.keypair_material}</b></nobr><br/>
+ <nobr><b>${liveInstance.uci.key_pair_material}</b></nobr><br/>
<a onclick="document.getElementById('shortComment2').style.display = 'block';
document.getElementById('fullComment2').style.display = 'none'; return 0;"
href="javascript:void(0)">
1
0

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/b35215d0913e
changeset: 3084:b35215d0913e
user: Enis Afgan <afgane(a)gmail.com>
date: Thu Nov 05 23:30:50 2009 -0500
description:
Minor style modifications/clarifications.
diffstat:
lib/galaxy/cloud/providers/ec2.py | 1 +
lib/galaxy/model/migrate/versions/0014_cloud_tables.py | 73 ++----------------
lib/galaxy/web/controllers/cloud.py | 5 +
3 files changed, 15 insertions(+), 64 deletions(-)
diffs (115 lines):
diff -r 28252033d66a -r b35215d0913e lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Thu Nov 05 11:27:00 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Thu Nov 05 23:30:50 2009 -0500
@@ -506,6 +506,7 @@
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 )
+ uci.flush()
return None
# Update store status in local DB with info from cloud provider
diff -r 28252033d66a -r b35215d0913e lib/galaxy/model/migrate/versions/0014_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Thu Nov 05 11:27:00 2009 -0500
+++ b/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Thu Nov 05 23:30:50 2009 -0500
@@ -101,75 +101,20 @@
def upgrade():
metadata.reflect()
- log.debug( "Creating cloud_image table." )
- try:
- CloudImage_table.create()
- except Exception, e:
- log.debug( "Creating cloud_image table failed. Table probably exists already." )
- log.debug( "Creating cloud_uci table." )
- try:
- UCI_table.create()
- except Exception, e:
- log.debug( "Creating UCI table failed. Table probably exists already." )
- try:
- CloudUserCredentials_table.create()
- except Exception, e:
- log.debug( "Creating cloud_image table failed. Table probably exists already." )
- log.debug( "Creating cloud_provider table." )
- try:
- CloudProvider_table.create()
- except Exception, e:
- log.debug( "Creating cloud_provider table failed. Table probably exists already." )
- log.debug( "Creating cloud_instance table." )
- #try:
+
+ CloudImage_table.create()
+ UCI_table.create()
+ CloudUserCredentials_table.create()
+ CloudProvider_table.create()
CloudInstance_table.create()
- #except Exception, e:
- # log.debug( "Creating cloud_instance table failed. Table probably exists already." )
- #log.debug( "Creating cloud_store table." )
- #try:
CloudStore_table.create()
- #except Exception:
- # log.debug( "Creating cloud_store table failed. Table probably exists already." )
def downgrade():
metadata.reflect()
- try:
- #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 ) )
-# try:
-# #log.debug( "Would drop cloud_instance table." )
-# print "inst"
+ CloudImage_table.drop()
CloudInstance_table.drop()
-# except Exception, e:
-# log.debug( "Dropping cloud_instance table failed: %s" % str( e ) )
-
-# try:
-# #log.debug( "Would drop cloud_store table." )
-# print "store"
CloudStore_table.drop()
-# except Exception, e:
-# 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
- except Exception, e:
- log.debug( "Dropping cloud_user_credentials table failed: %s" % str( e ) )
-
- try:
- log.debug( "Would drop UCI table." )
- 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 ) )
-
-
-
+ CloudUserCredentials_table.drop()
+ UCI_table.drop()
+ CloudProvider_table.drop()
\ No newline at end of file
diff -r 28252033d66a -r b35215d0913e lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Thu Nov 05 11:27:00 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Thu Nov 05 23:30:50 2009 -0500
@@ -76,6 +76,11 @@
.filter_by( user=user ) \
.order_by( model.CloudUserCredentials.c.name ) \
.all()
+
+ cloudProviders = trans.sa_session.query( model.CloudProvider ) \
+ .filter_by( user=user ) \
+ .order_by( model.CloudUserCredentials.c.name ) \
+ .all()
liveInstances = trans.sa_session.query( model.UCI ) \
.filter_by( user=user ) \
1
0

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/dc1a6f3a2c08
changeset: 3085:dc1a6f3a2c08
user: Enis Afgan <afgane(a)gmail.com>
date: Fri Nov 06 12:27:52 2009 -0500
description:
Added table for seeing user-registerd cloud providers to the main UI and ability to manipulate the same.
diffstat:
lib/galaxy/web/controllers/cloud.py | 174 ++++++++++++++++-
templates/cloud/add_provider.mako | 4 +-
templates/cloud/configure_cloud.mako | 373 +++++++++++++++++++++----------------
templates/cloud/edit_provider.mako | 261 ++++++++++++++++++++++++++
templates/cloud/view_provider.mako | 126 ++++++++++++
5 files changed, 766 insertions(+), 172 deletions(-)
diffs (1068 lines):
diff -r b35215d0913e -r dc1a6f3a2c08 lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Thu Nov 05 23:30:50 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Fri Nov 06 12:27:52 2009 -0500
@@ -79,7 +79,7 @@
cloudProviders = trans.sa_session.query( model.CloudProvider ) \
.filter_by( user=user ) \
- .order_by( model.CloudUserCredentials.c.name ) \
+ .order_by( model.CloudProvider.c.name ) \
.all()
liveInstances = trans.sa_session.query( model.UCI ) \
@@ -126,7 +126,8 @@
return trans.fill_template( "cloud/configure_cloud.mako",
cloudCredentials = cloudCredentials,
liveInstances = liveInstances,
- prevInstances = prevInstances )
+ prevInstances = prevInstances,
+ cloudProviders = cloudProviders )
@web.require_login( "use Galaxy cloud" )
def makeDefault( self, trans, id=None ):
@@ -597,7 +598,6 @@
return trans.fill_template( "cloud/viewInstance.mako",
liveInstance = instances )
-
@web.expose
@web.require_login( "delete credentials" )
def delete( self, trans, id=None ):
@@ -636,7 +636,7 @@
try:
is_secure = int(is_secure)
except ValueError:
- error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'"
+ error['is_secure_error'] = "Field 'is secure' can only take on an integer value '0' or '1'"
if trans.app.model.CloudProvider \
.filter_by (user=user, name=name) \
@@ -647,7 +647,7 @@
elif type=='':
error['type_error'] = "Provider type must be selected."
elif not (is_secure == 0 or is_secure == 1):
- error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'"
+ error['is_secure_error'] = "Field 'is secure' can only take on an integer value '0' or '1'"
else:
provider = model.CloudProvider()
provider.user = user
@@ -664,10 +664,8 @@
provider.region_endpoint = None
if is_secure==0:
- log.debug("is_secure is false")
provider.is_secure = False
else:
- log.debug("is_secure is true")
provider.is_secure = True
if host:
@@ -745,6 +743,150 @@
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.expose
+ @web.require_login( "use Galaxy cloud" )
+ def view_provider( self, trans, id=None ):
+ """
+ View details about given cloud provider
+ """
+ # Load credentials from database
+ provider = get_provider_by_id( trans, id )
+
+ return trans.fill_template( "cloud/view_provider.mako",
+ provider = provider )
+
+ @web.expose
+ @web.require_login( "use Galaxy cloud" )
+ def edit_provider( self, trans, id, name='', type='', region_name='', region_endpoint='', is_secure='', host='', port='', proxy='', proxy_port='',
+ proxy_user='', proxy_pass='', debug='', https_connection_factory='', path='', edited=False ):
+ error = {}
+ if edited == False:
+ provider = get_provider_by_id( trans, id )
+ return trans.fill_template( "cloud/edit_provider.mako",
+ provider = provider,
+ error = error
+ )
+ else:
+ user = trans.get_user()
+ provider = get_provider_by_id( trans, id )
+
+ try:
+ is_secure = int(is_secure)
+ except ValueError:
+ error['is_secure_error'] = "Field 'is secure' can only take on an integer value '0' or '1'"
+
+ if name=='' or len( name ) > 255:
+ error['name_error'] = "Cloud provider name must be between 1 and 255 characters in length."
+ elif trans.app.model.CloudProvider \
+ .filter_by( user=user ) \
+ .filter( and_( trans.app.model.CloudProvider.table.c.id != provider.id, trans.app.model.CloudProvider.table.c.name == name ) ) \
+ .first():
+ error['name_error'] = "Cloud provider with name '" + name + "' already exist. Please choose an alternative name."
+ elif not ( is_secure == 0 or is_secure == 1):
+ error['is_secure_error'] = "Field 'is secure' can only take on an integer value '0' or '1'"
+
+ if error:
+ return trans.fill_template( "cloud/edit_provider.mako",
+ provider = provider,
+ error = error
+ )
+ else:
+ provider.name = name
+ if region_name and region_name != 'None':
+ provider.region_name = region_name
+ else:
+ provider.region_name = None
+
+ if region_endpoint and region_endpoint != 'None':
+ provider.region_endpoint = region_endpoint
+ else:
+ provider.region_endpoint = None
+
+ if is_secure==0:
+ provider.is_secure = False
+ else:
+ provider.is_secure = True
+
+ if host and host != 'None':
+ provider.host = host
+ else:
+ provider.host = None
+
+ if port and port != 'None':
+ provider.port = port
+ else:
+ provider.port = None
+
+ if proxy and proxy != 'None':
+ provider.proxy = proxy
+ else:
+ provider.proxy = None
+
+ if proxy_port and proxy_port != 'None':
+ provider.proxy_port = proxy_port
+ else:
+ provider.proxy_port = None
+
+ if proxy_user and proxy_user != 'None':
+ provider.proxy_user = proxy_user
+ else:
+ provider.proxy_user = None
+
+ if proxy_pass and proxy_pass != 'None':
+ provider.proxy_pass = proxy_pass
+ else:
+ provider.proxy_pass = None
+
+ if debug and debug != 'None':
+ provider.debug = debug
+ else:
+ provider.debug = None
+
+ if https_connection_factory and https_connection_factory != 'None':
+ provider.https_connection_factory = https_connection_factory
+ else:
+ provider.https_connection_factory = None
+
+ if path and path != 'None':
+ provider.path = path
+ else:
+ provider.path = None
+ # Persist
+ session = trans.sa_session
+ session.save_or_update( provider )
+ session.flush()
+ # Log and display the management page
+ trans.log_event( "User edited cloud provider: '%s'" % name )
+ trans.set_message( "Cloud provider '%s' edited." % name )
+ return self.list( trans )
+
+ @web.expose
+ @web.require_login( "delete credentials" )
+ def delete_provider( self, trans, id=None ):
+ """
+ Delete use-registered cloud provider checking that no registered credentials are tied to given provider.
+ """
+ # Load provider from database
+ user = trans.get_user()
+ provider = get_provider_by_id( trans, id )
+ creds = trans.sa_session.query( model.CloudUserCredentials ) \
+ .filter_by( user=user, provider_id=provider.id ) \
+ .filter( model.UCI.c.state!=uci_states.DELETED ) \
+ .all()
+
+ if len( creds ) == 0:
+ # Delete and save
+ sess = trans.sa_session
+ sess.delete( provider )
+ provider.flush()
+ # Display the management page
+ trans.set_message( "Cloud provider '%s' deleted." % provider.name )
+ return self.list( trans )
+
+ error( "Existing credentails depend on cloud provider '%s'. You must delete those credentials before being able \
+ to delete this cloud provider." % provider.name )
+ return self.list( trans )
+
@web.json
def json_update( self, trans ):
user = trans.get_user()
@@ -789,6 +931,24 @@
return trans.app.model.CloudProvider \
.filter_by (user=user, name=name) \
.first()
+
+def get_provider_by_id( trans, id, check_ownership=True ):
+ # 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)
+ if not isinstance( id, int ):
+ id = trans.security.decode_id( id )
+
+ stored = trans.sa_session.query( model.CloudProvider ).get( id )
+ if not stored:
+ error( "Cloud provider not found" )
+ # Verify ownership
+ user = trans.get_user()
+ if not user:
+ error( "Must be logged in to use the cloud." )
+ if check_ownership and not( stored.user == user ):
+ error( "Cloud provider '%s' is not registered by current user." % stored.name )
+ # Looks good
+ return stored
def get_stored_credentials( trans, id, check_ownership=True ):
"""
diff -r b35215d0913e -r dc1a6f3a2c08 templates/cloud/add_provider.mako
--- a/templates/cloud/add_provider.mako Thu Nov 05 23:30:50 2009 -0500
+++ b/templates/cloud/add_provider.mako Fri Nov 06 12:27:52 2009 -0500
@@ -32,15 +32,13 @@
function af(){
if ( $("#autofill").attr('checked') ) {
+ $("#name").val("Eucalyptus Public Cloud");
$("#region_name").val("eucalyptus");
$("#region_endpoint").val("mayhem9.cs.ucsb.edu");
$("#is_secure").val("0");
$("#port").val("8773");
$("#path").val("/services/Eucalyptus");
}
- else {
- clear();
- }
}
function clear() {
diff -r b35215d0913e -r dc1a6f3a2c08 templates/cloud/configure_cloud.mako
--- a/templates/cloud/configure_cloud.mako Thu Nov 05 23:30:50 2009 -0500
+++ b/templates/cloud/configure_cloud.mako Fri Nov 06 12:27:52 2009 -0500
@@ -94,202 +94,251 @@
</%def>
<h2>Galaxy in the clouds</h2>
-
-%if cloudCredentials:
- ## Manage user credentials
- <h3>Your registered credentials</h3>
+
+%if cloudProviders:
+ ## Manage user-registered cloud providers
+ <h3>Your registered cloud providers</h3>
<ul class="manage-table-actions">
<li>
- <a class="action-button" href="${h.url_for( action='add' )}">
+ <a class="action-button" href="${h.url_for( action='add_provider' )}">
<img src="${h.url_for('/static/images/silk/add.png')}" />
- <span>Add credentials</span>
+ <span>Add provider</span>
</a>
</li>
</ul>
<table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr class="header">
- <th>Credentials name</th>
- <th>Provider name (type)</th>
+ <th>Provider name</th>
+ <th>Provider type</th>
<th></th>
</tr>
- %for i, cloudCredential in enumerate( cloudCredentials ):
+ %for i, cloudProvder in enumerate( cloudProviders ):
<tr>
<td>
- ${cloudCredential.name}
- <a id="cr-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
+ ${cloudProvder.name}
+ <a id="cp-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
</td>
<td>
- ${cloudCredential.provider.name}
- (${cloudCredential.provider.type})
+ ${cloudProvder.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='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 popupmenu="cp-${i}-popup">
+ <a class="action-button" href="${h.url_for( action='view_provider', id=trans.security.encode_id(cloudProvder.id) )}">View</a>
+ <a class="action-button" href="${h.url_for( action='edit_provider', id=trans.security.encode_id(cloudProvder.id) )}">Edit</a>
+ <a class="action-button" confirm="Are you sure you want to delete cloud provider '${cloudProvder.name}'?" href="${h.url_for( action='delete_provider', id=trans.security.encode_id(cloudProvder.id) )}">Delete</a>
</div>
</td>
</tr>
%endfor
</table>
+
## *****************************************************
- ## Manage live instances
- <p />
- <h3>Manage your cloud instances</h3>
- <ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( action='configureNew' )}">
- <img src="${h.url_for('/static/images/silk/add.png')}" />
- <span>Configure new instance</span>
- </a>
- </li>
- </ul>
-
- <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
- <colgroup width="40%"></colgroup>
- <colgroup width="15%"></colgroup>
- <colgroup width="10%"></colgroup>
- <colgroup width="25%"></colgroup>
- <colgroup width="10%"></colgroup>
- <tr class="header">
- <th>Instance name (credentials)</th>
- <th>Storage size (GB)</th>
- <th>State</th>
- <th>Alive since</th>
- <th></th>
- <th></th>
- </tr>
- %if liveInstances:
- %for i, liveInstance in enumerate( liveInstances ):
+ ## Manage user credentials
+ <h3>Your registered cloud credentials</h3>
+
+ %if cloudCredentials:
+ <ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( action='add' )}">
+ <img src="${h.url_for('/static/images/silk/add.png')}" />
+ <span>Add credentials</span>
+ </a>
+ </li>
+ </ul>
+ <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <tr class="header">
+ <th>Credentials name</th>
+ <th>Provider name (type)</th>
+ <th></th>
+ </tr>
+
+ %for i, cloudCredential in enumerate( cloudCredentials ):
<tr>
<td>
- ${liveInstance.name} (${liveInstance.credentials.name})
- <a id="li-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
+ ${cloudCredential.name}
+ <a id="cr-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
</td>
- <td>${str(liveInstance.total_size)}</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
- #from datetime import timedelta
-
- # DB stores all times in GMT, so adjust for difference (4 hours)
- #adjustedStarttime = liveInstance.update_time - timedelta(hours=4)
-
- #delta = datetime.now() - adjustedStarttime
- #context.write( str(datetime.utcnow() ) )
- #context.write( str(delta) )
-
- # This is where current time and since duration is calculated
- if liveInstance.launch_time is None:
- context.write( 'N/A' )
- else:
- context.write( str( liveInstance.launch_time )[:16] )
- context.write( ' UTC (' )
- context.write( str(h.date.distance_of_time_in_words (liveInstance.launch_time, h.date.datetime.utcnow() ) ) )
- context.write( ')' )
- %>
+ <td>
+ ${cloudCredential.provider.name}
+ (${cloudCredential.provider.type})
</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>
- <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>
+ <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='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>
</tr>
%endfor
- %else:
- <tr>
- <td>Currently, you have no live instances.</td>
- </tr>
- %endif
- </table>
+ </table>
+
+ ## *****************************************************
+ ## Manage live instances
+ <p />
+ <h3>Manage your cloud instances</h3>
+ <ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( action='configureNew' )}">
+ <img src="${h.url_for('/static/images/silk/add.png')}" />
+ <span>Configure new instance</span>
+ </a>
+ </li>
+ </ul>
- ## *****************************************************
- ## Manage previously configured instances
- <table class="mange-table noHR" border="0" cellspacing="0" cellpadding="0" width="100%">
- <colgroup width="40%"></colgroup>
- <colgroup width="15%"></colgroup>
- <colgroup width="10%"></colgroup>
- <colgroup width="35%"></colgroup>
- ##<tr class="header">
- ##<th>Previously configured instances</th>
- ##<th>Storage size (GB)</th>
- ##<th>State</th>
- ##<th>Alive since</th>
- ##<th></th>
- ##<th></th>
- ##<th></th>
- ##<th></th>
- ##</tr>
-
- %if prevInstances:
- %for i, prevInstance in enumerate( prevInstances ):
- <tr>
- <td>
- ${prevInstance.name} (${prevInstance.credentials.name})
- <a id="pi-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
- </td>
- <td>${str(prevInstance.total_size)}</td>
- <td id="${ prevInstance.id }-state-p">
- <%state = str(prevInstance.state)%>
- %if state =='error':
- <div id="${prevInstance.name}-short">
- <a onclick="document.getElementById('${prevInstance.name}-full').style.display = 'block';
- document.getElementById('${prevInstance.name}-short').style.display = 'none'; return 0"
- href="javascript:void(0)">
- error
- </a>
- </div>
- <div id="${prevInstance.name}-full" style="DISPLAY: none">
- <a onclick="document.getElementById('${prevInstance.name}-short').style.display = 'block';
- document.getElementById('${prevInstance.name}-full').style.display = 'none'; return 0;"
- href="javascript:void(0)">
- 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)}
- %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), 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', 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>
- </tr>
- %endfor
- %else:
- <tr>
- <td>You have no previously configured instances (or they are all currently alive).</td>
- </tr>
- %endif
- </table>
-
+ <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <colgroup width="40%"></colgroup>
+ <colgroup width="15%"></colgroup>
+ <colgroup width="10%"></colgroup>
+ <colgroup width="25%"></colgroup>
+ <colgroup width="10%"></colgroup>
+ <tr class="header">
+ <th>Instance name (credentials)</th>
+ <th>Storage size (GB)</th>
+ <th>State</th>
+ <th>Alive since</th>
+ <th></th>
+ <th></th>
+ </tr>
+ %if liveInstances:
+ %for i, liveInstance in enumerate( liveInstances ):
+ <tr>
+ <td>
+ ${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 id="${ liveInstance.id }-state">${str(liveInstance.state)}</td>
+ <td id="${ liveInstance.id }-launch_time">
+ ##${str(liveInstance.launch_time)[:16]}
+ <%
+ #from datetime import datetime
+ #from datetime import timedelta
+
+ # DB stores all times in GMT, so adjust for difference (4 hours)
+ #adjustedStarttime = liveInstance.update_time - timedelta(hours=4)
+
+ #delta = datetime.now() - adjustedStarttime
+ #context.write( str(datetime.utcnow() ) )
+ #context.write( str(delta) )
+
+ # This is where current time and since duration is calculated
+ if liveInstance.launch_time is None:
+ context.write( 'N/A' )
+ else:
+ context.write( str( liveInstance.launch_time )[:16] )
+ context.write( ' UTC (' )
+ context.write( str(h.date.distance_of_time_in_words (liveInstance.launch_time, h.date.datetime.utcnow() ) ) )
+ context.write( ')' )
+ %>
+ </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>
+ <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>
+ %endfor
+ %else:
+ <tr>
+ <td>Currently, you have no live instances.</td>
+ </tr>
+ %endif
+ </table>
+
+ ## *****************************************************
+ ## Manage previously configured instances
+ <table class="mange-table noHR" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <colgroup width="40%"></colgroup>
+ <colgroup width="15%"></colgroup>
+ <colgroup width="10%"></colgroup>
+ <colgroup width="35%"></colgroup>
+ ##<tr class="header">
+ ##<th>Previously configured instances</th>
+ ##<th>Storage size (GB)</th>
+ ##<th>State</th>
+ ##<th>Alive since</th>
+ ##<th></th>
+ ##<th></th>
+ ##<th></th>
+ ##<th></th>
+ ##</tr>
+
+ %if prevInstances:
+ %for i, prevInstance in enumerate( prevInstances ):
+ <tr>
+ <td>
+ ${prevInstance.name} (${prevInstance.credentials.name})
+ <a id="pi-${i}-popup" class="popup-arrow" style="display: none;">▼</a>
+ </td>
+ <td>${str(prevInstance.total_size)}</td>
+ <td id="${ prevInstance.id }-state-p">
+ <%state = str(prevInstance.state)%>
+ %if state =='error':
+ <div id="${prevInstance.name}-short">
+ <a onclick="document.getElementById('${prevInstance.name}-full').style.display = 'block';
+ document.getElementById('${prevInstance.name}-short').style.display = 'none'; return 0"
+ href="javascript:void(0)">
+ error
+ </a>
+ </div>
+ <div id="${prevInstance.name}-full" style="DISPLAY: none">
+ <a onclick="document.getElementById('${prevInstance.name}-short').style.display = 'block';
+ document.getElementById('${prevInstance.name}-full').style.display = 'none'; return 0;"
+ href="javascript:void(0)">
+ 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)}
+ %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), 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', 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>
+ </tr>
+ %endfor
+ %else:
+ <tr>
+ <td>You have no previously configured instances (or they are all currently alive).</td>
+ </tr>
+ %endif
+ </table>
+
+ %else:
+ You have no 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 credentials</span>
+ </a>
+ or
+ <a href="http://aws.amazon.com/" target="_blank">
+ open AWS account with Amazon</a>.
+ %endif
+
%else:
- You have no AWS credentials associated with your Galaxy account:
- <a class="action-button" href="${h.url_for( action='add' )}">
+ You have no cloud providers registered with your Galaxy account:
+ <a class="action-button" href="${h.url_for( action='add_provider' )}">
<img src="${h.url_for('/static/images/silk/add.png')}" />
- <span>add credentials</span>
+ <span>add provider now</span>
</a>
- or
- <a href="http://aws.amazon.com/" target="_blank">
- open AWS account with Amazon</a>.
%endif
\ No newline at end of file
diff -r b35215d0913e -r dc1a6f3a2c08 templates/cloud/edit_provider.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/cloud/edit_provider.mako Fri Nov 06 12:27:52 2009 -0500
@@ -0,0 +1,261 @@
+<% _=n_ %>
+<%inherit file="/base.mako"/>
+<%def name="title()">Edit provider</%def>
+
+<%def name="javascripts()">
+${parent.javascripts()}
+<script type="text/javascript">
+$(function(){
+
+ $("input:text:first").focus();
+
+ $("#type").change(function() {
+ if ($(this).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 );
+ }
+ });
+})
+
+
+function af(){
+
+ if ( $("#autofill_epc").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 if ( $("#autofill_ec2").attr('checked') ) {
+ $("#region_name").val( "us-east-1" );
+ $("#region_endpoint").val( "us-east-1.ec2.amazonaws.com" );
+ $("#is_secure").val("1");
+ $("#debug").val("");
+ $("#path").val("/");
+ }
+}
+
+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("");
+
+}
+
+</script>
+</%def>
+
+%if header:
+ ${header}
+%endif
+
+%if provider:
+ <div class="form">
+ <div class="form-title">Edit cloud provider</div>
+ <div class="form-body">
+ <form name="edit_provider_form" action="${h.url_for( action='edit_provider', id=trans.security.encode_id(provider.id), edited="true" )}" 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">${provider.type}
+ %if provider.type == 'eucalyptus':
+ <p><input type="checkbox" id="autofill_epc" onclick="javascript:af()">
+ auto fill using Eucalyptus Public Cloud values
+ </p></div>
+ %elif provider.type == 'ec2':
+ <p><input type="checkbox" id="autofill_ec2" onclick="javascript:af()">
+ auto fill for Amazon EC2 (us-east-1 region)
+ </p></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="${provider.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 id="region_selection" class="form-row-input">
+ <input type="text" name="region_name" id="region_name" value="${provider.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="${provider.region_endpoint}" size="40">
+ </div>
+ <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">
+ %if provider.is_secure == True:
+ <input type="text" name="is_secure" id="is_secure" value="1" size="40">
+ %else:
+ <input type="text" name="is_secure" id="is_secure" value="0" size="40">
+ %endif
+ </div>
+ %if error.has_key('is_secure_error'):
+ <div class="form-row-error-message">${error['is_secure_error']}; you entered: '${provider.is_secure}'</div>
+ %endif
+ <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="${provider.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="${provider.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="${provider.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="${provider.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="${provider.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="${provider.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="${provider.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="${provider.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="${provider.path}" size="40">
+ </div>
+ <div style="clear: both"></div>
+ </div>
+
+ <div class="form-row"><input type="submit" value="Save"></div>
+
+ </form>
+
+ </div>
+ </div>
+%endif
diff -r b35215d0913e -r dc1a6f3a2c08 templates/cloud/view_provider.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/cloud/view_provider.mako Fri Nov 06 12:27:52 2009 -0500
@@ -0,0 +1,126 @@
+<%inherit file="/base.mako"/>
+
+<%def name="title()">Cloud provider</%def>
+
+<h2>Cloud provider 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 provider:
+ ${view_provider( provider )}
+%else:
+ There is no cloud provider under that name.
+%endif
+
+
+
+
+<%def name="view_provider( provider )">
+ <table class="mange-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
+ <tr>
+ <td> Cloud provider name: </td>
+ <td>
+ ${provider.name}
+ <a id="cp-popup" class="popup-arrow" style="display: none;">▼</a>
+ </td>
+ <td>
+ <div popupmenu="cp-popup">
+ <a class="action-button" href="${h.url_for( action='edit_provider', id=trans.security.encode_id(provider.id) )}">Edit</a>
+ <a class="action-button" confirm="Are you sure you want to delete cloud provider '${provider.name}'?" href="${h.url_for( action='delete_provider', id=trans.security.encode_id(provider.id) )}">Delete</a>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td> Last updated: </td>
+ <td> ${str(provider.update_time)[:16]}
+ <%
+ context.write( ' UTC (' )
+ context.write( str(h.date.distance_of_time_in_words (provider.update_time, h.date.datetime.utcnow() ) ) )
+ %> ago)
+ </td>
+ </tr>
+ <tr>
+ <td> Cloud provider type: </td>
+ <td> ${str(provider.type)[:16]}</td>
+ </tr>
+ %if provider.region_connection != None:
+ <tr>
+ <td> Region connection: </td>
+ <td> ${provider.region_connection} </td>
+ </tr>
+ %endif
+ %if provider.region_name != None:
+ <tr>
+ <td> Region name: </td>
+ <td> ${provider.region_name} </td>
+ </tr>
+ %endif
+ %if provider.region_endpoint != None:
+ <tr>
+ <td> Region endpoint: </td>
+ <td> ${provider.region_endpoint} </td>
+ </tr>
+ %endif
+ %if provider.is_secure != None:
+ <tr>
+ <td> Is secure: </td>
+ <td> ${provider.is_secure} </td>
+ </tr>
+ %endif
+ %if provider.host != None:
+ <tr>
+ <td> Host: </td>
+ <td> ${provider.host} </td>
+ </tr>
+ %endif
+ %if provider.port != None:
+ <tr>
+ <td> Port: </td>
+ <td> ${provider.port} </td>
+ </tr>
+ %endif
+ %if provider.proxy != None:
+ <tr>
+ <td> Proxy: </td>
+ <td> ${provider.proxy} </td>
+ </tr>
+ %endif
+ %if provider.proxy_port != None:
+ <tr>
+ <td> Proxy port: </td>
+ <td> ${provider.proxy_port} </td>
+ </tr>
+ %endif
+ %if provider.proxy_pass != None:
+ <tr>
+ <td> Proxy pass: </td>
+ <td> ${provider.proxy_pass} </td>
+ </tr>
+ %endif
+ %if provider.debug != None:
+ <tr>
+ <td> Debug: </td>
+ <td> ${provider.debug} </td>
+ </tr>
+ %endif
+ %if provider.https_connection_factory != None:
+ <tr>
+ <td> HTTPS connection factory: </td>
+ <td> ${provider.https_connection_factory} </td>
+ </tr>
+ %endif
+ %if provider.path != None:
+ <tr>
+ <td> Path: </td>
+ <td> ${provider.path} </td>
+ </tr>
+ %endif
+ </table>
+</%def>
1
0

23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/28252033d66a
changeset: 3083:28252033d66a
user: Enis Afgan <afgane(a)gmail.com>
date: Thu Nov 05 11:27:00 2009 -0500
description:
Bug fixes regarding registration of new providers and creation of new UCIs.
diffstat:
lib/galaxy/cloud/providers/ec2.py | 73 +++++++++---------
lib/galaxy/cloud/providers/eucalyptus.py | 76 +++++++++---------
lib/galaxy/model/migrate/versions/0014_cloud_tables.py | 53 +++++++-----
lib/galaxy/web/controllers/cloud.py | 13 ++-
templates/cloud/list_images.mako | 4 +
5 files changed, 118 insertions(+), 101 deletions(-)
diffs (340 lines):
diff -r 70ae74578254 -r 28252033d66a lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Wed Nov 04 17:19:18 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Thu Nov 05 11:27:00 2009 -0500
@@ -402,19 +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 and store.volume_id != None:
- log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider.type, store.volume_id ) )
+ if self.type == store.uci.credentials.provider.type: # and store.volume_id != None:
+ log.debug( "[%s] Running general status update on store with local database ID: '%s'" % ( store.uci.credentials.provider.type, store.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()
+# 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 entry 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()
@@ -509,30 +509,31 @@
return None
# Update store status in local DB with info from cloud provider
- 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
+ if len(vl) > 0:
+ 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 processZombie( self, inst ):
"""
diff -r 70ae74578254 -r 28252033d66a lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Wed Nov 04 17:19:18 2009 -0500
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Thu Nov 05 11:27:00 2009 -0500
@@ -368,19 +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 and store.volume_id != None:
- log.debug( "[%s] Running general status update on store '%s'" % ( store.uci.credentials.provider.type, store.volume_id ) )
+ if self.type == store.uci.credentials.provider.type: # and store.volume_id != None:
+ log.debug( "[%s] Running general status update on store with local database ID: '%s'" % ( store.uci.credentials.provider.type, store.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()
+# 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 entry 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()
@@ -465,39 +465,41 @@
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) ) )
uci.error( "Retrieving volume(s) from cloud failed: " + str(e) )
uci.state( uci_states.ERROR )
+ uci.flush()
return None
# Update store status in local DB with info from cloud provider
- 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
+ if len(vl) > 0:
+ 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 )
+ uci.flush()
+ return None
def processZombie( self, inst ):
"""
diff -r 70ae74578254 -r 28252033d66a lib/galaxy/model/migrate/versions/0014_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Wed Nov 04 17:19:18 2009 -0500
+++ b/lib/galaxy/model/migrate/versions/0014_cloud_tables.py Thu Nov 05 11:27:00 2009 -0500
@@ -101,65 +101,72 @@
def upgrade():
metadata.reflect()
+ log.debug( "Creating cloud_image table." )
try:
CloudImage_table.create()
except Exception, e:
log.debug( "Creating cloud_image table failed. Table probably exists already." )
+ log.debug( "Creating cloud_uci table." )
try:
UCI_table.create()
except Exception, e:
log.debug( "Creating UCI table failed. Table probably exists already." )
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." )
+ log.debug( "Creating cloud_provider table." )
try:
CloudProvider_table.create()
except Exception, e:
log.debug( "Creating cloud_provider table failed. Table probably exists already." )
+ log.debug( "Creating cloud_instance table." )
+ #try:
+ CloudInstance_table.create()
+ #except Exception, e:
+ # log.debug( "Creating cloud_instance table failed. Table probably exists already." )
+ #log.debug( "Creating cloud_store table." )
+ #try:
+ CloudStore_table.create()
+ #except Exception:
+ # log.debug( "Creating cloud_store table failed. Table probably exists already." )
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 ) )
- try:
- log.debug( "Would drop cloud_instance table." )
-# CloudInstance_table.drop()
- except Exception, e:
- log.debug( "Dropping cloud_instance table failed: %s" % str( e ) )
+# try:
+# #log.debug( "Would drop cloud_instance table." )
+# print "inst"
+ CloudInstance_table.drop()
+# except Exception, e:
+# log.debug( "Dropping cloud_instance table failed: %s" % str( e ) )
- try:
- log.debug( "Would drop cloud_store table." )
-# CloudStore_table.drop()
- except Exception, e:
- log.debug( "Dropping cloud_store table failed: %s" % str( e ) )
+# try:
+# #log.debug( "Would drop cloud_store table." )
+# print "store"
+ CloudStore_table.drop()
+# except Exception, e:
+# 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
+ CloudUserCredentials_table.drop() #Enable before putting final version
except Exception, e:
log.debug( "Dropping cloud_user_credentials table failed: %s" % str( e ) )
try:
log.debug( "Would drop UCI table." )
-# UCI_table.drop()
+ UCI_table.drop()
except Exception, e:
log.debug( "Dropping UCI table failed: %s" % str( e ) )
try:
-# log.debug( "Would drop cloud_provider table." )
+ 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 70ae74578254 -r 28252033d66a lib/galaxy/web/controllers/cloud.py
--- a/lib/galaxy/web/controllers/cloud.py Wed Nov 04 17:19:18 2009 -0500
+++ b/lib/galaxy/web/controllers/cloud.py Thu Nov 05 11:27:00 2009 -0500
@@ -626,12 +626,13 @@
proxy_user='', proxy_pass='', debug='', https_connection_factory='', path='' ):
user = trans.get_user()
error = {}
- try:
- is_secure = int(is_secure)
- except ValueError:
- pass
if region_name or region_endpoint or name or is_secure or port or proxy or debug or path:
+ try:
+ is_secure = int(is_secure)
+ except ValueError:
+ error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'"
+
if trans.app.model.CloudProvider \
.filter_by (user=user, name=name) \
.first():
@@ -657,9 +658,11 @@
else:
provider.region_endpoint = None
- if is_secure=='0':
+ if is_secure==0:
+ log.debug("is_secure is false")
provider.is_secure = False
else:
+ log.debug("is_secure is true")
provider.is_secure = True
if host:
diff -r 70ae74578254 -r 28252033d66a templates/cloud/list_images.mako
--- a/templates/cloud/list_images.mako Wed Nov 04 17:19:18 2009 -0500
+++ b/templates/cloud/list_images.mako Thu Nov 05 11:27:00 2009 -0500
@@ -60,6 +60,10 @@
</tr>
%endfor
</table>
+%else:
+ <h3>There are no registered machine images.</h3><br />
+ <a href="${h.url_for( controller='cloud', action='addNewImage' )}" target="galaxy_main">Add machine image now?</a>
+
%endif
1
0