/*___INFO__MARK_BEGIN__*/
/*************************************************************************
 * 
 *  The Contents of this file are made available subject to the terms of
 *  the Sun Industry Standards Source License Version 1.2
 * 
 *  Sun Microsystems Inc., March, 2001
 * 
 * 
 *  Sun Industry Standards Source License Version 1.2
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.2 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html
 * 
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 * 
 *   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 * 
 *   Copyright: 2001 by Sun Microsystems, Inc.
 * 
 *   All Rights Reserved.
 * 
 ************************************************************************/
/*___INFO__MARK_END__*/

#include <stdlib.h>
#include <string.h>
#include <strings.h>

#ifdef SOLARISAMD64
#  include <sys/stream.h>
#endif   

#include "cull.h"
#include "sge_gdi.h"
#include "sgermon.h"
#include "sge_log.h"

#include "sge_answer.h"
#include "sge_job.h"
#include "sge_ja_task.h"
#include "sge_object.h"
#include "sge_qinstance.h"
#include "sge_range.h"

#include "sge_idL.h"
#include "sge_orderL.h"

#include "sge_orders.h"
#include "msg_schedd.h"

#include "sge_ssi.h"


static bool parse_job_identifier(const char *id, u_long32 *job_id, u_long32 *ja_task_id)
{
   char *copy;

   DENTER(TOP_LAYER, "parse_job_identifier");

   copy = strdup(id);
   *job_id = atoi(strtok(copy, "."));
   *ja_task_id = atoi(strtok(NULL, "."));
   free(copy);

   if(*job_id > 0 && *ja_task_id > 0) {
      DEXIT;
      return true;
   }

   WARNING((SGE_EVENT, MSG_SSI_ERRORPARSINGJOBIDENTIFIER_S, id));

   DEXIT;
   return false;
}

/****** schedlib/ssi/sge_ssi_job_cancel() **************************************
*  NAME
*     sge_ssi_job_cancel() -- delete or restart a job
*
*  SYNOPSIS
*     bool sge_ssi_job_cancel(const char *job_identifier, bool reschedule) 
*
*  FUNCTION
*     Delete the given job.
*     If reschedule is set to true, reschedule the job.
*
*  INPUTS
*     const char *job_identifier - job identifier in the form 
*                                  <jobid>.<ja_task_id>, e.g. 123.1
*     bool reschedule            - if true, reschedule job
*
*  RESULT
*     bool - true, if the job could be successfully deleted (rescheduled),
*           else false.
*
*  NOTES
*     The reschedule parameter is igored in the current implementation.
*
*  SEE ALSO
*     schedlib/ssi/sge_ssi/job_start()
*******************************************************************************/
bool sge_ssi_job_cancel(const char *job_identifier, bool reschedule) 
{
   u_long32 job_id, ja_task_id;
   lList *ref_list = NULL, *alp;
   lListElem *ref_ep;
   char job_id_str[100];

   DENTER(TOP_LAYER, "sge_ssi_job_cancel");

   /* reschedule not yet implemented */
   if(reschedule) {
      DEXIT;
      return false;
   }

   if(!parse_job_identifier(job_identifier, &job_id, &ja_task_id)) {
      DEXIT;
      return false;
   }

   /* create id structure */
   sprintf(job_id_str, U32CFormat, u32c(job_id));
   ref_ep = lAddElemStr(&ref_list, ID_str, job_id_str, ID_Type);
   ref_ep = lAddSubUlong(ref_ep, RN_min, ja_task_id, ID_ja_structure, RN_Type);
   lSetUlong(ref_ep, RN_max, ja_task_id);
   lSetUlong(ref_ep, RN_step, 1);

   DPRINTF(("deleting job "SFN"\n", job_get_id_string(job_id, ja_task_id, NULL)));

   /* send delete request */
   alp = sge_gdi(SGE_JOB_LIST, SGE_GDI_DEL, &ref_list, NULL, NULL);

   answer_list_on_error_print_or_exit(&alp, stderr);

   DEXIT;
   return true;
}


/****** schedlib/ssi/sge_ssi_job_start() ***************************************
*  NAME
*     sge_ssi_job_start() -- start a job
*
*  SYNOPSIS
*     bool sge_ssi_job_start(const char *job_identifier, const char *pe, 
*                           task_map tasks[]) 
*
*  FUNCTION
*     Start the job described by job_identifier, pe and tasks.
*     job_identifier has to be given in the form "<job_id>.<ja_task_id>",
*     e.g. "123.1" and must reference a pending job/array task.
*     For parallel jobs, pe has to be the name of an existing parallel
*     environment.
*     tasks describes how many tasks are to be started per host.
*
*     The function creates a scheduling order and sends it to qmaster.
*
*  INPUTS
*     const char *job_identifier - unique job identifier
*     const char *pe             - name of a parallel environment 
*                                  or NULL for sequential jobs
*     task_map tasks[]           - mapping host->number of tasks
*
*  RESULT
*     bool - true on success, else false
*
*  SEE ALSO
*     libsched/ssi/--Simple-Scheduler-Interface
*     libsched/ssi/-Simple-Scheduler-Interface-Typedefs
*******************************************************************************/
bool sge_ssi_job_start(const char *job_identifier, const char *pe, task_map tasks[])
{
   u_long32 job_id, ja_task_id;
   lListElem *job, *ja_task;
   lList *order_list = NULL; /* list to be sent to qmaster */
   lList *granted = NULL;    /* granted queues */

   int i;


   DENTER(TOP_LAYER, "sge_ssi_job_start");

   if(!parse_job_identifier(job_identifier, &job_id, &ja_task_id)) {
      DEXIT;
      return false;
   }

   /* create job element */
   job = lCreateElem(JB_Type);
   lSetUlong(job, JB_job_number, job_id);

   /* create array task */
   ja_task = lCreateElem(JAT_Type);
   lSetUlong(ja_task, JAT_task_number, ja_task_id);
   if(pe != NULL) {
      lSetString(ja_task, JAT_granted_pe, pe);
   }

   /* create granted queue list 
    * we expect exactly one queue per host to exist
    */
   for(i = 0; tasks[i].procs != 0; i++) {
      lListElem *queue, *granted_queue;

      if(tasks[i].host_name == NULL) {
         ERROR((SGE_EVENT, MSG_SSI_MISSINGHOSTNAMEINTASKLIST));
         DEXIT;
         return false;
      }

      DPRINTF(("job requests %d slots on host %s\n", tasks[i].procs, tasks[i].host_name));
  
      queue = lGetElemHost(*(object_type_get_master_list(SGE_TYPE_CQUEUE)), 
                           QU_qhostname, tasks[i].host_name);
      if(queue == NULL) {
         ERROR((SGE_EVENT, MSG_SSI_COULDNOTFINDQUEUEFORHOST_S, tasks[i].host_name));
         DEXIT;
         return false;
      }

      granted_queue = lAddElemStr(&granted, JG_qname, lGetString(queue, QU_full_name), JG_Type);
      lSetUlong(granted_queue, JG_qversion, lGetUlong(queue, QU_version));
      lSetHost(granted_queue, JG_qhostname, lGetHost(queue, QU_qhostname));
      lSetUlong(granted_queue, JG_slots, tasks[i].procs);
   }

   /* create and send order */
   order_list = sge_create_orders(order_list, ORT_start_job, job, ja_task, granted, true);
   sge_send_orders2master(&order_list);

   if (order_list != NULL) {
      order_list = lFreeList(order_list);
   }   

   DEXIT;
   return true;
}

