diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php index 8f5211052f6933ac8fb768cc3d12e7471a991b9a..3f2aa08f8876c1c72fc98a638cde91d1d9d7ded0 100644 --- a/public_html/deployment/classes/project.class.php +++ b/public_html/deployment/classes/project.class.php @@ -28,7 +28,7 @@ require_once 'htmlguielements.class.php'; (...) 2024-08-28 Axel php8 only; added variable types; short array syntax 2024-09-20 Axel build(): check if checkout of sources failed - 2024-11-29 Axel multiple instances for rollout plugins + 2024-12-05 Axel multiple instances for rollout plugins; handle errors from callback ###################################################################### */ /** @@ -1583,22 +1583,25 @@ class project extends base $sPluginId = (isset($this->_aPrjConfig['deploy']['enabled_rollout_plugin']) && $this->_aPrjConfig['deploy']['enabled_rollout_plugin']) ? $this->_aPrjConfig['deploy']['enabled_rollout_plugin'] : 'default'; - $sPluginName=$this->_aConfig['plugins']['rollout'][$sPluginId]['plugin']; + $sPluginName=$this->_aConfig['plugins']['rollout'][$sPluginId]['plugin'] ?? false; unset($this->oRolloutPlugin); - try { - require_once $this->_getPluginFilename('rollout', $sPluginName); - $sPluginClassname = 'rollout_' . $sPluginName; - $this->oRolloutPlugin = new $sPluginClassname([ - 'id' => $sPluginId, - 'lang' => $this->_aConfig['lang'], - 'phase' => false, - 'globalcfg' => isset($this->_aConfig['plugins']['rollout'][$sPluginId]) ? $this->_aConfig['plugins']['rollout'][$sPluginId] : [], - 'projectcfg' => $this->_aPrjConfig, - ]); - // print_r($this->_oRolloutPlugin->getPluginfos()); - // print_r($this->_oRolloutPlugin->getName()); - } catch (Exception $ex) { - } + if ($sPluginName){ + + try { + require_once $this->_getPluginFilename('rollout', $sPluginName); + $sPluginClassname = 'rollout_' . $sPluginName; + $this->oRolloutPlugin = new $sPluginClassname([ + 'id' => $sPluginId, + 'lang' => $this->_aConfig['lang'], + 'phase' => false, + 'globalcfg' => isset($this->_aConfig['plugins']['rollout'][$sPluginId]) ? $this->_aConfig['plugins']['rollout'][$sPluginId] : [], + 'projectcfg' => $this->_aPrjConfig, + ]); + // print_r($this->_oRolloutPlugin->getPluginfos()); + // print_r($this->_oRolloutPlugin->getName()); + } catch (Exception $ex) { + } + } return true; } diff --git a/public_html/deployment/classes/project_gui.class.php b/public_html/deployment/classes/project_gui.class.php index e43497ffca0e09bb12aaac8b51c60f162a2d615a..4c58299ecb2a30211e3ccf291524f73a22eb7941 100644 --- a/public_html/deployment/classes/project_gui.class.php +++ b/public_html/deployment/classes/project_gui.class.php @@ -15,7 +15,7 @@ require_once 'htmlguielements.class.php'; 2013-11-08 Axel <axel.hahn@iml.unibe.ch> (...) 2024-08-26 Axel php8 only; added variable types; short array syntax - 2024-11-29 Axel multiple instances for rollout plugins + 2024-12-05 Axel multiple instances for rollout plugins; handle errors from callback ###################################################################### */ /** @@ -851,7 +851,7 @@ class projectgui extends project $sMyDivId = 'rollout-' . $sPluginId . '-config'; $sMyDivClass = 'rolloutconfigdiv'; $sMyDivClassActive = 'rolloutconfigdiv-' . $sPluginId; - $bActive = $sPluginId === $this->oRolloutPlugin->getId(); + $bActive = isset($this->oRolloutPlugin) && $sPluginId === $this->oRolloutPlugin->getId(); if (file_exists($sPluginFile)) { try { @@ -1467,14 +1467,18 @@ class projectgui extends project // Tab for raw data // -------------------------------------------------- - $sRolloutDebug = '<hr>DEBUG:<br>'; - foreach (array_keys($this->getPhases()) as $sPhase) { - if ($this->isActivePhase($sPhase)) { - $sRolloutDebug .= '<strong>' . $sPhase . '</strong>' - . '<pre>Config = ' . print_r($this->oRolloutPlugin->getConfig($sPhase, 1), 1) . '</pre>' - . '<pre>Commands = ' . print_r($this->oRolloutPlugin->getDeployCommands($sPhase, 1), 1) . '</pre>' - ; + $sRolloutDebug = '<hr>DEBUG for rollout:<br>'; + if (isset($this->oRolloutPlugin)) { + foreach (array_keys($this->getPhases()) as $sPhase) { + if ($this->isActivePhase($sPhase)) { + $sRolloutDebug .= '<strong>' . $sPhase . '</strong>' + . '<pre>Config = ' . print_r($this->oRolloutPlugin->getConfig($sPhase, 1), 1) . '</pre>' + . '<pre>Commands = ' . print_r($this->oRolloutPlugin->getDeployCommands($sPhase, 1), 1) . '</pre>' + ; + } } + } else { + $sRolloutDebug .= 'INFO: No rollout plugin loaded<br>'; } $aForms["setup"]["form"]['input' . $i++] = [ diff --git a/public_html/deployment/classes/rollout_base.class.php b/public_html/deployment/classes/rollout_base.class.php index f9369b8bb44e453a1e46ba224d5f1841c0fec92f..51c8582dd77ef09c41a5f9a215a9c193e87192fa 100644 --- a/public_html/deployment/classes/rollout_base.class.php +++ b/public_html/deployment/classes/rollout_base.class.php @@ -9,7 +9,8 @@ require_once __DIR__ . '/../../vendor/axelhahn/ahcache/cache.class.php'; * @author axel * * Axel <axel.hahn@unibe.ch> - * 2024-08-29 Axel php8 only; added variable types; short array syntax + * 2024-08-29 Axel php8 only; added variable types; short array syntax + * 2024-12-05 Axel multiple instances for rollout plugins; handle errors from callback */ class rollout_base implements iRolloutplugin { @@ -127,6 +128,20 @@ class rollout_base implements iRolloutplugin } } + /** + * Add a log messsage + * @global object $oLog + * + * @param string $sMessage messeage text + * @param string $sLevel warnlevel of the given message + * @return bool + */ + private function log(string $sMessage, string $sLevel = "info"): bool + { + global $oCLog; + return $oCLog->add(basename(__FILE__) . " class " . __CLASS__ . " - " ." ".$this->getId()." - ". $sMessage, $sLevel); + } + // --------------------------------------------------------------- // FORM HELPER // --------------------------------------------------------------- @@ -160,11 +175,16 @@ class rollout_base implements iRolloutplugin { $oCache = new AhCache('rollout-' . $this->getId(), 'callback-' . $sFunctionname . '-' . $sKey); if ($oCache->isExpired()) { + $this->log(__FUNCTION__. " calling $sFunctionname to get fresh data"); $aMydata = call_user_func([$this, $sFunctionname]); - // echo "$sFunctionname fresh ".($aMydata ? "OK": "false")." - storing for $iTtl sec<br>"; - $oCache->write($aMydata, ($aMydata ? $iTtl : $iTtlOnError)); + + $bIsError=!$aMydata || isset($aMydata['error']); + $iMyTtl=($bIsError ? $iTtlOnError : $iTtl); + $this->log(__FUNCTION__. " $sFunctionname was [".($bIsError ? "ERROR": "OK")."] - storing in cache for $iMyTtl sec<br>"); + // $this->log(__FUNCTION__. ' storing <pre>'.print_r($aMydata,1).'</pre>'); + $oCache->write($aMydata, $iMyTtl); } else { - // echo "$sFunctionname from cache ... ".$oCache->iExpired()." sec <br>"; + $this->log(__FUNCTION__. " SKIP $sFunctionname and use cache ... ".( - $oCache->iExpired())." sec left<br>"); $aMydata = $oCache->read(); } // echo '<pre>'.print_r($aMydata, 1).'</pre>'; die(__METHOD__); @@ -188,9 +208,11 @@ class rollout_base implements iRolloutplugin $sReturn = ''; $sKeyPrefix = $this->getId() . '_' . $sKey; + // $this->log(__FUNCTION__ . " <pre>".print_r($aFormdata, 1).'</pre>'); $oForm = new formgen(); foreach ($aFormdata as $elementData) { $elementKey = $sKeyPrefix . '_' . $i++; + // echo "<hr>$elementKey"; print_r($elementData); $sReturn .= $oForm->renderHtmlElement($elementKey, $elementData); } return $sReturn; @@ -252,28 +274,39 @@ class rollout_base implements iRolloutplugin (isset($aVarinfos['per_scope']) && $aVarinfos['per_scope'] ? $sKey : ''), (isset($aVarinfos['ttl']) ? $aVarinfos['ttl'] : 60) ); + // $this->log('<pre>'.print_r($aCallbackData,1).'</pre>'); if (!$aCallbackData) { - $aVarinfos['type'] = $aVarinfos['type'] ?? 'text'; + // $aVarinfos['type'] = $aVarinfos['type'] ?? 'text'; + $aVarinfos['type'] = 'text'; + $sMyPlaceholder .= ' ... ERROR: Missing data. Callback function failed.'; + $this->log( 'variable field ['.$sVarname.'] ... ERROR: no data callback function '.$aVarinfos['callback'], 'error'); } else { - $aEffectiveConfig = $this->getConfig($sPhase); - // echo $sKey.' ... '; print_r($aEffectiveConfig[$sVarname]); echo '<br>'; - - // mark entry as active ... for select and radio - if (isset($aEffectiveConfig[$sVarname]) && isset($aCallbackData[$aEffectiveConfig[$sVarname]])) { - $aCallbackData[$aEffectiveConfig[$sVarname]]['selected'] = 'selected'; - $aCallbackData[$aEffectiveConfig[$sVarname]]['checked'] = 'checked'; - $aCallbackData[$aEffectiveConfig[$sVarname]]['label'] .= ' ««'; - } elseif ($aVarinfos['type'] === 'select') { - $aCallbackData = array_merge(['NO_SELECTED_ITEM_YET' => ['value' => '', 'label' => '...']], $aCallbackData); - } - - // wenn value = defaultvalue, dann value auf '' setzen (damit bei Default vom Scope - // davor ein Leerstring übergeben wird - analog onfocusout im Text Input - if (isset($aCallbackData[$aDefaultValues[$sVarname]])) { - $aCallbackData[$aDefaultValues[$sVarname]]['value'] = ''; - $aCallbackData[$aDefaultValues[$sVarname]]['label'] .= ' (*)'; + // $this->log(__FUNCTION__ . ' var '.$sVarname.' ... aCallbackData = <pre>'.print_r($aCallbackData,1).'</pre>'); + if($aCallbackData['error']){ + $aVarinfos['type'] = 'text'; + $sMyPlaceholder .= ' ... '.$aCallbackData['error']; + $this->log(' variable field ['.$sVarname.'] ... error from callback function '.$aVarinfos['callback'].' <pre>'.print_r($aCallbackData,1).'</pre>', 'error'); + } else { + $aEffectiveConfig = $this->getConfig($sPhase); + + // mark entry as active ... for select and radio + if (isset($aEffectiveConfig[$sVarname]) && isset($aCallbackData[$aEffectiveConfig[$sVarname]])) { + $aCallbackData[$aEffectiveConfig[$sVarname]]['selected'] = 'selected'; + $aCallbackData[$aEffectiveConfig[$sVarname]]['checked'] = 'checked'; + $aCallbackData[$aEffectiveConfig[$sVarname]]['label'] .= ' ««'; + } elseif ($aVarinfos['type'] === 'select') { + // not needed with bootstrap-select + // $aCallbackData = array_merge(['NO_SELECTED_ITEM_YET' => ['value' => '', 'label' => '...']], $aCallbackData); + } + + // wenn value = defaultvalue, dann value auf '' setzen (damit bei Default vom Scope + // davor ein Leerstring übergeben wird - analog onfocusout im Text Input + if (isset($aCallbackData[$aDefaultValues[$sVarname]])) { + $aCallbackData[$aDefaultValues[$sVarname]]['value'] = ''; + $aCallbackData[$aDefaultValues[$sVarname]]['label'] .= ' (*)'; + } + // print_r($aCallbackData[$sVarname]); echo "<br>"; } - // print_r($aCallbackData[$sVarname]); echo "<br>"; } // echo '<pre>'.$sCallbackfunktion .' = '. print_r($aMydata,1 ).'</pre>'; } diff --git a/public_html/deployment/plugins/rollout/awx/rollout_awx.php b/public_html/deployment/plugins/rollout/awx/rollout_awx.php index 5c791f2e7328cf9f7fdf990db0d3c84b5ee4421b..b97500b9daffada7b412a6c1f1c86263dae7ffa7 100644 --- a/public_html/deployment/plugins/rollout/awx/rollout_awx.php +++ b/public_html/deployment/plugins/rollout/awx/rollout_awx.php @@ -9,6 +9,7 @@ * @author <axel.hahn@iml.unibe.ch> * * 2024-09-02 Axel php8 only; added variable types; short array syntax + * 2024-12-05 Axel multiple instances for rollout plugins; handle errors from callback */ class rollout_awx extends rollout_base { @@ -85,6 +86,7 @@ class rollout_awx extends rollout_base curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + // $this->log(__METHOD__ . "Start request $sAwxApiUrl"); $res = curl_exec($ch); if (!$res) { $iErrorCode = curl_errno($ch); @@ -95,7 +97,7 @@ class rollout_awx extends rollout_base ]; } - $aReturn = ['info' => curl_getinfo($ch), 'error' => curl_error($ch)]; + $aReturn = ['info' => curl_getinfo($ch)]; curl_close($ch); $sHeader = substr($res, 0, $aReturn['info']['header_size']); @@ -122,16 +124,29 @@ class rollout_awx extends rollout_base ] ); + if (isset($aResponse['error'])) { + $aReturn = [ + 'error' => $aResponse['error'] , + 'value' => false, + 'label' => '!!! Access to awx api for inventories failed !!! ' . $aResponse['error'] + ]; + return $aReturn; + } if (!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code'] !== 200) { - return false; + $aReturn = [ + 'error' => 'Non 200 status code '. $aResponse['info']['http_code'] ?? 'none', + 'value' => false, + 'label' => '!!! Error from awx api !!! Non 200 status code ' . $aResponse['info']['http_code'] + ]; + return $aReturn; } $aData = json_decode($aResponse['body'], 1); $aReturn = []; if (!$aData || !isset($aData['count'])) { - $aReturn[] = [ + $aReturn = [ 'value' => false, - 'label' => '!!! Access to awx api failed !!!' + 'label' => '!!! No data from awx api !!!' ]; return $aReturn; } @@ -156,8 +171,9 @@ class rollout_awx extends rollout_base * [id] => ['value' => [ID], 'label' => [PLAYBOOK] [ID]] * @return bool|array */ - public function getAwxJobTemplates() + public function getAwxJobTemplates(): array { + $aReturn = []; $aResponse = $this->_httpRequest( [ 'url' => '/job_templates/?order_by=name' . $this->_sAwxApiPaging, @@ -165,16 +181,29 @@ class rollout_awx extends rollout_base ] ); + if (isset($aResponse['error'])) { + $aReturn = [ + 'error' => $aResponse['error'] , + 'value' => false, + 'label' => '!!! Access to awx api for job templates failed !!! ' . $aResponse['error'] + ]; + return $aReturn; + } if (!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code'] !== 200) { - return false; + $aReturn = [ + 'error' => 'Non 200 status code '. $aResponse['info']['http_code'], + 'value' => false, + 'label' => '!!! Error from awx api !!! Non 200 status code ' . $aResponse['info']['http_code'] + ]; + return $aReturn; } - + $aData = json_decode($aResponse['body'], 1); - $aReturn = []; if (!$aData || !isset($aData['count'])) { - $aReturn[] = [ + $aReturn = [ + 'error' => 'no data', 'value' => false, - 'label' => '!!! Access to awx api failed !!!' + 'label' => '!!! no job templates !!!' ]; return $aReturn; }