final changes to repair cssh:
- restored macosx functionality in the form of python code (not tested though) - fixed bugs in the linux version fixes [https://bugzilla.ipr.univ-rennes.fr/show_bug.cgi?id=4405]
This commit is contained in:
parent
87a2d3ca22
commit
0944f9feca
236
home/bin/cssh
236
home/bin/cssh
|
|
@ -5,17 +5,21 @@ import argparse
|
|||
import re
|
||||
import subprocess
|
||||
import abc
|
||||
import atexit
|
||||
|
||||
|
||||
class RgbColor():
|
||||
red: int
|
||||
green: int
|
||||
blue: int
|
||||
|
||||
def __init__(self, red: int, green: int, blue: int):
|
||||
self.red = red
|
||||
self.green = green
|
||||
self.blue = blue
|
||||
|
||||
def __str__(self):
|
||||
return f'[{self.red}, {self.green}, {self.blue}]'
|
||||
|
||||
UserId = str # eg 'root'
|
||||
HostId = str # eg alambix50.ipr.univ-rennes.fr or alambix50
|
||||
|
|
@ -54,70 +58,72 @@ class ITerminalLauncher(abc.ABC):
|
|||
'''
|
||||
|
||||
|
||||
OsxWindowId = str
|
||||
|
||||
# '''
|
||||
# function getHostRealFqdn()
|
||||
# {
|
||||
# local strHostname="$1"
|
||||
class DarwinTerminalLauncher(ITerminalLauncher):
|
||||
'''
|
||||
macos terminal launcher
|
||||
'''
|
||||
THIS_OSX_TERM_WINDOW_ID = None
|
||||
SOLARIZED_BACKGROUND = RgbColor( 0 / 256, 8256 / 256, 10368 / 256) # '{0, 8256, 10368}' #002b36 (solarized dark base03)
|
||||
SAILOR_BLUE = RgbColor( 8832 / 256, 14208 / 256, 18816 / 256) # '{8832, 14208, 18816}' # 2e4a62
|
||||
PRUNE = RgbColor(15360 / 256, 10944 / 256, 14592 / 256) # '{15360, 10944, 14592}' #50394c
|
||||
TAUPE = RgbColor(19968 / 256, 18816 / 256, 16512 / 256) # '{19968, 18816, 16512}' #686256
|
||||
DARK_ORANGE = RgbColor(27456 / 256, 12864 / 256, 0 / 256) # '{27456, 12864, 0}' #8f4300
|
||||
DARK_OLIVE = RgbColor( 8832 / 256, 11904 / 256, 5568 / 256) # '{8832, 11904, 5568}' #2e3e1d
|
||||
|
||||
# local strHostFqdn="$(host $strHostname | tail -1 | awk '{print $1}')"
|
||||
# echo "$strHostFqdn"
|
||||
# }
|
||||
|
||||
# HOST_FQDN=$(getHostRealFqdn $HOSTNAME)
|
||||
|
||||
# SOLARIZED_BACKGROUND='{0, 8256, 10368}' #002b36 (solarized dark base03)
|
||||
# SAILOR_BLUE='{8832, 14208, 18816}' # 2e4a62
|
||||
# PRUNE='{15360, 10944, 14592}' #50394c
|
||||
# TAUPE='{19968, 18816, 16512}' #686256
|
||||
# DARK_ORANGE='{27456, 12864, 0}' #8f4300
|
||||
# DARK_OLIVE='{8832, 11904, 5568}' #2e3e1d
|
||||
|
||||
# function get_current_terminal_window_id()
|
||||
# {
|
||||
@staticmethod
|
||||
def get_current_terminal_window_id() -> OsxWindowId:
|
||||
# local terminal_windows_id=''
|
||||
# terminal_windows_id=$(osascript -e "tell application \"Terminal\" to get id of window 1")
|
||||
# echo "$terminal_windows_id"
|
||||
# }
|
||||
|
||||
# function set_bg ()
|
||||
# {
|
||||
# local strOsxTermWindowId="$1"
|
||||
# local strColor="$2" # eg {45000, 0, 0, 50000}
|
||||
|
||||
# osascript -e "tell application \"Terminal\" to set background color of (every window whose id is $strOsxTermWindowId) to $strColor"
|
||||
# }
|
||||
|
||||
# function on_exit ()
|
||||
# {
|
||||
# set_bg "$THIS_OSX_TERM_WINDOW_ID" "$SOLARIZED_BACKGROUND"
|
||||
# }
|
||||
osacommand = 'tell application "Terminal" to get id of window 1'
|
||||
command = ['osascript', '-e', osacommand]
|
||||
proc = subprocess.run(command, check=True, stdout=subprocess.PIPE)
|
||||
lines = proc.stdout.decode('utf8').split('\n')
|
||||
assert len(lines) == 2
|
||||
window_id = lines[0]
|
||||
return window_id
|
||||
|
||||
|
||||
@staticmethod
|
||||
def color_as_osx_str(color: RgbColor) -> str:
|
||||
'''
|
||||
SOLARIZED_BACKGROUND = '{0, 8256, 10368}'
|
||||
'''
|
||||
return '{%d, %d, %d}' % (color.red * 256, color.green * 256, color.blue * 256)
|
||||
|
||||
@staticmethod
|
||||
def set_bg(window_id: OsxWindowId, color: RgbColor):
|
||||
# window_id
|
||||
# color: eg {45000, 0, 0, 50000}
|
||||
osacommand = f'tell application "Terminal" to set background color of (every window whose id is {window_id}) to {DarwinTerminalLauncher.color_as_osx_str()}'
|
||||
command = ['osascript', '-e', osacommand]
|
||||
subprocess.run(command, check=True)
|
||||
|
||||
@staticmethod
|
||||
def on_exit():
|
||||
DarwinTerminalLauncher.set_bg(DarwinTerminalLauncher.THIS_OSX_TERM_WINDOW_ID, DarwinTerminalLauncher.SOLARIZED_BACKGROUND)
|
||||
|
||||
|
||||
def launch_terminal(self, terminal_label: TerminalLabel, bg_color: RgbColor, command: List[str]):
|
||||
'''
|
||||
command: the command to execute in the terminal eg ['/usr/bin/ssh', f'{str(ssh_target)}']
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
DarwinTerminalLauncher.THIS_OSX_TERM_WINDOW_ID = DarwinTerminalLauncher.get_current_terminal_window_id()
|
||||
# echo "THIS_OSX_TERM_WINDOW_ID=$THIS_OSX_TERM_WINDOW_ID"
|
||||
DarwinTerminalLauncher.set_bg(THIS_OSX_TERM_WINDOW_ID, bg_color)
|
||||
|
||||
# make cssh restore the default terminal color when exiting
|
||||
atexit.register(DarwinTerminalLauncher.on_exit)
|
||||
|
||||
# /usr/bin/ssh "$@"
|
||||
subprocess.run(command, check=True)
|
||||
|
||||
|
||||
# # if [ $(echo $HOST_FQDN | grep '^simpatix') ]
|
||||
# # then
|
||||
# # BG_COLOR="$SAILOR_BLUE"
|
||||
# # elif [ $(echo $HOST_FQDN | grep '^physix') ]
|
||||
# # then
|
||||
# # BG_COLOR="$TAUPE"
|
||||
# # else
|
||||
# # case $HOST_FQDN in
|
||||
# # 'pr079234.spm.univ-rennes1.fr')
|
||||
# # BG_COLOR="$SOLARIZED_BACKGROUND"
|
||||
# # ;;
|
||||
# # 'puppet3.ipr.univ-rennes1.fr')
|
||||
# # BG_COLOR="$PRUNE"
|
||||
# # ;;
|
||||
# # *)
|
||||
# # BG_COLOR="$DARK_OLIVE"
|
||||
# # ;;
|
||||
# # esac
|
||||
# # fi
|
||||
|
||||
# #if [ "$AS_ROOT" = 'true' ]
|
||||
# #then
|
||||
# # BG_COLOR="$DARK_ORANGE"
|
||||
# #fi
|
||||
|
||||
def is_valid_uuid(uuid: str) -> bool:
|
||||
'''
|
||||
|
|
@ -161,12 +167,13 @@ class GnomeSettings():
|
|||
'''
|
||||
profile_uuid: eg '2e0dec33-5ec2-4355-b671-7922010cee9f'
|
||||
'''
|
||||
|
||||
assert is_valid_uuid(profile_uuid), f'invalid uuid: "{profile_uuid}"'
|
||||
profiles_key_id = 'org.gnome.Terminal.ProfilesList'
|
||||
profile_uuids = self.get_list(profiles_key_id)
|
||||
assert profile_uuid not in profile_uuids
|
||||
profile_uuids.append(profile_uuid)
|
||||
self.set_list(profiles_key_id, profile_uuids)
|
||||
self.set_list(profiles_key_id, profile_uuids) # side effect: this adds the dconf key "/org/gnome/terminal/legacy/profiles:/list"
|
||||
|
||||
|
||||
class GnomeTerminal():
|
||||
|
|
@ -202,11 +209,16 @@ class GnomeTerminal():
|
|||
logging.info("creating profile profile_uuid=%s with profile_name=%s", profile_uuid, profile_name)
|
||||
# echo "new_profiles_list=$new_profiles_list"
|
||||
# dconf write $DCONF_PROFILES_PATH/list "$new_profiles_list"
|
||||
proc = subprocess.run(['dconf', 'write', f"{GnomeTerminal.DCONF_PROFILES_PATH}/{profile_uuid}/visible-name", f"'{profile_name}'"], check=True)
|
||||
|
||||
assert GnomeTerminal.get_term_profile_name(profile_name) == profile_uuid
|
||||
proc = subprocess.run(['dconf', 'write', f"{GnomeTerminal.DCONF_PROFILES_PATH}/:{profile_uuid}/visible-name", f"'{profile_name}'"], check=True)
|
||||
# dconf write /org/gnome/terminal/legacy/profiles:/:dc932a58-6a38-4511-876a-d453b5e33a26/visible-name "'toto'"
|
||||
|
||||
if True:
|
||||
# check that the profile was successfully created
|
||||
assert GnomeTerminal.get_term_profile_name(profile_uuid) == profile_name
|
||||
existing_profile_uuid = GnomeTerminal.get_terminal_profile_uuid(profile_name)
|
||||
assert existing_profile_uuid is not None, f'failed to find the profile for name {profile_name}, it is expected to be {profile_uuid}'
|
||||
assert existing_profile_uuid == profile_uuid
|
||||
|
||||
gsettings = GnomeSettings()
|
||||
gsettings.register_profile_uuid(profile_uuid)
|
||||
return profile_uuid
|
||||
|
|
@ -216,9 +228,8 @@ class GnomeTerminal():
|
|||
proc = subprocess.run(['dconf', 'list', f"{GnomeTerminal.DCONF_PROFILES_PATH}/"], check=True, stdout=subprocess.PIPE)
|
||||
profile_uuids = []
|
||||
for line in proc.stdout.decode('utf8').split('\n'):
|
||||
print(line)
|
||||
# eg ':b1dcc9dd-5262-4d8d-a863-c897e6d979b9/'
|
||||
m = re.match(r'^:?(?P<uuid>[0-9a-f\-]+)/$', line)
|
||||
m = re.match(r'^:(?P<uuid>[0-9a-f\-]+)/$', line)
|
||||
if m is None:
|
||||
assert line in ['list', ''], f'unexpected format for line: {line}'
|
||||
else:
|
||||
|
|
@ -229,7 +240,7 @@ class GnomeTerminal():
|
|||
@staticmethod
|
||||
def get_term_profile_name(profile_uuid: TermProfileUuid) -> Optional[TermProfileName]:
|
||||
profile_name = None
|
||||
proc = subprocess.run(['dconf', 'read', f"{GnomeTerminal.DCONF_PROFILES_PATH}/{profile_uuid}/visible-name"], check=True, stdout=subprocess.PIPE)
|
||||
proc = subprocess.run(['dconf', 'read', f"{GnomeTerminal.DCONF_PROFILES_PATH}/:{profile_uuid}/visible-name"], check=True, stdout=subprocess.PIPE)
|
||||
lines = proc.stdout.decode('utf8').split('\n')
|
||||
if (len(lines) >= 2):
|
||||
|
||||
|
|
@ -239,6 +250,7 @@ class GnomeTerminal():
|
|||
assert m, f'unexpected line : {line}'
|
||||
profile_name = m['profile_name']
|
||||
else:
|
||||
logging.debug('name lines for profile %s : %s', profile_uuid, str(lines))
|
||||
pass # (the default profile has no name)
|
||||
#assert False, f'unexpected case: the terminal profile profile_uuid={profile_uuid} has no name!!!'
|
||||
return profile_name
|
||||
|
|
@ -255,12 +267,14 @@ class GnomeTerminal():
|
|||
for profile_uuid in GnomeTerminal.get_term_profiles():
|
||||
logging.debug(f'processing profile_uuid={profile_uuid}')
|
||||
prof_name = GnomeTerminal.get_term_profile_name(profile_uuid)
|
||||
logging.debug('prof_name = %s', prof_name)
|
||||
logging.debug('profile_uuid = %s, prof_name = %s', profile_uuid, prof_name)
|
||||
if profile_name == prof_name:
|
||||
return profile_uuid
|
||||
|
||||
logging.info('the profile named %s doesnt exist... create it then', profile_name)
|
||||
GnomeTerminal.check_validity()
|
||||
profile_uuid = GnomeTerminal.create_new_profile(profile_name)
|
||||
GnomeTerminal.check_validity()
|
||||
|
||||
return profile_uuid
|
||||
|
||||
|
|
@ -269,13 +283,38 @@ class GnomeTerminal():
|
|||
# function set_terminal_profile_bg_color()
|
||||
# profile_name # eg physix.ipr.univ-rennes1.fr
|
||||
# bg_color # eg RgbColor(17, 25, 24)
|
||||
|
||||
logging.debug('setting color for profile %s : %s', profile_name, str(bg_color))
|
||||
profile_uuid = GnomeTerminal.get_terminal_profile_uuid(profile_name)
|
||||
logging.debug("profile_name=%s profile_uuid=%s", profile_name, profile_uuid)
|
||||
|
||||
subprocess.run(['dconf', 'write', f"{GnomeTerminal.DCONF_PROFILES_PATH}/{profile_uuid}/use-theme-colors", 'false'], check=True, stdout=subprocess.PIPE)
|
||||
subprocess.run(['dconf', 'write', f"{GnomeTerminal.DCONF_PROFILES_PATH}/{profile_uuid}/background-color", f'({bg_color.red}, {bg_color.green}, {bg_color.blue})'], check=True, stdout=subprocess.PIPE)
|
||||
fg_color = RgbColor(255, 255, 255)
|
||||
subprocess.run(['dconf', 'write', f"{GnomeTerminal.DCONF_PROFILES_PATH}/:{profile_uuid}/use-theme-colors", 'false'], check=True, stdout=subprocess.PIPE)
|
||||
subprocess.run(['dconf', 'write', f"{GnomeTerminal.DCONF_PROFILES_PATH}/:{profile_uuid}/background-color", f"'rgb({bg_color.red}, {bg_color.green}, {bg_color.blue})'"], check=True, stdout=subprocess.PIPE)
|
||||
subprocess.run(['dconf', 'write', f"{GnomeTerminal.DCONF_PROFILES_PATH}/:{profile_uuid}/foreground-color", f"'rgb({fg_color.red}, {fg_color.green}, {fg_color.blue})'"], check=True, stdout=subprocess.PIPE)
|
||||
|
||||
@staticmethod
|
||||
def delete_term_profile(profile_uuid: TermProfileUuid):
|
||||
# dconf reset -f /org/gnome/terminal/legacy/profiles:/2a05665f-baa7-484e-b179-4e94f3814238
|
||||
logging.warning('deleting terminal profile %s', profile_uuid)
|
||||
subprocess.run(['dconf', 'reset', '-f', f"{GnomeTerminal.DCONF_PROFILES_PATH}/:{profile_uuid}/"], check=True)
|
||||
|
||||
@staticmethod
|
||||
def delete_all_term_profiles():
|
||||
for term_profile_uuid in GnomeTerminal.get_term_profiles():
|
||||
GnomeTerminal.delete_term_profile(term_profile_uuid)
|
||||
subprocess.run(['dconf', 'reset', '-f', f"{GnomeTerminal.DCONF_PROFILES_PATH}/list"], check=True)
|
||||
|
||||
@staticmethod
|
||||
def check_validity():
|
||||
proc = subprocess.run(['dconf', 'list', f"{GnomeTerminal.DCONF_PROFILES_PATH}/"], check=True, stdout=subprocess.PIPE)
|
||||
profile_uuids = []
|
||||
for line in proc.stdout.decode('utf8').split('\n'):
|
||||
m = re.match(r'^:(?P<uuid>[0-9a-f\-]+)/$', line)
|
||||
if m:
|
||||
uuid = m['uuid']
|
||||
assert is_valid_uuid(uuid)
|
||||
else:
|
||||
assert line in ['list', ''], f'unexpected key: {line}'
|
||||
|
||||
class GnomeTerminalLauncher(ITerminalLauncher):
|
||||
|
||||
|
|
@ -284,31 +323,78 @@ class GnomeTerminalLauncher(ITerminalLauncher):
|
|||
command: the command to execute in the terminal eg ['/usr/bin/ssh', f'{str(ssh_target)}']
|
||||
'''
|
||||
|
||||
if False: # deleting the term profiles affects the already open terminals, that then switch to default colors
|
||||
# delete all profiles to delete the broken ones and the duplicates
|
||||
GnomeTerminal.delete_all_term_profiles()
|
||||
|
||||
GnomeTerminal.set_terminal_profile_bg_color(terminal_label, bg_color)
|
||||
|
||||
profile_uuid = GnomeTerminal.get_terminal_profile_uuid(terminal_label)
|
||||
logging.info('using terminal profile %s (uuid=%s)', terminal_label, profile_uuid)
|
||||
|
||||
launch_term_command = ['gnome-terminal', f'--window-with-profile={profile_uuid}', f'--title={terminal_label}', '--'] + command
|
||||
logging.debug('launch_term_command = %s', launch_term_command)
|
||||
subprocess.run(launch_term_command, check=True)
|
||||
|
||||
|
||||
def is_valid_host_fqdn(host_fqdn: str) -> bool:
|
||||
m = re.match(r'^[a-z0-9.\-]+$', host_fqdn)
|
||||
return m is not None
|
||||
|
||||
|
||||
def get_host_real_fqdn(host_id: HostId) -> HostId:
|
||||
# 20260617-11:47:29 graffy@graffy-ws2:~/work/graffyworkenv.git$ host alambix.ipr.univ-rennes.fr
|
||||
# alambix.ipr.univ-rennes.fr is an alias for alambix-frontal.ipr.univ-rennes.fr.
|
||||
# alambix-frontal.ipr.univ-rennes.fr has address 129.20.27.230
|
||||
# last command status : [0]
|
||||
command = "host %s | tail -1 | awk '{print $1}'" % host_id
|
||||
proc = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE)
|
||||
lines = proc.stdout.decode('utf8').split('\n')
|
||||
host_real_fqdn = lines[0]
|
||||
assert is_valid_host_fqdn(host_real_fqdn), f'invalid host name: {host_real_fqdn}'
|
||||
return host_real_fqdn
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
arg_parser = argparse.ArgumentParser('cssh (colored ssh) opens a terminal window with a background color that depends on the fully qualified domain name of the target machine')
|
||||
arg_parser.add_argument('ssh_target', help='eg root@alambix50.ipr.univ-rennes.fr')
|
||||
args = arg_parser.parse_args()
|
||||
logging.debug(args)
|
||||
ssh_target = SshTarget(args.ssh_target) # eg 'root@alambix50.ipr.univ-rennes.fr'
|
||||
|
||||
|
||||
ssh_target.host_id = get_host_real_fqdn(ssh_target.host_id)
|
||||
logging.debug('ssh_target.host_id = %s', ssh_target.host_id)
|
||||
# AS_ROOT='false'
|
||||
# if [ "$(echo $@ | grep '^root@' > /dev/null)" ]
|
||||
# then
|
||||
# AS_ROOT='true'
|
||||
# fi
|
||||
|
||||
# if [ $(echo $HOST_FQDN | grep '^simpatix') ]
|
||||
# then
|
||||
# BG_COLOR="$SAILOR_BLUE"
|
||||
# elif [ $(echo $HOST_FQDN | grep '^physix') ]
|
||||
# then
|
||||
# BG_COLOR="$TAUPE"
|
||||
# else
|
||||
# case $HOST_FQDN in
|
||||
# 'pr079234.spm.univ-rennes1.fr')
|
||||
# BG_COLOR="$SOLARIZED_BACKGROUND"
|
||||
# ;;
|
||||
# 'puppet3.ipr.univ-rennes1.fr')
|
||||
# BG_COLOR="$PRUNE"
|
||||
# ;;
|
||||
# *)
|
||||
# BG_COLOR="$DARK_OLIVE"
|
||||
# ;;
|
||||
# esac
|
||||
# fi
|
||||
|
||||
#if [ "$AS_ROOT" = 'true' ]
|
||||
#then
|
||||
# BG_COLOR="$DARK_ORANGE"
|
||||
#fi
|
||||
|
||||
color_saturation = 0.3
|
||||
color_value = 0.2
|
||||
|
|
@ -316,29 +402,19 @@ def main():
|
|||
os_name = proc.stdout.decode('utf8').strip()
|
||||
terminal_launcher: Optional[ITerminalLauncher] = None
|
||||
if os_name == 'Darwin':
|
||||
raise NotImplementedError()
|
||||
# 'Darwin')
|
||||
# BG_COLOR=$(make_color.py $HOST_FQDN $COLOR_VALUE $COLOR_SATURATION osx)
|
||||
# THIS_OSX_TERM_WINDOW_ID=$(get_current_terminal_window_id)
|
||||
# echo "THIS_OSX_TERM_WINDOW_ID=$THIS_OSX_TERM_WINDOW_ID"
|
||||
# set_bg "$THIS_OSX_TERM_WINDOW_ID" "$BG_COLOR"
|
||||
# trap on_exit EXIT
|
||||
# /usr/bin/ssh "$@"
|
||||
# ;;
|
||||
terminal_launcher = DarwinTerminalLauncher()
|
||||
elif os_name == 'Linux':
|
||||
|
||||
terminal_launcher = GnomeTerminalLauncher()
|
||||
else:
|
||||
raise NotImplementedError(f'unhandled os : "{os_name}"')
|
||||
|
||||
proc = subprocess.run(['make_color.py', ssh_target.host_id, str(color_value), str(color_saturation), os_name.lower()], check=True, stdout=subprocess.PIPE)
|
||||
proc = subprocess.run(['make_color.py', ssh_target.host_id, str(color_value), str(color_saturation), 'linux'], check=True, stdout=subprocess.PIPE)
|
||||
bg_color_as_str = proc.stdout.decode('utf8').strip()
|
||||
match = re.match(r'\((?P<red>[0-9]+), (?P<green>[0-9]+), (?P<blue>[0-9]+)\)', bg_color_as_str)
|
||||
assert match, f'unexpected color as string: {bg_color_as_str}'
|
||||
bg_color = RgbColor(match['red'], match['green'], match['blue'])
|
||||
logging.debug('bg_color = %s', str(bg_color)) # eg (35, 43, 51)
|
||||
|
||||
|
||||
command = ['/usr/bin/ssh', f'{str(ssh_target)}']
|
||||
terminal_launcher.launch_terminal(terminal_label=ssh_target.host_id, bg_color=bg_color, command=command)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue