blob: 811c864917e3506a4f28e7418c1f0eb5b1f291a9 [file] [log] [blame]
<?php
use Airavata\API\Error\AuthorizationException;
use Airavata\API\Error\ExperimentNotFoundException;
use Airavata\Model\Status\JobState;
class ExperimentController extends BaseController
{
/**
* Limit used in fetching paginated results
* @var int
*/
var $limit = 20;
/**
* Instantiate a new ExperimentController Instance
**/
public function __construct()
{
$this->beforeFilter('verifylogin');
$this->beforeFilter('verifyauthorizeduser');
Session::put("nav-active", "experiment");
}
public function createView()
{
Session::forget('exp_create_app_id');
Session::forget('exp_create_continue');
return View::make('experiment/create');
}
public function createSubmit()
{
if (isset($_POST['continue'])) {
Session::put('exp_create_continue', true);
$appInterfaces = AppUtilities::get_all_applications();
foreach($appInterfaces as $id=>$name){
if($id == $_POST['application']){
Session::put('exp_create_app_id', AppUtilities::get_application_interface($id)->applicationModules[0]);
}
}
$computeResources = CRUtilities::create_compute_resources_select($_POST['application'], null);
$nodeCount = Config::get('pga_config.airavata')["node-count"];
$cpuCount = Config::get('pga_config.airavata')["total-cpu-count"];
$wallTimeLimit = Config::get('pga_config.airavata')["wall-time-limit"];
$queueDefaults = array("queueName" => Config::get('pga_config.airavata')["queue-name"],
"nodeCount" => $nodeCount,
"cpuCount" => $cpuCount,
"wallTimeLimit" => $wallTimeLimit
);
$experimentInputs = array(
"disabled" => ' disabled',
"experimentName" => $_POST['experiment-name'],
"experimentDescription" => $_POST['experiment-description'] . ' ',
"project" => $_POST['project'],
"application" => $_POST['application'],
"echo" => ($_POST['application'] == 'Echo') ? ' selected' : '',
"wrf" => ($_POST['application'] == 'WRF') ? ' selected' : '',
"queueDefaults" => $queueDefaults,
"advancedOptions" => Config::get('pga_config.airavata')["advanced-experiment-options"],
"computeResources" => $computeResources,
"resourceHostId" => null,
"advancedOptions" => Config::get('pga_config.airavata')["advanced-experiment-options"],
"allowedFileSize" => $this->getAllowedFileSize()
);
if(Config::get('pga_config.airavata')["data-sharing-enabled"]){
$users = SharingUtilities::getProfilesForSharedUsers($_POST['project']);
$owner = array();
$projectOwner = array();
$sharedProjectOwner = SharingUtilities::getSharedResourceOwner($_POST['project']);
if (Session::get("username") !== $sharedProjectOwner) {
$projectOwner[$sharedProjectOwner] = $users[$sharedProjectOwner];
$users = array_diff_key($users, $projectOwner);
}
return View::make("experiment/create-complete", array("expInputs" => $experimentInputs,
"users" => json_encode($users), "owner" => json_encode($owner),
"canEditSharing" => true,
"projectOwner" => json_encode($projectOwner),
"updateSharingViaAjax" => false));
}else{
return View::make("experiment/no-sharing-create-complete", array("expInputs" => $experimentInputs));
}
} else if (isset($_POST['save']) || isset($_POST['launch'])) {
try {
$computeResourceId = Input::get("compute-resource");
//Validate entered queue details
$queueValues = array("queueName" => Input::get("queue-name"),
"nodeCount" => Input::get("node-count"),
"cpuCount" => Input::get("cpu-count"),
"wallTimeLimit" => Input::get("walltime-count")
);
if($this->validateQueueData($computeResourceId, $queueValues))
$expId = ExperimentUtilities::create_experiment();
else
return Redirect::to("experiment/create")->with("error-message", "Validate the number of nodes, CPUs and the wall time limit");
} catch (Exception $ex) {
Log::error("Failed to create experiment!");
Log::error($ex);
return Redirect::to("experiment/create")->with("error-message", "Failed to create experiment: " . $ex->getMessage());
}
if (isset($_POST['launch']) && $expId) {
ExperimentUtilities::launch_experiment($expId);
}
/* Not required.
else
{
CommonUtilities::print_success_message("<p>Experiment {$_POST['experiment-name']} created!</p>" .
'<p>You will be redirected to the summary page shortly, or you can
<a href=' . URL::to('/') . '"/experiment/summary?expId=' . $expId . '">go directly</a> to experiment summary page.</p>');
}*/
return Redirect::to('experiment/summary?expId=' . urlencode($expId));
} else
return Redirect::to("home")->with("message", "Something went wrong here. Please file a bug report using the link in the Help menu.");
}
public function summary()
{
$experiment = null;
try {
$experiment = ExperimentUtilities::get_experiment($_GET['expId']);
} catch (ExperimentNotFoundException $enf) {
Log::error("Experiment wasn't found", array("message" => $enf->getMessage(), "username" => Session::get("username"), "gateway_id" => Session::get("gateway_id")));
return $this->makeInvalidExperimentView();
} catch (AuthorizationException $ae) {
Log::error("User isn't authorized to see experiment", array("message" => $ae->getMessage(), "username" => Session::get("username"), "gateway_id" => Session::get("gateway_id")));
return $this->makeInvalidExperimentView();
}
// Assume that experiment is not null now
if(isset($_GET['isAutoRefresh']) && $_GET['isAutoRefresh'] == 'true'){
$autoRefresh = true;
}else{
$autoRefresh = false;
}
//viewing experiments of other gateways is not allowed if user is not super admin
if( $experiment->gatewayId != Session::get("gateway_id") && !Session::has("super-admin")){
Session::put("permissionDenied", true);
CommonUtilities::print_error_message('It seems that you do not have permissions to view this experiment or it belongs to another gateway.');
if (Input::has("dashboard"))
return View::make("partials/experiment-info", array("invalidExperimentId" => 1, "users" => json_encode(array())));
else
return View::make("experiment/summary", array("invalidExperimentId" => 1, "users" => json_encode(array())));
}
else
Session::forget("permissionDenied");
$project = null;
if(Config::get('pga_config.airavata')["data-sharing-enabled"]){
if (SharingUtilities::userCanRead(Session::get("username"), $experiment->projectId)) {
$project = ProjectUtilities::get_project($experiment->projectId);
}
} elseif ($experiment->userName == Session::get("username")){
// When sharing is disabled the backend checks the auth token claims map
// to make sure the authenticating user is the same as the project
// owner. So the project can only be loaded when the user is the
// project owner, which can be inferred from the experiment's owner.
$project = ProjectUtilities::get_project($experiment->projectId);
}
$expVal = ExperimentUtilities::get_experiment_values($experiment);
$jobDetails = ExperimentUtilities::get_job_details($experiment->experimentId);
// var_dump( $jobDetails); exit;
foreach( $jobDetails as $index => $jobDetail){
if(isset($jobDetail->jobStatuses) and !empty($jobDetail->jobStatuses)){
ExperimentUtilities::latestStatus($jobDetails[$index]->jobStatuses)->jobStateName = JobState::$__names[ExperimentUtilities::latestStatus($jobDetail->jobStatuses)->jobState];
}
else{
$jobDetails[ $index]->jobStatuses = [new stdClass()];
$jobDetails[ $index]->jobStatuses[0]->jobStateName = null;
}
}
$expVal["jobDetails"] = $jobDetails;
$writeableProjects = ProjectUtilities::get_all_user_writeable_projects(Session::get("gateway_id"), Session::get("username"));
$data = array(
"expId" => Input::get("expId"),
"experiment" => $experiment,
"project" => $project,
"jobDetails" => $jobDetails,
"expVal" => $expVal,
"autoRefresh"=> $autoRefresh,
"writeableProjects" => $writeableProjects
);
if(Config::get('pga_config.airavata')["data-sharing-enabled"]){
$users = SharingUtilities::getProfilesForSharedUsers(Input::get("expId"));
$sharedExperimentOwner = SharingUtilities::getSharedResourceOwner($experiment->experimentId);
$sharedProjectOwner = SharingUtilities::getSharedResourceOwner($experiment->projectId);
$owner = array();
$projectOwner = array();
if (Session::get("username") !== $sharedExperimentOwner) {
$owner[$sharedExperimentOwner] = $users[$sharedExperimentOwner];
}
if (Session::get("username") !== $sharedProjectOwner) {
$projectOwner[$sharedProjectOwner] = $users[$sharedProjectOwner];
}
// Subtract out the owner and project owner from list of users
$users = array_diff_key($users, $owner);
$users = array_diff_key($users, $projectOwner);
// If project owner is the same as owner, just show the owner, not the project owner
$projectOwner = array_diff_key($projectOwner, $owner);
// Only allow editing sharing on the summary page if the owner
// and the experiment isn't editable. If the experiment is
// editable, the sharing can be edited on the edit page.
$canEditSharing = (Session::get("username") === $sharedExperimentOwner) && !$expVal["editable"];
$data['can_write'] = SharingUtilities::userCanWrite(Session::get("username"), $experiment->experimentId);
$data["users"] = json_encode($users);
$data["owner"] = json_encode($owner);
$data["projectOwner"] = json_encode($projectOwner);
$data["canEditSharing"] = $canEditSharing;
// The summary page has it's own Update Sharing button
$data["updateSharingViaAjax"] = true;
}
if( Input::has("dashboard"))
{
$detailedExperiment = ExperimentUtilities::get_detailed_experiment( $_GET['expId']);
$data["detailedExperiment"] = $detailedExperiment;
}
if (Request::ajax()) {
//admin wants to see an experiment summary
if (Input::has("dashboard")) {
$data["dashboard"] = true;
return View::make("partials/experiment-info", $data);
} else
return json_encode($data);
} else {
return View::make("experiment/summary", $data);
}
}
private function makeInvalidExperimentView() {
if (Input::has("dashboard"))
return View::make("partials/experiment-info", array("invalidExperimentId" => 1));
else
return View::make("experiment/summary", array("invalidExperimentId" => 1));
}
public function expChange()
{
//var_dump( Input::all() ); exit;
$experiment = ExperimentUtilities::get_experiment(Input::get('expId'));
$expVal = ExperimentUtilities::get_experiment_values($experiment);
$expVal["jobState"] = ExperimentUtilities::get_job_status($experiment);
/*if (isset($_POST['save']))
{
$updatedExperiment = CommonUtilities::apply_changes_to_experiment($experiment);
CommonUtilities::update_experiment($experiment->experimentId, $updatedExperiment);
}*/
if (isset($_POST['launch'])) {
ExperimentUtilities::launch_experiment($experiment->experimentId);
return Redirect::to('experiment/summary?expId=' . urlencode($experiment->experimentId));
} elseif (isset($_POST['cancel'])) {
ExperimentUtilities::cancel_experiment($experiment->experimentId);
return Redirect::to('experiment/summary?expId=' . urlencode($experiment->experimentId));
} elseif (isset($_POST['update-sharing'])) {
if(Config::get('pga_config.airavata')["data-sharing-enabled"]){
$share = $_POST['share-settings'];
ExperimentUtilities::update_experiment_sharing($experiment->experimentId, json_decode($share));
}
return Redirect::to('experiment/summary?expId=' . urlencode($experiment->experimentId));
}
}
public function editView()
{
$experiment = ExperimentUtilities::get_experiment($_GET['expId']);
$expVal = ExperimentUtilities::get_experiment_values($experiment);
$expVal["jobState"] = ExperimentUtilities::get_job_status($experiment);
$appInterfaces = AppUtilities::get_all_applications();
foreach($appInterfaces as $id=>$name) {
if ($id == $experiment->executionId) {
$appId = AppUtilities::get_application_interface($id)->applicationModules[0];
}
}
$nodeCount = Config::get('pga_config.airavata')["node-count"];
$cpuCount = Config::get('pga_config.airavata')["total-cpu-count"];
$wallTimeLimit = Config::get('pga_config.airavata')["wall-time-limit"];
$cpusPerNode = 0;
$queueDefaults = array("queueName" => Config::get('pga_config.airavata')["queue-name"],
"nodeCount" => $nodeCount,
"cpuCount" => $cpuCount,
"wallTimeLimit" => $wallTimeLimit,
"cpusPerNode" => $cpusPerNode
);
$computeResourceId = $experiment->userConfigurationData->computationalResourceScheduling->resourceHostId;
$appDeployments = Airavata::getAllApplicationDeployments(Session::get('authz-token'), Session::get("gateway_id"));
$correctAppDeployment = null;
foreach($appDeployments as $appDeployment){
if($appDeployment->computeHostId == $computeResourceId && $appDeployment->appModuleId == $appId){
$correctAppDeployment = $appDeployment;
break;
}
}
$appDeploymentDefaults = array();
if($correctAppDeployment != null){
$appDeploymentDefaults['nodeCount'] = $correctAppDeployment->defaultNodeCount;
$appDeploymentDefaults['cpuCount'] = $correctAppDeployment->defaultCPUCount;
$appDeploymentDefaults['wallTimeLimit'] = $wallTimeLimit;
$appDeploymentDefaults['queueName'] = $correctAppDeployment->defaultQueueName;
}
$computeResources = CRUtilities::create_compute_resources_select($experiment->executionId, $expVal['scheduling']->resourceHostId);
$userComputeResourcePreferences = URPUtilities::get_all_validated_user_compute_resource_prefs();
$userHasComputeResourcePreference = array_key_exists($expVal['scheduling']->resourceHostId, $userComputeResourcePreferences);
$batchQueues = ExperimentUtilities::getQueueDatafromResourceId($computeResourceId);
$experimentInputs = array(
"disabled" => ' ',
"experimentName" => $experiment->experimentName,
"experimentDescription" => $experiment->description,
"application" => $experiment->executionId,
"autoSchedule" => $experiment->userConfigurationData->airavataAutoSchedule,
"userDN" => $experiment->userConfigurationData->userDN,
"userHasComputeResourcePreference" => $userHasComputeResourcePreference,
"useUserCRPref" => $experiment->userConfigurationData->useUserCRPref,
"allowedFileSize" => $this->getAllowedFileSize(),
'experiment' => $experiment,
"queueDefaults" => $queueDefaults,
'computeResources' => $computeResources,
"resourceHostId" => $expVal['scheduling']->resourceHostId,
'project' => $experiment->projectId,
'expVal' => $expVal,
'cloning' => true,
'advancedOptions' => Config::get('pga_config.airavata')["advanced-experiment-options"],
'batchQueues' => $batchQueues
);
if(Config::get('pga_config.airavata')["data-sharing-enabled"]){
if (SharingUtilities::userCanWrite(Session::get("username"), $_GET['expId']) === true) {
$users = SharingUtilities::getProfilesForSharedUsers($_GET['expId']);
$sharedExperimentOwner = SharingUtilities::getSharedResourceOwner($experiment->experimentId);
$sharedProjectOwner = SharingUtilities::getSharedResourceOwner($experiment->projectId);
$owner = array();
$projectOwner = array();
if (Session::get("username") !== $sharedExperimentOwner) {
$owner[$sharedExperimentOwner] = $users[$sharedExperimentOwner];
}
if (Session::get("username") !== $sharedProjectOwner) {
$projectOwner[$sharedProjectOwner] = $users[$sharedProjectOwner];
}
// Subtract out owner and project owner from list of users
$users = array_diff_key($users, $owner);
$users = array_diff_key($users, $projectOwner);
// If project owner is the same as owner, just show the owner, not the project owner
$projectOwner = array_diff_key($projectOwner, $owner);
$canEditSharing = Session::get("username") === $sharedExperimentOwner;
return View::make("experiment/edit", array("expInputs" => $experimentInputs,
"users" => json_encode($users), "owner" => json_encode($owner),
"canEditSharing" => $canEditSharing,
"projectOwner" => json_encode($projectOwner),
"updateSharingViaAjax" => false,
"cpusPerNode" => $cpusPerNode,
"appDeploymentDefaults" => $appDeploymentDefaults,
"queueDefaults" => $queueDefaults
));
}
else {
Redirect::to("experiment/summary?expId=" . urlencode($experiment->experimentId))->with("error", "You do not have permission to edit this experiment");
}
}else {
return View::make("experiment/no-sharing-edit", array("expInputs" => $experimentInputs));
}
}
public function cloneExperiment()
{
try{
$cloneId = ExperimentUtilities::clone_experiment(Input::get('expId'), Input::get('projectId'));
return Redirect::to('experiment/edit?expId=' . urlencode($cloneId));
}catch (Exception $ex){
Log::error($ex);
return Redirect::to("experiment/summary?expId=" . urlencode(Input::get('expId')))
->with("cloning-error", "Failed to clone experiment: " . $ex->getMessage());
}
}
public function editSubmit()
{
$experiment = ExperimentUtilities::get_experiment(Input::get('expId')); // update local experiment variable
try {
$computeResourceId = Input::get("compute-resource");
//Validate entered queue details
$queueValues = array("queueName" => Input::get("queue-name"),
"nodeCount" => Input::get("node-count"),
"cpuCount" => Input::get("cpu-count"),
"wallTimeLimit" => Input::get("walltime-count")
);
if($this->validateQueueData($computeResourceId, $queueValues)) {
$updatedExperiment = ExperimentUtilities::apply_changes_to_experiment($experiment, Input::all());
} else {
$errMessage = "Validate the number of nodes, CPUs and the wall time limit";
return Redirect::to("experiment/edit?expId=" . urlencode(Input::get('expId')))->with("error-message", $errMessage);
}
} catch (Exception $ex) {
$errMessage = "Failed to update experiment: " . $ex->getMessage();
Log::error($errMessage);
Log::error($ex);
return Redirect::to("experiment/edit?expId=" . urlencode(Input::get('expId')))->with("error-message", $errMessage);
}
if(Config::get('pga_config.airavata')["data-sharing-enabled"]){
if (SharingUtilities::userCanWrite(Session::get("username"), Input::get('expId'))) {
if (isset($_POST['save']) || isset($_POST['launch'])) {
ExperimentUtilities::update_experiment($experiment->experimentId, $updatedExperiment);
if (isset($_POST['save'])) {
$experiment = ExperimentUtilities::get_experiment(Input::get('expId')); // update local experiment variable
}
if (isset($_POST['launch'])) {
ExperimentUtilities::launch_experiment($experiment->experimentId);
}
return Redirect::to('experiment/summary?expId=' . urlencode($experiment->experimentId));
} else
return View::make("home");
}
}else{
if (isset($_POST['save']) || isset($_POST['launch'])) {
ExperimentUtilities::update_experiment($experiment->experimentId, $updatedExperiment);
if (isset($_POST['save'])) {
$experiment = ExperimentUtilities::get_experiment(Input::get('expId')); // update local experiment variable
}
if (isset($_POST['launch'])) {
ExperimentUtilities::launch_experiment($experiment->experimentId);
}
return Redirect::to('experiment/summary?expId=' . urlencode($experiment->experimentId));
} else
return View::make("home");
}
}
public function getQueueView()
{
$appId = Session::get('exp_create_app_id');
$computeResourceId = Input::get("crId");
$appDeployments = Airavata::getAllApplicationDeployments(Session::get('authz-token'), Session::get("gateway_id"));
$nodeCount = Config::get('pga_config.airavata')["node-count"];
$cpuCount = Config::get('pga_config.airavata')["total-cpu-count"];
$wallTimeLimit = Config::get('pga_config.airavata')["wall-time-limit"];
$cpusPerNode = 0;
$queueName = Config::get('pga_config.airavata')["queue-name"];
$queueDefaults = array("queueName" => $queueName,
"nodeCount" => $nodeCount,
"cpuCount" => $cpuCount,
"wallTimeLimit" => $wallTimeLimit,
"cpusPerNode" => $cpusPerNode
);
$queues = ExperimentUtilities::getQueueDatafromResourceId($computeResourceId);
$userComputeResourcePreferences = URPUtilities::get_all_validated_user_compute_resource_prefs();
$userHasComputeResourcePreference = array_key_exists($computeResourceId, $userComputeResourcePreferences);
if ($userHasComputeResourcePreference)
{
$queueDefaults['queueName'] = $userComputeResourcePreferences[$computeResourceId]->preferredBatchQueue;
}else{
foreach($queues as $aQueue){
if($aQueue->isDefaultQueue){
$queueDefaults['queueName'] = $aQueue->queueName;
break;
}
}
}
$correctAppDeployment = null;
foreach($appDeployments as $appDeployment){
if($appDeployment->computeHostId == $computeResourceId && $appDeployment->appModuleId == $appId){
$correctAppDeployment = $appDeployment;
break;
}
}
$appDeploymentDefaults = array();
if($correctAppDeployment != null){
$appDeploymentDefaults['nodeCount'] = $correctAppDeployment->defaultNodeCount;
$appDeploymentDefaults['cpuCount'] = $correctAppDeployment->defaultCPUCount;
$appDeploymentDefaults['queueName'] = $correctAppDeployment->defaultQueueName;
}
return View::make("partials/experiment-queue-block", array("queues" => $queues,
"queueDefaults" => $queueDefaults,
"appDeploymentDefaults" => $appDeploymentDefaults,
"useUserCRPref" => $userHasComputeResourcePreference,
"userHasComputeResourcePreference" => $userHasComputeResourcePreference,
"cpusPerNode" => $cpusPerNode));
}
public function browseView()
{
$pageNo = Input::get('pageNo');
$prev = Input::get('prev');
$isSearch = Input::get('search');
if (empty($pageNo) || isset($isSearch) ) {
$pageNo = 1;
} else {
if (isset($prev)) {
$pageNo -= 1;
} else {
$pageNo += 1;
}
}
$expContainer = ExperimentUtilities::get_expsearch_results_with_pagination(Input::all(), $this->limit,
($pageNo - 1) * $this->limit);
$experimentStates = ExperimentUtilities::getExpStates();
$can_write = array();
foreach ($expContainer as $experiment) {
if(Config::get('pga_config.airavata')["data-sharing-enabled"]){
$can_write[$experiment['experiment']->experimentId] = SharingUtilities::userCanWrite(Session::get("username"),
$experiment['experiment']->experimentId);
} else {
$can_write[$experiment['experiment']->experimentId] = true;
}
}
return View::make('experiment/browse', array(
'input' => Input::all(),
'pageNo' => $pageNo,
'limit' => $this->limit,
'expStates' => $experimentStates,
'expContainer' => $expContainer,
'can_write' => $can_write
));
}
/**
* Generate JSON containing permissions information for this project.
*
* This function retrieves the user profile and permissions for every user
* other than the client that has access to the project. In the event that
* the project does not exist, return an error message.
*/
public function sharedUsers()
{
if (Session::has("authz-token") && array_key_exists('resourceId', $_GET)) {
return Response::json(SharingUtilities::getProfilesForSharedUsers($_GET['resourceId']));
}
else {
return Response::json(array("error" => "Error: No project specified"));
}
}
public function unsharedUsers()
{
if (Session::has("authz-token") && array_key_exists('resourceId', $_GET)) {
return Response::json(SharingUtilities::getProfilesForUnsharedUsers($_GET['resourceId']));
}
else {
return Response::json(array("error" => "Error: No experiment specified"));
}
}
public function updateSharing()
{
try{
// Convert the JSON array to an object
$sharing_info = json_decode(json_encode(Input::json()->all()));
ExperimentUtilities::update_experiment_sharing(Input::get('expId'), $sharing_info);
return Response::json(array("success" => true));
}catch (Exception $ex){
Log::error("failed to update sharing for experiment", array(Input::all()));
Log::error($ex);
return Response::json(array("success" => false, "error" => "Error: failed to update sharing: " . $ex->getMessage()));
}
}
private function getAllowedFileSize()
{
// Condition added to deal with php ini default value set for post_max_size issue.
// NOTE: the following assumes that upload_max_filesize and
// post_max_size are in megabytes (for example, if
// upload_max_filesize is 8M then $allowedFileSize is 8, but the 'M'
// is assumed and not considered)
$allowedFileSize = intval( ini_get( 'upload_max_filesize' ) );
$serverLimit = intval( ini_get( 'post_max_size' ) );
if( $serverLimit < $allowedFileSize) {
$allowedFileSize = $serverLimit;
}
return $allowedFileSize;
}
private function validateQueueData($computeResourceId, $queue)
{
$queues = ExperimentUtilities::getQueueDatafromResourceId($computeResourceId);
$queueName = $queue['queueName'];
foreach($queues as $aQueue){
if($aQueue->queueName == $queueName) {
if($queue['nodeCount'] <= $aQueue->maxNodes && $queue['cpuCount'] <= $aQueue->maxProcessors && $queue['wallTimeLimit'] <= $aQueue->maxRunTime)
return true;
}
}
return false;
}
}
?>