Ejemplo n.º 1
0
 def test_configure_plugins_plugin_refresh(self):
     _id = 0
     plugin = Plugin()
     plugin.name = "Foo"
     #skip call to DB and return this Plugin stub instead
     when(iondb.rundb.configure.views).get_object_or_404(Plugin,_id).thenReturn(plugin)
     
     data = {}
     response = self.client.get('configure/plugins/%s/refresh/' % _id, data)
     self.assertEqual(response.status_code, 200)
     self.assertEqual(response.context['plugin'], plugin)
     self.assertEqual(response.context['action'], reverse('api_dispatch_info', kwargs={'resource_name': 'plugin', 'api_name': 'v1', 'pk': int(_id)}) + '?use_cache=false')
     self.assertEqual(response.context['method'], 'get')
     
     self.assertIn('Refresh Plugin %s Information' % plugin.name, response.content)
Ejemplo n.º 2
0
    def validateStep(self):
        """This method overrides the abstract class'es implementation and will validate each of the plugins individually."""

        # we don't need to perform this validation for templates
        if self.sh_type in StepHelperType.TEMPLATE_TYPES:
            return

        self.updateSavedObjectsFromSavedFields()
        # reset the validation errors
        self.validationErrors.clear()

        for plugin_id, values in self.savedObjects[
                PluginFieldNames.PLUGINS].items():
            plugin_model = Plugin.objects.get(id=plugin_id)
            if values[PluginFieldNames.
                      SELECTED] and plugin_model.requires_configuration:
                configuration = dict(
                ) if values[PluginFieldNames.CONFIG] is None else json.loads(
                    values[PluginFieldNames.CONFIG])
                plugin_validation_errors = Plugin.validate(
                    plugin_id, configuration, 'Automatic')

                if plugin_validation_errors:
                    self.validationErrors[values[PluginFieldNames.PLUGIN].
                                          name] = plugin_validation_errors
Ejemplo n.º 3
0
def validate_plugin_configurations(selected_plugins):
    """this will validate all of the plugins as part of the plan.  It returns a list of all of the validation error messages"""
    validation_messages = list()
    for name, plugin_parameters in selected_plugins.items():
        try:
            configuration = plugin_parameters.get('userInput', {}) or {}
            plugin_model = Plugin.objects.get(name=plugin_parameters['name'], active=True)
            validation_messages += Plugin.validate(plugin_model.id, configuration, 'pipeline')
        except Exception as exc:
            validation_messages += [str(exc)]
    return validation_messages
Ejemplo n.º 4
0
    def upgrade_helper(self, plugin, current_path=None):
        # TODO: Rename this method to be _upgrade_helper
        """ Handle additional tasks during upgrade of plugin.
            Currently deactivates old versions of plugins to avoid version conflicts.
            Pass in the new plugin object - the one you want to keep.
        """
        from iondb.rundb.models import Plugin

        # Some behavior here is just to disable old versions during upgrade.
        count = 0
        oldplugin = None

        # Get all plugins with the same name and different version
        for oldp in Plugin.objects.filter(name=plugin.name).exclude(
                version=plugin.version):
            # FIXME - multi-version support needs new behavior here
            if oldp.active or oldp.path:
                logger.info("Disabling old version of plugin %s v%s",
                            oldp.name, oldp.version)
                count += 1
            else:
                continue

            # Plugins other than our version are disabled

            # Uninstall - Allows manual install of plugin previously installed via zeroinstall
            # Careful not to delete new path if installed in-place!!!
            current_path = current_path or plugin.path
            if oldp.path and oldp.path != current_path:
                # farm the uninstall off to the plugin daemon
                Plugin.Uninstall(oldp.id)
            else:
                oldp.path = ""
            if oldp.active:
                oldp.active = False
            oldp.save()

            # Important! This is passed back to preserve autorun and selected settings
            oldplugin = oldp

        if count:
            logger.debug(
                "Deactivated %d old versions while upgrading to %s v%s",
                count,
                plugin.name,
                plugin.version,
            )

        return count, oldplugin
Ejemplo n.º 5
0
    def test_configure_plugins_plugin_refresh(self):
        _id = 0
        plugin = Plugin()
        plugin.name = "Foo"
        #skip call to DB and return this Plugin stub instead
        when(iondb.rundb.configure.views).get_object_or_404(
            Plugin, _id).thenReturn(plugin)

        data = {}
        response = self.client.get('configure/plugins/%s/refresh/' % _id, data)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context['plugin'], plugin)
        self.assertEqual(
            response.context['action'],
            reverse('api_dispatch_info',
                    kwargs={
                        'resource_name': 'plugin',
                        'api_name': 'v1',
                        'pk': int(_id)
                    }) + '?use_cache=false')
        self.assertEqual(response.context['method'], 'get')

        self.assertIn('Refresh Plugin %s Information' % plugin.name,
                      response.content)
Ejemplo n.º 6
0
def validate_plugin_configurations(selected_plugins):
    """this will validate all of the plugins as part of the plan.  It returns a list of all of the validation error messages"""
    validation_messages = list()
    if not selected_plugins:
        return validation_messages
    for name, plugin_parameters in list(selected_plugins.items()):
        try:
            configuration = plugin_parameters.get("userInput", {}) or {}
            plugin_model = Plugin.objects.get(name=plugin_parameters["name"],
                                              active=True)
            if plugin_model.requires_configuration:
                validation_messages += Plugin.validate(plugin_model.id,
                                                       configuration)
        except Exception as exc:
            validation_messages += [str(exc)]
    return validation_messages
Ejemplo n.º 7
0
    def validateStep(self):
        """This method overrides the abstract class'es implementation and will validate each of the plugins individually."""

        # we don't need to perform this validation for templates
        if self.sh_type in StepHelperType.TEMPLATE_TYPES:
            return

        self.updateSavedObjectsFromSavedFields()
        # reset the validation errors
        self.validationErrors.clear()

        for plugin_id, values in self.savedObjects[PluginFieldNames.PLUGINS].items():
            plugin_model = Plugin.objects.get(id=plugin_id)
            if values[PluginFieldNames.SELECTED] and plugin_model.requires_configuration:
                configuration = dict() if values[PluginFieldNames.CONFIG] is None else json.loads(values[PluginFieldNames.CONFIG])
                plugin_validation_errors = Plugin.validate(plugin_id, configuration, 'Automatic')

                if plugin_validation_errors:
                    self.validationErrors[values[PluginFieldNames.PLUGIN].name] = plugin_validation_errors
Ejemplo n.º 8
0
    def launchPlugins(self,
                      result_pk,
                      plugins,
                      net_location,
                      username,
                      runlevel=RunLevel.DEFAULT,
                      params={}):
        """
        Launch multiple plugins with dependencies
        For multi-runlevel plugins the input 'plugins' is common for all runlevels
        """
        msg = ''
        logger.debug("[launchPlugins] result %s requested plugins: %s" %
                     (result_pk, ','.join(plugins.keys())))

        try:
            # get plugins to run for this runlevel
            plugins, plugins_to_run, satisfied_dependencies = get_plugins_to_run(
                plugins, result_pk, runlevel)

            if len(plugins_to_run) > 0:
                logger.debug(
                    "[launchPlugins] runlevel: %s, depsolved launch order: %s"
                    % (runlevel, ','.join(plugins_to_run)))
            else:
                logger.debug(
                    "[launchPlugins] no plugins to run at runlevel: %s" %
                    runlevel)
                return plugins, msg

            result = Results.objects.get(pk=result_pk)
            report_dir = result.get_report_dir()
            url_root = result.reportWebLink()

            # get pluginresult owner - must be a valid TS user
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                user = User.objects.get(pk=1)
                logger.error(
                    "Invalid user specified for plugin launch: %s, will use %s"
                    % (username, user.username))

            for name in plugins_to_run:
                try:
                    p = plugins[name]
                    # get params for this plugin, make empty json value if doesn't exist
                    plugin_params = params.setdefault('plugins',
                                                      {}).setdefault(name, {})

                    # Get pluginresult for multi-runlevel plugins or if specified to be reused by manual launch
                    pr = None
                    pluginresult_pk = p.get(
                        'pluginresult') or plugin_params.get('pluginresult')

                    if pluginresult_pk:
                        logger.debug("Searching for PluginResult: %s",
                                     pluginresult_pk)
                        try:
                            pr = result.pluginresult_set.get(
                                pk=pluginresult_pk)
                        except:
                            logger.error(
                                "Failed to find pluginresult for plugin %s, result %s: %s"
                                % (name, result.resultsName, pluginresult_pk))
                            pr = None

                    # Create new pluginresult - this is the most common path
                    if not pr:
                        pr = PluginResult.objects.create(result_id=result_pk,
                                                         plugin_id=p['id'],
                                                         owner=user)
                        logger.debug(
                            "New pluginresult id=%s created for plugin %s and result %s."
                            % (pr.pk, name, result.resultsName))
                        # Always create new, unique output folder.
                        # Never fallback to old *_out format.
                        plugin_output = pr.path(create=True, fallback=False)
                    else:
                        # Use existing output folder
                        plugin_output = pr.path(create=False)

                    p['results_dir'] = plugin_output
                    p['pluginresult'] = pr.pk
                    p, holding_for = add_hold_jid(p, plugins, runlevel,
                                                  satisfied_dependencies)

                    start_json = make_plugin_json(
                        result_pk, report_dir, p, plugin_output,
                        net_location, url_root, username, runlevel,
                        params.get('blockId', ''),
                        params.get('block_dirs', ["."]),
                        plugin_params.get('instance_config', {}))

                    # Pass on run_mode (launch source - manual/instance, pipeline)
                    run_mode = params.get('run_mode', '')
                    start_json['runplugin']['run_mode'] = run_mode

                    # add dependency info to startplugin json
                    if p.get('depends') and isinstance(p['depends'], list):
                        start_json['depends'] = {}
                        for depends_name in p['depends']:
                            if depends_name in satisfied_dependencies:
                                start_json['depends'][
                                    depends_name] = satisfied_dependencies[
                                        depends_name]
                            elif depends_name in plugins and plugins[
                                    depends_name].get('pluginresult'):
                                start_json['depends'][depends_name] = {
                                    'pluginresult':
                                    plugins[depends_name]['pluginresult'],
                                    'version':
                                    plugins[depends_name].get('version', ''),
                                    'pluginresult_path':
                                    plugins[depends_name].get('results_dir')
                                }

                    # prepare for launch: updates config, sets pluginresults status, generates api key
                    pr.prepare()

                    # perform the validation of the plugin configuration here
                    pr.validation_errors = {
                        'validation_errors':
                        Plugin.validate(p['id'], start_json['pluginconfig'],
                                        run_mode)
                    }
                    pr.save()
                    if pr.validation_errors.get('validation_errors', list()):
                        continue

                    # update startplugin json with pluginresult info
                    start_json['runinfo']['pluginresult'] = pr.pk
                    start_json['runinfo']['api_key'] = pr.apikey

                    # NOTE: Job is held on start, and subsequently released
                    # to avoid any race condition on updating job queue status
                    # launch plugin
                    jid = SGEPluginJob(start_json, hold=True)

                    if jid:
                        # Update pluginresult status
                        prj, created = PluginResultJob.objects.get_or_create(
                            plugin_result=pr,
                            run_level=runlevel,
                            grid_engine_jobid=jid,
                            state='Queued',
                            config=start_json['pluginconfig'],
                        )
                        prj.save()

                        # Release now that jobid and queued state are set.
                        _session.control(
                            jid,
                            drmaa.JobControlAction.RELEASE)  # no return value

                    msg += 'Plugin: %s result: %s, jid %s, depends %s, holding for %s \n' % (
                        p.get('name', ''), result.resultsName, jid,
                        p.get('depends', []), holding_for)

                    if runlevel != RunLevel.BLOCK:
                        p['jid'] = jid
                    else:
                        p.setdefault('block_jid', []).append(jid)

                except Exception as exc:
                    logger.error(traceback.format_exc())
                    msg += 'ERROR: Plugin %s failed to launch.\n' % p['name']
                    pr = PluginResult.objects.get(pk=pr.pk)
        except:
            logger.error(traceback.format_exc())
            msg += 'ERROR: Failed to launch requested plugins.'

        return plugins, msg
Ejemplo n.º 9
0
    def install(self, pname, full_path, launch_script=None, info=None):
        """Install (or upgrade) a plugin given a src path.

        Safe to use for already installed plugins, with these cautions:
        * Will reactivate if previously marked inactive.
        * May refresh pluginconfig or upgrade to new version if found.

        TODO - handle embedded version number in path
        """
        from iondb.rundb.models import Plugin

        # Cleanup, normalize and validate inputs
        pname = pname.strip()
        full_path = os.path.normpath(full_path)
        if not os.path.exists(full_path):
            logger.error("Path specified for install does not exist '%s'",
                         full_path)

        # Check Plugin Blacklist:
        if pname in ("scratch", "implementations"):
            logger.error(
                "Scratch and Implementations are reserved folders, and cannot be installed."
            )
            return None, False

        if not launch_script:
            (launch_script,
             isLaunch) = self.find_pluginscript(full_path, pname)

        logger.debug("Plugin Info: %s", info)
        if not info:
            # Worst case, should have been pre-fetched above
            logger.error("Need to rescan plugin info..")
            info = PluginManager.get_plugininfo(pname,
                                                launch_script,
                                                existing_pk=None,
                                                use_cache=False)
            logger.debug("Plugin Rescan Info: %s", info)
        if info is None:
            raise ValueError("No plugininfo for '%s' in '%s'" %
                             (pname, launch_script))

        version = info.get("version", "0")
        majorBlock = info.get("major_block", False)

        # Only used if new plugin record is created
        packageName = getPackageName(full_path)
        plugin_defaults = {
            "path": full_path,
            "date": datetime.datetime.now(),
            "active": True,  # auto activate new versions
            "selected": True,  # Auto enable new plugins
            "majorBlock": majorBlock,
            "description": info.get("docs", None),
            "userinputfields": info.get("config", None),
            "packageName":
            packageName if not "ion-plugins" in packageName else "",
        }

        logger.debug("Plugin Install/Upgrade checking for plugin: %s %s",
                     pname, version)
        # needs_save is aka created. Tracks if anything changed.
        p, needs_save = Plugin.objects.get_or_create(name=pname,
                                                     version=version,
                                                     defaults=plugin_defaults)

        # update the plugin package name
        if plugin_defaults["packageName"] != p.packageName:
            p.packageName = plugin_defaults["packageName"]
            p.save()

        if needs_save:
            # Newly created plugin - never before seen with this name and version
            logger.info("Installing New Plugin %s v%s at '%s'", pname, version,
                        full_path)
            # Set pluginconfig.json if needed - only for new installs / version changes
            if self.set_pluginconfig(p):
                logger.info(
                    "Loaded new pluginconfig.json values for Plugin %s v%s at '%s'",
                    pname,
                    version,
                    full_path,
                )
        else:
            logger.debug("Existing plugin found: %s %s [%d]", p.name,
                         p.version, p.pk)

        p.updateFromInfo(info)

        # Handle any upgrade behaviors - deactivating old versions, etc.
        (count, oldp) = self.upgrade_helper(p, current_path=full_path)

        # Preserve old selected/defaultSelected state
        if count and oldp:
            p.selected = oldp.selected
            p.defaultSelected = oldp.defaultSelected
            # Merge stock config with previous config, preserving user settings
            p.config.update(oldp.config)

        # Be careful with path handling -- FIXME
        if not p.path:
            # 1. path was empty - set path and reactivate
            # Reinstall previously removed and inactive plugin - files were restored
            logger.info(
                "Reactivating previously uninstalled Plugin %s v%s at '%s'",
                pname,
                version,
                full_path,
            )
            if self.enable(p, full_path):
                needs_save = True

        elif not os.path.exists(p.path):
            # 2. path was set, but folder is missing - replace
            # Prior path was invalid, but caller insists full_path exists
            needs_save = p.path == full_path  # only if changed
            if needs_save:
                logger.info(
                    "Changing Plugin path value %s v%s to '%s' (was '%s')",
                    pname,
                    version,
                    full_path,
                    p.path,
                )

            if os.path.exists(full_path):
                self.enable(p, full_path)

        elif p.path != full_path:
            # 3. path was set and exists
            # FIXME - for now, replace old plugin with new.
            # TODO - With multi-version install, ignore duplicate of same version
            logger.info(
                "Found relocated Plugin %s v%s at '%s' (was '%s')",
                pname,
                version,
                full_path,
                p.path,
            )

            # uninstall the plugin
            Plugin.Uninstall(p.id)

            # And restore with full_path
            self.enable(p, full_path)

        # If the path exists, it is an active. Inactive plugins are removed
        if not p.active and os.path.exists(p.path):
            logger.info(
                "Reactivating plugin marked inactive but present on filesystem %s v%s at '%s'",
                pname,
                version,
                full_path,
            )
            self.enable(p)

        if not p.script:
            p.script = os.path.basename(launch_script)
            if launch_script != os.path.join(p.path, p.script):
                logger.error(
                    "Launch Script is not in plugin folder: '%s' '%s'",
                    p.path,
                    launch_script,
                )
            needs_save = True

        # create media symlinks if needed
        pluginMediaSrc = os.path.join(p.path, "pluginMedia")
        if os.path.exists(pluginMediaSrc):
            pluginMediaDst = os.path.join("/results/pluginMedia", p.name)
            try:
                if os.path.lexists(pluginMediaDst):
                    os.unlink(pluginMediaDst)
                # note os.path.exists returns False for broken symlinks
                os.symlink(
                    os.path.join(p.path, "pluginMedia"),
                    os.path.join("/results/pluginMedia", p.name),
                )
            except OSError:
                logger.exception("Failed to create pluginMedia Symlink: %s",
                                 pluginMediaDst)
            # No need to set needs_save - no db data changed

        # Update majorBlock to current setting
        if p.majorBlock != majorBlock:
            p.majorBlock = majorBlock
            needs_save = True

        if needs_save:
            p.save()

        # Return true if anything changed. Always return plugin object
        return p, needs_save
Ejemplo n.º 10
0
    def launchPlugins(self, result_pk, plugins, net_location, username, runlevel=RunLevel.DEFAULT, params={}):
        """
        Launch multiple plugins with dependencies
        For multi-runlevel plugins the input 'plugins' is common for all runlevels
        """
        msg = ''
        logger.debug("[launchPlugins] result %s requested plugins: %s" % (result_pk, ','.join(plugins.keys())))

        try:
            # get plugins to run for this runlevel
            plugins, plugins_to_run, satisfied_dependencies = get_plugins_to_run(plugins, result_pk, runlevel)

            if len(plugins_to_run) > 0:
                logger.debug("[launchPlugins] runlevel: %s, depsolved launch order: %s" % (runlevel, ','.join(plugins_to_run)))
            else:
                logger.debug("[launchPlugins] no plugins to run at runlevel: %s" % runlevel)
                return plugins, msg

            result = Results.objects.get(pk=result_pk)
            report_dir = result.get_report_dir()
            url_root = result.reportWebLink()

            # get pluginresult owner - must be a valid TS user
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                user = User.objects.get(pk=1)
                logger.error("Invalid user specified for plugin launch: %s, will use %s" % (username, user.username))

            for name in plugins_to_run:
                try:
                    p = plugins[name]
                    # get params for this plugin, make empty json value if doesn't exist
                    plugin_params = params.setdefault('plugins', {}).setdefault(name, {})

                    # Get pluginresult for multi-runlevel plugins or if specified to be reused by manual launch
                    pr = None
                    pluginresult_pk = p.get('pluginresult') or plugin_params.get('pluginresult')

                    if pluginresult_pk:
                        logger.debug("Searching for PluginResult: %s", pluginresult_pk)
                        try:
                            pr = result.pluginresult_set.get(pk=pluginresult_pk)
                        except:
                            logger.error("Failed to find pluginresult for plugin %s, result %s: %s" % (name, result.resultsName, pluginresult_pk))
                            pr = None

                    # Create new pluginresult - this is the most common path
                    if not pr:
                        pr = PluginResult.objects.create(result_id=result_pk, plugin_id=p['id'], owner=user)
                        logger.debug("New pluginresult id=%s created for plugin %s and result %s." % (pr.pk, name, result.resultsName))
                        # Always create new, unique output folder.
                        # Never fallback to old *_out format.
                        plugin_output = pr.path(create=True, fallback=False)
                    else:
                        # Use existing output folder
                        plugin_output = pr.path(create=False)

                    p['results_dir'] = plugin_output
                    p['pluginresult'] = pr.pk
                    p, holding_for = add_hold_jid(p, plugins, runlevel, satisfied_dependencies)

                    start_json = make_plugin_json(result_pk, report_dir, p, plugin_output, net_location, url_root, username, runlevel,
                                                  params.get('blockId', ''), params.get('block_dirs', ["."]), plugin_params.get('instance_config', {}))

                    # Pass on run_mode (launch source - manual/instance, pipeline)
                    run_mode = params.get('run_mode', '')
                    start_json['runplugin']['run_mode'] = run_mode

                    # add dependency info to startplugin json
                    if p.get('depends') and isinstance(p['depends'], list):
                        start_json['depends'] = {}
                        for depends_name in p['depends']:
                            if depends_name in satisfied_dependencies:
                                start_json['depends'][depends_name] = satisfied_dependencies[depends_name]
                            elif depends_name in plugins and plugins[depends_name].get('pluginresult'):
                                start_json['depends'][depends_name] = {
                                    'pluginresult': plugins[depends_name]['pluginresult'],
                                    'version': plugins[depends_name].get('version', ''),
                                    'pluginresult_path': plugins[depends_name].get('results_dir')
                                }

                    # prepare for launch: updates config, sets pluginresults status, generates api key
                    pr.prepare()

                    # perform the validation of the plugin configuration here
                    pr.validation_errors = {
                        'validation_errors': Plugin.validate(p['id'], start_json['pluginconfig'], run_mode)
                    }
                    pr.save()
                    if pr.validation_errors.get('validation_errors', list()):
                        continue

                    # update startplugin json with pluginresult info
                    start_json['runinfo']['pluginresult'] = pr.pk
                    start_json['runinfo']['api_key'] = pr.apikey

                    # NOTE: Job is held on start, and subsequently released
                    # to avoid any race condition on updating job queue status
                    # launch plugin
                    jid = SGEPluginJob(start_json, hold=True)

                    if jid:
                        # Update pluginresult status
                        prj, created = PluginResultJob.objects.get_or_create(
                            plugin_result=pr,
                            run_level=runlevel,
                            grid_engine_jobid=jid,
                            state='Queued',
                            config=start_json['pluginconfig'],
                        )
                        prj.save()

                        # Release now that jobid and queued state are set.
                        _session.control(jid, drmaa.JobControlAction.RELEASE)  # no return value

                    msg += 'Plugin: %s result: %s, jid %s, depends %s, holding for %s \n' % (p.get('name', ''), result.resultsName, jid, p.get('depends', []), holding_for)

                    if runlevel != RunLevel.BLOCK:
                        p['jid'] = jid
                    else:
                        p.setdefault('block_jid', []).append(jid)

                except Exception as exc:
                    logger.error(traceback.format_exc())
                    msg += 'ERROR: Plugin %s failed to launch.\n' % p['name']
                    pr = PluginResult.objects.get(pk=pr.pk)
        except:
            logger.error(traceback.format_exc())
            msg += 'ERROR: Failed to launch requested plugins.'

        return plugins, msg