diff --git a/config/lang/de.json b/config/lang/de.json
index 20ce33395f647a675691759439bd919936327784..b3dd2dac549efcd480c39b668442391f4be01fc8 100644
--- a/config/lang/de.json
+++ b/config/lang/de.json
@@ -252,7 +252,7 @@
     "deploytimes-immediately": "Ein Archiv in der Queue wird sofort ins Repo gestellt.",
     "deploymethod": "Deployment-Methode",
     "deploymethod-none": "Keine Aktion anstossen.",
-    "deploymethod-puppet": "Puppet aufrufen",
+    "deploymethod-rolloutplugin": "Rolloutplugin aufrufen",
     "deploymethod-sshproxy": "Skript via SSH-Proxy starten",
     "description": "Beschreibung",
     "developer": "Entwickler",
diff --git a/config/lang/en.json b/config/lang/en.json
index 9f575f385d20965c97c8dc3cd5abf9eaf3d3693b..50786dcdf6dac5b1cd520caa090caee0bac65942 100644
--- a/config/lang/en.json
+++ b/config/lang/en.json
@@ -252,7 +252,7 @@
     "deploy-settings": "Deployment settings",
     "deploymethod": "Deployment method",
     "deploymethod-none": "None. Do not trigger any action.",
-    "deploymethod-puppet": "Run Puppet on target host(s)",
+    "deploymethod-rolloutplugin": "Call rolloutplugin",
     "deploymethod-sshproxy": "Run a script via SSH-proxy",
     "deploytimes": "Deploy time window",
     "deploytimes-immediately": "A package in the queue will be deployed immediately.",
diff --git a/hooks/templates/inc_projects_config.php.erb b/hooks/templates/inc_projects_config.php.erb
index c972b90a3bd1af34841bc670e8e923081c296542..7dbb5f10259b923e73a574e8b486586c850e1624 100644
--- a/hooks/templates/inc_projects_config.php.erb
+++ b/hooks/templates/inc_projects_config.php.erb
@@ -18,9 +18,44 @@ $aConfig = array(
         ),
     ),
     'lang' => 'de', // for available languages see ./config/lang/*.json
+
     // rsync of archives
     'mirrorPackages' => array(),
-    // ssh install - if a host is given
+    
+    // task#4574
+    // plugins
+    'plugins'=>array(
+
+        // enabled rollout plugins
+        'rollout'=>array(
+            'default'=>array(),
+            'ssh'=>array(
+                'user'=>'<%= @replace["rollout-ssh-user"] %>',
+                'privatekey'=>'',
+                'addkeycommand'=>'/usr/bin/ssh-keygen -R %s; /usr/bin/ssh-keyscan -t rsa %s >> /home/www-data/.ssh/known_hosts',
+                'testcommand'=>'sudo puppet --version',
+                // 'command'=>'sudo puppet agent -t --detailed-exitcodes ; rc=$?; if [ $rc -eq 2 ]; then rc=0; fi ; exit $rc',
+                'command'=>'/usr/local/bin/puppetrun.sh',
+            ),
+            'awx'=>array(
+                'url'=>'<%= @replace["rollout-awx-url"] %>', // no ending "/"
+                'user'=>'<%= @replace["rollout-awx-user"] %>',
+                'password'=>'<%= @replace["rollout-awx-password"] %>',
+                'jobtemplate'=>'36', // 36 = iml wrapper playbook
+                'tags'=>'rollout',
+                // 'ignore-ssl-error'=>false,
+            ),
+            /*
+            'foreman'=>array(
+                'api'=>'https://foreman.one.iml.unibe.ch/', // with ending "/"
+                'user'=>'ci-server',
+                'password'=>'A2h0MUVcnfRN_KLYMpymgjsHv0wu8qiY',
+                'ignore-ssl-error'=>true,
+            ),
+             * 
+             */
+        ),
+    ),
     'installPackages' => array(
         'user' => 'imldeployment',
         
diff --git a/public_html/deployment/classes/formgen.class.php b/public_html/deployment/classes/formgen.class.php
index b6f9676733da30f211aed661346936db4f636289..7ca0a12b2389eeddd55be692fea0c535580cc843 100644
--- a/public_html/deployment/classes/formgen.class.php
+++ b/public_html/deployment/classes/formgen.class.php
@@ -119,7 +119,16 @@ class formgen {
     public function renderHtmlElement($sId, $elementData) {
         $sReturn = false;
         $aAllowedHtmlAttributes = array();
-        $sDefaultAttributes = "class,onclick,onmouseover,onmouseout,title";
+        $sDefaultAttributes = ""
+                . "class,"
+                
+                // events ... see https://developer.mozilla.org/en-US/docs/Web/API/Element
+                . "onauxclick,onclick,ondblclick,oncontextmenu,onfocusin,onfocusout,"
+                . "onkeydown,onkeypress,onkeyup,"
+                . "onmousedown,onmouseenter,onmouseleave,onmousemove,onmouseout,onmouseover,onmouseup,"
+                //
+                . "title"
+                ;
 
         if (!array_key_exists("type", $elementData)) {
             print_r($elementData);
diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php
index ae1b32f1641b1c4b188a5cca44d03ad6dbda7b10..62236b2ef7b5dbe1cdc2b84819347937f71a6612 100644
--- a/public_html/deployment/classes/project.class.php
+++ b/public_html/deployment/classes/project.class.php
@@ -2179,17 +2179,30 @@ class project extends base {
         // run action to install
         // --------------------------------------------------
         $sDeploymethod = array_key_exists("deploymethod", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["deploymethod"] : "none";
-        $sTargethosts = array_key_exists("hosts", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["hosts"] : '';
+        // $sTargethosts = array_key_exists("hosts", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["hosts"] : '';
 
         $sReturn.='<h3>' . t("class-project-info-deploy-start-by-method") . ' :: ' . $sDeploymethod . '</h3>'
                 . '<p>'
                 . t("deploymethod-$sDeploymethod") . '<br>'
-                . t("phase-targethosts") . ': ' . ($sTargethosts ? $sTargethosts : t("none"))
+                // . t("phase-targethosts") . ': ' . ($sTargethosts ? $sTargethosts : t("none"))
                 . '</p>'
         ;
-        if ($sDeploymethod === "none" || !$sTargethosts) {
+        if ($sDeploymethod === "none") {
             $sReturn.=t("class-project-info-deploy-start-by-method-skip") . "<br>";
         } else {
+            
+            $sReturn.='<p>'
+                    . 'Plugin: '.$this->oRolloutPlugin->getId().'<br>'
+                    . '</p>'
+                    . '<pre>Config for phase '.$sPhase.' = '.print_r($this->oRolloutPlugin->getConfig($sPhase), 1).'</pre>'
+                    . '<pre>Commands = '.print_r($this->oRolloutPlugin->getDeployCommands($sPhase), 1).'</pre>'
+                    ;
+
+            foreach($this->oRolloutPlugin->getDeployCommands($sPhase) as $sCmd){
+                $sReturn.=$this->_execAndSend("$sCmd");
+            }
+
+            /*
             $aTargethosts = explode(',', $sTargethosts);
             foreach ($aTargethosts as $sTargethost) {
                 $sReturn.='<h4>' . $sDeploymethod . ' - ' . $sTargethost . '</h4>';
@@ -2214,6 +2227,8 @@ class project extends base {
                     $sReturn.=$this->_execAndSend("$sCmd");
                 }
             }
+             * 
+             */
         }
         $aActionList['iActive'] ++;
         $this->_TempFill($sReturn, $aActionList);
@@ -3379,7 +3394,7 @@ class project extends base {
                 }
             } else {
                 $aRollout['project-select']['options'][$sPluginName]=array(
-                        'label' => "not found: " . $sMyClassname,
+                        'label' => 'not found: <span class="error">' . $sMyClassname . '</span>',
                         'checked' => false,
                         'disabled' => "disabled",
                     );
@@ -3834,10 +3849,10 @@ class project extends base {
                         'checked' => $sDeploymethod === "none",
                         'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'none\' : \'block\') )',
                     ),
-                    'puppet' => array(
+                    'rolloutplugin' => array(
                         // 'label' => t("deploymethod-puppet").' - '.  $this->oRolloutPlugin->getName(),
-                        'label' => t("deploy-rollout-plugin"),
-                        'checked' => $sDeploymethod === "puppet",
+                        'label' => t("deploymethod-rolloutplugin"),
+                        'checked' => $sDeploymethod === "rolloutplugin",
                         'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'block\' : \'none\') )',
                     ),
                 /*
@@ -3906,11 +3921,6 @@ class project extends base {
               }
              */
 
-            $aForms["setup"]["form"]['input' . $i++] = array(
-                'type' => 'markup',
-                'value' => ''
-                . '</div>'
-            );
             // when to deploy
             $aForms["setup"]["form"]['input' . $i++] = array(
                 'type' => 'text',
@@ -3922,6 +3932,11 @@ class project extends base {
                 'size' => 100,
                 'placeholder' => implode(", ", $this->_aConfig["phases"][$sPhase]["deploytimes"]),
             );
+            $aForms["setup"]["form"]['input' . $i++] = array(
+                'type' => 'markup',
+                'value' => ''
+                . '</div>'
+            );
 
             if ($aSelectForemanGroups) {
                 $aForms["setup"]["form"]['input' . $i++] = $aSelectForemanHostGroup;
diff --git a/public_html/deployment/classes/rollout.interface.php b/public_html/deployment/classes/rollout.interface.php
index ffdb3e482b74b64bd5be16d6c6862a3ed489db50..5b84025cd77fc134328c8ae540df994a1957d61c 100644
--- a/public_html/deployment/classes/rollout.interface.php
+++ b/public_html/deployment/classes/rollout.interface.php
@@ -34,6 +34,13 @@ interface iRolloutplugin {
      */
     public function getConfig($sPhase=false);
     
+    /**
+     * get an array with shell commands to execute
+     * @param  string  $sPhase
+     * @return array
+     */
+    public function getDeployCommands($sPhase);
+
     /**
      * get name of plugin as string ... language specific
      */
@@ -48,10 +55,11 @@ interface iRolloutplugin {
      * get array of data in info.js
      */
     public function getPluginInfos();
-    
     // ----------------------------------------------------------------------
     // RENDERER
     // ----------------------------------------------------------------------
+    public function renderFormdata4Project();
+    public function renderFormdata4Phase($sPhase);
 
     // ----------------------------------------------------------------------
     // ACTIONS
diff --git a/public_html/deployment/classes/rollout_base.class.php b/public_html/deployment/classes/rollout_base.class.php
index c27d702b4a73547bbad24ca8dbe509394937d3d1..9df330a8cb3f3facac599a057ef7dd71df55663c 100644
--- a/public_html/deployment/classes/rollout_base.class.php
+++ b/public_html/deployment/classes/rollout_base.class.php
@@ -152,7 +152,7 @@ class rollout_base implements iRolloutplugin{
         // test vars from info.json file
         $aInfos=$this->getPluginInfos();
         if(!isset($aInfos['vars'][$sScope]) || !count($aInfos['vars'][$sScope])){
-            return '---<br>';
+            return '';
         }
         
         $sKey=($sPhase ? 'phase_'.$sPhase : 'project');
@@ -170,7 +170,6 @@ class rollout_base implements iRolloutplugin{
                 : $this->_aCfgProject['plugins']['rollout'][$this->getId()]
         );
         
-        $aFormdata=array();
  
         // create form fields
         // $aFormdata[]=array('type' => 'markup','value' => '<br>'.$this->_t('section-override-'.$sScope.'-vars').':');
@@ -179,31 +178,65 @@ class rollout_base implements iRolloutplugin{
         $sMiss='';
         foreach ($aInfos['vars'][$sScope] as $sVarname=>$aVarinfos){
             if ($sScope==='global' && !isset($this->_aCfgGlobal[$sVarname])){
-                $sMiss.='- plugin var was not set in global CI config: plugins -> rollout -> '.$this->getId().' -> "'.$sVarname.'".<br>';
+                $sMiss.='- plugin var was not set in global CI config: "'.$sVarname.'".<br>';
             }
-            if($aVarinfos['type']=="text"){
-                $aFormdata[]=array(
-                    'type' => 'text',
-                    'name' => $sPrefixName.'['.$sVarname.']',
-                    'label' => $this->_t($sVarname.'-label'),
-                    'title' => $this->_t($sVarname.'-hint'),
-                    'value' => (isset($aValues[$sVarname]) ? $aValues[$sVarname] : ''),
-                    // 'required' => 'required',
-                    'validate' => 'isastring',
-                    // 'size' => 25,
-                    // 'placeholder' => (isset($this->_aCfgGlobal[$sVarname]) ? $this->_aCfgGlobal[$sVarname] : '') . ' | '.$aDefaultValues[$sVarname],
-                    'placeholder' => (isset($aDefaultValues[$sVarname]) 
-                            ? $aDefaultSource.': '.$aDefaultValues[$sVarname] 
-                            : (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.')
-                        ),
+            
+            
+            $sMyPlaceholder=(isset($aDefaultValues[$sVarname]) 
+                                ? $aDefaultValues[$sVarname] 
+                                : (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.')
                 );
-               } else {
-                   $sMiss.='- plugin var "'.$sVarname.'" does not have type text. It cannot be rendered so far.<br>';
-               }
+            switch ($aVarinfos['type']) {
+                case "password":
+                    $sMyPlaceholder=(isset($aDefaultValues[$sVarname]) 
+                                        ? '******************************'
+                                        : $sMyPlaceholder
+                        );
+                    $aFormdata[]=array(
+                        'type' => $aVarinfos['type'],
+                        'name' => $sPrefixName.'['.$sVarname.']',
+                        'label' => $this->_t($sVarname.'-label'),
+                        'title' => $this->_t($sVarname.'-hint'),
+                        'value' => (isset($aValues[$sVarname]) ? htmlentities($aValues[$sVarname]) : ''),
+                        // 'required' => 'required',
+                        'validate' => 'isastring',
+                        // 'size' => 25,
+                        // 'placeholder' => (isset($this->_aCfgGlobal[$sVarname]) ? $this->_aCfgGlobal[$sVarname] : '') . ' | '.$aDefaultValues[$sVarname],
+                        'placeholder' => $sMyPlaceholder       
+                    );
+                    break;
+                case "text":
+                    $aFormdata[]=array(
+                        'type' => $aVarinfos['type'],
+                        'name' => $sPrefixName.'['.$sVarname.']',
+                        'label' => $this->_t($sVarname.'-label'),
+                        'ondblclick' => ($aDefaultValues[$sVarname] ? 'if (this.value==\'\') { this.value=\''.$aDefaultValues[$sVarname].'\' }' : ''),
+                        'onfocusout' => ($aDefaultValues[$sVarname] ? 'if (this.value==\''.$aDefaultValues[$sVarname].'\') { this.value=\'\' }' : ''),
+                        'title' => htmlentities($this->_t($sVarname.'-hint')."\n"
+                            . ($this->_aCfgGlobal[$sVarname] ? '- global: '.$this->_aCfgGlobal[$sVarname]."\n" : '')
+                            . ($this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname] ? '- project: '.$this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname]."\n" : '')
+                            )
+                            ,
+                        'value' => (isset($aValues[$sVarname]) ? htmlentities($aValues[$sVarname]) : ''),
+                        // 'required' => 'required',
+                        'validate' => 'isastring',
+                        // 'size' => 25,
+                        // 'placeholder' => (isset($this->_aCfgGlobal[$sVarname]) ? $this->_aCfgGlobal[$sVarname] : '') . ' | '.$aDefaultValues[$sVarname],
+                        'placeholder' => $sMyPlaceholder       
+                    );
+                    break;
+
+                default:
+                   $sMiss.='- plugin var "'.$sVarname.'" was not rendered - its type "'.$aVarinfos['type'].'" is not supported in the general form renderer.<br>';
+                   break;
+            }
         }
         // $aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><br><br>');
         return $this->_renderForm($aFormdata, $sKey)
-            . ($sMiss ? '<pre>WARNINGS:<br>'.$sMiss.'</pre>' : '')
+            . ($sMiss 
+                ? '<pre>WARNINGS:<br>'.$sMiss.'</pre>' . ($sScope==='global' ? $this -> renderCfgExample() : '' )
+                : ''
+            )
             ;
     }
     
@@ -347,6 +380,18 @@ class rollout_base implements iRolloutplugin{
         ;
     }
     
+    /**
+     * get an array with shell commands to execute
+     * @param  string  $sPhase
+     * @return array
+     */
+    public function getDeployCommands($sPhase){
+        return [
+            'echo "ERROR: The method getDeployCommamds($sPhase) was not implemented in the rollout plugin ['.$this->getId().']"',
+            'exit 1'
+            ];
+    }
+    
     /**
      * get string with current ID
      * @return string
@@ -392,21 +437,77 @@ class rollout_base implements iRolloutplugin{
     // ----------------------------------------------------------------------
     // INTERFACE :: RENDERER
     // ----------------------------------------------------------------------
+    public function renderCfgExample(){
+        $sReturn='';
+        $sPre='                ';
+        
+        $aInfos=$this->getPluginInfos();
+        $sReturn.='<pre>$aConfig = array(
+    ...
+    \'plugins\'=>array(
+        ...
+        // enabled rollout plugins
+        \'rollout\'=>array(
+            ...
+            <strong>
+            \''.$this->getId().'\'=>array(
+                // '.$this->getName().'
+                // '.$this->getDescription().'
+                '.PHP_EOL;
+        
+        // add global vars
+        if(!isset($aInfos['vars']['global']) || !count($aInfos['vars']['global'])){
+            $sReturn.=$sPre.'// this plugin has no global config vars'.PHP_EOL;
+        } else {
+            foreach ($aInfos['vars']['global'] as $sVar=>$aItem){
+                $sReturn.=$sPre.'// '.$this->_t($sVar.'-hint').PHP_EOL;
+                $sReturn.=$sPre.'\''.$sVar.'\'=>\''.(isset($this->_aCfgGlobal[$sVar]) ? $this->_aCfgGlobal[$sVar] : $aItem['default']).'\','.PHP_EOL;
+                $sReturn.=PHP_EOL;
+            }
+        }
+        
+        $sReturn.='
+            ),
+            </strong>
+            ...
+        ),
+        ...
+    ),
+);</pre>';
+        return $sReturn;
+    }
+    protected function _renderMoreToggler($sContent){
+        $sDivId='rollout-more-toggler-'.$this->getId().'-'.md5($sContent);
+        return ''
+                . '<button onclick="$(\'#'.$sDivId.'\').slideToggle(); return false;"> ... </button>'
+                . '<div id="'.$sDivId.'" style="display: none;">'
+                    . $sContent
+                . '</div>'
+                ;
+        
+    }
     public function renderFormdata4Project() {
         return ''
-                . $this->_renderForm4Vars('global', false)
                 . $this->_renderForm4Vars('project', false)
+                . $this->_renderForm4Vars('global', false)
                 // . $this->_renderFormProjectVars($this->_sNamePrefix4Project, false)
                 // . '<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>'
         ;
     }
-    public function renderFormdata4Phase($sMyPhase){
+    public function renderFormdata4Phase($sPhase){
+        static $iCounter;
+        if(!isset($iCounter)){
+            $iCounter=0;
+        }
+        $sDivId='rollout-override-div-'.$this->getId().'-'.$sPhase.'-'.$iCounter;
         return ''
-            . $this->_renderForm4Vars('global', $sMyPhase)
-            . $this->_renderForm4Vars('project', $sMyPhase)
-            . $this->_renderForm4Vars('phase', $sMyPhase)
+            . $this->_renderForm4Vars('phase', $sPhase)
+            . $this->_renderMoreToggler(
+                $this->_renderForm4Vars('project', $sPhase)
+                . $this->_renderForm4Vars('global', $sPhase)
+            )
             // . $this->_renderForm($aFormdata, 'project')
             // .$sReturn
             // . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>'
diff --git a/public_html/deployment/plugins/rollout/awx/info.json b/public_html/deployment/plugins/rollout/awx/info.json
new file mode 100644
index 0000000000000000000000000000000000000000..79294a6d96722625da4cd04c12ebd2eff73b9135
--- /dev/null
+++ b/public_html/deployment/plugins/rollout/awx/info.json
@@ -0,0 +1,51 @@
+{
+    "name": "AWX Rest API",
+    "description": "Run an Https POST request to AWX.",
+    "author": "Axel Hahn; University of Bern; Institute for Medical education",
+    
+    "version": "1.0",
+    "url": "[included]",
+    "license": "GNU GPL 3.0",
+    
+    "vars": {
+        "global": {
+            "url": {
+                "type": "text",
+                "default": "example: https://awx.example.com/api/v2"
+            },
+            "user": {
+                "type": "text",
+                "default": "example: awx-api"
+            },
+            "password": {
+                "type": "password",
+                "default": "example: my-very-secret-password"
+            },
+            "jobtemplate": {
+                "type": "text",
+                "default": "36"
+            },
+            "tags": {
+                "type": "text",
+                "default": "rollout"
+            }
+        },
+        "project": {
+            "limit": {
+                "type": "text",
+                "default": "example: appserver,localhost,dbserver"
+            },
+            "extravars": {
+                "type": "text",
+                "default": "example: { data in JSON syntax here }"
+            }     
+        },
+        "phase": {
+            "inventory": {
+                "type": "text",
+                "default": "example: hosts/Appname/preview"
+            }     
+        }
+    }
+}
+
diff --git a/public_html/deployment/plugins/rollout/awx/lang_de.json b/public_html/deployment/plugins/rollout/awx/lang_de.json
new file mode 100644
index 0000000000000000000000000000000000000000..427a3f6f5a2df71bbe95d08a21c0b83c52692d66
--- /dev/null
+++ b/public_html/deployment/plugins/rollout/awx/lang_de.json
@@ -0,0 +1,25 @@
+{
+    "plugin_name": "AWX",
+    "description": "Https POST Request zu AWX absetzen.",
+
+    "url-label": "AWX API Url",
+    "url-hint": "Url zur AWX REST API mit /api/v2",
+    "user-label": "API User",
+    "user-hint": "AWX Account, um die API aufzurufen.",
+    "password-label": "Passwort",
+    "password-hint": "Passwort des AWX API Users",
+    "jobtemplate-label": "ID des Job-Templates",
+    "jobtemplate-hint": "In der AWX WebGUI nach Aufruf des Templates aus der URL auslesen.",
+    "tags-label": "Tags",
+    "tags-hint": "Wenn die Installation mit Tag gestartet werden kan, ohne das gesamte Playbook auszuführen, dann Tags kommasepariert angeben",
+    
+    "limit-label": "Limit",
+    "limit-hint": "Serverliste f&uuml;r die Installation der Applikation angeben",
+    "extravars-label": "Extravars",
+    "extravars-hint": "Extravars als JSON Syntax.",
+
+    "inventory-label": "Inventory ID",
+    "inventory-hint": "AWX inventory zum Setzen der Phase und der zu installierenden Hosts",
+
+    "endoffile": ""
+}
\ No newline at end of file
diff --git a/public_html/deployment/plugins/rollout/awx/lang_en.json b/public_html/deployment/plugins/rollout/awx/lang_en.json
new file mode 100644
index 0000000000000000000000000000000000000000..43b46920bcb402333d6841e7ab6d792b6709da16
--- /dev/null
+++ b/public_html/deployment/plugins/rollout/awx/lang_en.json
@@ -0,0 +1,26 @@
+{
+    "plugin_name": "AWX",
+    "description": "Send https POST request to AWX",
+
+    "url-label": "AWX API url",
+    "url-hint": "Url to AWX REST API including /api/v2",
+    "user-label": "API user",
+    "user-hint": "AWX account to access the API",
+    "password-label": "Password",
+    "password-hint": "Password of AWX API user",
+    "jobtemplate-label": "ID of the job template",
+    "jobtemplate-hint": "Get the id of the job template from AWX WebGUI by reading from the address bar",
+    "tags-label": "Tags",
+    "tags-hint": "Ansible tags if the installation could be done without executing the complete playbook",
+    
+    "limit-label": "Limit",
+    "limit-hint": "Serverlist to install application",
+    "extravars-label": "Extravars",
+    "extravars-hint": "Extravars in JSON syntax",
+
+    "inventory-label": "Inventory ID",
+    "inventory-hint": "AWX inventory to define phase and set of hosts",
+
+    "endoffile": ""
+    
+}
\ No newline at end of file
diff --git a/public_html/deployment/plugins/rollout/awx/rollout_awx.php b/public_html/deployment/plugins/rollout/awx/rollout_awx.php
new file mode 100644
index 0000000000000000000000000000000000000000..ff12d4c3c4e8280a452afb2924e31a753c75dc2d
--- /dev/null
+++ b/public_html/deployment/plugins/rollout/awx/rollout_awx.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * 
+ * Rollout plugin - default
+ * 
+ * no action
+ *
+ * @author axel
+ */
+class rollout_awx extends rollout_base {
+
+    /**
+     * check requirements if the plugin could work
+     */
+    public function checkRequirements() {
+        // no specific checks needed ... always true
+        return true;
+    }
+
+    /**
+     * check access to a deploy target
+     */
+    public function checkConnectionToTarget() {
+        // do nothing ... always true
+        return true;
+    }
+
+    public function getDeployCommands($sPhase){
+        $aReturn=array();
+        $aConfig=$this->getConfig($sPhase);
+        if(!(int)$aConfig['inventory']){
+            $aReturn[]='echo "ERROR: no inventory was given."; exit 1';
+        }
+        $sJson='{ 
+        "inventory": "'.$aConfig['inventory'].'",
+            "limit": "'.$aConfig['limit'].'",
+            "job_tags": "'.$aConfig['tags'].'", 
+            "extra_vars": '.$aConfig['extravars'].' 
+        }';
+        $sAuth=($aConfig['user'] ? '--user '.$aConfig['user'].':'.$aConfig['password'] : '');
+        $aReturn[]="curl -f -k -H 'Content-Type: application/json' -XPOST -d '".$sJson."' $sAuth ".$aConfig['url']."/job_templates/".$aConfig['jobtemplate']."/launch/";
+        return $aReturn;
+    }
+    
+
+}
diff --git a/public_html/deployment/plugins/rollout/default/info.json b/public_html/deployment/plugins/rollout/default/info.json
index b6f00239d4aeca148685ba6910b998995915184e..2e1dd3e59498e10bd2d3b851b6ac54d08a46ccda 100644
--- a/public_html/deployment/plugins/rollout/default/info.json
+++ b/public_html/deployment/plugins/rollout/default/info.json
@@ -1,7 +1,7 @@
 {
     "name": "default",
     "description": "Default rollout plugin - doing no action",
-    "author": "Axel Hahn; University odf Bern; Institute for Medical education",
+    "author": "Axel Hahn; University of Bern; Institute for Medical education",
     
     "version": "1.0",
     "url": "[included]",
diff --git a/public_html/deployment/plugins/rollout/default/rollout_default.php b/public_html/deployment/plugins/rollout/default/rollout_default.php
index b0e1df57041a0e303b0cd6b448cb7fa59353ba49..a8999a4495afeee275b36ce98a397c455cafdd7d 100644
--- a/public_html/deployment/plugins/rollout/default/rollout_default.php
+++ b/public_html/deployment/plugins/rollout/default/rollout_default.php
@@ -29,7 +29,7 @@ class rollout_default extends rollout_base {
         return $this->_t('no-cfg');
     }
 
-    public function renderFormdata4Phase() {
+    public function renderFormdata4Phase($sPhase) {
         return $this->_t('no-cfg');
     }    
 }
diff --git a/public_html/deployment/plugins/rollout/ssh/info.json b/public_html/deployment/plugins/rollout/ssh/info.json
index e148fb8c8dfaf33b63cd528bf76a55ae3a412c63..5eaa7e313364093ebdf604e5568230103cdec7f5 100644
--- a/public_html/deployment/plugins/rollout/ssh/info.json
+++ b/public_html/deployment/plugins/rollout/ssh/info.json
@@ -1,7 +1,7 @@
 {
     "name": "SSH",
     "description": "Run a SSH command on remote targets.",
-    "author": "Axel Hahn; University odf Bern; Institute for Medical education",
+    "author": "Axel Hahn; University of Bern; Institute for Medical education",
     
     "version": "1.0",
     "url": "[included]",
@@ -30,12 +30,7 @@
                 "default": "example: sudo echo OK"
             }
         },
-        "project": {
-            "projectfile": {
-                "type": "text",
-                "default": ""
-            }     
-        },
+        "project": {},
         "phase": {
             "hosts": {
                 "type": "text",
diff --git a/public_html/deployment/plugins/rollout/ssh/lang_de.json b/public_html/deployment/plugins/rollout/ssh/lang_de.json
index 8c73bfed5bb0077fc11ef21dbde4028e38f819aa..ec6a635859ec68a8e794ef5350d84fd26ba9731e 100644
--- a/public_html/deployment/plugins/rollout/ssh/lang_de.json
+++ b/public_html/deployment/plugins/rollout/ssh/lang_de.json
@@ -2,11 +2,6 @@
     "plugin_name": "SSH",
     "description": "Ein SSH Kommando auf einem Zielsystem starten.",
     
-
-    "section-required": "Erforderliche Angaben",
-    "section-optional": "optionale Angaben",
-    "section-commands": "Kommandos",
-
     "user-label": "SSH User",
     "user-hint": "Remote Account zu dem sich auf dem Zielsystem verbunden wird.",
     "command-label": "Kommando",
@@ -18,5 +13,8 @@
     "testcommand-label": "Test-Remote-Kommando",
     "testcommand-hint": "Auf dem Zielsystem zu startendes Testkommando, ob ein Rollout bereit w&auml;re.",
 
+    "hosts-label": "Zielsysteme",
+    "hosts-hint": "Namen von Hosts, auf die der SSH Aufruf erfolgen soll. Mehrere Server kommaseparaiert angeben.",
+
     "endoffile": ""
 }
\ No newline at end of file
diff --git a/public_html/deployment/plugins/rollout/ssh/lang_en.json b/public_html/deployment/plugins/rollout/ssh/lang_en.json
index 1a21a39a114fb69e6574f5638f9131d4281b0564..4735d7e5f928bfd3c38f26a62eac91a83935a532 100644
--- a/public_html/deployment/plugins/rollout/ssh/lang_en.json
+++ b/public_html/deployment/plugins/rollout/ssh/lang_en.json
@@ -1,4 +1,18 @@
 {
     "plugin_name": "SSH",
-    "description": "Run a command on a remote system via SSH ."
+    "description": "Run a command on a remote system via SSH .",
+
+    "user-label": "SSH user",
+    "user-hint": "Remote account to conect with on the target system",
+    "command-label": "Command",
+    "command-hint": "Command to execute on the target system",
+    "privatekey-label": "Filename of private key",
+    "privatekey-hint": "Full path to the private key file, needed if it differs from default (i.e. ~/.ssh/id_rsa)",
+    "addkeycommand-label": "known_hosts command",
+    "addkeycommand-hint": "Command to insert or update the ssh hostkey in the known_hosts file",
+    "testcommand-label": "Test remote command",
+    "testcommand-hint": "test command on the target system if the rollout can be done",
+
+    "hosts-label": "Target hosts",
+    "hosts-hint": "Hostnames to connect with ssh and to install the packages. Multiple hosts divide with a comma."
 }
\ No newline at end of file
diff --git a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php
index df5fa0c90b7af41fc58064450bc10281cdb7be85..66eaf98c61dc04d48ac07e8b8ba7cdda50def196 100644
--- a/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php
+++ b/public_html/deployment/plugins/rollout/ssh/rollout_ssh.php
@@ -26,6 +26,25 @@ class rollout_ssh extends rollout_base {
         return true;
     }
 
+    public function getDeployCommands($sPhase){
+        $aReturn=array();
+        $aConfig=$this->getConfig($sPhase);
+        $aTargethosts = explode(',', $aConfig['hosts']);
+        
+        // loop over hosts and create shell commands for it
+        foreach(explode(',', $aConfig['hosts']) as $sMyHost){
+            $aReturn[]= sprintf($aConfig['addkeycommand'], $sMyHost, $sMyHost);
+            $aReturn[]='ssh '
+                    . ($aConfig['privatekey'] ? '-i \''.$aConfig['privatekey'].'\' ' : '')
+                    . ($aConfig['user'] ? $aConfig['user'].'@' : '')
+                    . $sMyHost.' '
+                    . '\''.$aConfig['command'].'\''
+                    ;
+        }
+        
+        return $aReturn;
+    }
+    
     /**
      * form fields for project settings
      * @return type