Ejemplo n.º 1
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.º 2
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
        """

        # 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, 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 = iondb.rundb.models.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
            iondb.rundb.models.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.º 3
0
    def search_for_plugins(self, plugin_name_list=[]):
        """ Scan folder for uninstalled or upgraded plugins
            Returns number of plugins installed or upgraded
        """
        basedir = self.pluginroot
        # Basedir - typically '/results/plugins', passed in args
        if not os.path.exists(basedir):
            return None
        plugin_directories = (plugin_name_list
                              if plugin_name_list else os.listdir(basedir))

        # reset permissions assuming for all of the non supported plugins
        for i in plugin_directories:
            if i in ["scratch", "implementations", "archive"]:
                continue
            if not getPackageName(os.path.join(basedir, i)):
                try:
                    subprocess.check_call([
                        "sudo",
                        "/opt/ion/iondb/bin/ion_plugin_migrate_permissions.py",
                        i,
                    ])
                except Exception:
                    logger.exception("Failed to change permissions")

        logger.debug("Scanning %s for plugin folders", basedir)
        # only list files in the 'plugin' directory if they are actually folders
        folder_list = list()

        for i in plugin_directories:
            if i in ["scratch", "implementations", "archive"]:
                continue
            full_path = os.path.join(basedir, i)
            if not os.path.isdir(full_path):
                continue
            (plugin_script, islaunch) = self.find_pluginscript(full_path, i)
            if not plugin_script or not os.path.exists(plugin_script):
                logger.info(
                    "Non-plugin in plugin folder '%s': '%s', '%s'",
                    basedir,
                    i,
                    plugin_script,
                )
                continue
            # Candidate Plugin Found
            folder_list.append((i, full_path, plugin_script))

        # Pre-scan plugins - one big celery task rather than many small ones
        pluginlist = [(n, s, None) for n, p, s in folder_list]
        infocache = {}
        try:
            infocache = iondb.plugins.tasks.scan_all_plugins(pluginlist)
        except Exception as exc:
            logger.exception(
                "Failed to rescan plugin info in background task" + str(exc))

        count = 0
        for pname, full_path, plugin_script in folder_list:
            # Do early check to see if plugin script is valid
            # - cannot install without name and version.
            info = infocache.get(plugin_script)
            if not info:
                logger.error("Missing info for %s", plugin_script)
                continue

            if "version" not in info:
                logger.error("Missing VERSION info for %s", plugin_script)
                # Cannot install versionless plugin
                continue

            # Quick skip of already installed plugins
            # if iondb.rundb.models.Plugin.objects.filter(pname=pname, version=info.get('version'), active=True).exists():
            #    continue

            # For now, install handles "reinstall" or "refresh" cases.
            try:
                (newplugin, updated) = self.install(pname, full_path,
                                                    plugin_script, info)
            except ValueError:
                logger.exception(
                    "Plugin not installable due to error querying name and version '%s'",
                    full_path,
                )
            if updated:
                count += 1

        return count
Ejemplo n.º 4
0
    def search_for_plugins(self, basedir=None):
        """ Scan folder for uninstalled or upgraded plugins
            Returns number of plugins installed or upgraded
        """
        if not basedir:
            basedir = self.pluginroot

        # Basedir - typically '/results/plugins', passed in args
        if not os.path.exists(basedir):
            return None

        # reset permissions assuming for all of the non supported plugins
        for i in os.listdir(basedir):
            if i in ["scratch", "implementations", "archive"]:
                continue
            if not getPackageName(os.path.join(basedir, i)):
                try:
                    subprocess.check_call(['sudo', '/opt/ion/iondb/bin/ion_plugin_migrate_permissions.py', i])
                except:
                    logger.exception("Failed to change permissions")


        logger.debug("Scanning %s for plugin folders", basedir)
        # only list files in the 'plugin' directory if they are actually folders
        folder_list = []
        for i in os.listdir(basedir):
            if i in ["scratch", "implementations", "archive"]:
                continue
            full_path = os.path.join(basedir, i)
            if not os.path.isdir(full_path):
                continue
            (plugin_script, islaunch) = self.find_pluginscript(full_path, i)
            if not plugin_script or not os.path.exists(plugin_script):
                logger.info("Non-plugin in plugin folder '%s': '%s', '%s'", basedir, i, plugin_script)
                continue
            # Candidate Plugin Found
            folder_list.append((i, full_path, plugin_script))

        ## Pre-scan plugins - one big celery task rather than many small ones
        pluginlist = [ (n,s,None) for n,p,s in folder_list ]
        infocache = {}
        try:
            infocache = iondb.plugins.tasks.scan_all_plugins(pluginlist)
        except Exception as exc:
            logger.exception("Failed to rescan plugin info in background task" + str(exc))

        count = 0
        for pname, full_path, plugin_script in folder_list:
            # Do early check to see if plugin script is valid
            # - cannot install without name and version.
            info = infocache.get(plugin_script)
            if not info:
                logger.error("Missing info for %s", plugin_script)
                continue

            if 'version' not in info:
                logger.error("Missing VERSION info for %s", plugin_script)
                # Cannot install versionless plugin
                continue

            # Quick skip of already installed plugins
            #if iondb.rundb.models.Plugin.objects.filter(pname=pname, version=info.get('version'), active=True).exists():
            #    continue

            # For now, install handles "reinstall" or "refresh" cases.
            try:
                (newplugin, updated) = self.install(pname, full_path, plugin_script, info)
            except ValueError:
                logger.exception("Plugin not installable due to error querying name and version '%s'", full_path)
            if updated:
                count += 1

        return count