Skip to content

Commit 05bc9f2

Browse files
LINxianshenglinguanren
authored andcommitted
support mac
1 parent 61aefd0 commit 05bc9f2

22 files changed

Lines changed: 655 additions & 99 deletions

_cmd.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3076,6 +3076,8 @@ def __init__(self):
30763076
self.parser._add_version_option()
30773077

30783078
if __name__ == '__main__':
3079+
import multiprocessing
3080+
multiprocessing.freeze_support()
30793081
defaultencoding = 'utf-8'
30803082
if sys.getdefaultencoding() != defaultencoding:
30813083
try:

_errno.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class InitDirFailedErrorMessage(object):
111111
PERMISSION_DENIED = ': {path} permission denied .'
112112

113113

114-
DOC_LINK = '<DOC_LINK>'
114+
DOC_LINK = ''
115115
DOC_LINK_MSG = 'See {}'.format(DOC_LINK if DOC_LINK else "https://www.oceanbase.com/product/ob-deployer/error-codes .")
116116

117117
# generic error code

_plugin.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,10 @@ def _new_func(
283283
namespace_vars.update(kwargs)
284284
if arg:
285285
idx = 0
286-
params = list(inspect2.signature(method).parameters.keys())[1:-2]
286+
try:
287+
params = list(inspect2.signature(method).parameters.keys())[1:-2]
288+
except (ValueError, TypeError):
289+
params = []
287290
num = min(len(arg), len(params))
288291
while idx < num:
289292
key = params[idx]
@@ -871,6 +874,9 @@ def __init__(self, home_path, script_name=None, dev_mode=False, stdio=None):
871874
super(PyScriptPluginLoader, self).__init__(home_path, dev_mode=dev_mode, stdio=stdio)
872875

873876
def _create_(self, script_name):
877+
# Use an explicit namespace dict for exec() because Python 3.12+ (PEP 667)
878+
# changed locals() to return a snapshot, so exec() results won't appear in locals().
879+
_ns = {}
874880
exec('''
875881
class %s(PyScriptPlugin):
876882
@@ -890,8 +896,8 @@ def %s(
890896
repositories, components, clients, cluster_config, cmd,
891897
options, stdio, *arg, **kwargs):
892898
pass
893-
''' % (self.PLUGIN_TYPE.value, script_name, script_name, self.PLUGIN_TYPE.value, self.PLUGIN_TYPE.value, script_name))
894-
clz = locals()[self.PLUGIN_TYPE.value]
899+
''' % (self.PLUGIN_TYPE.value, script_name, script_name, self.PLUGIN_TYPE.value, self.PLUGIN_TYPE.value, script_name), globals(), _ns)
900+
clz = _ns[self.PLUGIN_TYPE.value]
895901
setattr(sys.modules[__name__], self.PLUGIN_TYPE.value, clz)
896902
clz.set_plugin_type(self.PLUGIN_TYPE)
897903
return clz

_stdio.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,11 @@ def decorated(func):
11501150
is_bond_method = True
11511151
_type = type(func)
11521152
func = func.__func__
1153-
all_parameters = inspect2.signature(func).parameters
1153+
try:
1154+
all_parameters = inspect2.signature(func).parameters
1155+
except (ValueError, TypeError):
1156+
# Builtin slot wrappers (e.g. object.__init__) can't be inspected on Python 3.13+
1157+
return _type(func) if is_bond_method else func
11541158
if "stdio" in all_parameters:
11551159
default_stdio_in_params = all_parameters["stdio"].default
11561160
if not isinstance(default_stdio_in_params, Parameter.empty):

_workflow.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ def __init__(self, home_path, workflow_name=None, dev_mode=False, stdio=None):
218218
self.workflow_name = workflow_name
219219

220220
def _create_(self, workflow_name):
221+
# Use an explicit namespace dict for exec() because Python 3.12+ (PEP 667)
222+
# changed locals() to return a snapshot, so exec() results won't appear in locals().
223+
_ns = {}
221224
exec('''
222225
class %s(PyScriptPlugin):
223226
@@ -237,8 +240,8 @@ def %s(
237240
repositories, components, clients, cluster_config, cmd,
238241
options, stdio, *arg, **kwargs):
239242
pass
240-
''' % (self.PLUGIN_TYPE.value, workflow_name, workflow_name, self.PLUGIN_TYPE.value, self.PLUGIN_TYPE.value, workflow_name))
241-
clz = locals()[self.PLUGIN_TYPE.value]
243+
''' % (self.PLUGIN_TYPE.value, workflow_name, workflow_name, self.PLUGIN_TYPE.value, self.PLUGIN_TYPE.value, workflow_name), globals(), _ns)
244+
clz = _ns[self.PLUGIN_TYPE.value]
242245
setattr(sys.modules[__name__], self.PLUGIN_TYPE.value, clz)
243246
clz.set_plugin_type(self.PLUGIN_TYPE)
244247
return clz

plugins-requirements3.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ bcrypt==4.0.0
44
configparser>=5.2.0
55
urllib3==2.5.0
66
influxdb==5.3.2
7+
PyYAML>=6.0
78
obshell

plugins/general/0.1/install_repo.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from __future__ import absolute_import, division, print_function
1717

1818
import os
19+
import platform
1920
import re
2021

2122
from _plugin import InstallPlugin
@@ -158,7 +159,10 @@ def check_lib():
158159
for file_item in check_file_map.values():
159160
if file_item.type == InstallPlugin.FileItemType.BIN:
160161
remote_file_path = os.path.join(remote_home_path, file_item.target_path)
161-
ret = client.execute_command('ldd %s' % remote_file_path)
162+
if platform.system() == 'Darwin':
163+
ret = client.execute_command('otool -L %s' % remote_file_path)
164+
else:
165+
ret = client.execute_command('ldd %s' % remote_file_path)
162166
libs = re.findall('(/?[\w+\-/]+\.\w+[\.\w]+)[\s\\n]*\=\>[\s\\n]*not found', ret.stdout)
163167
if not libs:
164168
libs = re.findall('(/?[\w+\-/]+\.\w+[\.\w]+)[\s\\n]*\=\>[\s\\n]*not found', ret.stderr)

plugins/mysqltest/3.1.0/run_test.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919
import os
2020
import time
2121
import shlex
22+
import platform
2223
import requests
2324
import urllib
24-
from subprocess import Popen, PIPE
25+
from subprocess import Popen, PIPE, TimeoutExpired
2526
from copy import deepcopy
2627
from ssh import LocalClient
2728
from tool import DirectoryUtil
@@ -380,20 +381,42 @@ def return_true(**kw):
380381
LocalClient.execute_command('%s "alter system set _enable_static_typing_engine = %s;select sleep(2);"' % (exec_sql_cmd, opt['_enable_static_typing_engine']), stdio=stdio)
381382

382383
start_time = time.time()
383-
cmd = 'timeout %s %s %s' % (case_timeout, mysqltest_bin, str(Arguments(opt)))
384+
IS_DARWIN = platform.system() == 'Darwin'
385+
if IS_DARWIN:
386+
# macOS does not have GNU timeout; use Python subprocess timeout instead
387+
cmd = '%s %s' % (mysqltest_bin, str(Arguments(opt)))
388+
else:
389+
cmd = 'timeout %s %s %s' % (case_timeout, mysqltest_bin, str(Arguments(opt)))
384390
try:
385391
stdio.verbose('local execute: %s ' % cmd)
386392
p = Popen(shlex.split(cmd), env=test_env, stdout=PIPE, stderr=PIPE)
387-
output, errput = p.communicate()
388-
retcode = p.returncode
389-
if retcode == 124:
390-
output = ''
391-
if 'source_limit' in opt and 'g.buffer' in opt['source_limit']:
392-
errput = "%s secs out of soft limit (%s secs), sql may be hung, please check" % (opt['source_limit']['g.buffer'], case_timeout)
393+
if IS_DARWIN:
394+
try:
395+
output, errput = p.communicate(timeout=case_timeout)
396+
except TimeoutExpired:
397+
p.kill()
398+
output, errput = p.communicate()
399+
retcode = 124 # mimic GNU timeout exit code
400+
output = ''
401+
if 'source_limit' in opt and 'g.buffer' in opt['source_limit']:
402+
errput = "%s secs out of soft limit (%s secs), sql may be hung, please check" % (opt['source_limit']['g.buffer'], case_timeout)
403+
else:
404+
errput = "%s seconds timeout, sql may be hung, please check" % case_timeout
393405
else:
394-
errput = "%s seconds timeout, sql may be hung, please check" % case_timeout
395-
elif isinstance(errput, bytes):
396-
errput = errput.decode(errors='replace')
406+
retcode = p.returncode
407+
if isinstance(errput, bytes):
408+
errput = errput.decode(errors='replace')
409+
else:
410+
output, errput = p.communicate()
411+
retcode = p.returncode
412+
if retcode == 124:
413+
output = ''
414+
if 'source_limit' in opt and 'g.buffer' in opt['source_limit']:
415+
errput = "%s secs out of soft limit (%s secs), sql may be hung, please check" % (opt['source_limit']['g.buffer'], case_timeout)
416+
else:
417+
errput = "%s seconds timeout, sql may be hung, please check" % case_timeout
418+
elif isinstance(errput, bytes):
419+
errput = errput.decode(errors='replace')
397420
except Exception as e:
398421
errput = str(e)
399422
output = ''

plugins/seekdb/1.0.0/environment_check.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
from __future__ import absolute_import, division, print_function
1616

1717
import os
18+
import platform
1819
import re
1920

2021
import _errno as err
2122
from _arch import getBaseArch
2223
from _rpm import Version
2324
from tool import get_port_socket_inode, contains_duplicate_nodes
2425

26+
IS_DARWIN = platform.system() == 'Darwin'
27+
2528

2629
def environment_check(plugin_context, work_dir_empty_check=True, generate_configs={}, *args, **kwargs):
2730
cluster_config = plugin_context.cluster_config
@@ -140,7 +143,10 @@ def environment_check(plugin_context, work_dir_empty_check=True, generate_config
140143

141144
basearch = getBaseArch()
142145
stdio.verbose("basearch: %s" % basearch)
143-
if 'x86' in basearch and len(re.findall(r'(^avx\s+)|(\s+avx\s+)|(\s+avx$)', client.execute_command('lscpu | grep avx').stdout)) == 0:
146+
if IS_DARWIN:
147+
# macOS does not have lscpu; Apple Silicon natively supports atomics and AVX is N/A.
148+
stdio.verbose('Skip CPU instruction set check on macOS')
149+
elif 'x86' in basearch and len(re.findall(r'(^avx\s+)|(\s+avx\s+)|(\s+avx$)', client.execute_command('lscpu | grep avx').stdout)) == 0:
144150
if not (Version('4.2.5.6') <= repository.version < Version('4.3.0.0') or Version('4.3.5.4') <= repository.version < Version('4.4.0.0') or Version('4.4.1.0') <= repository.version):
145151
critical(server, 'cpu', err.EC_CPU_NOT_SUPPORT_INSTRUCTION_SET.format(server=server, instruction_set='avx'), [err.SUG_CHANGE_SERVER.format()])
146152
elif ('arm' in basearch or 'aarch' in basearch) and len(re.findall(r'(^atomics\s+)|(\s+atomics\s+)|(\s+atomics$)', client.execute_command('lscpu | grep atomics').stdout)) == 0 and 'nonlse' not in repository.release:

plugins/seekdb/1.0.0/generate_general_config.py

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515

1616
from __future__ import absolute_import, division, print_function
1717

18+
import platform
1819
import re
1920
import os
2021

2122
from _types import Capacity
2223
from _errno import EC_OBSERVER_NOT_ENOUGH_MEMORY_ALAILABLE, EC_OBSERVER_NOT_ENOUGH_MEMORY_CACHED, EC_OBSERVER_GET_MEMINFO_FAIL
2324
import _errno as err
2425

26+
IS_DARWIN = platform.system() == 'Darwin'
27+
2528

2629
def get_system_memory(memory_limit):
2730
if memory_limit < 12 << 30:
@@ -102,22 +105,44 @@ def generate_general_config(plugin_context, generate_config_mini=False, auto_dep
102105
min_pool_memory = server_config['__min_full_resource_pool_memory']
103106
min_memory = max(system_memory, MIN_MEMORY)
104107
if ip not in ip_server_memory_info:
105-
ret = client.execute_command('cat /proc/meminfo')
106-
if ret:
107-
ip_server_memory_info[ip] = server_memory_stats = {}
108-
memory_key_map = {
109-
'MemTotal': 'total',
110-
'MemFree': 'free',
111-
'MemAvailable': 'available',
112-
'Buffers': 'buffers',
113-
'Cached': 'cached'
114-
}
115-
for key in memory_key_map:
116-
server_memory_stats[memory_key_map[key]] = 0
117-
for k, v in re.findall('(\w+)\s*:\s*(\d+\s*\w+)', ret.stdout):
118-
if k in memory_key_map:
119-
key = memory_key_map[k]
120-
server_memory_stats[key] = Capacity(str(v)).bytes
108+
if IS_DARWIN:
109+
ret = client.execute_command('sysctl hw.memsize')
110+
if ret:
111+
try:
112+
total_mem = int(re.findall(r'hw\.memsize:\s*(\d+)', ret.stdout)[0])
113+
vm_ret = client.execute_command('vm_stat')
114+
page_size = 16384
115+
ps_match = re.search(r'page size of (\d+) bytes', vm_ret.stdout) if vm_ret else None
116+
if ps_match:
117+
page_size = int(ps_match.group(1))
118+
free_pages = int(re.findall(r'Pages free:\s+(\d+)', vm_ret.stdout)[0]) if vm_ret else 0
119+
inactive_pages = int(re.findall(r'Pages inactive:\s+(\d+)', vm_ret.stdout)[0]) if vm_ret else 0
120+
ip_server_memory_info[ip] = server_memory_stats = {
121+
'total': total_mem,
122+
'free': free_pages * page_size,
123+
'available': (free_pages + inactive_pages) * page_size,
124+
'buffers': 0,
125+
'cached': inactive_pages * page_size
126+
}
127+
except Exception:
128+
stdio.exception('Failed to parse macOS memory info')
129+
else:
130+
ret = client.execute_command('cat /proc/meminfo')
131+
if ret:
132+
ip_server_memory_info[ip] = server_memory_stats = {}
133+
memory_key_map = {
134+
'MemTotal': 'total',
135+
'MemFree': 'free',
136+
'MemAvailable': 'available',
137+
'Buffers': 'buffers',
138+
'Cached': 'cached'
139+
}
140+
for key in memory_key_map:
141+
server_memory_stats[memory_key_map[key]] = 0
142+
for k, v in re.findall('(\w+)\s*:\s*(\d+\s*\w+)', ret.stdout):
143+
if k in memory_key_map:
144+
key = memory_key_map[k]
145+
server_memory_stats[key] = Capacity(str(v)).bytes
121146

122147
if user_server_config.get('memory_limit_percentage'):
123148
if ip in ip_server_memory_info:
@@ -180,7 +205,10 @@ def generate_general_config(plugin_context, generate_config_mini=False, auto_dep
180205

181206
# cpu
182207
if not server_config.get('cpu_count'):
183-
ret = client.execute_command("grep -e 'processor\s*:' /proc/cpuinfo | wc -l")
208+
if IS_DARWIN:
209+
ret = client.execute_command("sysctl -n hw.ncpu")
210+
else:
211+
ret = client.execute_command("grep -e 'processor\s*:' /proc/cpuinfo | wc -l")
184212
if ret and ret.stdout.strip().isdigit():
185213
cpu_num = int(ret.stdout)
186214
server_config['cpu_count'] = max(MIN_CPU_COUNT, int(cpu_num - 2))
@@ -196,7 +224,8 @@ def generate_general_config(plugin_context, generate_config_mini=False, auto_dep
196224
log_disk_size = Capacity(server_config.get('log_disk_size', 0)).bytes
197225
if not server_config.get('datafile_size') or not server_config.get('log_disk_size'):
198226
disk = {'/': 0}
199-
ret = client.execute_command('df --block-size=1024')
227+
df_cmd = 'df -Pk' if IS_DARWIN else 'df --block-size=1024'
228+
ret = client.execute_command(df_cmd)
200229
if ret:
201230
for total, used, avail, puse, path in re.findall('(\d+)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)', ret.stdout):
202231
disk[path] = {
@@ -206,7 +235,7 @@ def generate_general_config(plugin_context, generate_config_mini=False, auto_dep
206235
}
207236
for include_dir in dirs.values():
208237
while include_dir not in disk:
209-
ret = client.execute_command('df --block-size=1024 %s' % include_dir)
238+
ret = client.execute_command('%s %s' % (df_cmd, include_dir))
210239
if ret:
211240
for total, used, avail, puse, path in re.findall('(\d+)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)', ret.stdout):
212241
disk[path] = {

0 commit comments

Comments
 (0)