galaxy-dev
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
November 2009
- 26 participants
- 233 discussions
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/53de6aea6445
changeset: 3110:53de6aea6445
user: Nate Coraor <nate(a)bx.psu.edu>
date: Wed Nov 18 16:18:09 2009 -0500
description:
Only fix OS X platform name on 2.5, the only version on which it's broken
diffstat:
lib/galaxy/__init__.py | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diffs (15 lines):
diff -r c507bad7e373 -r 53de6aea6445 lib/galaxy/__init__.py
--- a/lib/galaxy/__init__.py Wed Nov 18 13:45:53 2009 -0500
+++ b/lib/galaxy/__init__.py Wed Nov 18 16:18:09 2009 -0500
@@ -12,8 +12,9 @@
import os, sys
from distutils.sysconfig import get_config_vars
-if ( os.uname()[-1] in ( 'i386', 'ppc' ) and sys.platform == 'darwin' and os.path.abspath( sys.prefix ).startswith( '/System' ) ) or \
- ( sys.platform == 'darwin' and get_config_vars().get('UNIVERSALSDK', '').strip() ):
+if sys.version_info[:2] == ( 2, 5 ) and \
+ ( ( os.uname()[-1] in ( 'i386', 'ppc' ) and sys.platform == 'darwin' and os.path.abspath( sys.prefix ).startswith( '/System' ) ) or \
+ ( sys.platform == 'darwin' and get_config_vars().get('UNIVERSALSDK', '').strip() ) ):
# Has to be before anything imports pkg_resources
def _get_platform_monkeypatch():
plat = distutils.util._get_platform()
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/bcb3c0eeb72f
changeset: 3108:bcb3c0eeb72f
user: Enis Afgan <afgane(a)gmail.com>
date: Wed Nov 18 13:37:41 2009 -0500
description:
merge
diffstat:
lib/galaxy/web/framework/helpers/grids.py | 3 +++
static/images/mag_glass.png |
templates/grid_base.mako | 35 +++++++++++++++++++++++++++++++++++
templates/grid_common.mako | 13 ++++++++-----
4 files changed, 46 insertions(+), 5 deletions(-)
diffs (97 lines):
diff -r 5fbda2fdb3ca -r bcb3c0eeb72f lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py Wed Nov 18 13:36:37 2009 -0500
+++ b/lib/galaxy/web/framework/helpers/grids.py Wed Nov 18 13:37:41 2009 -0500
@@ -120,6 +120,9 @@
column_filter = from_json_string_recurse( column_filter )
if len( column_filter ) == 1:
column_filter = column_filter[0]
+ # If filter criterion is empty, do nothing.
+ if column_filter == '':
+ continue
# Update query.
query = column.filter( trans.sa_session, query, column_filter )
# Upate current filter dict.
diff -r 5fbda2fdb3ca -r bcb3c0eeb72f static/images/mag_glass.png
Binary file static/images/mag_glass.png has changed
diff -r 5fbda2fdb3ca -r bcb3c0eeb72f templates/grid_base.mako
--- a/templates/grid_base.mako Wed Nov 18 13:36:37 2009 -0500
+++ b/templates/grid_base.mako Wed Nov 18 13:37:41 2009 -0500
@@ -62,6 +62,21 @@
t2.autocomplete("${h.url_for( controller='history', action='name_autocomplete_data' )}", autocomplete_options);
}
+
+ // Initialize submit image elements.
+ $('.submit-image').each( function()
+ {
+ // On mousedown, add class to simulate click.
+ $(this).mousedown( function() {
+ $(this).addClass('gray-background');
+ });
+
+ // On mouseup, add class to simulate click.
+ $(this).mouseup( function() {
+ $(this).removeClass('gray-background');
+ });
+
+ });
});
## Can this be moved into base.mako?
%if refresh_frames:
@@ -168,6 +183,26 @@
text-align: center;
display: inline-block;
}
+ .submit-image {
+ vertical-align: text-bottom;
+ margin: 0;
+ padding: 0;
+ }
+ .no-padding-or-margin {
+ margin: 0;
+ padding: 0;
+ }
+ .gray-background {
+ background-color: #DDDDDD;
+ }
+ .text-filter-val {
+ border: solid 1px #AAAAAA;
+ padding: 1px 3px 1px 3px;
+ margin-right: 5px;
+ -moz-border-radius: .5em;
+ -webkit-border-radius: .5em;
+ font-style: italic;
+ }
</style>
</%def>
diff -r 5fbda2fdb3ca -r bcb3c0eeb72f templates/grid_common.mako
--- a/templates/grid_common.mako Wed Nov 18 13:36:37 2009 -0500
+++ b/templates/grid_common.mako Wed Nov 18 13:37:41 2009 -0500
@@ -32,10 +32,11 @@
<% column_filter = cur_filter_dict[column.key] %>
%if isinstance( column_filter, basestring ):
%if column_filter != "All":
- <span style="font-style: italic">${cur_filter_dict[column.key]}</span>
- <% filter_all = GridColumnFilter( "", { column.key : "All" } ) %>
- <a href="${url( filter_all.get_url_args() )}"><img src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a>
- |
+ <span class='text-filter-val'>
+ ${cur_filter_dict[column.key]}
+ <% filter_all = GridColumnFilter( "", { column.key : "All" } ) %>
+ <a href="${url( filter_all.get_url_args() )}"><img src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a>
+ </span>
%endif
%elif isinstance( column_filter, list ):
%for i, filter in enumerate( column_filter ):
@@ -53,7 +54,9 @@
%endif
%endif
- <span><input id="input-${column.key}-filter" name="f-${column.key}" type="text" value="" size="15"/></span>
+ <span>
+ <input class="no-padding-or-margin" id="input-${column.key}-filter" name="f-${column.key}" type="text" value="" size="15"/>
+ <input class='submit-image' type='image' src='${h.url_for('/static/images/mag_glass.png')}' alt='Filter'/></span>
</form>
%else:
%for i, filter in enumerate( column.get_accepted_filters() ):
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/5fbda2fdb3ca
changeset: 3107:5fbda2fdb3ca
user: Enis Afgan <afgane(a)gmail.com>
date: Wed Nov 18 13:36:37 2009 -0500
description:
Fixed table migration bug encountered with PostgreSQL.
diffstat:
lib/galaxy/cloud/__init__.py | 12 ++++++++++--
lib/galaxy/cloud/providers/ec2.py | 8 ++++++--
lib/galaxy/cloud/providers/eucalyptus.py | 14 +++++++++-----
lib/galaxy/model/__init__.py | 1 -
lib/galaxy/model/mapping.py | 6 +++---
lib/galaxy/model/migrate/versions/0026_cloud_tables.py | 4 ++--
templates/cloud/view_instance.mako | 6 ++++--
universe_wsgi.ini.sample | 12 ++++++------
8 files changed, 40 insertions(+), 23 deletions(-)
diffs (258 lines):
diff -r f4c2bf76b2e2 -r 5fbda2fdb3ca lib/galaxy/cloud/__init__.py
--- a/lib/galaxy/cloud/__init__.py Wed Nov 18 10:57:24 2009 -0500
+++ b/lib/galaxy/cloud/__init__.py Wed Nov 18 13:36:37 2009 -0500
@@ -45,6 +45,14 @@
ERROR = "error"
)
+store_status = Bunch(
+ WAITING = "waiting",
+ IN_USE = "in-use",
+ CREATING = "creating",
+ DELETED = 'deleted',
+ ERROR = "error"
+)
+
snapshot_status = Bunch(
SUBMITTED = 'submitted',
PENDING = 'pending',
@@ -413,7 +421,7 @@
be given in following format: 'vol-78943248'
"""
vol = self.sa_session.query( model.CloudStore ).filter( model.CloudStore.table.c.volume_id == vol_id ).first()
- vol.i_id = instance_id
+ vol.inst.instance_id = instance_id
self.sa_session.add( vol )
self.sa_session.flush()
@@ -559,7 +567,7 @@
def get_mi_id( self, instance_id=0 ):
uci = self.sa_session.query( model.UCI ).get( self.uci_id )
self.sa_session.refresh( uci )
- return uci.instance[instance_id].mi_id
+ return uci.instance[instance_id].image.image_id
def get_public_dns( self, instance_id=0 ):
uci = self.sa_session.query( model.UCI ).get( self.uci_id )
diff -r f4c2bf76b2e2 -r 5fbda2fdb3ca lib/galaxy/cloud/providers/ec2.py
--- a/lib/galaxy/cloud/providers/ec2.py Wed Nov 18 10:57:24 2009 -0500
+++ b/lib/galaxy/cloud/providers/ec2.py Wed Nov 18 13:36:37 2009 -0500
@@ -49,6 +49,7 @@
)
store_status = Bunch(
+ WAITING = "waiting",
IN_USE = "in-use",
CREATING = "creating",
DELETED = 'deleted',
@@ -441,6 +442,8 @@
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 )
+ vol_id = uci_wrapper.get_store_volume_id( store_id=0 ) # TODO: Once more that one vol/UCI is allowed, update this!
+ uci_wrapper.set_store_status( vol_id, store_status.WAITING )
log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_uci_state() ) )
except boto.exception.EC2ResponseError, e:
err = "EC2 response error when retrieving instance information for UCI '" + uci_wrapper.get_name() + "': " + str( e )
@@ -560,6 +563,7 @@
stores = self.sa_session.query( model.CloudStore ) \
.filter( or_( model.CloudStore.table.c.status==store_status.IN_USE,
model.CloudStore.table.c.status==store_status.CREATING,
+ model.CloudStore.table.c.status==store_status.WAITING,
model.CloudStore.table.c.status==None ) ) \
.all()
for store in stores:
@@ -717,8 +721,8 @@
store.status = vl[0].status
self.sa_session.add( store )
self.sa_session.flush()
- if store.i_id != vl[0].instance_id:
- store.i_id = vl[0].instance_id
+ if store.inst.instance_id != vl[0].instance_id:
+ store.inst.instance_id = vl[0].instance_id
self.sa_session.add( store )
self.sa_session.flush()
if store.attach_time != vl[0].attach_time:
diff -r f4c2bf76b2e2 -r 5fbda2fdb3ca lib/galaxy/cloud/providers/eucalyptus.py
--- a/lib/galaxy/cloud/providers/eucalyptus.py Wed Nov 18 10:57:24 2009 -0500
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Wed Nov 18 13:36:37 2009 -0500
@@ -49,6 +49,7 @@
)
store_status = Bunch(
+ WAITING = "waiting",
IN_USE = "in-use",
CREATING = "creating",
DELETED = 'deleted',
@@ -428,6 +429,8 @@
uci_wrapper.set_instance_id( i_index, i_id )
s = reservation.instances[0].state
uci_wrapper.change_state( s, i_id, s )
+ vol_id = uci_wrapper.get_store_volume_id( store_id=0 ) # TODO: Once more that one vol/UCI is allowed, update this!
+ uci_wrapper.set_store_status( vol_id, store_status.WAITING )
log.debug( "Instance of UCI '%s' started, current state: '%s'" % ( uci_wrapper.get_name(), uci_wrapper.get_uci_state() ) )
except boto.exception.EC2ResponseError, e:
err = "EC2 response error when retrieving instance information for UCI '" + uci_wrapper.get_name() + "': " + str( e )
@@ -497,7 +500,7 @@
## 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.inst.instance_id = None
# store.status = volStat
# log.debug ( '***** volume status: %s' % volStat )
#
@@ -549,6 +552,7 @@
stores = self.sa_session.query( model.CloudStore ) \
.filter( or_( model.CloudStore.table.c.status==store_status.IN_USE,
model.CloudStore.table.c.status==store_status.CREATING,
+ model.CloudStore.table.c.status==store_status.WAITING,
model.CloudStore.table.c.status==None ) ) \
.all()
for store in stores:
@@ -691,7 +695,7 @@
# Update store status in local DB with info from cloud provider
if len(vl) > 0:
try:
- if store.status != vl[0].status:
+ if store.status != vl[0].status and store.availability_zone != 'epc':
# 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 ):
@@ -714,8 +718,8 @@
store.status = vl[0].status
self.sa_session.add( store )
self.sa_session.flush()
- if store.i_id != vl[0].instance_id:
- store.i_id = vl[0].instance_id
+ if store.inst.instance_id != vl[0].instance_id:
+ store.inst.instance_id = vl[0].instance_id
self.sa_session.add( store )
self.sa_session.flush()
if store.attach_time != vl[0].attach_time:
@@ -981,7 +985,7 @@
# try:
# volumes = conn.get_all_volumes( vols )
# for i, v in enumerate( volumes ):
-# uci.store[i].i_id = v.instance_id
+# uci.store[i].inst.instance_id = v.instance_id
# uci.store[i].status = v.status
# uci.store[i].device = v.device
# uci.store[i].flush()
diff -r f4c2bf76b2e2 -r 5fbda2fdb3ca lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Wed Nov 18 10:57:24 2009 -0500
+++ b/lib/galaxy/model/__init__.py Wed Nov 18 13:36:37 2009 -0500
@@ -1077,7 +1077,6 @@
def __init__( self ):
self.id = None
self.volume_id = None
- self.i_id = None
self.user = None
self.size = None
self.availability_zone = None
diff -r f4c2bf76b2e2 -r 5fbda2fdb3ca lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Wed Nov 18 10:57:24 2009 -0500
+++ b/lib/galaxy/model/mapping.py Wed Nov 18 13:36:37 2009 -0500
@@ -429,7 +429,7 @@
Column( "type", TEXT ),
Column( "reservation_id", TEXT ),
Column( "instance_id", TEXT ),
- Column( "mi_id", TEXT, ForeignKey( "cloud_image.image_id" ), index=True ),
+ Column( "mi_id", Integer, ForeignKey( "cloud_image.id" ), index=True ),
Column( "state", TEXT ),
Column( "error", TEXT ),
Column( "public_dns", TEXT ),
@@ -447,7 +447,7 @@
Column( "volume_id", TEXT ),
Column( "size", Integer, nullable=False ),
Column( "availability_zone", TEXT ),
- Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ) ),
+ Column( "inst_id", Integer, ForeignKey( "cloud_instance.id" ) ),
Column( "status", TEXT ),
Column( "device", TEXT ),
Column( "space_consumed", Integer ),
@@ -1133,7 +1133,7 @@
assign_mapper( context, CloudStore, CloudStore.table,
properties=dict( user=relation( User ),
- i=relation( CloudInstance ),
+ inst=relation( CloudInstance ),
snapshot=relation( CloudSnapshot, backref="store" )
) )
diff -r f4c2bf76b2e2 -r 5fbda2fdb3ca lib/galaxy/model/migrate/versions/0026_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Wed Nov 18 10:57:24 2009 -0500
+++ b/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Wed Nov 18 13:36:37 2009 -0500
@@ -56,7 +56,7 @@
Column( "type", TEXT ),
Column( "reservation_id", TEXT ),
Column( "instance_id", TEXT ),
- Column( "mi_id", TEXT, ForeignKey( "cloud_image.id" ), index=True ),
+ Column( "mi_id", Integer, ForeignKey( "cloud_image.id" ), index=True ),
Column( "state", TEXT ),
Column( "error", TEXT ),
Column( "public_dns", TEXT ),
@@ -74,7 +74,7 @@
Column( "volume_id", TEXT ),
Column( "size", Integer, nullable=False ),
Column( "availability_zone", TEXT ),
- Column( "i_id", TEXT, ForeignKey( "cloud_instance.id" ) ),
+ Column( "inst_id", Integer, ForeignKey( "cloud_instance.id" ) ),
Column( "status", TEXT ),
Column( "device", TEXT ),
Column( "space_consumed", Integer ),
diff -r f4c2bf76b2e2 -r 5fbda2fdb3ca templates/cloud/view_instance.mako
--- a/templates/cloud/view_instance.mako Wed Nov 18 10:57:24 2009 -0500
+++ b/templates/cloud/view_instance.mako Wed Nov 18 13:36:37 2009 -0500
@@ -67,7 +67,7 @@
%endif
<tr>
<td> AMI: </td>
- <td> ${liveInstance.mi_id} </td>
+ <td> ${liveInstance.image.image_id} </td>
</tr>
<tr>
<td> State:</td>
@@ -81,6 +81,7 @@
<td> Storage size:</td>
<td> ${liveInstance.uci.total_size} </td>
</tr>
+ %if liveInstance.public_dns != None and liveInstance.public_dns != '':
<tr>
<td> Public DNS:</td>
<%
@@ -88,7 +89,8 @@
%>
<td> <a href="${lnk}" target="_blank">${liveInstance.public_dns}</a></td>
</tr>
- %if liveInstance.private_dns != None:
+ %endif
+ %if liveInstance.private_dns != None and liveInstance.private_dns != '':
<tr>
<td> Private DNS:</td>
<td> ${liveInstance.private_dns}</td>
diff -r f4c2bf76b2e2 -r 5fbda2fdb3ca universe_wsgi.ini.sample
--- a/universe_wsgi.ini.sample Wed Nov 18 10:57:24 2009 -0500
+++ b/universe_wsgi.ini.sample Wed Nov 18 13:36:37 2009 -0500
@@ -31,15 +31,15 @@
paste.app_factory = galaxy.web.buildapp:app_factory
# By default, Galaxy uses a SQLite database found here
-database_file = database/universe.sqlite
+#database_file = database/universe.sqlite
# You may use a SQLAlchemy connection string to specify an external database
# instead. PostgreSQL and MySQL are supported.
-#database_connection = postgres:///galaxy
-#database_engine_option_echo = true
-#database_engine_option_echo_pool = true
-#database_engine_option_pool_size = 10
-#database_engine_option_max_overflow = 20
+database_connection = postgres:///galaxy
+database_engine_option_echo = true
+database_engine_option_echo_pool = true
+database_engine_option_pool_size = 10
+database_engine_option_max_overflow = 20
# If using MySQL, see:
# http://rapd.wordpress.com/2008/03/02/sqlalchemy-sqlerror-operationalerror-2…
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/2beaa0783b69
changeset: 3104:2beaa0783b69
user: James Taylor <james(a)jamestaylor.org>
date: Wed Nov 18 10:47:14 2009 -0500
description:
Another tweak to cloud table order
diffstat:
lib/galaxy/model/migrate/versions/0026_cloud_tables.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diffs (14 lines):
diff -r cffcf1cae94c -r 2beaa0783b69 lib/galaxy/model/migrate/versions/0026_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Wed Nov 18 10:43:08 2009 -0500
+++ b/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Wed Nov 18 10:47:14 2009 -0500
@@ -138,9 +138,9 @@
CloudImage_table.create()
UCI_table.create()
+ CloudInstance_table.create()
CloudStore_table.create()
CloudSnapshot_table.create()
- CloudInstance_table.create()
def downgrade():
metadata.reflect()
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/f4c2bf76b2e2
changeset: 3105:f4c2bf76b2e2
user: James Taylor <james(a)jamestaylor.org>
date: Wed Nov 18 10:57:24 2009 -0500
description:
Change a few foreign keys, this may change behavior of the cloud stuff, needs to be checked.
diffstat:
lib/galaxy/model/migrate/versions/0026_cloud_tables.py | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diffs (21 lines):
diff -r 2beaa0783b69 -r f4c2bf76b2e2 lib/galaxy/model/migrate/versions/0026_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Wed Nov 18 10:47:14 2009 -0500
+++ b/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Wed Nov 18 10:57:24 2009 -0500
@@ -56,7 +56,7 @@
Column( "type", TEXT ),
Column( "reservation_id", TEXT ),
Column( "instance_id", TEXT ),
- Column( "mi_id", TEXT, ForeignKey( "cloud_image.image_id" ), index=True ),
+ Column( "mi_id", TEXT, ForeignKey( "cloud_image.id" ), index=True ),
Column( "state", TEXT ),
Column( "error", TEXT ),
Column( "public_dns", TEXT ),
@@ -74,7 +74,7 @@
Column( "volume_id", TEXT ),
Column( "size", Integer, nullable=False ),
Column( "availability_zone", TEXT ),
- Column( "i_id", TEXT, ForeignKey( "cloud_instance.instance_id" ) ),
+ Column( "i_id", TEXT, ForeignKey( "cloud_instance.id" ) ),
Column( "status", TEXT ),
Column( "device", TEXT ),
Column( "space_consumed", Integer ),
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/fb7dd12ea39c
changeset: 3106:fb7dd12ea39c
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Wed Nov 18 11:51:09 2009 -0500
description:
Tweaks to grid filter UI: (1) added search image/submit button for initiating search and (2) styled text filter criteria.
diffstat:
lib/galaxy/web/framework/helpers/grids.py | 3 +++
static/images/mag_glass.png |
templates/grid_base.mako | 35 +++++++++++++++++++++++++++++++++++
templates/grid_common.mako | 13 ++++++++-----
4 files changed, 46 insertions(+), 5 deletions(-)
diffs (97 lines):
diff -r f4c2bf76b2e2 -r fb7dd12ea39c lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py Wed Nov 18 10:57:24 2009 -0500
+++ b/lib/galaxy/web/framework/helpers/grids.py Wed Nov 18 11:51:09 2009 -0500
@@ -120,6 +120,9 @@
column_filter = from_json_string_recurse( column_filter )
if len( column_filter ) == 1:
column_filter = column_filter[0]
+ # If filter criterion is empty, do nothing.
+ if column_filter == '':
+ continue
# Update query.
query = column.filter( trans.sa_session, query, column_filter )
# Upate current filter dict.
diff -r f4c2bf76b2e2 -r fb7dd12ea39c static/images/mag_glass.png
Binary file static/images/mag_glass.png has changed
diff -r f4c2bf76b2e2 -r fb7dd12ea39c templates/grid_base.mako
--- a/templates/grid_base.mako Wed Nov 18 10:57:24 2009 -0500
+++ b/templates/grid_base.mako Wed Nov 18 11:51:09 2009 -0500
@@ -62,6 +62,21 @@
t2.autocomplete("${h.url_for( controller='history', action='name_autocomplete_data' )}", autocomplete_options);
}
+
+ // Initialize submit image elements.
+ $('.submit-image').each( function()
+ {
+ // On mousedown, add class to simulate click.
+ $(this).mousedown( function() {
+ $(this).addClass('gray-background');
+ });
+
+ // On mouseup, add class to simulate click.
+ $(this).mouseup( function() {
+ $(this).removeClass('gray-background');
+ });
+
+ });
});
## Can this be moved into base.mako?
%if refresh_frames:
@@ -168,6 +183,26 @@
text-align: center;
display: inline-block;
}
+ .submit-image {
+ vertical-align: text-bottom;
+ margin: 0;
+ padding: 0;
+ }
+ .no-padding-or-margin {
+ margin: 0;
+ padding: 0;
+ }
+ .gray-background {
+ background-color: #DDDDDD;
+ }
+ .text-filter-val {
+ border: solid 1px #AAAAAA;
+ padding: 1px 3px 1px 3px;
+ margin-right: 5px;
+ -moz-border-radius: .5em;
+ -webkit-border-radius: .5em;
+ font-style: italic;
+ }
</style>
</%def>
diff -r f4c2bf76b2e2 -r fb7dd12ea39c templates/grid_common.mako
--- a/templates/grid_common.mako Wed Nov 18 10:57:24 2009 -0500
+++ b/templates/grid_common.mako Wed Nov 18 11:51:09 2009 -0500
@@ -32,10 +32,11 @@
<% column_filter = cur_filter_dict[column.key] %>
%if isinstance( column_filter, basestring ):
%if column_filter != "All":
- <span style="font-style: italic">${cur_filter_dict[column.key]}</span>
- <% filter_all = GridColumnFilter( "", { column.key : "All" } ) %>
- <a href="${url( filter_all.get_url_args() )}"><img src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a>
- |
+ <span class='text-filter-val'>
+ ${cur_filter_dict[column.key]}
+ <% filter_all = GridColumnFilter( "", { column.key : "All" } ) %>
+ <a href="${url( filter_all.get_url_args() )}"><img src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/></a>
+ </span>
%endif
%elif isinstance( column_filter, list ):
%for i, filter in enumerate( column_filter ):
@@ -53,7 +54,9 @@
%endif
%endif
- <span><input id="input-${column.key}-filter" name="f-${column.key}" type="text" value="" size="15"/></span>
+ <span>
+ <input class="no-padding-or-margin" id="input-${column.key}-filter" name="f-${column.key}" type="text" value="" size="15"/>
+ <input class='submit-image' type='image' src='${h.url_for('/static/images/mag_glass.png')}' alt='Filter'/></span>
</form>
%else:
%for i, filter in enumerate( column.get_accepted_filters() ):
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/b39f3d484220
changeset: 3102:b39f3d484220
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Wed Nov 18 09:52:38 2009 -0500
description:
Unicode fix: make it possible to display non-acii characters in dataset name.
diffstat:
lib/galaxy/datatypes/data.py | 5 ++++-
templates/root/history_common.mako | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diffs (27 lines):
diff -r 03eb232d1111 -r b39f3d484220 lib/galaxy/datatypes/data.py
--- a/lib/galaxy/datatypes/data.py Tue Nov 17 19:07:19 2009 -0500
+++ b/lib/galaxy/datatypes/data.py Wed Nov 18 09:52:38 2009 -0500
@@ -146,7 +146,10 @@
def display_name(self, dataset):
"""Returns formatted html of dataset name"""
try:
- return escape(dataset.name)
+ if type ( dataset.name ) is unicode:
+ return escape( dataset.name )
+ else:
+ return escape( unicode( dataset.name, 'utf-8 ') )
except:
return "name unavailable"
def display_info(self, dataset):
diff -r 03eb232d1111 -r b39f3d484220 templates/root/history_common.mako
--- a/templates/root/history_common.mako Tue Nov 17 19:07:19 2009 -0500
+++ b/templates/root/history_common.mako Wed Nov 18 09:52:38 2009 -0500
@@ -43,7 +43,7 @@
%endif
</div>
<span class="state-icon"></span>
- <span class="historyItemTitle"><b>${hid}: ${data.display_name().decode('utf-8')}</b></span>
+ <span class="historyItemTitle"><b>${hid}: ${data.display_name()}</b></span>
</div>
## Body for history items, extra info and actions, data "peek"
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/cffcf1cae94c
changeset: 3103:cffcf1cae94c
user: James Taylor <james(a)jamestaylor.org>
date: Wed Nov 18 10:43:08 2009 -0500
description:
Change cloud table creation order
diffstat:
lib/galaxy/model/migrate/versions/0026_cloud_tables.py | 18 +++++++++++-------
1 files changed, 11 insertions(+), 7 deletions(-)
diffs (37 lines):
diff -r b39f3d484220 -r cffcf1cae94c lib/galaxy/model/migrate/versions/0026_cloud_tables.py
--- a/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Wed Nov 18 09:52:38 2009 -0500
+++ b/lib/galaxy/model/migrate/versions/0026_cloud_tables.py Wed Nov 18 10:43:08 2009 -0500
@@ -132,21 +132,25 @@
# Load existing tables
metadata.reflect()
+ CloudProvider_table.create()
+ CloudUserCredentials_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()
+ CloudInstance_table.drop()
+ CloudSnapshot_table.drop()
+ CloudStore_table.drop()
+
+ UCI_table.drop()
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
+ CloudProvider_table.drop()
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/03eb232d1111
changeset: 3101:03eb232d1111
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Tue Nov 17 19:07:19 2009 -0500
description:
merge
diffstat:
eggs.ini | 6 +-
lib/galaxy/app.py | 4 +-
lib/galaxy/cloud/__init__.py | 653 ++++++++++++
lib/galaxy/cloud/providers/ec2.py | 995 ++++++++++++++++++
lib/galaxy/cloud/providers/eucalyptus.py | 1019 ++++++++++++++++++
lib/galaxy/config.py | 6 +
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 | 1188 ++++++++++++++++++++++
lib/galaxy/web/controllers/forms.py | 252 ++-
lib/galaxy/web/controllers/library_common.py | 2 +-
lib/galaxy/web/controllers/requests.py | 302 +++--
lib/galaxy/web/controllers/requests_admin.py | 663 +++++++----
static/images/silk/resultset_previous.png |
templates/admin/forms/edit_form.mako | 2 +-
templates/admin/forms/grid.mako | 1 +
templates/admin/forms/manage_forms.mako | 76 -
templates/admin/forms/show_form_read_only.mako | 4 +-
templates/admin/index.mako | 9 +
templates/admin/requests/create_request_type.mako | 92 +-
templates/admin/requests/grid.mako | 218 +----
templates/admin/requests/manage_request_types.mako | 69 +-
templates/admin/requests/show_request.mako | 2 +-
templates/admin/requests/view_request_type.mako | 70 +-
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 | 354 ++++++
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/requests/grid.mako | 218 +----
templates/requests/show_request.mako | 2 +-
templates/root/index.mako | 13 +-
test/base/twilltestcase.py | 34 +-
test/functional/test_forms_and_requests.py | 44 +-
test/functional/test_user_info.py | 9 +-
universe_wsgi.ini.sample | 5 +
48 files changed, 7244 insertions(+), 1191 deletions(-)
diffs (truncated from 9241 to 3000 lines):
diff -r 90723c58b1a6 -r 03eb232d1111 eggs.ini
--- a/eggs.ini Tue Nov 17 19:06:42 2009 -0500
+++ b/eggs.ini Tue Nov 17 19:07:19 2009 -0500
@@ -52,6 +52,7 @@
wsgiref = 0.1.2
Babel = 0.9.4
wchartype = 0.1
+boto = 1.8d
; extra version information
[tags]
@@ -60,7 +61,7 @@
MySQL_python = _5.0.67_static
python_lzo = _static
bx_python = _dev_r4bf1f32e6b76
-GeneTrack = _dev_raa786e9fc131d998e532a1aef39d108850c9e93d
+GeneTrack = _dev_e380f21c704218622155b9d230a44b3c9c452524
SQLAlchemy = _dev_r6498
; nose = .dev_r7156749efc58
@@ -82,7 +83,7 @@
decorator = http://pypi.python.org/packages/source/d/decorator/decorator-3.1.2.tar.gz
docutils = http://downloads.sourceforge.net/docutils/docutils-0.4.tar.gz
elementtree = http://effbot.org/downloads/elementtree-1.2.6-20050316.tar.gz
-GeneTrack = http://github.com/ialbert/genetrack-central/tarball/aa786e9fc131d998e532a1a…
+GeneTrack = http://github.com/ialbert/genetrack-central/tarball/e380f21c704218622155b9d…
lrucache = http://evan.prodromou.name/lrucache/lrucache-0.2.tar.gz
Mako = http://www.makotemplates.org/downloads/Mako-0.2.5.tar.gz
nose = http://pypi.python.org/packages/source/n/nose/nose-0.11.1.tar.gz
@@ -103,3 +104,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 90723c58b1a6 -r 03eb232d1111 lib/galaxy/app.py
--- a/lib/galaxy/app.py Tue Nov 17 19:06:42 2009 -0500
+++ b/lib/galaxy/app.py Tue Nov 17 19:07:19 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 90723c58b1a6 -r 03eb232d1111 lib/galaxy/cloud/__init__.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/cloud/__init__.py Tue Nov 17 19:07:19 2009 -0500
@@ -0,0 +1,653 @@
+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
+ self.sa_session = app.model.context
+ if self.app.config.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()
+ self.sa_session = app.model.context
+
+ # 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:
+ 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.
+ """
+ model = self.app.model
+ new_requests = []
+
+ for r in self.sa_session.query( model.UCI ) \
+ .filter( or_( model.UCI.table.c.state==uci_states.NEW_UCI,
+ model.UCI.table.c.state==uci_states.SUBMITTED_UCI,
+ model.UCI.table.c.state==uci_states.SHUTTING_DOWN_UCI,
+ model.UCI.table.c.state==uci_states.DELETING_UCI,
+ model.UCI.table.c.state==uci_states.SNAPSHOT_UCI ) ) \
+ .all():
+ uci_wrapper = UCIwrapper( r, self.app )
+ new_requests.append( uci_wrapper )
+
+ for uci_wrapper in new_requests:
+ self.sa_session.expunge_all()
+ self.put( uci_wrapper )
+
+ 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, app ):
+ self.uci_id = uci.id
+ self.app = app
+ self.sa_session = self.app.model.context
+
+ # --------- 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 = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ uci.state = uci_state
+ self.sa_session.flush()
+ if ( instance_id is not None ) and ( i_state is not None ):
+ instance = self.sa_session.query( model.CloudInstance ).filter_by( uci_id=self.uci_id, instance_id=instance_id).first()
+ instance.state = i_state
+ self.sa_session.add( instance )
+ self.sa_session.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 = self.sa_session.query( model.CloudImage ).filter( model.CloudImage.table.c.image_id==mi_id ).first()
+ instance = self.sa_session.query( model.CloudInstance ).get( i_index )
+ instance.image = mi
+ self.sa_session.add( instance )
+ self.sa_session.flush()
+
+ def set_key_pair( self, key_name, key_material=None ):
+ """
+ Sets key pair value for current UCI.
+ """
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ uci.key_pair_name = key_name
+ if key_material is not None:
+ uci.key_pair_material = key_material
+ self.sa_session.flush()
+
+ def set_instance_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 = self.sa_session.query( model.CloudInstance ).get( i_index )
+ elif i_id != None:
+ instance = self.sa_session.query( model.CloudInstance ).filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ else:
+ return None
+
+ instance.launch_time = launch_time
+ self.sa_session.add( instance )
+ self.sa_session.flush()
+
+ def set_uci_launch_time( self, launch_time ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ uci.launch_time = launch_time
+ self.sa_session.add( uci )
+ self.sa_session.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 = self.sa_session.query( model.CloudInstance ).get( i_index )
+ elif i_id != None:
+ instance = self.sa_session.query( model.CloudInstance ).filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ else:
+ return None
+
+ instance.stop_time = stop_time
+ self.sa_session.add( instance )
+ self.sa_session.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 = self.sa_session.query( model.CloudInstance ).get( i_index )
+ elif i_id != None:
+ instance = self.sa_session.query( model.CloudInstance ).filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ else:
+ return None
+
+ instance.security_group = security_group_name
+ self.sa_session.add( instance )
+ self.sa_session.flush()
+
+ def set_reservation_id( self, i_index, reservation_id ):
+ instance = self.sa_session.query( model.CloudInstance ).get( i_index )
+ instance.reservation_id = reservation_id
+ self.sa_session.add( instance )
+ self.sa_session.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 = self.sa_session.query( model.CloudInstance ).get( i_index )
+ instance.instance_id = instance_id
+ self.sa_session.add( instance )
+ self.sa_session.flush()
+
+# def set_public_dns( self, instance_id, public_dns ):
+# uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+# self.sa_session.refresh( uci )
+# uci.instance[instance_id].public_dns = public_dns
+# uci.instance[instance_id].flush()
+#
+# def set_private_dns( self, instance_id, private_dns ):
+# uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+# self.sa_session.refresh( uci )
+# uci.instance[instance_id].private_dns = private_dns
+# uci.instance[instance_id].flush()
+
+ def reset_uci_launch_time( self ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ uci.launch_time = None
+ self.sa_session.add( uci )
+ self.sa_session.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 = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ uci.error = error
+ if set_state:
+ uci.state = uci_states.ERROR
+ # Process all instances associated with this UCI
+ instances = self.sa_session.query( model.CloudInstance ) \
+ .filter_by( uci=uci ) \
+ .filter( or_( model.CloudInstance.table.c.state==None, model.CloudInstance.table.c.state==instance_states.SUBMITTED ) ) \
+ .all()
+ for i in instances:
+ i.error = error
+ i.state = instance_states.ERROR
+ self.sa_session.add( i )
+ self.sa_session.flush()
+
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+
+ def set_deleted( self ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ uci.state = uci_states.DELETED # for bookkeeping reasons, mark as deleted but don't actually delete.
+ uci.deleted = True
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+
+ def set_store_device( self, store_id, device ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ 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 = self.sa_session.query( model.CloudStore ).get( store_index )
+ elif store_id != None:
+ store = self.sa_session.query( model.CloudStore ).filter_by( volume_id = store_id ).first()
+ else:
+ return None
+
+ store.error = error
+ self.sa_session.add( store )
+ self.sa_session.flush()
+
+ def set_store_status( self, vol_id, status ):
+ vol = self.sa_session.query( model.CloudStore ).filter( model.CloudStore.table.c.volume_id == vol_id ).first()
+ vol.status = status
+ self.sa_session.add( vol )
+ self.sa_session.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 = self.sa_session.query( model.CloudStore ).filter( model.CloudStore.table.c.volume_id == vol_id ).all()
+ else:
+ vol = self.sa_session.query( model.CloudStore ).filter( model.CloudStore.table.c.uci_id == self.uci_id ).all()
+
+ for v in vol:
+ v.availability_zone = availability_zone
+ self.sa_session.add( v )
+ self.sa_session.flush()
+
+ def set_store_volume_id( self, store_index, volume_id ):
+ """
+ Given store index associated with this UCI in local database, set volume ID as it is registered
+ on the cloud provider (e.g., vol-39890501)
+ """
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ uci.store[store_index].volume_id = volume_id
+ #uci.store[store_index].flush()
+ self.sa_session.add( uci )
+ self.sa_session.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 = self.sa_session.query( model.CloudStore ).filter( model.CloudStore.table.c.volume_id == vol_id ).first()
+ vol.i_id = instance_id
+ self.sa_session.add( vol )
+ self.sa_session.flush()
+
+ def set_snapshot_id( self, snap_index, id ):
+ snap = model.CloudSnapshot.get( snap_index )
+
+ snap.snapshot_id = id
+ self.sa_session.add( snap )
+ self.sa_session.flush()
+
+ def set_snapshot_status( self, status, snap_index=None, snap_id=None ):
+ if snap_index != None:
+ snap = self.sa_session.query( model.CloudSnapshot ).get( snap_index )
+ elif snap_id != None:
+ snap = self.sa_session.query( model.CloudSnapshot ).filter_by( snapshot_id = snap_id).first()
+ else:
+ return
+ snap.status = status
+ self.sa_session.add( snap )
+ self.sa_session.flush()
+
+ def set_snapshot_error( self, error, snap_index=None, snap_id=None, set_status=False ):
+ if snap_index != None:
+ snap = self.sa_session.query( model.CloudSnapshot ).get( snap_index )
+ elif snap_id != None:
+ snap = self.sa_session.query( model.CloudSnapshot ).filter_by( snapshot_id = snap_id).first()
+ else:
+ return
+ snap.error = error
+ if set_status:
+ snap.status = snapshot_status.ERROR
+
+ self.sa_session.add( snap )
+ self.sa_session.flush()
+
+ # --------- Getter methods -----------------
+
+ def get_provider_type( self ):
+ """ Returns type of cloud provider associated with given UCI. """
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.credentials.provider.type
+
+ def get_provider( self ):
+ """ Returns database object of cloud provider associated with credentials of given UCI. """
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.credentials.provider
+
+ def get_instance_type( self, i_index ):
+ instance = self.sa_session.query( model.CloudInstance ).get( i_index )
+ self.sa_session.refresh( instance )
+ return instance.type
+
+ def get_uci_state( self ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ 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 = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ instances = self.sa_session.query( model.CloudInstance ) \
+ .filter_by( uci=uci ) \
+ .filter( model.CloudInstance.table.c.state==state ) \
+ .all()
+ il = []
+ for i in instances:
+ il.append( i.id )
+
+ return il
+
+ def get_instance_state( self, instance_id ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ 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' or
+ 'error' but the state is defined (i.e., state is not None)
+ (e.g., return value: ['i-402906D2', 'i-q0290dsD2'] ).
+ """
+ il = self.sa_session.query( model.CloudInstance ) \
+ .filter_by( uci_id=self.uci_id ) \
+ .filter( or_( model.CloudInstance.table.c.state != 'terminated',
+ model.CloudInstance.table.c.state != 'error',
+ model.CloudInstance.table.c.state != None ) ) \
+ .all()
+ instanceList = []
+ for i in il:
+ instanceList.append( i.instance_id )
+ return instanceList
+
+ def get_name( self ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.name
+
+ def get_key_pair_name( self ):
+ """
+ Returns keypair name associated with given UCI.
+ """
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.key_pair_name
+
+ def get_key_pair_material( self ):
+ """
+ Returns keypair material (i.e., private key) associated with given UCI.
+ """
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ 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 = self.sa_session.query( model.CloudInstance ).get( i_index )
+ return instance.security_group
+ elif i_id != None:
+ instance = self.sa_session.query( model.CloudInstance ).filter_by( uci_id=self.uci_id, instance_id=i_id).first()
+ return instance.security_group
+
+ def get_access_key( self ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.credentials.access_key
+
+ def get_secret_key( self ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.credentials.secret_key
+
+ def get_mi_id( self, instance_id=0 ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.instance[instance_id].mi_id
+
+ def get_public_dns( self, instance_id=0 ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.instance[instance_id].public_dns
+
+ def get_private_dns( self, instance_id=0 ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ 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 = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.store[0].availability_zone
+
+ def get_store_size( self, store_id=0 ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ 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 = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci.store[store_id].volume_id
+
+ def get_all_stores( self ):
+ """ Returns all storage volumes' database objects associated with this UCI. """
+ return self.sa_session.query( model.CloudStore ).filter( model.CloudStore.table.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 self.sa_session.query( model.CloudSnapshot ).filter_by( uci_id=self.uci_id, status=status ).all()
+
+ def get_uci( self ):
+ """ Returns database object for given UCI. """
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ return uci
+
+ def uci_launch_time_set( self ):
+ uci = self.sa_session.query( model.UCI ).get( self.uci_id )
+ self.sa_session.refresh( uci )
+ 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 90723c58b1a6 -r 03eb232d1111 lib/galaxy/cloud/providers/ec2.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/cloud/providers/ec2.py Tue Nov 17 19:07:19 2009 -0500
@@ -0,0 +1,995 @@
+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.sa_session = app.model.context
+
+ 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 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_uci_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 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_uci_state()
+ if uci_state is self.STOP_SIGNAL:
+ return
+ try:
+ if uci_state==uci_states.NEW:
+ self.create_uci( uci_wrapper )
+ elif uci_state==uci_states.DELETING:
+ self.delete_uci( uci_wrapper )
+ elif uci_state==uci_states.SUBMITTED:
+ self.start_uci( uci_wrapper )
+ elif uci_state==uci_states.SHUTTING_DOWN:
+ self.stop_uci( uci_wrapper )
+ elif uci_state==uci_states.SNAPSHOT:
+ self.snapshot_uci( 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_instance_type( i_index )
+ if i_type=='m1.small' or i_type=='c1.medium':
+ arch = 'i386'
+ else:
+ arch = 'x86_64'
+
+ mi = self.sa_session.query( 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 create_uci( 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 delete_uci( 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
+ self.sa_session.add( v )
+ self.sa_session.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: " + str( failedList ) + ". However, these volumes were successfully deleted: " \
+ + str( deletedList ) + ". MANUAL intervention and processing needed."
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+ def snapshot_uci( self, uci_wrapper ):
+ """
+ Creates snapshot of all storage volumes associated with this UCI.
+ """
+ if uci_wrapper.get_uci_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 add_storage_to_uci( self, name ):
+ """ Adds more storage to specified UCI
+ TODO"""
+
+ def dummy_start_uci( 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 start_uci( self, uci_wrapper ):
+ """
+ Starts instance(s) of given UCI on the cloud.
+ """
+ if uci_wrapper.get_uci_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 mi_id != None:
+ # 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_uci_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_instance_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_instance_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:
+ l_time = datetime.utcnow()
+ # uci_wrapper.set_instance_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
+ uci_wrapper.set_instance_launch_time( l_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 )
+ 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_uci_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 stop_uci( 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
+ # Process list of instances and remove any references to empty instance id's
+ for i in il:
+ if i is None:
+ il.remove( i )
+ 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'." % ( 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 = self.sa_session.query( model.CloudInstance ) \
+ .filter( or_( model.CloudInstance.table.c.state==instance_states.RUNNING,
+ model.CloudInstance.table.c.state==instance_states.PENDING,
+ model.CloudInstance.table.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.update_instance( inst )
+
+ # Update storage volume(s)
+ stores = self.sa_session.query( model.CloudStore ) \
+ .filter( or_( model.CloudStore.table.c.status==store_status.IN_USE,
+ model.CloudStore.table.c.status==store_status.CREATING,
+ model.CloudStore.table.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.update_store( 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 = self.sa_session.query( model.CloudSnapshot ) \
+ .filter( or_( model.CloudSnapshot.table.c.status == snapshot_status.PENDING, model.CloudSnapshot.table.c.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 = self.sa_session.query( model.UCI ).filter_by( state=uci_states.SUBMITTED ).all()
+ for zombie in zombies:
+ z_instances = self.sa_session.query( model.CloudInstance ) \
+ .filter_by( uci_id=zombie.id ) \
+ .filter( or_( model.CloudInstance.table.c.state != instance_states.TERMINATED,
+ model.CloudInstance.table.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.process_zombie( z_inst )
+
+ def update_instance( self, inst ):
+
+ # Get credentials associated wit this instance
+ uci_id = inst.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+ 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
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+ 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
+ self.sa_session.add( inst )
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( inst )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+ if cInst.public_dns_name != inst.public_dns:
+ inst.public_dns = cInst.public_dns_name
+ self.sa_session.add( inst )
+ self.sa_session.flush()
+ if cInst.private_dns_name != inst.private_dns:
+ inst.private_dns = cInst.private_dns_name
+ self.sa_session.add( inst )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+ return None
+
+ def update_store( self, store ):
+ # Get credentials associated wit this store
+ uci_id = store.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+ conn = self.get_connection_from_uci( uci )
+
+ # Get reservations handle for given store
+ try:
+ log.debug( "Updating storage volume command: vl = conn.get_all_volumes( [%s] )" % store.volume_id )
+ 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
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+
+ store.status = vl[0].status
+ self.sa_session.add( store )
+ self.sa_session.flush()
+ if store.i_id != vl[0].instance_id:
+ store.i_id = vl[0].instance_id
+ self.sa_session.add( store )
+ self.sa_session.flush()
+ if store.attach_time != vl[0].attach_time:
+ store.attach_time = vl[0].attach_time
+ self.sa_session.add( store )
+ self.sa_session.flush()
+ if store.device != vl[0].device:
+ store.device = vl[0].device
+ self.sa_session.add( store )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( store )
+ self.sa_session.flush()
+
+ def update_snapshot( self, snapshot ):
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+ 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
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.flush()
+
+ def delete_snapshot( self, snapshot ):
+ if snapshot.status == snapshot_status.DELETE:
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+ 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
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( snapshot )
+ self.sa_session.flush()
+
+ def process_zombie( 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.
+ """
+ uci_id = inst.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+
+ # 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
+ uci.state = state
+ self.sa_session.add( inst )
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( inst )
+ self.sa_session.flush()
+ if inst.uci.launch_time == None:
+ uci.launch_time = launch_time
+ self.sa_session.add( uci )
+ self.sa_session.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 )
+ self.sa_session.add( inst )
+ self.sa_session.add( uci )
+ self.sa_session.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
+ uci.error = err
+ uci.state = uci_states.ERROR
+ log.error( err )
+ self.sa_session.add( inst )
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.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.table.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.table.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 90723c58b1a6 -r 03eb232d1111 lib/galaxy/cloud/providers/eucalyptus.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/cloud/providers/eucalyptus.py Tue Nov 17 19:07:19 2009 -0500
@@ -0,0 +1,1019 @@
+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.sa_session = app.model.context
+
+ 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 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 ):
+ """
+ Adds uci_wrapper object to the end of the request queue to be handled by
+ this cloud provider.
+ """
+ state = uci_wrapper.get_uci_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 run_next( self ):
+ """Process next request, waiting until one is available if necessary."""
+ cnt = 0
+ while 1:
+ uci_wrapper = self.queue.get()
+ uci_state = uci_wrapper.get_uci_state()
+ if uci_state is self.STOP_SIGNAL:
+ return
+ try:
+ if uci_state==uci_states.NEW:
+ self.create_uci( uci_wrapper )
+ elif uci_state==uci_states.DELETING:
+ self.delete_uci( uci_wrapper )
+ elif uci_state==uci_states.SUBMITTED:
+ self.start_uci( uci_wrapper )
+ #self.dummy_start_uci( uci_wrapper )
+ elif uci_state==uci_states.SHUTTING_DOWN:
+ self.stop_uci( uci_wrapper )
+ elif uci_state==uci_states.SNAPSHOT:
+ self.snapshot_uci( uci_wrapper )
+ except:
+ log.exception( "Uncaught exception executing cloud request." )
+ cnt += 1
+
+ def get_connection( self, uci_wrapper ):
+ """
+ Establishes 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 ):
+ """
+ Check if a key pair associated with this UCI exists on cloud provider.
+ If yes, return key pair name; otherwise, generate a key pair with the cloud
+ provider and, again, return key pair name.
+ Key pair name for given UCI is generated from UCI's name and suffix '_kp'
+ """
+ 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 ):
+ """ Initiate creation of key pair under kp_name by current cloud provider. """
+ 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) ID based on instance type.
+ """
+ i_type = uci_wrapper.get_instance_type( i_index )
+ if i_type=='m1.small' or i_type=='c1.medium':
+ arch = 'i386'
+ else:
+ arch = 'x86_64'
+
+ mi = self.sa_session.query( 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 create_uci( self, uci_wrapper ):
+ """
+ Create User Configured Instance (UCI) - i.e., create storage volume on cloud provider
+ and register relevant information in local 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 delete_uci( self, uci_wrapper ):
+ """
+ Delete UCI - i.e., delete all storage volumes associated with this UCI.
+ NOTE that this implies deletion of any and all data associated
+ with this UCI from the cloud. All data will be deleted.
+ Information in local Galaxy database is marked as deleted but not actually removed
+ from the database.
+ """
+ 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
+ self.sa_session.add( v )
+ self.sa_session.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: "+ str( failedList )+". However, these volumes were successfully deleted: " \
+ + str( deletedList ) +". MANUAL intervention and processing needed."
+ log.error( err )
+ uci_wrapper.set_error( err, True )
+
+ def snapshot_uci( self, uci_wrapper ):
+ """
+ Initiate creation of a snapshot by cloud provider for all storage volumes
+ associated with this UCI.
+ """
+ if uci_wrapper.get_uci_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_uci_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 add_storage_to_uci( self, uci_wrapper ):
+ """ Adds more storage to specified UCI """
+
+ def dummy_start_uci( 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 start_uci( self, uci_wrapper ):
+ """
+ Start instance(s) of given UCI on the cloud.
+ """
+ if uci_wrapper.get_uci_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_uci_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_instance_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_instance_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:
+ l_time = datetime.utcnow()
+# uci_wrapper.set_instance_launch_time( self.format_time( reservation.instances[0].launch_time ), i_index=i_index )
+ uci_wrapper.set_instance_launch_time( l_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_uci_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 stop_uci( self, uci_wrapper):
+ """
+ Stop all 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
+ # Process list of instances and remove any references to empty instance id's
+ for i in il:
+ if i is None:
+ il.remove( i )
+ 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 ):
+ """
+ Run status update on all instances that are in 'running', 'pending', or 'shutting-down' state.
+ Run status update on all storage volumes whose status is 'in-use', 'creating', or 'None'.
+ Run status update on all snapshots whose status is 'pending' or 'delete'
+ Run status update on any zombie UCIs, i.e., UCI's that is in 'submitted' state for an
+ extended period of time.
+
+ 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 = self.sa_session.query( model.CloudInstance ) \
+ .filter( or_( model.CloudInstance.table.c.state==instance_states.RUNNING,
+ model.CloudInstance.table.c.state==instance_states.PENDING,
+ model.CloudInstance.table.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.update_instance( inst )
+
+ # Update storage volume(s)
+ stores = self.sa_session.query( model.CloudStore ) \
+ .filter( or_( model.CloudStore.table.c.status==store_status.IN_USE,
+ model.CloudStore.table.c.status==store_status.CREATING,
+ model.CloudStore.table.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.update_store( store )
+
+ # Update pending snapshots or delete ones marked for deletion
+ snapshots = self.sa_session.query( model.CloudSnapshot ) \
+ .filter( or_( model.CloudSnapshot.table.c.status == snapshot_status.PENDING, model.CloudSnapshot.table.c.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 = self.sa_session.query( model.UCI ).filter_by( state=uci_states.SUBMITTED ).all()
+ for zombie in zombies:
+ log.debug( "zombie UCI: %s" % zombie.name )
+ z_instances = self.sa_session.query( model.CloudInstance ) \
+ .filter( or_( model.CloudInstance.table.c.state != instance_states.TERMINATED,
+ model.CloudInstance.table.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
+# 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.process_zombie( z_inst )
+
+ def update_instance( self, inst ):
+ """
+ Update information in local database for given instance as it is obtained from cloud provider.
+ Along with updating information about given instance, information about the UCI controlling
+ this instance is also updated.
+ """
+ # Get credentials associated wit this instance
+ uci_id = inst.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+ 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
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+ 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
+ self.sa_session.add( inst )
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( inst )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+ if cInst.public_dns_name != inst.public_dns:
+ inst.public_dns = cInst.public_dns_name
+ self.sa_session.add( inst )
+ self.sa_session.flush()
+ if cInst.private_dns_name != inst.private_dns:
+ inst.private_dns = cInst.private_dns_name
+ self.sa_session.add( inst )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+ return None
+
+ def update_store( self, store ):
+ """
+ Update information in local database for given storage volume as it is obtained from cloud provider.
+ Along with updating information about given storage volume, information about the UCI controlling
+ this storage volume is also updated.
+ """
+ # Get credentials associated wit this store
+ uci_id = store.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+ conn = self.get_connection_from_uci( uci )
+
+ if store.volume_id != None:
+ # Get reservations handle for given store
+ try:
+ log.debug( "Updating storage volume command: vl = conn.get_all_volumes( [%s] )" % store.volume_id )
+ 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
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.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
+
+ self.sa_session.add( uci )
+ self.sa_session.flush()
+
+ store.status = vl[0].status
+ self.sa_session.add( store )
+ self.sa_session.flush()
+ if store.i_id != vl[0].instance_id:
+ store.i_id = vl[0].instance_id
+ self.sa_session.add( store )
+ self.sa_session.flush()
+ if store.attach_time != vl[0].attach_time:
+ store.attach_time = vl[0].attach_time
+ self.sa_session.add( store )
+ self.sa_session.flush()
+ if store.device != vl[0].device:
+ store.device = vl[0].device
+ self.sa_session.add( store )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( store )
+ self.sa_session.flush()
+ else:
+ err = "Missing storage volume ID in local database on general update. Manual check is needed to check " \
+ "if storage volume was actually created by cloud provider."
+ 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
+ self.sa_session.add( uci )
+ self.sa_session.add( store )
+ self.sa_session.flush()
+
+ def update_snapshot( self, snapshot ):
+ """
+ Update information in local database for given snapshot as it is obtained from cloud provider.
+ Along with updating information about given snapshot, information about the UCI controlling
+ this snapshot is also updated.
+ """
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+ 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
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.flush()
+
+ def delete_snapshot( self, snapshot ):
+ """
+ Initiate deletion of given snapshot from cloud provider.
+ """
+ if snapshot.status == snapshot_status.DELETE:
+ # Get credentials associated wit this store
+ uci_id = snapshot.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+ 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
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( uci )
+ self.sa_session.add( snapshot )
+ self.sa_session.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
+ self.sa_session.add( snapshot )
+ self.sa_session.flush()
+
+ def process_zombie( self, inst ):
+ """
+ Attempt at discovering if starting a cloud 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.
+ """
+ uci_id = inst.uci_id
+ uci = self.sa_session.query( model.UCI ).get( uci_id )
+ self.sa_session.refresh( uci )
+
+ # 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
+ uci.state = state
+ self.sa_session.add( inst )
+ self.sa_session.add( uci )
+ self.sa_session.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
+ self.sa_session.add( inst )
+ self.sa_session.flush()
+ if inst.uci.launch_time == None:
+ uci.launch_time = launch_time
+ self.sa_session.add( uci )
+ self.sa_session.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 )
+ self.sa_session.add( inst )
+ self.sa_session.add( uci )
+ self.sa_session.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
+ uci.error = err
+ uci.state = uci_states.ERROR
+ log.error( err )
+ self.sa_session.add( inst )
+ self.sa_session.add( uci )
+ self.sa_session.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 )
+ log.debug( "[%s] Using following command to connect to cloud provider: "
+ "conn = EC2Connection( aws_access_key_id=%s, "
+ "aws_secret_access_key=%s, "
+ "port=%s, "
+ "is_secure=%s, "
+ "region=region, "
+ "path=%s )" % ( self.type, a_key, s_key, uci.credentials.provider.is_secure, uci.credentials.provider.port, uci.credentials.provider.path ) )
+ 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
+ self.sa_session.add( uci )
+ self.sa_session.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.table.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.table.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 90723c58b1a6 -r 03eb232d1111 lib/galaxy/config.py
--- a/lib/galaxy/config.py Tue Nov 17 19:06:42 2009 -0500
+++ b/lib/galaxy/config.py Tue Nov 17 19:07:19 2009 -0500
@@ -113,6 +113,12 @@
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' ) )
+ if self.cloud_controller_instance == True:
+ 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 90723c58b1a6 -r 03eb232d1111 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Tue Nov 17 19:06:42 2009 -0500
+++ b/lib/galaxy/model/__init__.py Tue Nov 17 19:07:19 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 90723c58b1a6 -r 03eb232d1111 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Tue Nov 17 19:06:42 2009 -0500
+++ b/lib/galaxy/model/mapping.py Tue Nov 17 19:07:19 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 90723c58b1a6 -r 03eb232d1111 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 Tue Nov 17 19:07:19 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 *
1
0
23 Nov '09
details: http://www.bx.psu.edu/hg/galaxy/rev/90723c58b1a6
changeset: 3100:90723c58b1a6
user: jeremy goecks <jeremy.goecks(a)emory.edu>
date: Tue Nov 17 19:06:42 2009 -0500
description:
'Insert history link' functionality added to pages. A user can select 1 or more histories and create links that point to a view of each history.
diffstat:
lib/galaxy/web/controllers/history.py | 23 +++++
lib/galaxy/web/controllers/page.py | 4 +-
static/scripts/jquery.wymeditor.js | 91 ----------------------
static/wymeditor/lang/en.js | 4 +-
templates/grid_base_async.mako | 10 ++-
templates/page/editor.mako | 117 +++++++++++++++++++---------
templates/page/select_histories_grid.mako | 6 +
templates/root/history.mako | 13 +++
8 files changed, 133 insertions(+), 135 deletions(-)
diffs (421 lines):
diff -r 4c3cddd02b09 -r 90723c58b1a6 lib/galaxy/web/controllers/history.py
--- a/lib/galaxy/web/controllers/history.py Mon Nov 16 18:38:32 2009 -0500
+++ b/lib/galaxy/web/controllers/history.py Tue Nov 17 19:06:42 2009 -0500
@@ -369,7 +369,30 @@
history.name = new_name
trans.sa_session.add( history )
trans.sa_session.flush()
+
+ @web.expose
+ @web.require_login( "get history name" )
+ def get_name_async( self, trans, id=None ):
+ """ Returns the name for a given history. """
+ history = get_history( trans, id, False )
+ # To get name: user must own history, history must be importable.
+ if history.user == trans.get_user() or history.importable or trans.get_user() in history.users_shared_with:
+ return history.name
+ return
+
+ @web.expose
+ @web.require_login( "set history's importable flag" )
+ def set_importable_async( self, trans, id=None, importable=False ):
+ """ Set history's importable attribute. """
+ history = get_history( trans, id, True )
+
+ if history:
+ history.importable = importable
+ trans.sa_session.flush()
+
+ return
+
@web.expose
def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
"""Return autocomplete data for history names"""
diff -r 4c3cddd02b09 -r 90723c58b1a6 lib/galaxy/web/controllers/page.py
--- a/lib/galaxy/web/controllers/page.py Mon Nov 16 18:38:32 2009 -0500
+++ b/lib/galaxy/web/controllers/page.py Tue Nov 17 19:06:42 2009 -0500
@@ -116,13 +116,13 @@
# Grid definition.
title = "Saved Histories"
- template = "grid_base_async.mako"
+ template = "/page/select_histories_grid.mako"
async_template = "grid_body_async.mako"
model_class = model.History
default_filter = { "deleted" : "False" , "shared" : "All" }
default_sort_key = "-update_time"
use_paging = True
- num_rows_per_page = 5
+ num_rows_per_page = 10
columns = [
NameColumn( "Name", key="name", model_class=model.History, filterable="advanced" ),
grids.TagsColumn( "Tags", "tags", model.History, model.HistoryTagAssociation, filterable="advanced"),
diff -r 4c3cddd02b09 -r 90723c58b1a6 static/scripts/jquery.wymeditor.js
--- a/static/scripts/jquery.wymeditor.js Mon Nov 16 18:38:32 2009 -0500
+++ b/static/scripts/jquery.wymeditor.js Tue Nov 17 19:06:42 2009 -0500
@@ -295,67 +295,6 @@
});
-/*
- Galaxy code that integrates into the WYM Editor.
- */
-var Galaxy =
-{
- /*
- Galaxy constants for WYM Editor:
- TOOLS - A string replaced by the galaxy toolbar's HTML.
- TOOLS_ITEMS - A string replaced by the galaxy toolbar items.
- INSERT_HISTORY - Command: open the insert history dialog.
- INSERT_DATASET - Command: open the insert dataset dialog.
- DIALOG_HISTORY - A dialog to insert a history.
- DIALOG_DATASET - A dialog to insert a dataset.
- */
- TOOLS : "{Galaxy_Tools}",
- TOOLS_ITEMS : "{Galaxy_Tools_Items}",
- INSERT_HISTORY : "InsertHistory",
- INSERT_DATASET : "InsertDataset",
- DIALOG_HISTORY : "DialogHistory",
- DIALOG_DATASET : "DialogDataset",
-
- // Tool items overview.
- toolsItems: [
- {'name': "InsertHistory", 'title': 'History', 'css': 'galaxy_tools_insert_history_link'},
- {'name': "InsertDataset", 'title': 'Dataset', 'css': 'galaxy_dataset'}
- ],
-
- // Tools HTML.
- toolsHtml: "<div class='wym_tools wym_section'>"
- + "<h2>" + this.TOOLS + "</h2>"
- + "<ul>"
- + this.TOOLS_ITEMS
- + "</ul>"
- + "</div>",
-
- // Insert history dialog.
- dialogHistoryHtml: "<body class='wym_dialog wym_dialog_history'"
- + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
- + ">"
- + "<form>"
- + "<fieldset>"
- + "<input type='hidden' class='wym_dialog_type' value='"
- + this.DIALOG_HISTORY
- + "' />"
- + "<legend>{Link}</legend>"
- + "<div class='row'>"
- + "<label>{Title}</label>"
- + "<input type='text' class='wym_title' value='' size='40' />"
- + "</div>"
- + "<div class='row row-indent'>"
- + "<input class='wym_submit' type='button'"
- + " value='{Add}' />"
- + "<input class='wym_cancel' type='button'"
- + "value='{Cancel}' />"
- + "</div>"
- + "</fieldset>"
- + "</form>"
- + "</body>",
-};
-
-
/********** JQUERY **********/
/**
@@ -414,7 +353,6 @@
+ "<div class='wym_area_right'>"
+ WYMeditor.CONTAINERS
+ WYMeditor.CLASSES
- + Galaxy.TOOLS
+ "</div>"
+ "<div class='wym_area_main'>"
+ WYMeditor.HTML
@@ -445,8 +383,6 @@
+ "<h2>{Tools}</h2>"
+ "<ul>"
+ WYMeditor.TOOLS_ITEMS
- // Add Galaxy Tools.
- //+ Galaxy.TOOLS_ITEMS
+ "</ul>"
+ "</div>",
@@ -821,7 +757,6 @@
boxHtml = h.replaceAll(boxHtml, WYMeditor.LOGO, this._options.logoHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS, this._options.toolsHtml);
- boxHtml = h.replaceAll(boxHtml, Galaxy.TOOLS, Galaxy.toolsHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS,this._options.containersHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES, this._options.classesHtml);
boxHtml = h.replaceAll(boxHtml, WYMeditor.HTML, this._options.htmlHtml);
@@ -846,24 +781,6 @@
boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS_ITEMS, sTools);
- // Construct Galaxy tools list.
- var galaxyTools = eval(Galaxy.toolsItems);
- sTools = "";
- for(var i = 0; i < galaxyTools.length; i++) {
- var galaxyTool = galaxyTools[i];
- if(galaxyTool.name && galaxyTool.title) {
- var sTool = this._options.toolsItemHtml;
- var sTool = h.replaceAll(sTool, WYMeditor.TOOL_NAME, galaxyTool.name);
- sTool = h.replaceAll(sTool, WYMeditor.TOOL_TITLE, this._options.stringDelimiterLeft
- + galaxyTool.title
- + this._options.stringDelimiterRight);
- sTool = h.replaceAll(sTool, WYMeditor.TOOL_CLASS, galaxyTool.css);
- sTools += sTool;
- }
- }
-
- //boxHtml = h.replaceAll(boxHtml, Galaxy.TOOLS_ITEMS, sTools);
-
//construct classes list
var aClasses = eval(this._options.classesItems);
var sClasses = "";
@@ -1029,10 +946,6 @@
this.dialog(WYMeditor.PREVIEW, this._options.dialogFeaturesPreview);
break;
- case Galaxy.INSERT_HISTORY:
- this.dialog(Galaxy.DIALOG_HISTORY);
- break;
-
default:
this._exec(cmd);
break;
@@ -1263,10 +1176,6 @@
case(WYMeditor.PREVIEW):
sBodyHtml = this._options.dialogPreviewHtml;
break;
- case(Galaxy.DIALOG_HISTORY):
- sBodyHtml = Galaxy.dialogHistoryHtml;
- break;
-
default:
sBodyHtml = bodyHtml;
}
diff -r 4c3cddd02b09 -r 90723c58b1a6 static/wymeditor/lang/en.js
--- a/static/wymeditor/lang/en.js Mon Nov 16 18:38:32 2009 -0500
+++ b/static/wymeditor/lang/en.js Tue Nov 17 19:06:42 2009 -0500
@@ -43,7 +43,7 @@
Source_Code: 'Source code',
// Galaxy replacements.
- History: 'History',
- Dataset: 'Dataset',
+ Galaxy_History_Link: 'Insert Link to History',
+ Galaxy_Dataset_Link: 'Insert Link to Dataset',
};
diff -r 4c3cddd02b09 -r 90723c58b1a6 templates/grid_base_async.mako
--- a/templates/grid_base_async.mako Mon Nov 16 18:38:32 2009 -0500
+++ b/templates/grid_base_async.mako Tue Nov 17 19:06:42 2009 -0500
@@ -407,7 +407,11 @@
// Update grid.
function update_grid(maintain_page_links)
{
+ // If there's an operation in the args, do POST; otherwise, do GET.
+ var operation = url_args['operation'];
+ var method = (operation != null && operation != undefined ? "POST" : "GET" );
$.ajax({
+ type: method,
url: "${h.url_for()}",
data: url_args,
error: function() { alert( "Grid refresh failed" ) },
@@ -504,9 +508,11 @@
<%namespace file="./grid_common_async.mako" import="*" />
## Print grid header.
-<%def name="render_grid_header()">
+<%def name="render_grid_header(include_title)">
<div class="grid-header">
- <h2>${grid.title}</h2>
+ %if include_title:
+ <h2>${grid.title}</h2>
+ %endif
%if grid.global_actions:
<ul class="manage-table-actions">
diff -r 4c3cddd02b09 -r 90723c58b1a6 templates/page/editor.mako
--- a/templates/page/editor.mako Mon Nov 16 18:38:32 2009 -0500
+++ b/templates/page/editor.mako Tue Nov 17 19:06:42 2009 -0500
@@ -39,6 +39,13 @@
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.autocomplete.js')}"> </script>
<script type="text/javascript">
+
+ // Useful Galaxy stuff.
+ var Galaxy =
+ {
+ DIALOG_HISTORY_LINK : "history_link",
+ };
+
## Completely replace WYM's dialog handling
WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) {
@@ -187,45 +194,73 @@
}
// HISTORY DIALOG
- if ( dialogType == Galaxy.DIALOG_HISTORY ) {
- show_modal(
- "Insert History",
- "<div class='row'>"
- + "<label>History Name</label><br>"
- + "<input id='history_name_input' type='text' class='wym_galaxy_history_name' value='' size='40' />"
- + "</div>"
- + "<div class='row'>"
- + "<label>Select History</label><br>"
- + "<input type='text' class='wym_galaxy_history_selected' value='' size='40' />"
- + "</div>"
- ,
- {
- "Insert": function() {
- var sUrl = jQuery(wym._options.hrefSelector).val();
- if(sUrl.length > 0) {
-
- wym._exec(WYMeditor.CREATE_LINK, sStamp);
-
- jQuery("a[href=" + sStamp + "]", wym._doc.body)
- .attr(WYMeditor.HREF, sUrl)
- .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val());
- hide_modal();
-
- // TODO: remove autocomplete.
- },
- "Cancel": function() {
- hide_modal();
-
- // TODO: remove autocomplete.
- }
+ if ( dialogType == Galaxy.DIALOG_HISTORY_LINK ) {
+ $.ajax(
+ {
+ url: "${h.url_for( action='list_histories_for_selection' )}",
+ data: {},
+ error: function() { alert( "Grid refresh failed" ) },
+ success: function(table_html)
+ {
+ show_modal(
+ "Insert Link to History",
+ table_html +
+ "<div><input id='make-importable' type='checkbox' checked/>" +
+ "Publish the selected histories so that they can viewed by everyone.</div>"
+ ,
+ {
+ "Insert": function()
+ {
+ // Make histories public/importable?
+ var make_importable = false;
+ if ( $('#make-importable:checked').val() !== null )
+ make_importable = true;
+
+ // Insert links to history for each checked item.
+ var item_ids = new Array();
+ $('input[name=id]:checked').each(function() {
+ var item_id = $(this).val();
+
+ // Make history importable?
+ if (make_importable)
+ $.ajax({
+ type: "POST",
+ url: '${h.url_for( controller='history', action='set_importable_async' )}',
+ data: { id: item_id, importable: 'True' },
+ error: function() { alert('Make history importable failed; id=' + item_id) }
+ });
+
+ // Insert link.
+ wym._exec(WYMeditor.CREATE_LINK, sStamp);
+ if ( $("a[href=" + sStamp + "]", wym._doc.body).length != 0)
+ {
+ // Link created from selected text; add href and title.
+ $("a[href=" + sStamp + "]", wym._doc.body)
+ .attr(WYMeditor.HREF, '${h.url_for( controller='history', action='view' )}' + '?id=' + item_id)
+ .attr(WYMeditor.TITLE, "History" + item_id);
+ }
+ else
+ {
+ // User selected no text; create link from scratch and use default text.
+
+ // Get history name.
+ $.get( '${h.url_for( controller='history', action='get_name_async' )}?id=' + item_id, function( history_name ) {
+ var href = '${h.url_for( controller='history', action='view' )}?id=' + item_id;
+ wym.insert("<a href='" + href + "'>History '" + history_name + "'</a>");
+ });
+ }
+ });
+
+ hide_modal();
+ },
+ "Cancel": function()
+ {
+ hide_modal();
+ }
+ }
+ );
}
- );
-
- // Set up autocomplete for name input.
- var t = $("#history_name_input");
- var autocomplete_options =
- { selectFirst: false, autoFill: false, highlight: false, mustMatch: false };
- t.autocomplete("${h.url_for( controller='history', action='name_autocomplete_data' )}", autocomplete_options);
+ });
}
};
</script>
@@ -272,6 +307,7 @@
{'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'},
{'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'},
{'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'},
+ {'name': 'Insert Galaxy History Link', 'title' : 'Galaxy_History_Link', 'css' : 'galaxy_tools_insert_history_link'}
]
});
## Get the editor object
@@ -320,6 +356,11 @@
window.document.location = "${next_url}";
}
});
+
+ // Initialize 'Insert history link' button.
+ $('.galaxy_tools_insert_history_link').children().click( function() {
+ editor.dialog(Galaxy.DIALOG_HISTORY_LINK);
+ });
});
</script>
</%def>
diff -r 4c3cddd02b09 -r 90723c58b1a6 templates/page/select_histories_grid.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/page/select_histories_grid.mako Tue Nov 17 19:06:42 2009 -0500
@@ -0,0 +1,6 @@
+## Template generates a grid that enables user to select histories.
+<%namespace file="../grid_base_async.mako" import="*" />
+
+${javascripts()}
+${render_grid_header(False)}
+${render_grid_table()}
\ No newline at end of file
diff -r 4c3cddd02b09 -r 90723c58b1a6 templates/root/history.mako
--- a/templates/root/history.mako Mon Nov 16 18:38:32 2009 -0500
+++ b/templates/root/history.mako Tue Nov 17 19:06:42 2009 -0500
@@ -246,6 +246,19 @@
}
});
};
+
+ //TODO: this function is a duplicate of array_length defined in galaxy.base.js ; not sure why it needs to be redefined here (due to streaming?).
+ // Returns the number of keys (elements) in an array/dictionary.
+ var array_length = function(an_array)
+ {
+ if (an_array.length)
+ return an_array.length;
+
+ var count = 0;
+ for (element in an_array)
+ count++;
+ return count;
+ };
//
// Function provides text for tagging toggle link.
1
0