Mini Shell
"""whmapi1 functions"""
from pathlib import Path
from typing import Literal, TypedDict
from typing import Union
from ._base import CpAPIBase
from ._errors import CpAPIErrorMsg
class ResellerIPsDict(TypedDict):
"""Return format of whmapi1.getresellerips"""
all: Literal[0, 1] # if all of the reseller's IP addresses are available
ip: list[str] # list of IPs
class Whmapi1(CpAPIBase):
"""whmapi1 functions"""
__module__ = 'cpapis'
def __init__(self):
if self._can_exec('/usr/sbin/whmapi1'):
super().__init__('/usr/sbin/whmapi1')
else:
super().__init__('/usr/local/cpanel/bin/whmapi1')
def __call__(
self,
function: str,
args: Union[dict, None] = None,
timeout: Union[float, None] = None,
*,
check: bool = False,
) -> dict:
"""Query whmapi1
Args:
function: whmapi1 function to use
args: key-vals to send to whmapi1
timeout: timeout for the whmapi1 command in secs
check: check .metadata.result from the result and raise
an CpAPIErrorMsg containing .metadata.reason on failure.
Defaults False.
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: check=True was set and the API reported an error
"""
ret = self._exec(
module_args=[function],
user=None,
args=args,
timeout=timeout,
)
if check:
self.check(ret)
return ret
@classmethod
def check(cls, data: dict) -> None:
"""Parse output common to most whmapi1 functions and raise an
exception if the function failed
Args:
data: result data from a whmapi1 call
Raises:
CpAPIErrorMsg: the API reported an error
"""
try:
if data['metadata']['result'] != 1:
raise CpAPIErrorMsg(msg=data['metadata']['reason'], data=data)
except (TypeError, KeyError) as exc:
raise CpAPIErrorMsg(
msg=f"{type(exc).__name__}: {exc}", data=data
) from exc
def set_owner(
self, user: str, owner: str, timeout: Union[float, None] = None
) -> None:
"""Change a user's owner
Args:
user: cPanel user to modify
owner: username of a reseller to assign ``user`` to
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
"""
self(
'modifyacct',
{'user': user, 'owner': owner},
check=True,
timeout=timeout,
)
def setsiteip(
self, user: str, addr: str, timeout: Union[float, None] = None
) -> None:
"""Change a user's IP address
Args:
user: cPanel user to modify
addr: IP address
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
"""
# the api can handle "domain" en lieu of "user" but we only support
# specifying by user here; it seems safer that way
self(
'setsiteip', {'user': user, 'ip': addr}, check=True, timeout=timeout
)
def removeacct(
self, user: str, keepdns: bool, timeout: Union[float, None] = None
) -> None:
"""Remove/Delete an account
Args:
user: cPanel user to delete
keepdns: whether to retain the account's DNS entries
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
"""
self(
'removeacct',
{'username': user, 'keepdns': int(keepdns)},
check=True,
timeout=timeout,
)
def setupreseller(
self,
user: str,
makeowner: bool = False,
timeout: Union[float, None] = None,
) -> None:
"""Grants reseller status to an account
Args:
user: cPanel username
makeowner: whether to make the user own their own account
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
"""
ret = self(
'setupreseller',
{'user': user, 'makeowner': int(makeowner)},
check=False,
timeout=timeout,
)
try:
if ret['metadata']['result'] == 1:
return
msg = ret['metadata']['reason']
if 'tried to make a reseller out of a reseller' in msg:
return
raise CpAPIErrorMsg(msg=ret['metadata']['reason'], data=ret)
except (TypeError, KeyError) as exc:
raise CpAPIErrorMsg(
msg=f"{type(exc).__name__}: {exc}", data=ret
) from exc
def set_acllist(
self,
user: str,
acl: Union[str, None],
timeout: Union[float, None] = None,
) -> list[str]:
"""Assign a predefined ACL to a reseller
Args:
user: cPanel reseller to modify
acl: name of the ACL list in /var/cpanel/acllists or None to strip
all permissions
timeout: timeout for the whmapi1 command in secs
Returns:
a list of permissions applied to the account
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: ACL did not exist or the API reported an error
"""
if acl:
if not Path('/var/cpanel/acllists', acl).is_file():
raise CpAPIErrorMsg(msg=f"ACL {acl!r} does not exist", data={})
args = {'user': user, 'acllist': acl}
else:
args = {'user': user}
ret = self(
'setacls',
args,
check=True,
timeout=timeout,
)
return ret['data']['acl']
def changepackage(
self, user: str, pkg: str, timeout: Union[float, None] = None
) -> None:
"""Changes a cPanel account's hosting plan / package
Args:
user: cPanel user to modify
pkg: the hosting plan's name (case sensitive!)
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
"""
self(
'changepackage',
{'user': user, 'pkg': pkg},
check=True,
timeout=timeout,
)
def list_users(self, timeout: Union[float, None] = None) -> list[str]:
"""lists the cPanel user accounts and the root user on the server
Args:
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
Returns:
list[str]: list of users + root
"""
ret = self('list_users', check=True, timeout=timeout)
return ret['data']['users']
def listresellers(self, timeout: Union[float, None] = None) -> list[str]:
"""lists the reseller accounts on the server
Args:
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
Returns:
list[str]: list of one or more valid usernames
"""
ret = self("listresellers", check=True, timeout=timeout)
return ret["data"]["reseller"]
def getresellerips(
self, user: str, timeout: Union[float, None] = None
) -> ResellerIPsDict:
"""lists a reseller's available IP addresses
Args:
user (str): cPanel username
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
Returns:
ResellerIPsDict: dict containing "ip" (list[str]) and "all" (0 or 1)
"""
ret = self(
'getresellerips', {'user': user}, check=True, timeout=timeout
)
return ret['data']
def setresellerips(
self,
user: str,
ips: Union[list[str], set[str]],
delegate: bool,
timeout: Union[float, None] = None,
) -> None:
"""adds IP addresses to a reseller's account
Args:
user (str): cPanel username
ips (list[str] | set[str]): IPs to assign
delegate (bool): restrict the reseller's account to its dedicated IP
addresses. If False, allow the user to dedicate any available IP
timeout: timeout for the whmapi1 command in secs
Raises:
CpAPIExecFail: command failed to execute or returned invalid json
CpAPIErrorMsg: the API reported an error
"""
if not isinstance(ips, (list, set)):
raise TypeError(f"{ips=}")
self(
'setresellerips',
{'user': user, 'ips': ','.join(ips), 'delegate': int(delegate)},
check=True,
timeout=timeout,
)
Zerion Mini Shell 1.0