Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(editors): enable editors to set file access permissions #24

Merged
merged 3 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions slurmutils/editors/cgroupconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
import os
from contextlib import contextmanager
from pathlib import Path
from typing import Union
from typing import Optional, Union

from ..models import CgroupConfig
from .editor import dumper, loader
from .editor import dumper, loader, set_file_permissions

_logger = logging.getLogger("slurmutils")

Expand All @@ -40,9 +40,16 @@ def loads(content: str) -> CgroupConfig:


@dumper
def dump(config: CgroupConfig, file: Union[str, os.PathLike]) -> None:
def dump(
config: CgroupConfig,
file: Union[str, os.PathLike],
mode: int = 0o644,
user: Optional[Union[str, int]] = None,
group: Optional[Union[str, int]] = None,
) -> None:
"""Dump `cgroup.conf` data model into cgroup.conf file."""
Path(file).write_text(dumps(config))
set_file_permissions(file, mode, user, group)


def dumps(config: CgroupConfig) -> str:
Expand All @@ -51,12 +58,19 @@ def dumps(config: CgroupConfig) -> str:


@contextmanager
def edit(file: Union[str, os.PathLike]) -> CgroupConfig:
def edit(
file: Union[str, os.PathLike],
mode: int = 0o644,
user: Optional[Union[str, int]] = None,
group: Optional[Union[str, int]] = None,
) -> CgroupConfig:
"""Edit a cgroup.conf file.

Args:
file: Path to cgroup.conf file to edit. If cgroup.conf does
not exist at the specified file path, it will be created.
file: cgroup.conf file to edit. An empty config will be created if it does not exist.
mode: Access mode to apply to the cgroup.conf file. (Default: rw-r--r--)
user: User to set as owner of the cgroup.conf file. (Default: $USER)
group: Group to set as owner of the cgroup.conf file. (Default: None)
"""
if not os.path.exists(file):
_logger.warning("file %s not found. creating new empty cgroup.conf configuration", file)
Expand All @@ -65,4 +79,4 @@ def edit(file: Union[str, os.PathLike]) -> CgroupConfig:
config = load(file)

yield config
dump(config, file)
dump(config, file, mode, user, group)
29 changes: 26 additions & 3 deletions slurmutils/editors/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,42 @@
"""Base methods for Slurm workload manager configuration file editors."""

import logging
import os
import shutil
from functools import wraps
from os import path
from pathlib import Path
from typing import Optional, Union

_logger = logging.getLogger("slurmutils")


def set_file_permissions(
file: Union[str, os.PathLike],
mode: int = 0o644,
user: Optional[Union[str, int]] = None,
group: Optional[Union[str, int]] = None,
) -> None:
"""Set file permissions to configuration file.

Args:
file: File to apply permission settings to.
mode: Access mode to apply to file. (Default: rw-r--r--)
user: User to set as owner of file. (Default: $USER)
group: Group to set as owner of file. (Default: None)
"""
Path(file).chmod(mode=mode)
if user is None:
user = os.getuid()
NucciTheBoss marked this conversation as resolved.
Show resolved Hide resolved
shutil.chown(file, user, group)


def loader(func):
"""Wrap function that loads configuration data from file."""

@wraps(func)
def wrapper(*args, **kwargs):
fin = args[0]
if not path.exists(fin):
if not os.path.exists(fin):
raise FileNotFoundError(f"could not locate {fin}")

_logger.debug("reading contents of %s", fin)
Expand All @@ -42,7 +65,7 @@ def dumper(func):
@wraps(func)
def wrapper(*args, **kwargs):
fout = args[1]
if path.exists(fout):
if os.path.exists(fout):
_logger.debug("overwriting current contents of %s", fout)

_logger.debug("updating contents of %s", fout)
Expand Down
28 changes: 21 additions & 7 deletions slurmutils/editors/slurmconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
import os
from contextlib import contextmanager
from pathlib import Path
from typing import Union
from typing import Optional, Union

from ..models import SlurmConfig
from .editor import dumper, loader
from .editor import dumper, loader, set_file_permissions

_logger = logging.getLogger("slurmutils")

Expand All @@ -40,9 +40,16 @@ def loads(content: str) -> SlurmConfig:


@dumper
def dump(config: SlurmConfig, file: Union[str, os.PathLike]) -> None:
def dump(
config: SlurmConfig,
file: Union[str, os.PathLike],
mode: int = 0o644,
user: Optional[Union[str, int]] = None,
group: Optional[Union[str, int]] = None,
) -> None:
"""Dump `slurm.conf` data model into slurm.conf file."""
Path(file).write_text(dumps(config))
set_file_permissions(file, mode, user, group)


def dumps(config: SlurmConfig) -> str:
Expand All @@ -51,12 +58,19 @@ def dumps(config: SlurmConfig) -> str:


@contextmanager
def edit(file: Union[str, os.PathLike]) -> SlurmConfig:
def edit(
file: Union[str, os.PathLike],
mode: int = 0o644,
user: Optional[Union[str, int]] = None,
group: Optional[Union[str, int]] = None,
) -> SlurmConfig:
"""Edit a slurm.conf file.

Args:
file: Path to slurm.conf file to edit. If slurm.conf does
not exist at the specified file path, it will be created.
file: slurm.conf file to edit. An empty config will be created if it does not exist.
mode: Access mode to apply to the slurm.conf file. (Default: rw-r--r--)
user: User to set as owner of the slurm.conf file. (Default: $USER)
group: Group to set as owner of the slurm.conf file. (Default: None)
"""
if not os.path.exists(file):
_logger.warning("file %s not found. creating new empty slurm.conf configuration", file)
Expand All @@ -65,4 +79,4 @@ def edit(file: Union[str, os.PathLike]) -> SlurmConfig:
config = load(file)

yield config
dump(config, file)
dump(config, file, mode, user, group)
31 changes: 22 additions & 9 deletions slurmutils/editors/slurmdbdconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@
import os
from contextlib import contextmanager
from pathlib import Path
from typing import Union
from typing import Optional, Union

from slurmutils.models import SlurmdbdConfig

from .editor import dumper, loader
from ..models import SlurmdbdConfig
from .editor import dumper, loader, set_file_permissions

_logger = logging.getLogger("slurmutils")

Expand All @@ -41,9 +40,16 @@ def loads(content: str) -> SlurmdbdConfig:


@dumper
def dump(config: SlurmdbdConfig, file: Union[str, os.PathLike]) -> None:
def dump(
config: SlurmdbdConfig,
file: Union[str, os.PathLike],
mode: int = 0o644,
user: Optional[Union[str, int]] = None,
group: Optional[Union[str, int]] = None,
) -> None:
"""Dump `slurmdbd.conf` data model into slurmdbd.conf file."""
Path(file).write_text(dumps(config))
set_file_permissions(file, mode, user, group)


def dumps(config: SlurmdbdConfig) -> str:
Expand All @@ -52,12 +58,19 @@ def dumps(config: SlurmdbdConfig) -> str:


@contextmanager
def edit(file: Union[str, os.PathLike]) -> SlurmdbdConfig:
def edit(
file: Union[str, os.PathLike],
mode: int = 0o644,
user: Optional[Union[str, int]] = None,
group: Optional[Union[str, int]] = None,
) -> SlurmdbdConfig:
"""Edit a slurmdbd.conf file.

Args:
file: Path to slurmdbd.conf file to edit. If slurmdbd.conf does
not exist at the specified file path, it will be created.
file: slurmdbd.conf file to edit. An empty config will be created if it does not exist.
mode: Access mode to apply to the slurmdbd.conf file. (Default: rw-r--r--)
user: User to set as owner of the slurmdbd.conf file. (Default: $USER)
group: Group to set as owner of the slurmdbd.conf file. (Default: None)
"""
if not os.path.exists(file):
_logger.warning("file %s not found. creating new empty slurmdbd.conf configuration", file)
Expand All @@ -66,4 +79,4 @@ def edit(file: Union[str, os.PathLike]) -> SlurmdbdConfig:
config = load(file)

yield config
dump(config, file)
dump(config, file, mode, user, group)
108 changes: 108 additions & 0 deletions tests/unit/editors/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Copyright 2024 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

EXAMPLE_SLURM_CONFIG = """#
# `slurm.conf` file generated at 2024-01-30 17:18:36.171652 by slurmutils.
#
SlurmctldHost=juju-c9fc6f-0(10.152.28.20)
SlurmctldHost=juju-c9fc6f-1(10.152.28.100)

ClusterName=charmed-hpc
AuthType=auth/munge
Epilog=/usr/local/slurm/epilog
Prolog=/usr/local/slurm/prolog
FirstJobId=65536
InactiveLimit=120
JobCompType=jobcomp/filetxt
JobCompLoc=/var/log/slurm/jobcomp
KillWait=30
MaxJobCount=10000
MinJobAge=3600
PluginDir=/usr/local/lib:/usr/local/slurm/lib
ReturnToService=0
SchedulerType=sched/backfill
SlurmctldLogFile=/var/log/slurm/slurmctld.log
SlurmdLogFile=/var/log/slurm/slurmd.log
SlurmctldPort=7002
SlurmdPort=7003
SlurmdSpoolDir=/var/spool/slurmd.spool
StateSaveLocation=/var/spool/slurm.state
SwitchType=switch/none
TmpFS=/tmp
WaitTime=30

#
# Node configurations
#
NodeName=juju-c9fc6f-2 NodeAddr=10.152.28.48 CPUs=1 RealMemory=1000 TmpDisk=10000
NodeName=juju-c9fc6f-3 NodeAddr=10.152.28.49 CPUs=1 RealMemory=1000 TmpDisk=10000
NodeName=juju-c9fc6f-4 NodeAddr=10.152.28.50 CPUs=1 RealMemory=1000 TmpDisk=10000
NodeName=juju-c9fc6f-5 NodeAddr=10.152.28.51 CPUs=1 RealMemory=1000 TmpDisk=10000

#
# Down node configurations
#
DownNodes=juju-c9fc6f-5 State=DOWN Reason="Maintenance Mode"

#
# Partition configurations
#
PartitionName=DEFAULT MaxTime=30 MaxNodes=10 State=UP
PartitionName=batch Nodes=juju-c9fc6f-2,juju-c9fc6f-3,juju-c9fc6f-4,juju-c9fc6f-5 MinNodes=4 MaxTime=120 AllowGroups=admin
"""

EXAMPLE_SLURMDBD_CONFIG = """#
# `slurmdbd.conf` file generated at 2024-01-30 17:18:36.171652 by slurmutils.
#
ArchiveEvents=yes
ArchiveJobs=yes
ArchiveResvs=yes
ArchiveSteps=no
ArchiveTXN=no
ArchiveUsage=no
ArchiveScript=/usr/sbin/slurm.dbd.archive
AuthInfo=/var/run/munge/munge.socket.2
AuthType=auth/munge
AuthAltTypes=auth/jwt
AuthAltParameters=jwt_key=16549684561684@
DbdHost=slurmdbd-0
DbdBackupHost=slurmdbd-1
DebugLevel=info
PluginDir=/all/these/cool/plugins
PurgeEventAfter=1month
PurgeJobAfter=12month
PurgeResvAfter=1month
PurgeStepAfter=1month
PurgeSuspendAfter=1month
PurgeTXNAfter=12month
PurgeUsageAfter=24month
LogFile=/var/log/slurmdbd.log
PidFile=/var/run/slurmdbd.pid
SlurmUser=slurm
StoragePass=supersecretpasswd
StorageType=accounting_storage/mysql
StorageUser=slurm
StorageHost=127.0.0.1
StoragePort=3306
StorageLoc=slurm_acct_db
"""

EXAMPLE_CGROUP_CONFIG = """#
# `cgroup.conf` file generated at 2024-09-18 15:10:44.652017 by slurmutils.
#
ConstrainCores=yes
ConstrainDevices=yes
ConstrainRAMSpace=yes
ConstrainSwapSpace=yes
"""
Loading