# Copyright (c) 2025 Kim Jarvis TPF Software Services S.A. kim.jarvis@tpfsystems.com # This software is licensed under the MIT License. See the LICENSE file for details.#importasyncsshfromreemote.commandimportCommandfromtypingimportOptional,Dict,Any
[docs]classSetstat:""" A class to encapsulate the functionality of setstat for setting builtin attributes using SFTP setstat in Unix-like operating systems. Attributes: path (str): The path of the builtin to set attributes for. attrs (dict): Dictionary of attributes to set on the builtin. **Examples:** .. code:: python yield Setstat( path="/path/to/builtin.txt", attrs={ "permissions": 0o644, "uid": 1000, "gid": 1000, "mtime": 1672531200 } ) Usage: This class is designed to be used in a generator-based workflow where commands are yielded for execution. The operation result will indicate whether the attribute setting was successful for each host. Notes: Common attributes include: permissions, uid, gid, mtime, atime, size. """def__init__(self,path:str,attrs:Dict[str,Any]):self.path=pathself.attrs=attrsdef__repr__(self):returnf"Setstat(path={self.path!r}, attrs={self.attrs!r})"@staticmethoddef_dict_to_sftp_attrs(attrs_dict:Dict[str,Any])->asyncssh.SFTPAttrs:"""Convert dictionary to SFTPAttrs object"""attrs=asyncssh.SFTPAttrs()# Set available attributes from dictionaryif'permissions'inattrs_dictandattrs_dict['permissions']isnotNone:# Handle both octal string and integer permissionsperms=attrs_dict['permissions']ifisinstance(perms,str)andperms.startswith('0o'):attrs.permissions=int(perms,8)else:attrs.permissions=int(perms)if'uid'inattrs_dictandattrs_dict['uid']isnotNone:attrs.uid=attrs_dict['uid']if'gid'inattrs_dictandattrs_dict['gid']isnotNone:attrs.gid=attrs_dict['gid']if'atime'inattrs_dictandattrs_dict['atime']isnotNone:attrs.atime=attrs_dict['atime']if'mtime'inattrs_dictandattrs_dict['mtime']isnotNone:attrs.mtime=attrs_dict['mtime']if'size'inattrs_dictandattrs_dict['size']isnotNone:attrs.size=attrs_dict['size']if'nlink'inattrs_dictandattrs_dict['nlink']isnotNone:attrs.nlink=attrs_dict['nlink']returnattrs@staticmethodasyncdef_setstat_callback(host_info,global_info,command,cp,caller):"""Static callback method for setting builtin attributes"""# Validate host_info (matching Rmdir error handling)required_keys=['host','username','password']forkeyinrequired_keys:ifkeynotinhost_infoorhost_info[key]isNone:raiseValueError(f"Missing or invalid value for '{key}' in host_info.")# Validate caller attributes (matching Rmdir error handling)ifcaller.pathisNone:raiseValueError("The 'path' attribute of the caller cannot be None.")ifcaller.attrsisNoneornotisinstance(caller.attrs,dict):raiseValueError("The 'attrs' attribute must be a non-empty dictionary.")try:# Connect to the SSH server and set builtin attributesasyncwithasyncssh.connect(**host_info)asconn:asyncwithconn.start_sftp_client()assftp:# Convert dictionary to SFTPAttrssftp_attrs=caller._dict_to_sftp_attrs(caller.attrs)# Set attributes using setstatawaitsftp.setstat(caller.path,sftp_attrs)except(OSError,asyncssh.Error)asexc:raise# Re-raise the exception to handle it in the callerdefexecute(self):r=yieldCommand(f"{self}",local=True,callback=self._setstat_callback,caller=self)r.executed=Truer.changed=True# This operation changes the system statereturnr