from pathlib import Path
from cmdbox.app import common, filer
from cmdbox.app.commons import convert, redis_client
from typing import Dict, Any, List, Tuple
import base64
import logging
import json
[docs]
class Client(object):
def __init__(self, logger:logging.Logger, redis_host:str = "localhost", redis_port:int = 6379, redis_password:str = None, svname:str = 'server'):
"""
Redisサーバーとの通信を行うクラス
Args:
logger (logging): ロガー
redis_host (str, optional): Redisサーバーのホスト名. Defaults to "localhost".
redis_port (int, optional): Redisサーバーのポート番号. Defaults to 6379.
redis_password (str, optional): Redisサーバーのパスワード. Defaults to None.
svname (str, optional): サーバーのサービス名. Defaults to 'server'.
"""
self.logger = logger
if svname is None or svname == "":
raise Exception("svname is empty.")
if svname.find('-') >= 0:
raise ValueError(f"Server name is invalid. '-' is not allowed. svname={svname}")
self.redis_cli = redis_client.RedisClient(logger, host=redis_host, port=redis_port, password=redis_password, svname=svname)
self.is_running = False
def __exit__(self, a, b, c):
pass
[docs]
def stop_server(self, retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
Redisサーバーを停止する
Args:
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
res_json = self.redis_cli.send_cmd('stop_server', [], retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json
[docs]
def file_list(self, svpath:str, recursive:bool, scope:str="client", client_data:Path=None,
fwpaths:List[str]=None, listregs:str="*", retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバー上のファイルリストを取得する
Args:
svpath (Path): サーバー上のファイルパス
recursive (bool): 再帰的に取得するかどうか
scope (str, optional): 参照先のスコープ. Defaults to "client".
client_data (Path, optional): ローカルを参照させる場合のデータフォルダ. Defaults to None.
fwpaths (List[str], optional): 範囲内かどうかを示すパスのリスト. Defaults to None.
listregs (str, optional): リストアップするgrep条件. Defaults to "*".
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
if scope == "client":
if client_data is not None:
f = filer.Filer(client_data, self.logger)
_, res_json = f.file_list(svpath, recursive, fwpaths, listregs)
return res_json
else:
self.logger.warning(f"client_data is empty.")
return dict(warn=f"client_data is empty.")
elif scope == "current":
f = filer.Filer(Path.cwd(), self.logger)
_, res_json = f.file_list(svpath, recursive, fwpaths, listregs)
return res_json
elif scope == "server":
payload = dict(svpath=svpath, recursive=recursive, fwpaths=fwpaths, listregs=listregs)
payload_b64 = convert.str2b64str(json.dumps(payload, default=common.default_json_enc))
res_json = self.redis_cli.send_cmd('file_list', [payload_b64],
retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json
else:
self.logger.warning(f"scope is invalid. {scope}")
return dict(warn=f"scope is invalid. {scope}")
[docs]
def file_mkdir(self, svpath:str, scope:str="client", client_data:Path=None, fwpaths:List[str]=None,
retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバー上にディレクトリを作成する
Args:
svpath (Path): サーバー上のディレクトリパス
scope (str, optional): 参照先のスコープ. Defaults to "client".
client_data (Path, optional): ローカルを参照させる場合のデータフォルダ. Defaults to None.
fwpaths (List[str], optional): 範囲内かどうかを示すパスのリスト. Defaults to None.
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
if scope == "client":
if client_data is not None:
f = filer.Filer(client_data, self.logger)
_, res_json = f.file_mkdir(svpath, fwpaths)
return res_json
else:
self.logger.warning(f"client_data is empty.")
return dict(warn=f"client_data is empty.")
elif scope == "current":
f = filer.Filer(Path.cwd(), self.logger)
_, res_json = f.file_mkdir(svpath, fwpaths)
return res_json
elif scope == "server":
payload = dict(svpath=svpath, fwpaths=fwpaths)
payload_b64 = convert.str2b64str(json.dumps(payload, default=common.default_json_enc))
res_json = self.redis_cli.send_cmd('file_mkdir', [payload_b64],
retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json
else:
self.logger.warning(f"scope is invalid. {scope}")
return dict(warn=f"scope is invalid. {scope}")
[docs]
def file_rmdir(self, svpath:str, scope:str="client", client_data:Path=None, fwpaths:List[str]=None,
retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバー上のディレクトリを削除する
Args:
svpath (Path): サーバー上のディレクトリパス
scope (str, optional): 参照先のスコープ. Defaults to "client".
client_data (Path, optional): ローカルを参照させる場合のデータフォルダ. Defaults to None.
fwpaths (List[str], optional): 範囲内かどうかを示すパスのリスト. Defaults to None.
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
if scope == "client":
if client_data is not None:
f = filer.Filer(client_data, self.logger)
_, res_json = f.file_rmdir(svpath, fwpaths)
return res_json
else:
self.logger.warning(f"client_data is empty.")
return dict(warn=f"client_data is empty.")
elif scope == "current":
f = filer.Filer(Path.cwd(), self.logger)
_, res_json = f.file_rmdir(svpath, fwpaths)
return res_json
elif scope == "server":
payload = dict(svpath=svpath, fwpaths=fwpaths)
payload_b64 = convert.str2b64str(json.dumps(payload, default=common.default_json_enc))
res_json = self.redis_cli.send_cmd('file_rmdir', [payload_b64],
retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json
else:
self.logger.warning(f"scope is invalid. {scope}")
return dict(warn=f"scope is invalid. {scope}")
[docs]
def file_download(self, svpath:str, download_file:Path, scope:str="client", client_data:Path=None, fwpaths:List[str]=None,
rpath:str="", img_thumbnail:float=0.0,
retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバー上のファイルをダウンロードする
Args:
svpath (Path): サーバー上のファイルパス
download_file (Path): ローカルのファイルパス
scope (str, optional): 参照先のスコープ. Defaults to "client".
client_data (Path, optional): ローカルを参照させる場合のデータフォルダ. Defaults to None.
fwpaths (List[str], optional): 範囲内かどうかを示すパスのリスト. Defaults to None.
rpath (str, optional): リクエストパス. Defaults to "".
img_thumbnail (float, optional): サムネイル画像のサイズ. Defaults to 0.0.
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
bytes: ダウンロードファイルの内容
"""
if scope == "client":
if client_data is not None:
f = filer.Filer(client_data, self.logger)
_, res_json = f.file_download(svpath, img_thumbnail, fwpaths)
else:
self.logger.warning(f"client_data is empty.")
return dict(warn=f"client_data is empty.")
elif scope == "current":
f = filer.Filer(Path.cwd(), self.logger)
_, res_json = f.file_download(svpath, img_thumbnail, fwpaths)
elif scope == "server":
payload = dict(svpath=svpath, img_thumbnail=img_thumbnail, fwpaths=fwpaths)
payload_b64 = convert.str2b64str(json.dumps(payload, default=common.default_json_enc))
res_json = self.redis_cli.send_cmd('file_download', [payload_b64],
retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
else:
self.logger.warning(f"scope is invalid. {scope}")
return dict(warn=f"scope is invalid. {scope}")
if "success" in res_json:
res_json["success"]["rpath"] = rpath
res_json["success"]["svpath"] = svpath
if download_file is not None:
if download_file.is_dir():
download_file = download_file / res_json["success"]["name"]
if download_file.exists():
self.logger.warning(f"download_file {download_file} already exists.")
return dict(warn=f"download_file {download_file} already exists.")
def _wd(f):
f.write(base64.b64decode(res_json["success"]["data"]))
del res_json["success"]["data"]
res_json["success"]["download_file"] = str(download_file.absolute())
common.save_file(download_file, _wd, mode='wb')
return res_json
[docs]
def file_upload(self, svpath:str, upload_file:Path, scope:str="client", client_data:Path=None, fwpaths:List[str]=None,
mkdir:bool=False, orverwrite:bool=False,
retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバー上にファイルをアップロードする
Args:
svpath (Path): サーバー上のファイルパス
upload_file (Path): ローカルのファイルパス
scope (str, optional): 参照先のスコープ. Defaults to "client".
mkdir (bool, optional): ディレクトリを作成するかどうか. Defaults to False.
orverwrite (bool, optional): 上書きするかどうか. Defaults to False.
client_data (Path, optional): ローカルを参照させる場合のデータフォルダ. Defaults to None.
fwpaths (List[str], optional): 範囲内かどうかを示すパスのリスト. Defaults to None.
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
if upload_file is None:
self.logger.warning(f"upload_file is empty.")
return dict(warn=f"upload_file is empty.")
if not upload_file.exists():
self.logger.warning(f"input_file {upload_file} does not exist.")
return dict(warn=f"input_file {upload_file} does not exist.")
if upload_file.is_dir():
self.logger.warning(f"input_file {upload_file} is directory.")
return dict(warn=f"input_file {upload_file} is directory.")
with open(upload_file, "rb") as f:
if scope == "client":
if client_data is not None:
fi = filer.Filer(client_data, self.logger)
_, res_json = fi.file_upload(svpath, upload_file.name, f.read(), mkdir, orverwrite, fwpaths)
return res_json
else:
self.logger.warning(f"client_data is empty.")
return dict(warn=f"client_data is empty.")
elif scope == "current":
fi = filer.Filer(Path.cwd(), self.logger)
_, res_json = fi.file_upload(svpath, upload_file.name, f.read(), mkdir, orverwrite, fwpaths)
return res_json
elif scope == "server":
payload = dict(svpath=svpath, file_name=upload_file.name, file_data=convert.bytes2b64str(f.read()),
mkdir=mkdir, orverwrite=orverwrite, fwpaths=fwpaths)
payload_b64 = convert.str2b64str(json.dumps(payload, default=common.default_json_enc))
res_json = self.redis_cli.send_cmd('file_upload', [payload_b64,],
retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json
else:
self.logger.warning(f"scope is invalid. {scope}")
return dict(warn=f"scope is invalid. {scope}")
[docs]
def file_remove(self, svpath:str, scope:str="client", client_data:Path = None, fwpaths:List[str]=None,
retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバー上のファイルを削除する
Args:
svpath (Path): サーバー上のファイルパス
scope (str, optional): 参照先のスコープ. Defaults to "client".
client_data (Path, optional): ローカルを参照させる場合のデータフォルダ. Defaults to None.
fwpaths (List[str], optional): 範囲内かどうかを示すパスのリスト. Defaults to None.
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
if scope == "client":
if client_data is not None:
f = filer.Filer(client_data, self.logger)
_, res_json = f.file_remove(svpath, fwpaths)
return res_json
else:
self.logger.warning(f"client_data is empty.")
return dict(warn=f"client_data is empty.")
elif scope == "current":
f = filer.Filer(Path.cwd(), self.logger)
_, res_json = f.file_remove(svpath, fwpaths)
return res_json
elif scope == "server":
payload = dict(svpath=svpath, fwpaths=fwpaths)
payload_b64 = convert.str2b64str(json.dumps(payload, default=common.default_json_enc))
res_json = self.redis_cli.send_cmd('file_remove', [payload_b64],
retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json
else:
self.logger.warning(f"scope is invalid. {scope}")
return dict(warn=f"scope is invalid. {scope}")
[docs]
def file_copy(self, from_path:str, to_path:str, orverwrite:bool=False,
from_fwpaths:List[str]=None, to_fwpaths:List[str]=None, scope:str="client", client_data:Path = None,
retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバー上のファイルをコピーする
Args:
from_path (Path): コピー元のファイルパス
to_path (Path): コピー先のファイルパス
orverwrite (bool, optional): 上書きするかどうか. Defaults to False.
from_fwpaths (List[str], optional): 範囲内かどうかを示すパスのリスト. Defaults to None.
to_fwpaths (List[str], optional): 範囲内かどうかを示すパスのリスト. Defaults to None.
scope (str, optional): 参照先のスコープ. Defaults to "client".
client_data (Path, optional): ローカルを参照させる場合のデータフォルダ. Defaults to None.
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
if scope == "client":
if client_data is not None:
f = filer.Filer(client_data, self.logger)
_, res_json = f.file_copy(from_path, to_path, orverwrite, from_fwpaths, to_fwpaths)
return res_json
else:
self.logger.warning(f"client_data is empty.")
return dict(warn=f"client_data is empty.")
elif scope == "current":
f = filer.Filer(Path.cwd(), self.logger)
_, res_json = f.file_copy(from_path, to_path, orverwrite, from_fwpaths, to_fwpaths)
return res_json
elif scope == "server":
payload = dict(from_path=from_path, to_path=to_path, orverwrite=orverwrite,
from_fwpaths=from_fwpaths, to_fwpaths=to_fwpaths)
payload_b64 = convert.str2b64str(json.dumps(payload, default=common.default_json_enc))
res_json = self.redis_cli.send_cmd('file_copy', [payload_b64],
retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json
else:
self.logger.warning(f"scope is invalid. {scope}")
return dict(warn=f"scope is invalid. {scope}")
[docs]
def file_move(self, from_path:str, to_path:str, from_fwpaths:List[str]=None, to_fwpaths:List[str]=None,
scope:str="client", client_data:Path=None,
retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバー上のファイルを移動する
Args:
from_path (Path): 移動元のファイルパス
to_path (Path): 移動先のファイルパス
from_fwpaths (List[str], optional): 移動元の範囲内かどうかを示すパスのリスト. Defaults to None.
to_fwpaths (List[str], optional): 移動先の範囲内かどうかを示すパスのリスト. Defaults to None.
scope (str, optional): 参照先のスコープ. Defaults to "client".
client_data (Path, optional): ローカルを参照させる場合のデータフォルダ. Defaults to None.
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
if scope == "client":
if client_data is not None:
f = filer.Filer(client_data, self.logger)
_, res_json = f.file_move(from_path, to_path, from_fwpaths, to_fwpaths)
return res_json
else:
self.logger.warning(f"client_data is empty.")
return dict(warn=f"client_data is empty.")
elif scope == "current":
f = filer.Filer(Path.cwd(), self.logger)
_, res_json = f.file_move(from_path, to_path, from_fwpaths, to_fwpaths)
return res_json
elif scope == "server":
payload = dict(from_path=from_path, to_path=to_path, from_fwpaths=from_fwpaths, to_fwpaths=to_fwpaths)
payload_b64 = convert.str2b64str(json.dumps(payload, default=common.default_json_enc))
res_json = self.redis_cli.send_cmd('file_move', [payload_b64],
retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json
else:
self.logger.warning(f"scope is invalid. {scope}")
return dict(warn=f"scope is invalid. {scope}")
[docs]
def server_info(self, retry_count:int=3, retry_interval:int=5, timeout:int=60):
"""
サーバーの情報を取得する
Args:
retry_count (int, optional): リトライ回数. Defaults to 3.
retry_interval (int, optional): リトライ間隔. Defaults to 5.
timeout (int, optional): タイムアウト時間. Defaults to 60.
Returns:
dict: Redisサーバーからの応答
"""
res_json = self.redis_cli.send_cmd('server_info', [], retry_count=retry_count, retry_interval=retry_interval, timeout=timeout)
return res_json