details: http://www.bx.psu.edu/hg/galaxy/rev/802b761c5032 changeset: 3074:802b761c5032 user: Enis Afgan <afgane@gmail.com> date: Tue Oct 27 15:32:44 2009 -0400 description: UI enhancements regarding provider registration and error reporting. diffstat: lib/galaxy/cloud/providers/ec2.py | 37 +- lib/galaxy/cloud/providers/eucalyptus.py | 15 +- lib/galaxy/web/controllers/cloud.py | 553 ++++++------------------------------ templates/cloud/add_credentials.mako | 4 +- templates/cloud/add_provider.mako | 52 ++- templates/cloud/configure_cloud.mako | 43 +- 6 files changed, 200 insertions(+), 504 deletions(-) diffs (922 lines): diff -r 6632a5d39f41 -r 802b761c5032 lib/galaxy/cloud/providers/ec2.py --- a/lib/galaxy/cloud/providers/ec2.py Mon Oct 26 17:04:47 2009 -0400 +++ b/lib/galaxy/cloud/providers/ec2.py Tue Oct 27 15:32:44 2009 -0400 @@ -95,7 +95,13 @@ Establishes EC2 cloud connection using user's credentials associated with given UCI """ log.debug( '##### Establishing EC2 cloud connection' ) - conn = EC2Connection( uci_wrapper.get_access_key(), uci_wrapper.get_secret_key() ) + provider = uci_wrapper.get_provider() + region = RegionInfo( None, provider.region_name, provider.region_endpoint ) + conn = EC2Connection( aws_access_key_id=uci_wrapper.get_access_key(), + aws_secret_access_key=uci_wrapper.get_secret_key(), + is_secure=provider.is_secure, + region=region, + path=provider.path ) return conn def set_keypair( self, uci_wrapper, conn ): @@ -458,16 +464,24 @@ a_key = uci.credentials.access_key s_key = uci.credentials.secret_key # Get connection - conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key ) + region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint ) + conn = EC2Connection( aws_access_key_id=a_key, + aws_secret_access_key=s_key, + is_secure=uci.credentials.provider.is_secure, + region=region, + path=uci.credentials.provider.path ) # Get reservations handle for given instance rl= conn.get_all_instances( [inst.instance_id] ) # Because EPC deletes references to reservations after a short while after instances have terminated, getting an empty list as a response to a query - # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. As a result, below code simply - # marks given instance as having terminated. Note that an instance might have also crashed and this code will not catch the difference... + # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. Until alternative solution + # is found, below code sets state of given UCI to 'error' to indicate to the user something out of ordinary happened. if len( rl ) == 0: log.info( "Instance ID '%s' was not found by the cloud provider. Instance might have crashed or otherwise been terminated." % inst.instance_id ) + inst.error = "Instance ID was not found by the cloud provider. Instance might have crashed or otherwise been terminated. State set to 'terminated'." + uci.error = "Instance ID '"+inst.instance_id+"' was not found by the cloud provider. Instance might have crashed or otherwise been terminated."+ \ + "Manual check is recommended." inst.state = instance_states.TERMINATED - uci.state = uci_states.AVAILABLE + uci.state = uci_states.ERROR uci.launch_time = None inst.flush() uci.flush() @@ -479,11 +493,13 @@ if s != inst.state: inst.state = s inst.flush() - if s == instance_states.TERMINATED: # After instance has shut down, ensure UCI is marked as 'available' + # After instance has shut down, ensure UCI is marked as 'available' + if s == instance_states.TERMINATED and uci.state != uci_states.ERROR: uci.state = uci_states.AVAILABLE + uci.launch_time = None uci.flush() + # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed. if s != uci.state and s != instance_states.TERMINATED: - # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed. uci.state = s uci.flush() if cInst.public_dns_name != inst.public_dns: @@ -501,7 +517,12 @@ a_key = uci.credentials.access_key s_key = uci.credentials.secret_key # Get connection - conn = EC2Connection( aws_access_key_id=a_key, aws_secret_access_key=s_key ) + region = RegionInfo( None, uci.credentials.provider.region_name, uci.credentials.provider.region_endpoint ) + conn = EC2Connection( aws_access_key_id=a_key, + aws_secret_access_key=s_key, + is_secure=uci.credentials.provider.is_secure, + region=region, + path=uci.credentials.provider.path ) # Get reservations handle for given store vl = conn.get_all_volumes( [store.volume_id] ) # log.debug( "Store '%s' vl: '%s'" % ( store.volume_id, vl ) ) diff -r 6632a5d39f41 -r 802b761c5032 lib/galaxy/cloud/providers/eucalyptus.py --- a/lib/galaxy/cloud/providers/eucalyptus.py Mon Oct 26 17:04:47 2009 -0400 +++ b/lib/galaxy/cloud/providers/eucalyptus.py Tue Oct 27 15:32:44 2009 -0400 @@ -443,12 +443,15 @@ # Get reservations handle for given instance rl= conn.get_all_instances( [inst.instance_id] ) # Because EPC deletes references to reservations after a short while after instances have terminated, getting an empty list as a response to a query - # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. As a result, below code simply - # marks given instance as having terminated. Note that an instance might have also crashed and this code will not catch the difference... + # typically means the instance has successfully shut down but the check was not performed in short enough amount of time. Until alternative solution + # is found, below code sets state of given UCI to 'error' to indicate to the user something out of ordinary happened. if len( rl ) == 0: log.info( "Instance ID '%s' was not found by the cloud provider. Instance might have crashed or otherwise been terminated." % inst.instance_id ) + inst.error = "Instance ID was not found by the cloud provider. Instance might have crashed or otherwise been terminated. State set to 'terminated'." + uci.error = "Instance ID '"+inst.instance_id+"' was not found by the cloud provider. Instance might have crashed or otherwise been terminated."+ \ + "Manual check is recommended." inst.state = instance_states.TERMINATED - uci.state = uci_states.AVAILABLE + uci.state = uci_states.ERROR uci.launch_time = None inst.flush() uci.flush() @@ -460,11 +463,13 @@ if s != inst.state: inst.state = s inst.flush() - if s == instance_states.TERMINATED: # After instance has shut down, ensure UCI is marked as 'available' + # After instance has shut down, ensure UCI is marked as 'available' + if s == instance_states.TERMINATED and uci.state != uci_states.ERROR: uci.state = uci_states.AVAILABLE + uci.launch_time = None uci.flush() + # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed. if s != uci.state and s != instance_states.TERMINATED: - # Making sure state of UCI is updated. Once multiple instances become associated with single UCI, this will need to be changed. uci.state = s uci.flush() if cInst.public_dns_name != inst.public_dns: diff -r 6632a5d39f41 -r 802b761c5032 lib/galaxy/web/controllers/cloud.py --- a/lib/galaxy/web/controllers/cloud.py Mon Oct 26 17:04:47 2009 -0400 +++ b/lib/galaxy/web/controllers/cloud.py Tue Oct 27 15:32:44 2009 -0400 @@ -273,11 +273,10 @@ inst_error = vol_error = cred_error = None error = {} user = trans.get_user() - # TODO: Hack until present user w/ bullet list w/ registered credentials storedCreds = trans.sa_session.query( model.CloudUserCredentials ).filter_by( user=user ).all() if len( storedCreds ) == 0: return trans.show_error_message( "You must register credentials before configuring a Galaxy instance." ) - + # Create dict mapping of cloud providers to zones available by those providers providersToZones = {} for storedCred in storedCreds: if storedCred.provider.type == 'ec2': @@ -413,6 +412,21 @@ return trans.show_form( web.FormBuilder( url_for( id=trans.security.encode_id(instance.id) ), "Rename instance", submit_text="Rename" ) .add_text( "new_name", "Instance name", value=instance.name ) ) + + @web.expose + @web.require_login( "use Galaxy cloud" ) + def set_uci_state( self, trans, id, state='available', clear_error=True ): + """ + Sets state of UCI to given state, optionally resets error field, and resets UCI's launch time field to 'None'. + """ + uci = get_uci( trans, id ) + uci.state = state + if clear_error: + uci.error = None + uci.launch_time = None + trans.sa_session.flush() + trans.set_message( "Instance '%s' state reset." % uci.name ) + return self.list( trans ) @web.expose @web.require_login( "add credentials" ) @@ -527,42 +541,94 @@ proxy_user='', proxy_pass='', debug='', https_connection_factory='', path='' ): user = trans.get_user() error = {} + try: + is_secure = int(is_secure) + except ValueError: + pass # Check if Amazon EC2 has already been registered by this user ec2_registered = trans.sa_session.query( model.CloudProvider ).filter_by( user=user, type='ec2' ).first() if region_name or region_endpoint or name or is_secure or port or proxy or debug or path: + log.debug (" in if ") if trans.app.model.CloudProvider \ .filter_by (user=user, name=name) \ .first(): + log.debug (" in if 2 ") error['name_error'] = "A provider with that name already exist." elif name=='' or len( name ) > 255: + log.debug (" in if 3") error['name_error'] = "Provider name must be between 1 and 255 characters long." elif type=='': + log.debug (" in if 4") error['type_error'] = "Provider type must be selected." elif ec2_registered: + log.debug (" in if 5") error['type_error'] = "Amazon EC2 has already been registered as a provider." - elif is_secure!=0 or is_secure!=1: - error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'." + elif not (is_secure == 0 or is_secure == 1): + log.debug (" in if 6") + error['is_secure_error'] = "Field 'is secure' can only take on a value '0' or '1'" else: + log.debug (" in else ") provider = model.CloudProvider() provider.user = user provider.type = type provider.name = name - provider.region_name = region_name - provider.region_endpoint = region_endpoint + if region_name: + provider.region_name = region_name + else: + provider.region_name = None + + if region_endpoint: + provider.region_endpoint = region_endpoint + else: + provider.region_endpoint = None + if is_secure=='0': provider.is_secure = False else: provider.is_secure = True - provider.host = host - provider.port = port - provider.proxy = proxy - provider.proxy_port = proxy_port - provider.proxy_user = proxy_user - provider.proxy_pass = proxy_pass - provider.debug = debug - provider.https_connection_factory = https_connection_factory + + if host: + provider.host = host + else: + provider.host = None + + if port: + provider.port = port + else: + provider.port = None + + if proxy: + provider.proxy = proxy + else: + provider.proxy = None + + if proxy_port: + provider.proxy_port = proxy_port + else: + provider.proxy_port = None + + if proxy_user: + provider.proxy_user = proxy_user + else: + provider.proxy_user = None + + if proxy_pass: + provider.proxy_pass = proxy_pass + else: + provider.proxy_pass = None + + if debug: + provider.debug = debug + else: + provider.debug = None + + if https_connection_factory: + provider.https_connection_factory = https_connection_factory + else: + provider.https_connection_factory = None + provider.path = path # Persist session = trans.sa_session @@ -601,456 +667,21 @@ .filter_by( user=user, type='ec2' ).first() if not exists: - provider = model.CloudProvider() - provider.user = user - provider.type = 'ec2' - provider.name = 'EC2' - # Persist - session = trans.sa_session - session.save_or_update( provider ) - session.flush() + self.add_provider( trans, name='Amazon EC2', type='ec2', region_name='us-east-1', region_endpoint='us-east-1.ec2.amazonaws.com', is_secure=1, path='/' ) + return self.add( trans ) +# providers = trans.sa_session.query( model.CloudProvider ).filter_by( user=user ).all() +# return trans.fill_template( "cloud/add_credentials.mako", +# credName = '', +# providerName = '', +# accessKey = '', +# secretKey = '', +# error = {}, +# providers = providers +# ) + trans.show_error_message( "EC2 is already registered as a cloud provider under name '%s'." % exists.name ) return self.list( trans ) - @web.expose - @web.require_login( "edit workflows" ) - def editor( self, trans, id=None ): - """ - Render the main workflow editor interface. The canvas is embedded as - an iframe (neccesary for scrolling to work properly), which is - rendered by `editor_canvas`. - """ - if not id: - error( "Invalid workflow id" ) - id = trans.security.decode_id( id ) - return trans.fill_template( "workflow/editor.mako", workflow_id=id ) - - @web.json - def editor_form_post( self, trans, type='tool', tool_id=None, **incoming ): - """ - Accepts a tool state and incoming values, and generates a new tool - form and some additional information, packed into a json dictionary. - This is used for the form shown in the right pane when a node - is selected. - """ - trans.workflow_building_mode = True - module = module_factory.from_dict( trans, { - 'type': type, - 'tool_id': tool_id, - 'tool_state': incoming.pop("tool_state") - } ) - module.update_state( incoming ) - return { - 'tool_state': module.get_state(), - 'data_inputs': module.get_data_inputs(), - 'data_outputs': module.get_data_outputs(), - 'tool_errors': module.get_errors(), - 'form_html': module.get_config_form() - } - - @web.json - def get_new_module_info( self, trans, type, **kwargs ): - """ - Get the info for a new instance of a module initialized with default - paramters (any keyword arguments will be passed along to the module). - Result includes data inputs and outputs, html representation - of the initial form, and the initial tool state (with default values). - This is called asynchronously whenever a new node is added. - """ - trans.workflow_building_mode = True - module = module_factory.new( trans, type, **kwargs ) - return { - 'type': module.type, - 'name': module.get_name(), - 'tool_id': module.get_tool_id(), - 'tool_state': module.get_state(), - 'data_inputs': module.get_data_inputs(), - 'data_outputs': module.get_data_outputs(), - 'form_html': module.get_config_form() - } - - @web.json - def load_workflow( self, trans, id ): - """ - Get the latest Workflow for the StoredWorkflow identified by `id` and - encode it as a json string that can be read by the workflow editor - web interface. - """ - user = trans.get_user() - id = trans.security.decode_id( id ) - trans.workflow_building_mode = True - # Load encoded workflow from database - stored = trans.sa_session.query( model.StoredWorkflow ).get( id ) - assert stored.user == user - workflow = stored.latest_workflow - # Pack workflow data into a dictionary and return - data = {} - data['name'] = workflow.name - data['steps'] = {} - data['upgrade_messages'] = {} - # For each step, rebuild the form and encode the state - for step in workflow.steps: - # Load from database representation - module = module_factory.from_workflow_step( trans, step ) - # Fix any missing parameters - upgrade_message = module.check_and_update_state() - if upgrade_message: - data['upgrade_messages'][step.order_index] = upgrade_message - # Pack atrributes into plain dictionary - step_dict = { - 'id': step.order_index, - 'type': module.type, - 'tool_id': module.get_tool_id(), - 'name': module.get_name(), - 'tool_state': module.get_state(), - 'tool_errors': module.get_errors(), - 'data_inputs': module.get_data_inputs(), - 'data_outputs': module.get_data_outputs(), - 'form_html': module.get_config_form(), - } - # Connections - input_conn_dict = {} - for conn in step.input_connections: - input_conn_dict[ conn.input_name ] = \ - dict( id=conn.output_step.order_index, output_name=conn.output_name ) - step_dict['input_connections'] = input_conn_dict - # Position - step_dict['position'] = step.position - # Add to return value - data['steps'][step.order_index] = step_dict - print data['upgrade_messages'] - return data - - @web.json - def save_workflow( self, trans, id, workflow_data ): - """ - Save the workflow described by `workflow_data` with id `id`. - """ - # Get the stored workflow - stored = get_stored_workflow( trans, id ) - # Put parameters in workflow mode - trans.workflow_building_mode = True - # Convert incoming workflow data from json - data = simplejson.loads( workflow_data ) - # Create new workflow from incoming data - workflow = model.Workflow() - # Just keep the last name (user can rename later) - workflow.name = stored.name - # Assume no errors until we find a step that has some - workflow.has_errors = False - # Create each step - steps = [] - # The editor will provide ids for each step that we don't need to save, - # but do need to use to make connections - steps_by_external_id = {} - # First pass to build step objects and populate basic values - for key, step_dict in data['steps'].iteritems(): - # Create the model class for the step - step = model.WorkflowStep() - steps.append( step ) - steps_by_external_id[ step_dict['id' ] ] = step - # FIXME: Position should be handled inside module - step.position = step_dict['position'] - module = module_factory.from_dict( trans, step_dict ) - module.save_to_step( step ) - if step.tool_errors: - workflow.has_errors = True - # Stick this in the step temporarily - step.temp_input_connections = step_dict['input_connections'] - # Second pass to deal with connections between steps - for step in steps: - # Input connections - for input_name, conn_dict in step.temp_input_connections.iteritems(): - if conn_dict: - conn = model.WorkflowStepConnection() - conn.input_step = step - conn.input_name = input_name - conn.output_name = conn_dict['output_name'] - conn.output_step = steps_by_external_id[ conn_dict['id'] ] - del step.temp_input_connections - # Order the steps if possible - attach_ordered_steps( workflow, steps ) - # Connect up - workflow.stored_workflow = stored - stored.latest_workflow = workflow - # Persist - trans.sa_session.flush() - # Return something informative - errors = [] - if workflow.has_errors: - errors.append( "Some steps in this workflow have validation errors" ) - if workflow.has_cycles: - errors.append( "This workflow contains cycles" ) - if errors: - rval = dict( message="Workflow saved, but will not be runnable due to the following errors", - errors=errors ) - else: - rval = dict( message="Workflow saved" ) - rval['name'] = workflow.name - return rval - - @web.json - def get_datatypes( self, trans ): - ext_to_class_name = dict() - classes = [] - for k, v in trans.app.datatypes_registry.datatypes_by_extension.iteritems(): - c = v.__class__ - ext_to_class_name[k] = c.__module__ + "." + c.__name__ - classes.append( c ) - class_to_classes = dict() - def visit_bases( types, cls ): - for base in cls.__bases__: - if issubclass( base, Data ): - types.add( base.__module__ + "." + base.__name__ ) - visit_bases( types, base ) - for c in classes: - n = c.__module__ + "." + c.__name__ - types = set( [ n ] ) - visit_bases( types, c ) - class_to_classes[ n ] = dict( ( t, True ) for t in types ) - return dict( ext_to_class_name=ext_to_class_name, class_to_classes=class_to_classes ) - - @web.expose - def build_from_current_history( self, trans, job_ids=None, dataset_ids=None, workflow_name=None ): - user = trans.get_user() - history = trans.get_history() - if not user: - return trans.show_error_message( "Must be logged in to create workflows" ) - if ( job_ids is None and dataset_ids is None ) or workflow_name is None: - jobs, warnings = get_job_dict( trans ) - # Render - return trans.fill_template( - "workflow/build_from_current_history.mako", - jobs=jobs, - warnings=warnings, - history=history ) - else: - # Ensure job_ids and dataset_ids are lists (possibly empty) - if job_ids is None: - job_ids = [] - elif type( job_ids ) is not list: - job_ids = [ job_ids ] - if dataset_ids is None: - dataset_ids = [] - elif type( dataset_ids ) is not list: - dataset_ids = [ dataset_ids ] - # Convert both sets of ids to integers - job_ids = [ int( id ) for id in job_ids ] - dataset_ids = [ int( id ) for id in dataset_ids ] - # Find each job, for security we (implicately) check that they are - # associated witha job in the current history. - jobs, warnings = get_job_dict( trans ) - jobs_by_id = dict( ( job.id, job ) for job in jobs.keys() ) - steps = [] - steps_by_job_id = {} - hid_to_output_pair = {} - # Input dataset steps - for hid in dataset_ids: - step = model.WorkflowStep() - step.type = 'data_input' - hid_to_output_pair[ hid ] = ( step, 'output' ) - steps.append( step ) - # Tool steps - for job_id in job_ids: - assert job_id in jobs_by_id, "Attempt to create workflow with job not connected to current history" - job = jobs_by_id[ job_id ] - tool = trans.app.toolbox.tools_by_id[ job.tool_id ] - param_values = job.get_param_values( trans.app ) - associations = cleanup_param_values( tool.inputs, param_values ) - step = model.WorkflowStep() - step.type = 'tool' - step.tool_id = job.tool_id - step.tool_inputs = tool.params_to_strings( param_values, trans.app ) - # NOTE: We shouldn't need to do two passes here since only - # an earlier job can be used as an input to a later - # job. - for other_hid, input_name in associations: - if other_hid in hid_to_output_pair: - other_step, other_name = hid_to_output_pair[ other_hid ] - conn = model.WorkflowStepConnection() - conn.input_step = step - conn.input_name = input_name - # Should always be connected to an earlier step - conn.output_step = other_step - conn.output_name = other_name - steps.append( step ) - steps_by_job_id[ job_id ] = step - # Store created dataset hids - for assoc in job.output_datasets: - hid_to_output_pair[ assoc.dataset.hid ] = ( step, assoc.name ) - # Workflow to populate - workflow = model.Workflow() - workflow.name = workflow_name - # Order the steps if possible - attach_ordered_steps( workflow, steps ) - # And let's try to set up some reasonable locations on the canvas - # (these are pretty arbitrary values) - levorder = order_workflow_steps_with_levels( steps ) - base_pos = 10 - for i, steps_at_level in enumerate( levorder ): - for j, index in enumerate( steps_at_level ): - step = steps[ index ] - step.position = dict( top = ( base_pos + 120 * j ), - left = ( base_pos + 220 * i ) ) - # Store it - stored = model.StoredWorkflow() - stored.user = user - stored.name = workflow_name - workflow.stored_workflow = stored - stored.latest_workflow = workflow - trans.sa_session.save_or_update( stored ) - trans.sa_session.flush() - # Index page with message - return trans.show_message( "Workflow '%s' created from current history." % workflow_name ) - ## return trans.show_ok_message( "<p>Workflow '%s' created.</p><p><a target='_top' href='%s'>Click to load in workflow editor</a></p>" - ## % ( workflow_name, web.url_for( action='editor', id=trans.security.encode_id(stored.id) ) ) ) - - @web.expose - def run( self, trans, id, check_user=True, **kwargs ): - stored = get_stored_workflow( trans, id, check_ownership=False ) - if check_user: - user = trans.get_user() - if stored.user != user: - if trans.sa_session.query( model.StoredWorkflowUserShareAssociation ) \ - .filter_by( user=user, stored_workflow=stored ).count() == 0: - error( "Workflow is not owned by or shared with current user" ) - # Get the latest revision - workflow = stored.latest_workflow - # It is possible for a workflow to have 0 steps - if len( workflow.steps ) == 0: - error( "Workflow cannot be run because it does not have any steps" ) - #workflow = Workflow.from_simple( simplejson.loads( stored.encoded_value ), trans.app ) - if workflow.has_cycles: - error( "Workflow cannot be run because it contains cycles" ) - if workflow.has_errors: - error( "Workflow cannot be run because of validation errors in some steps" ) - # Build the state for each step - errors = {} - if kwargs: - # If kwargs were provided, the states for each step should have - # been POSTed - for step in workflow.steps: - # Connections by input name - step.input_connections_by_name = \ - dict( ( conn.input_name, conn ) for conn in step.input_connections ) - # Extract just the arguments for this step by prefix - p = "%s|" % step.id - l = len(p) - step_args = dict( ( k[l:], v ) for ( k, v ) in kwargs.iteritems() if k.startswith( p ) ) - step_errors = None - if step.type == 'tool' or step.type is None: - module = module_factory.from_workflow_step( trans, step ) - # Any connected input needs to have value DummyDataset (these - # are not persisted so we need to do it every time) - module.add_dummy_datasets( connections=step.input_connections ) - # Get the tool - tool = module.tool - # Get the state - step.state = state = module.state - # Get old errors - old_errors = state.inputs.pop( "__errors__", {} ) - # Update the state - step_errors = tool.update_state( trans, tool.inputs, step.state.inputs, step_args, - update_only=True, old_errors=old_errors ) - else: - module = step.module = module_factory.from_workflow_step( trans, step ) - state = step.state = module.decode_runtime_state( trans, step_args.pop( "tool_state" ) ) - step_errors = module.update_runtime_state( trans, state, step_args ) - if step_errors: - errors[step.id] = state.inputs["__errors__"] = step_errors - if 'run_workflow' in kwargs and not errors: - # Run each step, connecting outputs to inputs - outputs = odict() - for i, step in enumerate( workflow.steps ): - if step.type == 'tool' or step.type is None: - tool = trans.app.toolbox.tools_by_id[ step.tool_id ] - input_values = step.state.inputs - # Connect up - def callback( input, value, prefixed_name, prefixed_label ): - if isinstance( input, DataToolParameter ): - if prefixed_name in step.input_connections_by_name: - conn = step.input_connections_by_name[ prefixed_name ] - return outputs[ conn.output_step.id ][ conn.output_name ] - visit_input_values( tool.inputs, step.state.inputs, callback ) - # Execute it - outputs[ step.id ] = tool.execute( trans, step.state.inputs ) - else: - outputs[ step.id ] = step.module.execute( trans, step.state ) - - return trans.fill_template( "workflow/run_complete.mako", - workflow=stored, - outputs=outputs ) - else: - for step in workflow.steps: - if step.type == 'tool' or step.type is None: - # Restore the tool state for the step - module = module_factory.from_workflow_step( trans, step ) - # Any connected input needs to have value DummyDataset (these - # are not persisted so we need to do it every time) - module.add_dummy_datasets( connections=step.input_connections ) - # Store state with the step - step.module = module - step.state = module.state - # Error dict - if step.tool_errors: - errors[step.id] = step.tool_errors - else: - ## Non-tool specific stuff? - step.module = module_factory.from_workflow_step( trans, step ) - step.state = step.module.get_runtime_state() - # Connections by input name - step.input_connections_by_name = dict( ( conn.input_name, conn ) for conn in step.input_connections ) - # Render the form - return trans.fill_template( - "workflow/run.mako", - steps=workflow.steps, - workflow=stored, - errors=errors, - incoming=kwargs ) - - @web.expose - def configure_menu( self, trans, workflow_ids=None ): - user = trans.get_user() - if trans.request.method == "POST": - if workflow_ids is None: - workflow_ids = [] - elif type( workflow_ids ) != list: - workflow_ids = [ workflow_ids ] - sess = trans.sa_session - # This explicit remove seems like a hack, need to figure out - # how to make the association do it automatically. - for m in user.stored_workflow_menu_entries: - sess.delete( m ) - user.stored_workflow_menu_entries = [] - q = sess.query( model.StoredWorkflow ) - # To ensure id list is unique - seen_workflow_ids = set() - for id in workflow_ids: - if id in seen_workflow_ids: - continue - else: - seen_workflow_ids.add( id ) - m = model.StoredWorkflowMenuEntry() - m.stored_workflow = q.get( id ) - user.stored_workflow_menu_entries.append( m ) - sess.flush() - return trans.show_message( "Menu updated", refresh_frames=['tools'] ) - else: - user = trans.get_user() - ids_in_menu = set( [ x.stored_workflow_id for x in user.stored_workflow_menu_entries ] ) - workflows = trans.sa_session.query( model.StoredWorkflow ) \ - .filter_by( user=user, deleted=False ) \ - .order_by( desc( model.StoredWorkflow.c.update_time ) ) \ - .all() - shared_by_others = trans.sa_session \ - .query( model.StoredWorkflowUserShareAssociation ) \ - .filter_by( user=user ) \ - .filter( model.StoredWorkflow.c.deleted == False ) \ - .all() - return trans.fill_template( "workflow/configure_menu.mako", - workflows=workflows, - shared_by_others=shared_by_others, - ids_in_menu=ids_in_menu ) @web.json def json_update( self, trans ): user = trans.get_user() diff -r 6632a5d39f41 -r 802b761c5032 templates/cloud/add_credentials.mako --- a/templates/cloud/add_credentials.mako Mon Oct 26 17:04:47 2009 -0400 +++ b/templates/cloud/add_credentials.mako Tue Oct 27 15:32:44 2009 -0400 @@ -103,9 +103,9 @@ </div> %else: - In order to add credentials, desired cloud provider needs to be registered.<p/> + In order to add credentials, desired cloud provider needs to be registered first.<p/> Register <a href="${h.url_for( action='add_ec2' )}"> - <span>Amazon EC2 automatically</span></a> + <span>Amazon EC2 (us-east-1 region) automatically</span></a> or add <a href="${h.url_for( action='add_provider' )}"> <span>custom cloud provider</span></a>. diff -r 6632a5d39f41 -r 802b761c5032 templates/cloud/add_provider.mako --- a/templates/cloud/add_provider.mako Mon Oct 26 17:04:47 2009 -0400 +++ b/templates/cloud/add_provider.mako Tue Oct 27 15:32:44 2009 -0400 @@ -11,7 +11,19 @@ $("#type").change(function() { if ($(this).val() == 'ec2') { - $("#name").val("EC2"); + clear(); + $("#autofill").attr( 'disabled', true ); + $("#autofill").attr( 'checked', false ); + $("#name").val( "EC2" ); + $("#region_name").val( "us-east-1" ); + $("#region_endpoint").val( "us-east-1.ec2.amazonaws.com" ); + $("#is_secure").val("1"); + $("#debug").val(""); + $("#path").val("/"); + } + else if ($(this).val() == 'eucalyptus') { + clear(); + $("#autofill").attr( 'disabled', false ); } }); }) @@ -27,12 +39,23 @@ $("#path").val("/services/Eucalyptus"); } else { - $("#region_name").val(""); - $("#region_endpoint").val(""); - $("#is_secure").val(""); - $("#port").val(""); - $("#path").val(""); + clear(); } +} + +function clear() { + $("#name").val(""); + $("#region_name").val(""); + $("#region_endpoint").val(""); + $("#is_secure").val(""); + $("#port").val(""); + $("#proxy").val(""); + $("#proxy_port").val(""); + $("#proxy_user").val(""); + $("#proxy_pass").val(""); + $("#debug").val(""); + $("#https_connection_factory").val(""); + $("#path").val(""); } @@ -60,8 +83,9 @@ <option value="eucalyptus">Eucalyptus</option> <option value="ec2">Amazon EC2</option> </select> - <br/><input type="checkbox" id="autofill" onclick="javascript:af()"> - auto fill using Eucalyptus Public Cloud values + <br/> + <input type="checkbox" id="autofill" onclick="javascript:af()" disabled="true"> + auto fill using Eucalyptus Public Cloud values </div> %if error.has_key('type_error'): <div class="form-row-error-message">${error['type_error']}</div> @@ -90,9 +114,12 @@ %> <div class="${cls}"> <label>Region name:</label> - <div class="form-row-input"> + <div id="region_selection" class="form-row-input"> <input type="text" name="region_name" id="region_name" value="${region_name}" size="40"> </div> + %if error.has_key('name_error'): + <div class="form-row-error-message">${error['name_error']}</div> + %endif <div style="clear: both"></div> </div> @@ -107,14 +134,19 @@ <div style="clear: both"></div> </div> - <% + <% cls = "form-row" + if error.has_key('is_secure_error'): + cls += " form-row-error" %> <div class="${cls}"> <label>Is secure ('O' for False or '1' for True):</label> <div class="form-row-input"> <input type="text" name="is_secure" id="is_secure" value="${is_secure}" size="40"> </div> + %if error.has_key('is_secure_error'): + <div class="form-row-error-message">${error['is_secure_error']}; you entered: '${is_secure}'</div> + %endif <div style="clear: both"></div> </div> diff -r 6632a5d39f41 -r 802b761c5032 templates/cloud/configure_cloud.mako --- a/templates/cloud/configure_cloud.mako Mon Oct 26 17:04:47 2009 -0400 +++ b/templates/cloud/configure_cloud.mako Tue Oct 27 15:32:44 2009 -0400 @@ -35,24 +35,29 @@ } else if ( old_state=='shutting-down' && new_state=='available' ) { location.reload(true); + } + else if ( old_state=='running' && new_state=='available' ) { + location.reload(true); + } + else if ( old_state=='running' && new_state=='error' ) { + location.reload(true); + } + else if ( old_state=='pending' && new_state=='error' ) { + location.reload(true); + } + else if ( old_state=='pending' && new_state=='available' ) { + location.reload(true); } else if ( new_state=='shutting-down' || new_state=='shutting-downUCI' ) { $(elem + "-link").text( "" ); } - xmlhttp = new XMLHttpRequest(); - xmlhttp.open( "HEAD", "http://127.0.0.1:8080/admin", false ); - xmlhttp.send( null ); - //alert(xmlhttp.getAllResponseHeaders()) - console.log( "xmlhttp.readyState: %s", xmlhttp.readyState ); - console.log( "xmlhttp.status: %s", xmlhttp.status ); - if ( new_state=='running' && xmlhttp.readyState==1 ) { - console.log ("in ready statsus = 1"); - //if (xmlhttp.status==200) { - // console.log( "in status = 200" ); - // location.reload(true); - //} - } - + // In order to show 'Access Galaxy' button, the whole page needs to be refreshed. So, while Galaxy is starting, + // keep refreshing the page. Should be handled better... + else if ( $(elem+"-link").text().search('starting') && old_state=='running' && new_state=='running' ) { + //location.reload(true); + $(elem + "-link").text("Still starting..."); + } + // Update 'state' and 'time alive' fields $(elem + "-state").text( data[i].state ); if (data[i].launch_time) { @@ -184,7 +189,7 @@ context.write( '<span>Access Galaxy</span>' ) context.write( '<img src="'+h.url_for('/static/images/silk/resultset_next.png')+'" /></a></div>' ) except urllib2.URLError: - context.write( '<span>Galaxy starting...</span>' ) + context.write( 'Galaxy starting...' ) %> %endif @@ -247,8 +252,10 @@ <a onclick="document.getElementById('short').style.display = 'block'; document.getElementById('full').style.display = 'none'; return 0;" href="javascript:void(0)"> - ${str(prevInstance.error)} - </a> + error:</a><br /> + ${str(prevInstance.error)} + <p /> + <a href="${h.url_for( action='set_uci_state', id=trans.security.encode_id(prevInstance.id), state='available' )}">reset state</a> </div> %else: ${str(prevInstance.state)} @@ -278,7 +285,7 @@ You have no AWS credentials associated with your Galaxy account: <a class="action-button" href="${h.url_for( action='add' )}"> <img src="${h.url_for('/static/images/silk/add.png')}" /> - <span>Add AWS credentials</span> + <span>add credentials</span> </a> or <a href="http://aws.amazon.com/" target="_blank">