docker/SASCommonBundle/Controller/JobManagerController.php line 384

Open in your IDE?
  1. <?php
  2. namespace CU\SASCommonBundle\Controller;
  3. use CU\SASCommonBundle\Annotation\CUSAS;
  4. use CU\SASCommonBundle\Entity\JobManagerOption;
  5. use CU\SASCommonBundle\Entity\JobRunConfig;
  6. use CU\SASCommonBundle\Form\Type\CUSASPlainType;
  7. use CU\SASCommonBundle\Service\JobManager;
  8. use CU\SASCommonBundle\Service\TableRenderer;
  9. use Doctrine\DBAL\Schema\Table;
  10. use Symfony\Component\HttpFoundation\RedirectResponse;
  11. use Symfony\Component\HttpFoundation\Response;
  12. use Symfony\Component\Routing\Annotation\Route;
  13. use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  14. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  15. use Symfony\Component\HttpFoundation\Request;
  16. /**
  17.  * Job Manager
  18.  *
  19.  * @author "Cornell University, Student Services IT"
  20.  * @Route("/common/job-manager")
  21.  * @CUSAS(priv="JOBMANAGER")
  22.  */
  23. class JobManagerController extends AbstractController
  24. {
  25.     /**
  26.      * @Route("/config", name="cusas_jobmanager", methods={"GET"})
  27.      *
  28.      * @return \Symfony\Component\HttpFoundation\Response
  29.      */
  30.     public function configAction(JobManager $jobManager)
  31.     {
  32.         $token $this->generateCsrfToken('jobmanager');
  33.         $form $this->getJobManagerOptionsForm($jobManager);
  34.         $tableSeqs $this->getSeqsTable($jobManager$token);
  35.         $tableJobs $this->getJobsTable($jobManager$token);
  36.         return $this->render('@CUSASCommon/JobManager/status.html.twig', ['tableSeqs' => $tableSeqs'tableJobs' => $tableJobs'form' => $form->createView()]);
  37.     }
  38.     /**
  39.      * @param string $token
  40.      * @return \CU\SASCommonBundle\Entity\ResultTable
  41.      * @throws \Exception
  42.      */
  43.     private function getSeqsTable(JobManager $jobManager$token)
  44.     {
  45.         // if we have JOBMANAGER-EDIT we'll show the schedule+token link
  46.         $hasJobManagerEdit $this->getUser()->hasPriv('JOBMANAGER-EDIT');
  47.         $table $this->getTableRenderer()->getTable();
  48.         $table
  49.         ->setColumnCount(6)
  50.         ->setColumnGridWidthsMd(224211)
  51.         ->setHeaderRow('Sequence''Min Rest Interval''Job Order''Last Started''Elapsed''Action');
  52.         $sequences $jobManager->getAllSequences();
  53.         foreach ($sequences as $sequence) {
  54.             $minRest $this->secondsToTime($sequence->getMinRestSec());
  55.             $sequenceTasks $sequence->getTasks();
  56.             $jobDescr '';
  57.             $i 0;
  58.             foreach ($sequenceTasks AS $sequenceTask) {
  59.                 if ($i$jobDescr .= '<br>';
  60.                 $jobDescr .= $sequenceTask->getJob()->getDescrshort();
  61.                 $i++;
  62.             }
  63.             $action '';
  64.             if ($hasJobManagerEdit) {
  65.                 $action '<a href="' $this->generateUrl('cusas_jobmanager_seq_enqueue', ['seqId' => $sequence->getId(), 'token' => $token]);
  66.                 $action .= '">Enqueue</a>';
  67.             }
  68.             $elapsed '';
  69.             if ($sequence->getLastStartDttm() && $sequence->getLastStopDttm() &&
  70.                 $sequence->getLastStopDttm() >= $sequence->getLastStartDttm()
  71.                 ) {
  72.                 $elapsed $this->secondsToTime($sequence->getLastStopDttm()->getTimestamp() - $sequence->getLastStartDttm()->getTimestamp());
  73.             }
  74.             $table->addRow($sequence->getDescrshort(),
  75.                 $minRest,
  76.                 $jobDescr,
  77.                 $sequence->getLastStartDttm()?$sequence->getLastStartDttm()->format('Y-m-d h:ia'):'',
  78.                 $elapsed,
  79.                 $action);
  80.         }
  81.         return $table;
  82.     }
  83.     /**
  84.      * @param string $token
  85.      * @return \CU\SASCommonBundle\Entity\ResultTable
  86.      * @throws \Exception
  87.      */
  88.     private function getJobsTable(JobManager $jobManager$token)
  89.     {
  90.         // if we have JOBMANAGER-EDIT we'll show the schedule+token link
  91.         $hasJobManagerEdit $this->getUser()->hasPriv('JOBMANAGER-EDIT');
  92.         $table $this->getTableRenderer()->getTable();
  93.         $table
  94.         ->setColumnCount(3)
  95.         ->setColumnGridWidthsMd(515)
  96.         ->setHeaderRow('Job''Hidden''Enqueue');
  97.         $jobs $jobManager->getAllJobs();
  98.         foreach ($jobs as $job) {
  99.             if (!$job->hasAllowExec()) {
  100.                 continue;
  101.             }
  102.             $runConfigCount 0;
  103.             // handle statically linked run configs
  104.             $runConfigs $job->getRunconfigs();
  105.             $runConfigCount += count($runConfigs);
  106.             $i 0;
  107.             foreach ($runConfigs as $runConfig) {
  108.                 $jobContent '';
  109.                 $jobContent .= '<a href="' $this->generateUrl('cusas_jobmanager_job_enqueue', ['jobId' => $job->getId(), 'runConfigId' => $runConfig->getId(), 'token' => $token]);
  110.                 $jobContent .= '">' $runConfig->getDescrshort() . '</a><br />';
  111.                 if ($hasJobManagerEdit) {
  112.                     $table->addRow($i==0?$job->getDescrshort():''$job->isHidden()?'Yes':'No'$jobContent);
  113.                 }
  114.                 $i++;
  115.             }
  116.             // handle support for dynamically linked run configs
  117.             $jobService $jobManager->getServiceByAlias($job->getServiceAlias());
  118.             $dynamicRunConfigs $jobService->getDynamicRunConfigs($job);
  119.             $runConfigCount += count($dynamicRunConfigs);
  120.             foreach ($dynamicRunConfigs as $dynamicRunConfig) {
  121.                 $jobContent '';
  122.                 $jobContent .= '<a href="' $this->generateUrl('cusas_jobmanager_job_enqueue_dynamic', ['jobId' => $job->getId(), 'id' => $dynamicRunConfig->getId(), 'token' => $token]);
  123.                 $jobContent .= '">' $dynamicRunConfig->getDescrshort() . '</a><br />';
  124.                 if ($hasJobManagerEdit) {
  125.                     $table->addRow($i==0?$job->getDescrshort():''$job->isHidden()?'Yes':'No'$jobContent);
  126.                 }
  127.                 $i++;
  128.             }
  129.             // if no run configs, include the default (empty run config)
  130.             if (!$runConfigCount) {
  131.                 $jobContent '<a href="' $this->generateUrl('cusas_jobmanager_job_enqueue', ['jobId' => $job->getId(), 'token' => $token]);
  132.                 $jobContent .= '">Default</a>';
  133.                 $table->addRow($job->getDescrshort(), $job->isHidden()?'Yes':'No'$hasJobManagerEdit?$jobContent:'');
  134.             }
  135.         }
  136.         return $table;
  137.     }
  138.     /**
  139.      * process save for roster specific filter edit
  140.      *
  141.      * @Route("/save", name="cusas_jobmanager_save", methods={"POST"})
  142.      * @CUSAS(priv="JOBMANAGER-EDIT")
  143.      *
  144.      * @param Request $request
  145.      * @return RedirectResponse
  146.      */
  147.     public function saveAction(JobManager $jobManagerRequest $request)
  148.     {
  149.         $form $this->getJobManagerOptionsForm($jobManager);
  150.         $form->handleRequest($request);
  151.         if ($form->isValid()) {
  152.             $data $form->getData();
  153.             $supportedOptions = ['enabled''sequence'];
  154.             foreach ($supportedOptions AS $supportedOption) {
  155.                 $jobManagerOption $jobManager->getJobManagerOption($supportedOption);
  156.                 if ($jobManagerOption) {
  157.                     $oldValue $jobManagerOption->getMetaValue();
  158.                     $jobManagerOption->setMetaValue($data[$supportedOption]);
  159.                     if ($oldValue != $data[$supportedOption]) {
  160.                         $jobManagerOption->setUpdatedDttm(new \DateTime());
  161.                     }
  162.                     $this->getEntityManager()->flush($jobManagerOption);
  163.                 } else {
  164.                     $jobManagerOption = new JobManagerOption($supportedOption$data[$supportedOption]);
  165.                     $jobManagerOption->setUpdatedDttm(new \DateTime());
  166.                     $this->getEntityManager()->persist($jobManagerOption);
  167.                     $this->getEntityManager()->flush($jobManagerOption);
  168.                 }
  169.             }
  170.             $this->get('session')->getFlashBag()->add('info''Job Manager configuration saved.');
  171.             $shouldClearQueue $data['clearqueue'];
  172.             if (in_array('Y'$shouldClearQueue)) {
  173.                 $this->get('session')->getFlashBag()->add('warning''Failed any running and cancelled any scheduled jobs.');
  174.                 $jobManager->forceClearQueue();
  175.             }
  176.         } else {
  177.             // for any errors, add to flashbag, will display after redirect
  178.             foreach ($form->getErrors() AS $error) {
  179.                 $this->get('session')->getFlashBag()->add('danger'$error->getMessage());
  180.             }
  181.         }
  182.         return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  183.     }
  184.     /**
  185.      * @Route("/job/queue-dynamic/{token}/{jobId}/{id}", name="cusas_jobmanager_job_enqueue_dynamic", methods={"GET"})
  186.      * @CUSAS(priv="JOBMANAGER-EDIT")
  187.      *
  188.      * @param string $token
  189.      * @param int $jobId
  190.      * @param int $id
  191.      * @return RedirectResponse
  192.      */
  193.     public function queueJobAddDynamicAction(JobManager $jobManager$token$jobId$id)
  194.     {
  195.         // does token validate?
  196.         $tokenValid $this->isCsrfTokenValid('jobmanager'$token);
  197.         if (!$tokenValid) {
  198.             $this->get('session')->getFlashBag()->add('danger''Invalid token.');
  199.             return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  200.         }
  201.         /* @var $job \CU\SASCommonBundle\Entity\Job */
  202.         $job $this->getEntityManager()->getRepository('CU\SASCommonBundle\Entity\Job')->find($jobId);
  203.         $jobService $jobManager->getServiceByAlias($job->getServiceAlias());
  204.         $dynamicRunConfig $jobService->getDynamicRunConfigById($job$id);
  205.         $runconfig = new JobRunConfig();
  206.         $runconfig->setDescrshort($dynamicRunConfig->getDescrshort());
  207.         $runconfig->setInitJson($dynamicRunConfig->getInitJson());
  208.         $this->getEntityManager()->persist($runconfig);
  209.         $this->getEntityManager()->flush($runconfig);
  210.         $isScheduled $jobManager->isJobScheduled($job$runconfig);
  211.         if ($isScheduled) {
  212.             $this->get('session')->getFlashBag()->add('danger''Job for roster is already scheduled.');
  213.         } else if (!$job->hasAllowExec()) {
  214.             $this->get('session')->getFlashBag()->add('danger''Job is disabled and cannot be scheduled.');
  215.         } else {
  216.             $userId $this->getUser()->getUserId();
  217.             $jobManager->scheduleJob($job$userId$runconfig);
  218.             $this->get('session')->getFlashBag()->add('info''Job has been enqueued.');
  219.         }
  220.         // redirect
  221.         return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  222.     }
  223.     /**
  224.      * @Route("/job/queue-static/{token}/{jobId}/{runConfigId}", name="cusas_jobmanager_job_enqueue", defaults={"runConfigId" =""}, methods={"GET"})
  225.      * @CUSAS(priv="JOBMANAGER-EDIT")
  226.      *
  227.      * @param string $token
  228.      * @param int $jobId
  229.      * @param int|string $runConfigId
  230.      * @return RedirectResponse
  231.      */
  232.     public function queueJobAddAction(JobManager $jobManager$token$jobId$runConfigId '')
  233.     {
  234.         // does token validate?
  235.         $tokenValid $this->isCsrfTokenValid('jobmanager'$token);
  236.         if (!$tokenValid) {
  237.             $this->get('session')->getFlashBag()->add('danger''Invalid token.');
  238.             return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  239.         }
  240.         /* @var $job \CU\SASCommonBundle\Entity\Job */
  241.         $job $this->getEntityManager()->getRepository('CU\SASCommonBundle\Entity\Job')->find($jobId);
  242.         $runconfig false;
  243.         if ($runConfigId) {
  244.             $runconfig $this->getEntityManager()->getRepository('CU\SASCommonBundle\Entity\JobRunConfig')->find($runConfigId);
  245.         }
  246.         $isScheduled $jobManager->isJobScheduled($job$runconfig);
  247.         if ($isScheduled) {
  248.             $this->get('session')->getFlashBag()->add('danger''Job for roster is already scheduled.');
  249.         } else if (!$job->hasAllowExec()) {
  250.             $this->get('session')->getFlashBag()->add('danger''Job is disabled and cannot be scheduled.');
  251.         } else {
  252.             $userId $this->getUser()->getUserId();
  253.             $jobManager->scheduleJob($job$userId$runconfig);
  254.             $this->get('session')->getFlashBag()->add('info''Job has been enqueued.');
  255.         }
  256.         // redirect
  257.         return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  258.     }
  259.     /**
  260.      * @Route("/seq/queue/{token}/{seqId}", name="cusas_jobmanager_seq_enqueue", methods={"GET"})
  261.      * @CUSAS(priv="JOBMANAGER-EDIT")
  262.      *
  263.      * @param string $token
  264.      * @param int $seqId
  265.      * @return RedirectResponse
  266.      */
  267.     public function queueSeqAddAction(JobManager $jobManager$token$seqId)
  268.     {
  269.         // does token validate?
  270.         $tokenValid $this->isCsrfTokenValid('jobmanager'$token);
  271.         if (!$tokenValid) {
  272.             $this->get('session')->getFlashBag()->add('danger''Invalid token.');
  273.             return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  274.         }
  275.         //$userId = $this->getUser()->getUserId();
  276.         // get the last run time...
  277.         $sequence $jobManager->getSequence($seqId);
  278.         if ($jobManager->canQueueSequence($sequence)) {
  279.             $jobsScheduledCount $jobManager->queueSequence($sequence);
  280.             $this->get('session')->getFlashBag()->add('info''Sequence has been enqueued.');
  281.         } else {
  282.             $this->get('session')->getFlashBag()->add('danger''Sequence cannot be enqueued.');
  283.         }
  284.         // redirect
  285.         return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  286.     }
  287.     /**
  288.      * @Route("/queue/cancel/{queueId}/{token}", name="cusas_jobmanager_queue_cancel", methods={"GET"})
  289.      * @CUSAS(priv="JOBMANAGER-EDIT")
  290.      *
  291.      * @param int $queueId
  292.      * @param string $token
  293.      * @return RedirectResponse
  294.      */
  295.     public function queueCancelAction(JobManager $jobManager$queueId$token)
  296.     {
  297.         // does token validate?
  298.         $tokenValid $this->isCsrfTokenValid('jobmanager'$token);
  299.         if (!$tokenValid) {
  300.             $this->get('session')->getFlashBag()->add('danger''Invalid token.');
  301.             return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  302.         }
  303.         $affectedJobs $jobManager->cancelQueueId($queueId);
  304.         if ($affectedJobs) {
  305.             $this->get('session')->getFlashBag()->add('info''Job has been cancelled.');
  306.         } else {
  307.             $this->get('session')->getFlashBag()->add('danger''Job could not be cancelled.');
  308.         }
  309.         // redirect
  310.         return $this->redirect($this->generateUrl('cusas_jobmanager', []), '302');
  311.     }
  312.     /**
  313.      * @Route("/history", name="cusas_jobmanager_history", methods={"POST"})
  314.      *
  315.      * @param Request $request
  316.      * @return Response
  317.      */
  318.     public function historyAjaxAction(Request $request)
  319.     {
  320.         $showHidden $request->request->get('showHidden'0);
  321.         $defaultConn $this->getConnection();
  322.         $token $this->generateCsrfToken('jobmanager');
  323.         $hasJobManagerEdit $this->getUser()->hasPriv('JOBMANAGER-EDIT');
  324.         $historyTable $this->getTableRenderer()->getTable();
  325.         $historyTable
  326.         ->setColumnCount(7)
  327.         ->setColumnGridWidthsSm(1,3,2,1,2,2,1)
  328.         ->setHeaderRow('Sequence''Job''Param''Status''Started''Elapsed''Action');
  329.         $dttm = new \DateTime();
  330.         $dttm->setTimestamp(time() - 10*86400);
  331.         $query $this->getEntityManager()->createQuery('
  332.             SELECT q
  333.               FROM CUSASCommonBundle:JobManagerQueue q
  334.                    JOIN q.job j
  335.              WHERE q.schedDttm >= :dt
  336.           ORDER BY q.schedDttm DESC, q.id DESC
  337.             ')
  338.             ->setParameter('dt'$dttm);
  339.         $queueEntries $query->getResult();
  340.         /* @var $queueEntry \CU\SASCommonBundle\Entity\JobManagerQueue */
  341.         foreach ($queueEntries AS $queueEntry) {
  342.             if (!$showHidden && $queueEntry->getJob()->isHidden()) {
  343.                 continue;
  344.             }
  345.             $elapsed '';
  346.             if ($queueEntry->getStatus() == 'COMPLETE' && $queueEntry->getStopDttm()
  347.                 && $queueEntry->getStopDttm() >= $queueEntry->getStartDttm()) {
  348.                     $elapsed $this->secondsToTime($queueEntry->getStopDttm()->getTimestamp() - $queueEntry->getStartDttm()->getTimestamp());
  349.             }
  350.             $otherAction '';
  351.             if ($queueEntry->getStatus() == 'SCHEDULED' && $hasJobManagerEdit) {
  352.                 $otherAction '<a href="' $this->generateUrl('cusas_jobmanager_queue_cancel', ['queueId' => $queueEntry->getId(), 'token' => $token]);
  353.                 $otherAction .= '">cancel</a>';
  354.             }
  355.             $historyTable->addRow(($queueEntry->getSequence()?$queueEntry->getSequence()->getName():''),
  356.                 $queueEntry->getJob()->getDescrshort(),
  357.                 $queueEntry->getRunconfig()?$queueEntry->getRunconfig()->getDescrshort():'',
  358.                 $queueEntry->getStatus(),
  359.                 $queueEntry->getStartDttm()?$queueEntry->getStartDttm()->format('Y-m-d h:ia'):'',
  360.                 $elapsed,
  361.                 $otherAction
  362.                 );
  363.         }
  364.         return $this->render('@CUSASCommon/JobManager/historyAjax.html.twig', ['historyTable' => $historyTable]);
  365.     }
  366.     /**
  367.      * @return \Symfony\Component\Form\FormInterface
  368.      */
  369.     private function getJobManagerOptionsForm(JobManager $jobManager)
  370.     {
  371.         $formBuilder $this->createFormBuilder();
  372.         $hasJobManagerEdit $this->getUser()->hasPriv('JOBMANAGER-EDIT');
  373.         $formBuilder
  374.         ->setMethod('post')
  375.         ->setAction($this->generateUrl('cusas_jobmanager_save'))
  376.         ->add('master_node_name',
  377.             CUSASPlainType::class,
  378.             ['data' => $jobManager->getMasterNodeName()?$jobManager->getMasterNodeName():'None',
  379.                 'label' => 'Master Node',
  380.                 'attr' => ['tip' => '']])
  381.         ->add('enabled',
  382.             ChoiceType::class,
  383.             ['choices' => ['Yes' => 'Y''No' => 'N'],
  384.                 'multiple' => false,
  385.                 'expanded' => false,
  386.                 'data' => $jobManager->isSchedulerEnabled()?'Y':'N',
  387.                 'label' => 'Enabled',
  388.                 'required' => true,
  389.                 'disabled' => !$hasJobManagerEdit,
  390.                 'attr' => ['tip' => 'Process queued sequences and jobs.']])
  391.         ->add('sequence',
  392.             ChoiceType::class,
  393.             ['choices' => ['Yes' => 'Y''No' => 'N'],
  394.                 'multiple' => false,
  395.                 'expanded' => false,
  396.                 'data' => $jobManager->areSequencesEnabled()?'Y':'N',
  397.                 'label' => 'Automatic Sequences',
  398.                 'required' => true,
  399.                 'disabled' => !$hasJobManagerEdit,
  400.                 'attr' => ['tip' => 'Automatically enqueue sequences. (Enabled must be Yes.)']]);
  401.         if ($this->getUser()->hasPriv('JOBMANAGER-EDIT')) {
  402.             $formBuilder
  403.             ->add('clearqueue',
  404.             ChoiceType::class,
  405.             ['choices' => ['Fail running sequences, jobs and cancel all scheduled.' => 'Y'],
  406.                 'multiple' => true,
  407.                 'expanded' => true,
  408.                 'data' => [false],
  409.                 'label' => 'Power Cancel',
  410.                 'required' => false,
  411.                 'attr' => ['tip' => 'For use only when JobManager is in a bad state.']])
  412.             ->add('save'SubmitType::class, ['attr' => ['class' => 'btn-primary']]);
  413.         }
  414.         return $formBuilder->getForm();
  415.     }
  416.     protected function secondsToTime($ss)
  417.     {
  418.         if ($ss == 0) {
  419.             return '0 secs';
  420.         }
  421.         $s $ss 60;
  422.         $m = (floor(($ss%3600)/60)>0)?floor(($ss%3600)/60):0;
  423.         $h = (floor(($ss 86400) / 3600)>0)?floor(($ss 86400) / 3600):0;
  424.         $d = (floor(($ss 604800) / 86400)>0)?floor(($ss 604800) / 86400):0;
  425.         $w = (floor($ss 604800)>0)?floor($ss 604800):0;
  426.         $str '';
  427.         if ($w)
  428.             $str .= ' ' $w ' wk' . ($w>1?'s':'');
  429.         if ($d)
  430.             $str .= ' ' $d ' day' . ($d>1?'s':'');
  431.         if ($h)
  432.             $str .= ' ' $h ' hr' . ($h>1?'s':'');
  433.         if ($m)
  434.             $str .= ' ' $m ' min' . ($m>1?'s':'');
  435.         if ($s)
  436.             $str .= ' ' $s ' sec' . ($s>1?'s':'');
  437.         return trim($str);
  438.     }
  439. }