Skip to content

Commit

Permalink
feat: abstract around base package manager
Browse files Browse the repository at this point in the history
  • Loading branch information
jedel1043 committed Aug 16, 2024
1 parent 4188e5c commit a30c30b
Showing 1 changed file with 105 additions and 40 deletions.
145 changes: 105 additions & 40 deletions lib/charms/hpc_libs/v0/slurm_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def _on_install(self, _) -> None:
from collections.abc import Mapping
from enum import Enum
from typing import Any, Optional
from abc import ABC, abstractmethod

import yaml

Expand All @@ -84,7 +85,7 @@ def _on_install(self, _) -> None:

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 5
LIBPATCH = 6

# Charm library dependencies to fetch during `charmcraft pack`.
PYDEPS = ["pyyaml>=6.0.1"]
Expand Down Expand Up @@ -124,20 +125,6 @@ def format_key(key: str) -> str:
return _kebabize.sub(r"-", key).lower()


def install() -> None:
"""Install Slurm."""
# FIXME: Pin slurm to the stable channel
_snap("install", "slurm", "--channel", "latest/candidate", "--classic")


def version() -> str:
"""Get the current version of Slurm installed on the system."""
info = yaml.safe_load(_snap("info", "slurm"))
if (ver := info.get("installed")) is None:
raise SlurmOpsError("unable to retrive snap info. Ensure slurm is correctly installed")
return ver.split(maxsplit=1)[0]


def _call(cmd: str, *args: str, stdin: Optional[str] = None) -> str:
"""Call a command with logging.
Expand Down Expand Up @@ -175,7 +162,6 @@ def _mungectl(*args: str, stdin: Optional[str] = None) -> str:
"""
return _call("slurm.mungectl", *args, stdin=stdin)


class ServiceType(Enum):
"""Type of Slurm service to manage."""

Expand All @@ -196,10 +182,100 @@ def config_name(self) -> str:

return self.value

class ServiceManager(ABC):
"""Control a Slurm service."""

@abstractmethod
def enable(self) -> None:
"""Enable service."""
pass

@abstractmethod
def disable(self) -> None:
"""Disable service."""
pass

@abstractmethod
def restart(self) -> None:
"""Restart service."""
pass

@abstractmethod
def active(self) -> bool:
"""Return True if the service is active."""
pass

class ConfigurationManager(ABC):
"""Control configuration of a Slurm component."""

def get_options(self, *keys: str) -> Mapping[str, Any]:
"""Get given configurations values for Slurm component."""
configs = {}
for key in keys:
config = self.get(key)
target = key.rsplit(".", maxsplit=1)[-1]
configs[target] = config

return configs

@abstractmethod
def get(self, key: Optional[str] = None) -> Any:
"""Get specific configuration value for Slurm component."""
pass

@abstractmethod
def set(self, config: Mapping[str, Any]) -> None:
"""Set configuration for Slurm component."""
pass

@abstractmethod
def unset(self, *keys: str) -> None:
"""Unset configuration for Slurm component."""
pass


class SlurmOpsManager(ABC):
@abstractmethod
def install(self) -> None:
"""Install Slurm."""
pass

@abstractmethod
def version(self) -> str:
"""Get the current version of Slurm installed on the system."""
pass

@abstractmethod
def service_for(self, service: ServiceType) -> ServiceManager:
pass

@abstractmethod
def configuration_manager_for(self, service: ServiceType) -> ConfigurationManager:
pass

class ServiceManager:
class SnapManager(SlurmOpsManager):
def install(self) -> None:
# FIXME: Pin slurm to the stable channel
_snap("install", "slurm", "--channel", "latest/candidate", "--classic")

def version(self) -> str:
info = yaml.safe_load(_snap("info", "slurm"))
if (ver := info.get("installed")) is None:
raise SlurmOpsError("unable to retrive snap info. Ensure slurm is correctly installed")
return ver.split(maxsplit=1)[0]

def service_for(self, service: ServiceType) -> ServiceManager:
return _SnapServiceManager(service)

def configuration_manager_for(self, service: ServiceType) -> ConfigurationManager:
return _SnapConfigurationManager(service.config_name)

class _SnapServiceManager(ServiceManager):
"""Control a Slurm service."""

def __init__(self, service: ServiceType) -> None:
self._service = service

def enable(self) -> None:
"""Enable service."""
_snap("start", "--enable", f"slurm.{self._service.value}")
Expand All @@ -224,22 +300,12 @@ def active(self) -> bool:
return "inactive" not in services[f"slurm.{self._service.value}"]


class ConfigurationManager:
"""Control configuration of a Slurm component."""
class _SnapConfigurationManager(ConfigurationManager):
"""Control configuration of a Slurm component using Snap"""

def __init__(self, name: str) -> None:
self._name = name

def get_options(self, *keys: str) -> Mapping[str, Any]:
"""Get given configurations values for Slurm component."""
configs = {}
for key in keys:
config = self.get(key)
target = key.rsplit(".", maxsplit=1)[-1]
configs[target] = config

return configs

def get(self, key: Optional[str] = None) -> Any:
"""Get specific configuration value for Slurm component."""
key = f"{self._name}.{key}" if key else self._name
Expand All @@ -257,13 +323,12 @@ def unset(self, *keys: str) -> None:
_snap("unset", "slurm", *args)


class MungeManager(ServiceManager):
class MungeManager:
"""Manage `munged` service operations."""

def __init__(self) -> None:
service = ServiceType.MUNGED
self._service = service
self.config = ConfigurationManager(service.config_name)
def __init__(self, ops_manager: SlurmOpsManager) -> None:
self.service = ops_manager.service_for(ServiceType.MUNGED)
self.config = ops_manager.configuration_manager_for(ServiceType.MUNGED)

def get_key(self) -> str:
"""Get the current munge key.
Expand All @@ -286,18 +351,18 @@ def generate_key(self) -> None:
_mungectl("key", "generate")


class PrometheusExporterManager(ServiceManager):
class PrometheusExporterManager:
"""Manage `slurm-prometheus-exporter` service operations."""

def __init__(self) -> None:
self._service = ServiceType.PROMETHEUS_EXPORTER
def __init__(self, ops_manager: SlurmOpsManager) -> None:
self.service = ops_manager.service_for(ServiceType.PROMETHEUS_EXPORTER)


class SlurmManagerBase(ServiceManager):
class SlurmManagerBase:
"""Base manager for Slurm services."""

def __init__(self, service: ServiceType) -> None:
self._service = service
def __init__(self, service: ServiceType, ops_manager: SlurmOpsManager) -> None:
self.service = ops_manager.service_for(service)
self.config = ConfigurationManager(service.config_name)
self.munge = MungeManager()
self.exporter = PrometheusExporterManager()
Expand Down

0 comments on commit a30c30b

Please sign in to comment.