def load(): """ Check available plugins and attempt to import them """ # Code is based on beaker-client's command.py script plugins = [] for filename in os.listdir(PLUGINS_PATH): if not filename.endswith(".py") or filename.startswith("_"): continue if not os.path.isfile(os.path.join(PLUGINS_PATH, filename)): continue plugin = filename[:-3] if plugin in FAILED_PLUGINS: # Skip loading plugins that already failed before continue try: __import__(PLUGINS.__name__, {}, {}, [plugin]) plugins.append(plugin) log.debug("Successfully imported {0} plugin".format(plugin)) except (ImportError, SyntaxError) as error: # Give a warning only when the plugin is configured message = "Failed to import {0} plugin ({1})".format(plugin, error) if Config().sections(kind=plugin): log.warn(message) else: log.debug(message) FAILED_PLUGINS.append(plugin) return plugins
def detect(): """ Detect available plugins and return enabled/configured stats Yields tuples of the form (section, statsgroup) sorted by the default StatsGroup order which maybe overriden in the config file. The 'section' is the name of the configuration section as well as the option used to enable those particular stats. """ # Load plugins and config plugins = load() config = Config() # Make sure that all sections have a valid plugin type defined for section in config.sections(): if section == 'general': continue try: type_ = config.item(section, 'type') except ConfigError: raise ConfigError( "Plugin type not defined in section '{0}'.".format(section)) if type_ not in plugins: raise ConfigError( "Invalid plugin type '{0}' in section '{1}'.".format( type_, section)) # Detect classes inherited from StatsGroup and return them sorted stats = [] for plugin in plugins: module = getattr(PLUGINS, plugin) for object_name in dir(module): statsgroup = getattr(module, object_name) # Filter out anything except for StatsGroup descendants if (not isinstance(statsgroup, (type, types.ClassType)) or not issubclass(statsgroup, StatsGroup) or statsgroup is StatsGroup or statsgroup is EmptyStatsGroup): continue # Search config for sections with type matching the plugin, # use order provided there or class default otherwise for section in config.sections(kind=plugin): try: order = int(config.item(section, "order")) except ConfigError: order = statsgroup.order except ValueError: log.warn("Invalid {0} stats order: '{1}'".format( section, config.item(section, "order"))) order = statsgroup.order stats.append((section, statsgroup, order)) log.info("Found {0}, an instance of {1}, order {2}".format( section, statsgroup.__name__, order)) # Custom stats are handled with a single instance if statsgroup.__name__ == "CustomStats": break for section, statsgroup, _ in sorted(stats, key=lambda x: x[2]): yield section, statsgroup
def session(self): """ Initialize the session """ if self._session is None: self._session = requests.Session() log.debug("Connecting to {0}".format(self.auth_url)) # Disable SSL warning when ssl_verify is False if not self.ssl_verify: requests.packages.urllib3.disable_warnings( InsecureRequestWarning) if self.auth_type == 'basic': basic_auth = (self.auth_username, self.auth_password) response = self._session.get( self.auth_url, auth=basic_auth, verify=self.ssl_verify) elif self.auth_type == "token": self.session.headers["Authorization"] = f"Bearer {self.token}" response = self._session.get( "{0}/rest/api/2/myself".format(self.url), verify=self.ssl_verify) else: gssapi_auth = HTTPSPNEGOAuth(mutual_authentication=DISABLED) response = self._session.get( self.auth_url, auth=gssapi_auth, verify=self.ssl_verify) try: response.raise_for_status() except requests.exceptions.HTTPError as error: log.error(error) raise ReportError( "Jira authentication failed. Check credentials or kinit.") if self.token_expiration: response = self._session.get( "{0}/rest/pat/latest/tokens".format(self.url), verify=self.ssl_verify) try: response.raise_for_status() token_found = None for token in response.json(): if token["name"] == self.token_name: token_found = token break if token_found is None: raise ValueError( f"Can't check validity for the '{self.token_name}' " f"token as it doesn't exist.") from datetime import datetime expiring_at = datetime.strptime( token_found["expiringAt"], r"%Y-%m-%dT%H:%M:%S.%f%z") delta = ( expiring_at.astimezone() - datetime.now().astimezone()) if delta.days < self.token_expiration: log.warn( f"Jira token '{self.token_name}' " f"expires in {delta.days} days.") except (requests.exceptions.HTTPError, KeyError, ValueError) as error: log.warn(error) return self._session
def __init__(cls, name, bases, attrs): if name in StatsGroupPlugin.ignore: return plugin_name = cls.__module__.split(".")[-1] registry = StatsGroupPlugin.registry if plugin_name in registry: orig = registry[plugin_name] log.warn("%s overriding %s" % (cls.__module__, orig.__module__)) registry[plugin_name] = cls
def commits(self, user, options): """ List commits for given user. """ # Prepare the command command = "git log --all --author={0}".format(user.login).split() command.append("--format=format:%h - %s") command.append("--since='{0} 00:00:00'".format(options.since)) command.append("--until='{0} 00:00:00'".format(options.until)) if options.verbose: command.append("--name-only") log.info("Checking commits in {0}".format(self.path)) log.details(pretty(command)) # Get the commit messages try: process = subprocess.Popen(command, cwd=self.path, encoding='utf-8', stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as error: log.debug(error) raise did.base.ReportError( "Unable to access git repo '{0}'".format(self.path)) output, errors = process.communicate() log.debug("git log output:") log.debug(output) if process.returncode == 0: if not output: return [] else: if not options.verbose: return output.split("\n") commits = [] for commit in output.split("\n\n"): summary = commit.split("\n")[0] directory = re.sub("/[^/]+$", "", commit.split("\n")[1]) commits.append("{0}\n{1}* {2}".format( summary, 8 * " ", directory)) return commits else: log.debug(errors.strip()) log.warn("Unable to check commits in '{0}'".format(self.path)) return []
def commits(self, user, options): """ List commits for given user. """ # Prepare the command command = "git log --all --author={0}".format(user.login).split() command.append("--format=format:%h - %s") command.append("--since='{0} 00:00:00'".format(options.since)) command.append("--until='{0} 00:00:00'".format(options.until)) if options.verbose: command.append("--name-only") log.info(u"Checking commits in {0}".format(self.path)) log.debug(pretty(command)) # Get the commit messages try: process = subprocess.Popen( command, cwd=self.path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as error: log.debug(error) raise ReportError( "Unable to access git repo '{0}'".format(self.path)) output, errors = process.communicate() log.debug("git log output:") log.debug(output) if process.returncode == 0: if not output: return [] else: if not options.verbose: return unicode(output, "utf8").split("\n") commits = [] for commit in unicode(output, "utf8").split("\n\n"): summary = commit.split("\n")[0] directory = re.sub("/[^/]+$", "", commit.split("\n")[1]) commits.append("{0}\n{1}* {2}".format( summary, 8 * " ", directory)) return commits else: log.debug(errors.strip()) log.warn("Unable to check commits in '{0}'".format(self.path)) return []