diff --git a/public_html/deployment/classes/formgen.class.php b/public_html/deployment/classes/formgen.class.php index 7ca0a12b2389eeddd55be692fea0c535580cc843..0e1ea574d1ed829579ef5bc0d279687aab368dc9 100644 --- a/public_html/deployment/classes/formgen.class.php +++ b/public_html/deployment/classes/formgen.class.php @@ -164,7 +164,7 @@ class formgen { $sFormElement.="\n".'<div class="checkbox">'; $s = preg_replace('/\W/iu', '', $sId . $idOption); $sOptionId = preg_replace('/[äöüß]/i', '', $s); - $sFormElement.=' <input type="checkbox" id="' . $sOptionId . '" value="' . $idOption . '" '; + $sFormElement.=' <input type="checkbox" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked"), $aOptionData); $sFormElement.=' name="' . $elementData["name"] . '[]"'; $sFormElement.='/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>'; @@ -200,7 +200,7 @@ class formgen { $sFormElement.="\n".'<div class="radio">'; $s = preg_replace('/\W/iu', '', $sId . $idOption); $sOptionId = preg_replace('/[äöüß]/i', '', $s); - $sFormElement.=' <input type="radio" id="' . $sOptionId . '" value="' . $idOption . '" '; + $sFormElement.=' <input type="radio" id="' . $sOptionId . '" value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,checked,disabled"), $aOptionData); $sFormElement.=" " . $this->_addHtmlAtrributes(explode(",", "name"), $elementData); $sFormElement.='/><label for="' . $sOptionId . '">' . $aOptionData["label"] . '</label></div>'; @@ -231,7 +231,7 @@ class formgen { foreach ($elementData["options"] as $idOption => $aOptionData) { $s = preg_replace('/\W/iu', '', $sId . $idOption); $sOptionId = preg_replace('/[äöüß]/i', '', $s); - $sFormElement.=' <option value="' . $idOption . '" '; + $sFormElement.=' <option value="' . (isset($aOptionData["value"]) ? $aOptionData["value"] : $idOption) . '" '; $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,selected"), $aOptionData); $sFormElement.='>' . $aOptionData["label"] . '</option>' . "\n"; } diff --git a/public_html/deployment/classes/rollout_base.class.php b/public_html/deployment/classes/rollout_base.class.php index ee86f5de1eb3acea4f935351f1c0918ab9a1d80f..2a7e6017503c4448a92fa0b39fff4335b929dc67 100644 --- a/public_html/deployment/classes/rollout_base.class.php +++ b/public_html/deployment/classes/rollout_base.class.php @@ -1,5 +1,6 @@ <?php require_once 'rollout.interface.php'; +require_once 'cache.class.php'; /** * rollout_base class that will beextended in a rollout plugin @@ -117,6 +118,30 @@ class rollout_base implements iRolloutplugin{ : 'plugins[rollout]['.$this->getId().']' ); } + + /** + * get Data from a callback function and store it in a cache + * The response type depends on the callback function + * + * @param string $sFunctionname name of the callback function + * @param string $sKey name of the key; "project" or name of phase + * @param integr $iTtl ttl value = how many seconds to use cache + * @param integr $iTtlOnError ttl value = how many seconds to use cache if there was no response + * @return type + */ + protected function _getCallback($sFunctionname, $sKey, $iTtl=15,$iTtlOnError=10){ + $oCache=new AhCache('rollout-'.$this->getId(), 'callback-'.$sFunctionname.'-'.$sKey); + if($oCache->isExpired()){ + $aMydata= call_user_func(array($this, $sFunctionname)); + // echo "$sFunctionname fresh ".($aMydata ? "OK": "false")." - storing for $iTtl sec<br>"; + $oCache->write($aMydata, ($aMydata ? $iTtl : $iTtlOnError)); + } else { + // echo "$sFunctionname from cache ... ".$oCache->iExpired()." sec <br>"; + $aMydata=$oCache->read(); + } + // echo '<pre>'.print_r($aMydata, 1).'</pre>'; die(__METHOD__); + return $aMydata; + } /** * render a form by given form elementes * @param array $aFormdata array of form elements @@ -186,6 +211,39 @@ class rollout_base implements iRolloutplugin{ ? htmlentities($aDefaultValues[$sVarname]) : (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.') ); + + // if a callback was set for this variable + if(isset($aVarinfos['callback'])){ + $aCallbackData=$this->_getCallback( + $aVarinfos['callback'], + (isset($aVarinfos['per_scope']) && $aVarinfos['per_scope'] ? $sKey : ''), + (isset($aVarinfos['ttl']) ? $aVarinfos['ttl'] : 60) + ); + if(!$aCallbackData){ + $aVarinfos['type']='text'; + } 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(array('NO_SELECTED_ITEM_YET'=>array('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>"; + } + // echo '<pre>'.$sCallbackfunktion .' = '. print_r($aMydata,1 ).'</pre>'; + } switch ($aVarinfos['type']) { case "password": $sMyPlaceholder=(isset($aDefaultValues[$sVarname]) @@ -201,10 +259,24 @@ class rollout_base implements iRolloutplugin{ // 'required' => 'required', 'validate' => 'isastring', // 'size' => 25, - // 'placeholder' => (isset($this->_aCfgGlobal[$sVarname]) ? $this->_aCfgGlobal[$sVarname] : '') . ' | '.$aDefaultValues[$sVarname], 'placeholder' => $sMyPlaceholder ); break; + case "select": + case "radio": + $aOptions=$aCallbackData; + $aFormdata[]=array( + 'type' => $aVarinfos['type'], + 'name' => $sPrefixName.'['.$sVarname.']', + 'label' => $this->_t($sVarname.'-label'), + 'title' => $this->_t($sVarname.'-hint'), + + 'validate' => 'isastring', + 'options' => $aOptions, + + // 'placeholder' => $sMyPlaceholder + ); + break; case "text": $aFormdata[]=array( 'type' => $aVarinfos['type'], @@ -221,7 +293,6 @@ class rollout_base implements iRolloutplugin{ // 'required' => 'required', 'validate' => 'isastring', // 'size' => 25, - // 'placeholder' => (isset($this->_aCfgGlobal[$sVarname]) ? $this->_aCfgGlobal[$sVarname] : '') . ' | '.$aDefaultValues[$sVarname], 'placeholder' => $sMyPlaceholder ); break; diff --git a/public_html/deployment/plugins/rollout/awx/info.json b/public_html/deployment/plugins/rollout/awx/info.json index 79294a6d96722625da4cd04c12ebd2eff73b9135..c1c0ec9ddc37a5ea8cf71a8887c62cebec6f09d5 100644 --- a/public_html/deployment/plugins/rollout/awx/info.json +++ b/public_html/deployment/plugins/rollout/awx/info.json @@ -22,8 +22,11 @@ "default": "example: my-very-secret-password" }, "jobtemplate": { - "type": "text", - "default": "36" + "type": "select", + "default": "", + "callback": "getAwxJobTemplates", + "ttl": 120, + "per_scope": false }, "tags": { "type": "text", @@ -38,12 +41,15 @@ "extravars": { "type": "text", "default": "example: { data in JSON syntax here }" - } + } }, "phase": { "inventory": { - "type": "text", - "default": "example: hosts/Appname/preview" + "type": "select", + "default": "", + "callback": "getAwxInventories", + "ttl": 120, + "per_scope": false } } } diff --git a/public_html/deployment/plugins/rollout/awx/lang_de.json b/public_html/deployment/plugins/rollout/awx/lang_de.json index 427a3f6f5a2df71bbe95d08a21c0b83c52692d66..ffa4d4efc03c9966d75805440b11a5bce1269d2f 100644 --- a/public_html/deployment/plugins/rollout/awx/lang_de.json +++ b/public_html/deployment/plugins/rollout/awx/lang_de.json @@ -18,7 +18,7 @@ "extravars-label": "Extravars", "extravars-hint": "Extravars als JSON Syntax.", - "inventory-label": "Inventory ID", + "inventory-label": "Inventory", "inventory-hint": "AWX inventory zum Setzen der Phase und der zu installierenden Hosts", "endoffile": "" diff --git a/public_html/deployment/plugins/rollout/awx/lang_en.json b/public_html/deployment/plugins/rollout/awx/lang_en.json index 43b46920bcb402333d6841e7ab6d792b6709da16..e662c504847f94bf811cb3dff400a45cec0d4581 100644 --- a/public_html/deployment/plugins/rollout/awx/lang_en.json +++ b/public_html/deployment/plugins/rollout/awx/lang_en.json @@ -18,7 +18,7 @@ "extravars-label": "Extravars", "extravars-hint": "Extravars in JSON syntax", - "inventory-label": "Inventory ID", + "inventory-label": "Inventory", "inventory-hint": "AWX inventory to define phase and set of hosts", "endoffile": "" diff --git a/public_html/deployment/plugins/rollout/awx/rollout_awx.php b/public_html/deployment/plugins/rollout/awx/rollout_awx.php index 2aa41d829b95f3ab65ca52cd68f38e32d80f173b..7ec6f2dcbbdccc55fb6aaa1c42afff493c27894c 100644 --- a/public_html/deployment/plugins/rollout/awx/rollout_awx.php +++ b/public_html/deployment/plugins/rollout/awx/rollout_awx.php @@ -6,7 +6,7 @@ * * Run an Https POST request to AWX * - * @author axel + * @author <axel.hahn@iml.unibe.ch> */ class rollout_awx extends rollout_base { @@ -18,6 +18,117 @@ class rollout_awx extends rollout_base { return true; } + /** + * make an http get request and return the response body + * it is called by _makeRequest + * $aRequest contains subkeys + * - url + * - method; one of GET|POST|PUT|DELETE + * - postdata; for POST only + * + * @param array $aRequest arrayurl for Foreman API + * @return string + */ + protected function _httpRequest($aRequest=false, $iTimeout = 5) { + + if (!function_exists("curl_init")) { + die("ERROR: PHP CURL module is not installed."); + } + $aConfig=$this->getConfig(); + + + $ch = curl_init($aConfig['url'].$aRequest['url']); + + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $aRequest['method']); + if ($this->_aRequest['method']==='POST'){ + curl_setopt($ch, CURLOPT_POSTFIELDS, $aRequest['postdata']); + } + + if ($aConfig['user']){ + curl_setopt($ch, CURLOPT_USERPWD, $aConfig['user'].':'.$aConfig['password']); + } + + if (isset($aConfig['ignore-ssl-error']) && $aConfig['ignore-ssl-error']){ + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + } + + curl_setopt($ch, CURLOPT_TIMEOUT, $iTimeout); + curl_setopt($ch, CURLOPT_USERAGENT, 'IML Deployment :: rollout plugin awx ' . __CLASS__); + curl_setopt($ch, CURLOPT_HEADER,1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); + + $res = curl_exec($ch); + + $aReturn=array('info'=>curl_getinfo($ch), 'error'=>curl_error($ch)); + curl_close($ch); + + $sHeader=substr($res, 0, $aReturn['info']['header_size']); + $aReturn['header']=explode("\n", $sHeader); + $aReturn['body']=str_replace($sHeader, "", $res); + + // print_r($aReturn); + return $aReturn; + } + + + + /** + * get AWX inventories and return them as array for select box + * [id] => array('value' => [ID], 'label' => [NAME] [ID]) + * @return array + */ + static public function getAwxInventories(){ + $aResponse=$this->_httpRequest(array( + 'url'=>'/inventories/?order_by=name', + 'method'=>'GET', + ) + ); + + if(!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code']!==200){ + return false; + } + + $aData=json_decode($aResponse['body'], 1); + $aReturn=array(); + + foreach ($aData['results'] as $aItem){ + $aReturn[$aItem['id']]= [ + 'value'=>$aItem['id'], + 'label'=>$aItem['name'].' (id: '.$aItem['id'].')' + ]; + } + + return $aReturn; + } + /** + * get AWX Job Templates and return them as array for select box + * [id] => array('value' => [ID], 'label' => [PLAYBOOK] [ID]) + * @return array + */ + static public function getAwxJobTemplates(){ + $aResponse=$this->_httpRequest(array( + 'url'=>'/job_templates/?order_by=name', + 'method'=>'GET', + ) + ); + + if(!isset($aResponse['info']['http_code']) || $aResponse['info']['http_code']!==200){ + return false; + } + + $aData=json_decode($aResponse['body'], 1); + $aReturn=array(); + + foreach ($aData['results'] as $aItem){ + $aReturn[$aItem['id']]= [ + 'value'=>$aItem['id'], + 'label'=>$aItem['name'].' (id: '.$aItem['id'].'; '.$aItem['playbook'].')' + ]; + } + return $aReturn; + } + /** * check access to a deploy target */ @@ -26,6 +137,12 @@ class rollout_awx extends rollout_base { return true; } + /** + * get array with commands to execute to deploy a package + * + * @param string $sPhase phase + * @return array + */ public function getDeployCommands($sPhase){ $aReturn=array(); $aConfig=$this->getConfig($sPhase); diff --git a/public_html/deployment/plugins/rollout/default/rollout_default.php b/public_html/deployment/plugins/rollout/default/rollout_default.php index a8999a4495afeee275b36ce98a397c455cafdd7d..a98ca37ace98359cca07afc6bb9d46020a8ac266 100644 --- a/public_html/deployment/plugins/rollout/default/rollout_default.php +++ b/public_html/deployment/plugins/rollout/default/rollout_default.php @@ -5,7 +5,7 @@ * * no action * - * @author axel + * @author <axel.hahn@iml.unibe.ch> */ class rollout_default extends rollout_base { @@ -25,10 +25,22 @@ class rollout_default extends rollout_base { return true; } + /** + * override general form renderer: show a single message that no + * configuration items exist + * + * @return string + */ public function renderFormdata4Project() { return $this->_t('no-cfg'); } + /** + * override general form renderer: show a single message that no + * configuration items exist + * + * @return string + */ public function renderFormdata4Phase($sPhase) { return $this->_t('no-cfg'); } diff --git a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php index 822d03756b0b43186e1ff21a0b49d6847c9c066e..f896fe92d73199bc46585ca547ae7ad820b6a7c2 100644 --- a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php +++ b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php @@ -6,7 +6,7 @@ * * Run a SSH command on remote targets * - * @author axel + * @author <axel.hahn@iml.unibe.ch> */ class rollout_ssh extends rollout_base { @@ -26,6 +26,12 @@ class rollout_ssh extends rollout_base { return true; } + /** + * get array with commands to execute to deploy a package + * + * @param string $sPhase phase + * @return array + */ public function getDeployCommands($sPhase){ $aReturn=array(); $aConfig=$this->getConfig($sPhase); @@ -45,99 +51,4 @@ class rollout_ssh extends rollout_base { return $aReturn; } - /** - * form fields for project settings - * @return type - */ - public function __DISABLED__renderFormdata4Project() { - /* - $sReturn=''; - $i=0; - - $sNamePrefix='plugins[rollout]['.$this->getId().']'; - $aValues=$this->getConfig(); - - $aFormdata=array( - array('type' => 'markup','value' => '<br>'.$this->_t('section-required').':'), - array( - 'type' => 'text', - 'name' => $sNamePrefix.'[user]', - 'label' => $this->_t('user'), - 'value' => $aValues['user'], - // 'required' => 'required', - 'validate' => 'isastring', - 'size' => 25, - 'placeholder' => $this->_aCfgGlobal['user'], - ), - array( - 'type' => 'text', - 'name' => $sNamePrefix.'[command]', - 'label' => $this->_t('command'), - 'value' => $aValues['command'], - // 'required' => 'required', - 'validate' => 'isastring', - // 'size' => 100, - 'placeholder' => $this->_aCfgGlobal['command'], - ), - array('type' => 'markup','value' => '<br><br>'.$this->_t('section-optional').':'), - array( - 'type' => 'text', - 'name' => $sNamePrefix.'[privatekey]', - 'label' => $this->_t('privatekey'), - 'value' => $aValues['privatekey'], - // 'required' => 'required', - 'validate' => 'isastring', - // 'size' => 100, - 'placeholder' => $this->_aCfgGlobal['privatekey'], - ), - array('type' => 'markup','value' => '<br><br>'.$this->_t('section-commands').':'), - array( - 'type' => 'text', - 'name' => $sNamePrefix.'[addkeycommand]', - 'label' => $this->_t('addkeycommand'), - 'value' => $aValues['addkeycommand'], - // 'required' => 'required', - 'validate' => 'isastring', - // 'size' => 100, - 'placeholder' => $this->_aCfgGlobal['addkeycommand'], - ), - array( - 'type' => 'text', - 'name' => $sNamePrefix.'[testcommand]', - 'label' => $this->_t('testcommand'), - 'value' => $aValues['testcommand'], - // 'required' => 'required', - 'validate' => 'isastring', - // 'size' => 100, - 'placeholder' => $this->_aCfgGlobal['testcommand'], - ), - ); - * - */ - - return 'WIP: project based setup for plugin [' . $this->getId() . ']<br>' - . $this->_renderForm4Vars('global', false) - // . $this->_renderForm($aFormdata, 'project') - // .$sReturn - . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' - . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' - // .'<pre>DEBUG: $this->_aCfgProject ... plugin = '.print_r($this->_aCfgProject, 1).'</pre>' - ; - } - - /** - * form fields for project settings - * @return type - */ - public function __DISABLED__renderFormdata4Phase($sMyPhase) { - return 'WIP: pase based setup for plugin [' . $this->getId() . '] in phase '.$sMyPhase.'<br>' - . $this->_renderForm4Vars('global', $sMyPhase) - // . $this->_renderForm($aFormdata, 'project') - // .$sReturn - . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>' - . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>' - . '<pre>DEBUG: PHASE settings - $this->getConfig("'.$sMyPhase.'") = ' . print_r($this->getConfig($sMyPhase), 1) . '</pre>' - ; - } - }