Fix plugin management with Jenkins restart

  - change plugin managent logic from per-plugin management to plugin set
  - for plugin set management add two parameters:
     * jenkins.client.plugin_remove_unwanted - to remove plugins not listed
       in jenkins.client.plugin
     * jenkins.client.plugin_force_remove - to forcible remove plugins
       including dependants but taking into account dependencies of installed
       plugins
  - update state waiting for jenkins availability
  - change error catching in API crumb getting to not fail on 50x errors,
    to be able to handle exception in state(s)

Change-Id: Ia62af392b30f92c7fdff87ea17fce2cf284d6818
diff --git a/_states/jenkins_plugin.py b/_states/jenkins_plugin.py
index 0f80d51..a96afed 100644
--- a/_states/jenkins_plugin.py
+++ b/_states/jenkins_plugin.py
@@ -1,81 +1,9 @@
+import json
 import logging
+import time
 
-logger = logging.getLogger(__name__)
 
-install_plugin_groovy = """\
-import jenkins.model.*
-import java.util.logging.Logger
-
-def logger = Logger.getLogger("")
-def installed = false
-def exists = false
-def pluginName="${plugin}"
-def instance = Jenkins.getInstance()
-def pm = instance.getPluginManager()
-def uc = instance.getUpdateCenter()
-def needUpdateSites(maxOldInSec = 1800){
-  long oldestTs = 0
-  for (UpdateSite s : Jenkins.instance.updateCenter.siteList) {
-    if(oldestTs == 0 || s.getDataTimestamp()<oldestTs){
-       oldestTs = s.getDataTimestamp()
-    }
-  }
-   return (System.currentTimeMillis()-oldestTs)/1000 > maxOldInSec
-}
-
-if (!pm.getPlugin(pluginName)) {
-  if(needUpdateSites()) {
-     uc.updateAllSites()
-  }
-  def plugin = uc.getPlugin(pluginName)
-  if (plugin) {
-    plugin.deploy()
-    installed = true
-  }
-}else{
-    exists = true
-    print("EXISTS")
-}
-if (installed) {
-  instance.save()
-  if(${restart}){
-      instance.doSafeRestart()
-   }
-  print("INSTALLED")
-}else if(!exists){
-  print("FAILED")
-}
-"""  # noqa
-
-remove_plugin_groovy = """
-import jenkins.model.*
-import java.util.logging.Logger
-
-def logger = Logger.getLogger("")
-def installed = false
-def initialized = false
-
-def pluginName="${plugin}"
-def instance = Jenkins.getInstance()
-def pm = instance.getPluginManager()
-
-def actPlugin = pm.getPlugin(pluginName)
-if (!actPlugin) {
-   def pluginToInstall = Jenkins.instance.updateCenter.getPlugin(pluginName)
-   if(!pluginToInstall){
-      print("FAILED")
-   }else{
-      print("NOT PRESENT")
-   }
-} else {
-   actPlugin.disable()
-   actPlugin.archive.delete()
-   if({restart}){
-      instance.doSafeRestart()
-   }
-   print("REMOVED")
-}
-"""  # noqa
+log = logging.getLogger(__name__)
 
 
 def __virtual__():
@@ -85,63 +13,46 @@
     if 'jenkins_common.call_groovy_script' not in __salt__:
         return (
             False,
-            'The jenkins_plugin state module cannot be loaded: '
+            'The jenkins_node state module cannot be loaded: '
             'jenkins_common not found')
     return True
 
-
-def present(name, restart=False):
+def managed(name, plugins, remove_unwanted=False, force_remove=False):
     """
-    Jenkins plugin present state method, for installing plugins
+    Manage jenkins plugins
 
-    :param name: plugin name
-    :param restart: do you want to restart jenkins after plugin install?
+    :param name: salt resource name (usually 'jenkins_plugin_manage')
+    :param plugins: map containing plugin names and parameters
+    :param remove_unwanted: whether to remove not listed plugins
+    :param force_remove: force removing plugins recursively with all dependent plugins
     :returns: salt-specified state dict
     """
-    return _plugin_call(name, restart, install_plugin_groovy, [
-                        "INSTALLED", "EXISTS"])
+    log.info('Managing jenkins plugins')
+    template = __salt__['jenkins_common.load_template'](
+        'salt://jenkins/files/groovy/plugin.template',
+        __env__)
+    result = __salt__['jenkins_common.api_call'](name, template,
+                        [ 'UPDATED', 'NO CHANGES' ],
+                        {
+                            'plugin_list': json.dumps(plugins),
+                            'clean_unwanted': remove_unwanted,
+                            'force_remove': force_remove
+                        },
+                        'Manage Jenkins plugins')
+    log.debug('Got result: ' + json.dumps(result))
 
+    log.info('Checking if restart is required...')
+    # While next code is successful, we should wait for jenkins shutdown
+    # either:
+    #   - false returned by isQuietingDown()
+    #   - any error meaning that jenkins is unavailable (restarting)
+    wait = { 'result': True }
+    while (wait['result']):
+        wait = __salt__['jenkins_common.api_call']('jenkins_restart_wait',
+                'println Jenkins.instance.isQuietingDown()', [ 'true' ], {},
+                'Wait for jenkins restart')
+        if (wait['result']):
+            log.debug('Jenkins restart is required. Waiting...')
+        time.sleep(5)
 
-def absent(name, restart=False):
-    """
-    Jenkins plugin absent state method, for removing plugins
-
-    :param name: plugin name
-    :param restart: do you want to restart jenkins after plugin remove?
-    :returns: salt-specified state dict
-    """
-    return _plugin_call(name, restart, remove_plugin_groovy, [
-                        "REMOVED", "NOT PRESENT"])
-
-
-def _plugin_call(name, restart, template, success_msgs):
-    test = __opts__['test']  # noqa
-    ret = {
-        'name': name,
-        'changes': {},
-        'result': False,
-        'comment': '',
-    }
-    result = False
-    if test:
-        status = success_msgs[0]
-        ret['changes'][name] = status
-        ret['comment'] = 'Jenkins plugin %s %s' % (name, status.lower())
-    else:
-        call_result = __salt__['jenkins_common.call_groovy_script'](
-            template, {"plugin": name, "restart": "true" if restart else "false"})
-        if call_result["code"] == 200 and call_result["msg"] in success_msgs:
-            status = call_result["msg"]
-            if status == success_msgs[0]:
-                ret['changes'][name] = status
-            ret['comment'] = 'Jenkins plugin %s %s%s' % (name, status.lower(
-            ), ", jenkins restarted" if status == success_msgs[0] and restart else "")
-            result = True
-        else:
-            status = 'FAILED'
-            logger.error(
-                "Jenkins plugin API call failure: %s", call_result["msg"])
-            ret['comment'] = 'Jenkins plugin API call failure: %s' % (call_result[
-                "msg"])
-    ret['result'] = None if test else result
-    return ret
+    return result