diff --git a/candore/__init__.py b/candore/__init__.py index e873b64..d86f7ef 100644 --- a/candore/__init__.py +++ b/candore/__init__.py @@ -11,6 +11,7 @@ from candore.modules.extractor import Extractor from candore.modules.finder import Finder from candore.modules.report import Reporting +from candore.config import candore_settings class Candore: @@ -39,6 +40,8 @@ async def save_all_entities(self, mode, output_file, full, max_pages=None, skip_ extractor.max_pages = max_pages extractor.skip_percent = skip_percent data = await extractor.extract_all_entities() + if hasattr(self.settings, 'rpms'): + data.update({'installed_rpms': await extractor.extract_all_rpms()}) if not data: click.echo("Entities data is not data found!") diff --git a/candore/config.py b/candore/config.py index df7c92d..49ae076 100644 --- a/candore/config.py +++ b/candore/config.py @@ -23,6 +23,7 @@ def candore_settings(option_settings_file=None, option_components_file=None): core_loaders=["YAML"], envvar_prefix="CANDORE", settings_files=[settings_file, components_file], + preload=["conf/*.yaml"], envless_mode=True, lowercase_read=True, ) diff --git a/candore/modules/extractor.py b/candore/modules/extractor.py index 8d2985b..79407d1 100644 --- a/candore/modules/extractor.py +++ b/candore/modules/extractor.py @@ -1,7 +1,8 @@ import asyncio # noqa: F401 import math from functools import cached_property - +from candore.modules.ssh import Session +import re import aiohttp # Max observed request duration in testing was approximately 888 seconds @@ -188,3 +189,15 @@ async def extract_all_entities(self): comp_entities = await self.process_entities(endpoints=endpoints) all_data[component] = comp_entities return all_data + + async def extract_all_rpms(self): + """Extracts all installed RPMs from server""" + with Session() as ssh_client: + rpms = ssh_client.execute('rpm -qa').stdout + rpms = rpms.splitlines() + name_version_pattern = rf'{self.settings.rpms.regex_pattern}' + rpms_matches = [ + re.compile(name_version_pattern).match(rpm) for rpm in rpms + ] + rpms_list = [rpm_match.groups()[:-1] for rpm_match in rpms_matches if rpm_match] + return dict(rpms_list) diff --git a/candore/modules/ssh.py b/candore/modules/ssh.py new file mode 100644 index 0000000..0cd8b83 --- /dev/null +++ b/candore/modules/ssh.py @@ -0,0 +1,28 @@ +from hussh import Connection +from functools import cached_property +from candore.config import candore_settings +from urllib.parse import urlparse + + +class Session: + + def __init__(self): + self.settings = candore_settings() + self.hostname = urlparse(self.settings.candore.base_url).hostname + self.username = self.settings.candore.ssh.username or 'root' + + @cached_property + def auth(self): + auth_kwargs = {} + if self.settings.candore.ssh.private_key: + auth_kwargs["private_key"] = self.settings.candore.ssh.private_key + elif self.settings.candore.ssh.password: + auth_kwargs["password"] = self.settings.candore.ssh.password + return auth_kwargs + + def __enter__(self): + self.client = Connection(self.hostname, username=self.username, **self.auth) + return self.client + + def __exit__(self, exc_type, exc_val, exc_tb): + pass diff --git a/conf/rpms.yaml b/conf/rpms.yaml new file mode 100644 index 0000000..a492c47 --- /dev/null +++ b/conf/rpms.yaml @@ -0,0 +1,2 @@ +rpms: + regex_pattern: '([a-zA-Z0-9_-]+)-([\d._-]+)\.(.*?)$' diff --git a/pyproject.toml b/pyproject.toml index 9fc0f43..dc09078 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ dependencies = [ "flask", "requests", "PyYAML", + "hussh", ] [project.optional-dependencies] diff --git a/settings.yaml.template b/settings.yaml.template index 1231913..b41a7a5 100644 --- a/settings.yaml.template +++ b/settings.yaml.template @@ -8,5 +8,9 @@ candore: parser: apipie var_file: "@jinja {{this.candore.product_version | replace('.', '_')}}_variations.yaml" constant_File: "@jinja {{this.candore.product_version | replace('.', '_')}}_constants.yaml" + ssh: + username: + private_key: + password: # The maximum number of concurrent (asynchronous) connections allowed against the host max_connections: 20