You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-07-30 19:23:07 +03:00
MCOL-5496: Merge CMAPI code to engine repo.
[add] cmapi code to engine
This commit is contained in:
committed by
Alan Mologorsky
parent
77eedd1756
commit
a079a2c944
0
cmapi/mcs_node_control/__init__.py
Normal file
0
cmapi/mcs_node_control/__init__.py
Normal file
269
cmapi/mcs_node_control/custom_dispatchers/container.sh
Executable file
269
cmapi/mcs_node_control/custom_dispatchers/container.sh
Executable file
@ -0,0 +1,269 @@
|
||||
#!/bin/bash
|
||||
|
||||
# TODO: remove in next releases
|
||||
|
||||
programname=$0
|
||||
|
||||
function usage {
|
||||
echo "usage: $programname op [service_name] [is_primary]"
|
||||
echo " op - operation name [start|stop]"
|
||||
echo " service_name - [mcs-controllernode|mcs-workernode etc]"
|
||||
echo " is_primary - [0|1]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
operation=$1
|
||||
service_name=$2
|
||||
is_primary=$3
|
||||
|
||||
if [[ -z "$operation" || -z "$service_name" || $is_primary -ne 0 && $is_primary -ne 1 ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
LOG_FILE=/var/log/mariadb/columnstore/container-sh.log
|
||||
|
||||
start_up_to_workernode() {
|
||||
# Set Variables
|
||||
IFLAG=/etc/columnstore/container-initialized
|
||||
LOG_PREFIX=/var/log/mariadb/columnstore
|
||||
MCS_INSTALL_PATH=/var/lib/columnstore
|
||||
MCS_INSTALL_BIN=/usr/bin
|
||||
PROGS='StorageManager mcs-loadbrm.py workernode'
|
||||
JEMALLOC_PATH=$(ldconfig -p | grep -m1 libjemalloc | awk '{print $1}')
|
||||
if [ -z "$JEMALLOC_PATH" && -f $MCS_INSTALL_PATH/libjemalloc.so.2 ]; then
|
||||
JEMALLOC_PATH=$MCS_INSTALL_PATH/libjemalloc.so.2
|
||||
fi
|
||||
export LD_PRELOAD=$JEMALLOC_PATH
|
||||
|
||||
# Intialize Container If Necessary
|
||||
if [ ! -e $IFLAG ]; then
|
||||
$MCS_INSTALL_BIN/columnstore-init &>> $LOG_PREFIX/columnstore-init.log
|
||||
fi
|
||||
|
||||
# Verify All Programs Are Available
|
||||
for i in $PROGS ; do
|
||||
if [ ! -x $MCS_INSTALL_BIN/$i ] ; then
|
||||
echo "$i doesn't exist."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Start System
|
||||
echo `date`: start_up_to_workernode\(\)... >> $LOG_FILE
|
||||
|
||||
touch $LOG_PREFIX/storagemanager.log && chmod 666 $LOG_PREFIX/storagemanager.log
|
||||
$MCS_INSTALL_BIN/StorageManager &>> $LOG_PREFIX/storagemanager.log &
|
||||
echo `date`: StorageManager PID = $! >> $LOG_FILE
|
||||
|
||||
sleep 1
|
||||
|
||||
echo `date`: loading BRM >> $LOG_FILE
|
||||
touch $LOG_PREFIX/mcs-loadbrm.log && chmod 666 $LOG_PREFIX/mcs-loadbrm.log
|
||||
# Argument "no" here means don't use systemd to start SM
|
||||
$MCS_INSTALL_BIN/mcs-loadbrm.py no >> $LOG_PREFIX/mcs-loadbrm.log 2>&1
|
||||
|
||||
touch $LOG_PREFIX/workernode.log && chmod 666 $LOG_PREFIX/workernode.log
|
||||
$MCS_INSTALL_BIN/workernode DBRM_Worker1 &>> $LOG_PREFIX/workernode.log &
|
||||
echo `date`: workernode PID = $! >> $LOG_FILE
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
start_those_left_at_master() {
|
||||
# Set Variables
|
||||
LOG_PREFIX=/var/log/mariadb/columnstore
|
||||
MCS_INSTALL_PATH=/var/lib/columnstore
|
||||
MCS_INSTALL_BIN=/usr/bin
|
||||
# TODO: remove fast fix
|
||||
# skip check binary for ExeMgr
|
||||
PROGS='controllernode PrimProc WriteEngineServer DMLProc DDLProc'
|
||||
JEMALLOC_PATH=$(ldconfig -p | grep -m1 libjemalloc | awk '{print $1}')
|
||||
if [ -z "$JEMALLOC_PATH" && -f $MCS_INSTALL_PATH/libjemalloc.so.2 ]; then
|
||||
JEMALLOC_PATH=$MCS_INSTALL_PATH/libjemalloc.so.2
|
||||
fi
|
||||
export LD_PRELOAD=$JEMALLOC_PATH
|
||||
|
||||
# Verify All Programs Are Available (except ExeMgr)
|
||||
for i in $PROGS ; do
|
||||
if [ ! -x $MCS_INSTALL_BIN/$i ] ; then
|
||||
echo "$i doesn't exist."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo `date`: start_those_left_at_master\(\) >> $LOG_FILE
|
||||
|
||||
if [[ $is_primary -eq 1 ]]; then
|
||||
touch $LOG_PREFIX/controllernode.log && chmod 666 $LOG_PREFIX/controllernode.log
|
||||
$MCS_INSTALL_BIN/controllernode fg &>> $LOG_PREFIX/controllernode.log &
|
||||
echo `date`: controllernode PID = $! >> $LOG_FILE
|
||||
fi
|
||||
|
||||
touch $LOG_PREFIX/primproc.log && chmod 666 $LOG_PREFIX/primproc.log
|
||||
$MCS_INSTALL_BIN/PrimProc &>> $LOG_PREFIX/primproc.log &
|
||||
echo `date`: PrimProc PID = $! >> $LOG_FILE
|
||||
|
||||
sleep 1
|
||||
|
||||
if [ -e $MCS_INSTALL_BIN/ExeMgr ] ; then
|
||||
touch $LOG_PREFIX/exemgr.log && chmod 666 $LOG_PREFIX/exemgr.log
|
||||
$MCS_INSTALL_BIN/ExeMgr &>> $LOG_PREFIX/exemgr.log &
|
||||
echo `date`: ExeMgr PID = $! >> $LOG_FILE
|
||||
fi
|
||||
|
||||
touch $LOG_PREFIX/writeengineserver.log && chmod 666 $LOG_PREFIX/writeengineserver.log
|
||||
$MCS_INSTALL_BIN/WriteEngineServer &>> $LOG_PREFIX/writeengineserver.log &
|
||||
echo `date`: WriteEngineServer PID = $! >> $LOG_FILE
|
||||
|
||||
sleep 3
|
||||
|
||||
touch $LOG_PREFIX/dmlproc.log && chmod 666 $LOG_PREFIX/dmlproc.log
|
||||
$MCS_INSTALL_BIN/DMLProc &>> $LOG_PREFIX/dmlproc.log &
|
||||
echo `date`: DMLProc PID = $! >> $LOG_FILE
|
||||
|
||||
touch $LOG_PREFIX/ddlproc.log && chmod 666 $LOG_PREFIX/ddlproc.log
|
||||
$MCS_INSTALL_BIN/DDLProc &>> $LOG_PREFIX/ddlproc.log &
|
||||
echo `date`: DDLProc PID = $! >> $LOG_FILE
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
start() {
|
||||
# Set Variables
|
||||
IFLAG=/etc/columnstore/container-initialized
|
||||
LOG_PREFIX=/var/log/mariadb/columnstore
|
||||
MCS_INSTALL_PATH=/var/lib/columnstore
|
||||
MCS_INSTALL_BIN=/usr/bin
|
||||
# TODO: remove fast fix
|
||||
# skip check binary for ExeMgr
|
||||
PROGS='StorageManager load_brm workernode controllernode PrimProc WriteEngineServer DMLProc DDLProc'
|
||||
JEMALLOC_PATH=$(ldconfig -p | grep -m1 libjemalloc | awk '{print $1}')
|
||||
if [ -z "$JEMALLOC_PATH" && -f $MCS_INSTALL_PATH/libjemalloc.so.2 ]; then
|
||||
JEMALLOC_PATH=$MCS_INSTALL_PATH/libjemalloc.so.2
|
||||
fi
|
||||
export LD_PRELOAD=$JEMALLOC_PATH
|
||||
|
||||
# Intialize Container If Necessary
|
||||
if [ ! -e $IFLAG ]; then
|
||||
$MCS_INSTALL_BIN/columnstore-init &>> $LOG_PREFIX/columnstore-init.log
|
||||
fi
|
||||
|
||||
# Verify All Programs Are Available (except ExeMgr)
|
||||
for i in $PROGS ; do
|
||||
if [ ! -x $MCS_INSTALL_BIN/$i ] ; then
|
||||
echo "$i doesn't exist."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Start System
|
||||
echo `date`: start\(\)... >> $LOG_FILE
|
||||
|
||||
touch $LOG_PREFIX/storagemanager.log && chmod 666 $LOG_PREFIX/storagemanager.log
|
||||
$MCS_INSTALL_BIN/StorageManager &>> $LOG_PREFIX/storagemanager.log &
|
||||
echo `date`: StorageManager PID = $! >> $LOG_FILE
|
||||
sleep 1
|
||||
|
||||
echo `date`: loading BRM >> $LOG_FILE
|
||||
touch $LOG_PREFIX/mcs-loadbrm.log && chmod 666 $LOG_PREFIX/mcs-loadbrm.log
|
||||
# Argument "no" here means don't use systemd to start SM
|
||||
$MCS_INSTALL_BIN/mcs-loadbrm.py no >> $LOG_PREFIX/mcs-loadbrm.log 2>&1
|
||||
|
||||
touch $LOG_PREFIX/workernode.log && chmod 666 $LOG_PREFIX/workernode.log
|
||||
$MCS_INSTALL_BIN/workernode DBRM_Worker2 &>> $LOG_PREFIX/workernode.log &
|
||||
echo `date`: workernode PID = $! >> $LOG_FILE
|
||||
|
||||
sleep 2
|
||||
|
||||
if [[ $is_primary -eq 1 ]]; then
|
||||
touch $LOG_PREFIX/controllernode.log && chmod 666 $LOG_PREFIX/controllernode.log
|
||||
$MCS_INSTALL_BIN/controllernode fg &>> $LOG_PREFIX/controllernode.log &
|
||||
echo `date`: controllernode PID = $! >> $LOG_FILE
|
||||
fi
|
||||
|
||||
touch $LOG_PREFIX/primproc.log && chmod 666 $LOG_PREFIX/primproc.log
|
||||
$MCS_INSTALL_BIN/PrimProc &>> $LOG_PREFIX/primproc.log &
|
||||
echo `date`: PrimProc PID = $! >> $LOG_FILE
|
||||
|
||||
sleep 1
|
||||
|
||||
if [ -e $MCS_INSTALL_BIN/ExeMgr ] ; then
|
||||
touch $LOG_PREFIX/exemgr.log && chmod 666 $LOG_PREFIX/exemgr.log
|
||||
$MCS_INSTALL_BIN/ExeMgr &>> $LOG_PREFIX/exemgr.log &
|
||||
echo `date`: ExeMgr PID = $! >> $LOG_FILE
|
||||
fi
|
||||
|
||||
touch $LOG_PREFIX/writeengineserver.log && chmod 666 $LOG_PREFIX/writeengineserver.log
|
||||
$MCS_INSTALL_BIN/WriteEngineServer &>> $LOG_PREFIX/writeengineserver.log &
|
||||
echo `date`: WriteEngineServer PID = $! >> $LOG_FILE
|
||||
|
||||
sleep 3
|
||||
|
||||
if [[ $is_primary -eq 1 ]]; then
|
||||
touch $LOG_PREFIX/dmlproc.log && chmod 666 $LOG_PREFIX/dmlproc.log
|
||||
$MCS_INSTALL_BIN/DMLProc &>> $LOG_PREFIX/dmlproc.log &
|
||||
echo `date`: DMLProc PID = $! >> $LOG_FILE
|
||||
touch $LOG_PREFIX/ddlproc.log && chmod 666 $LOG_PREFIX/ddlproc.log
|
||||
$MCS_INSTALL_BIN/DDLProc &>> $LOG_PREFIX/ddlproc.log &
|
||||
echo `date`: DDLProc PID = $! >> $LOG_FILE
|
||||
fi
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
stop() {
|
||||
# TODO: remove fast fix
|
||||
# skip check binary for ExeMgr
|
||||
PROGS='DMLProc DDLProc WriteEngineServer PrimProc workernode controllernode StorageManager'
|
||||
MCS_INSTALL_BIN=/usr/bin
|
||||
LOG_PREFIX=/var/log/mariadb/columnstore
|
||||
|
||||
# Stop System
|
||||
echo `date`: Stopping... >> $LOG_FILE
|
||||
|
||||
if [[ ! -z "$(pidof $PROGS)" ]]; then
|
||||
# Save BRM only on the primary node now.
|
||||
if [[ ! -z "$(pidof controllernode)" ]]; then
|
||||
$MCS_INSTALL_BIN/mcs-savebrm.py &>> $LOG_PREFIX/savebrm.log 2>&1
|
||||
fi
|
||||
|
||||
echo `date`: Sending SIGTERM >> $LOG_FILE
|
||||
kill $(pidof $PROGS) > /dev/null
|
||||
sleep 3
|
||||
# Make sure StorageManager had a chance to shutdown clean
|
||||
counter=1
|
||||
while [ -n "$(pidof StorageManager)" -a $counter -le 60 ]
|
||||
do
|
||||
sleep 1
|
||||
((counter++))
|
||||
done
|
||||
echo `date`: Sending SIGKILL >> $LOG_FILE
|
||||
kill -9 $(pidof $PROGS) > /dev/null
|
||||
fi
|
||||
|
||||
echo `date`: Clearing SHM >> $LOG_FILE
|
||||
$MCS_INSTALL_BIN/clearShm
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
case "$operation" in
|
||||
'start')
|
||||
# We start everything when controllernode starts at primary node and with workernode at non-primary
|
||||
if [[ $is_primary -eq 1 && "mcs-workernode" == "$service_name" ]]; then
|
||||
start_up_to_workernode $is_primary
|
||||
elif [[ $is_primary -eq 1 && "mcs-controllernode" == "$service_name" ]]; then
|
||||
start_those_left_at_master $is_primary
|
||||
elif [[ $is_primary -eq 0 && "mcs-workernode" == "$service_name" ]]; then
|
||||
start $is_primary
|
||||
fi
|
||||
;;
|
||||
|
||||
'stop')
|
||||
if [[ $is_primary -eq 1 && "mcs-controllernode" == "$service_name" || $is_primary -eq 0 && "mcs-workernode" == "$service_name" ]]; then
|
||||
stop
|
||||
fi
|
||||
;;
|
||||
esac
|
2
cmapi/mcs_node_control/models/__init__.py
Normal file
2
cmapi/mcs_node_control/models/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
from mcs_node_control.models.node_status import NodeStatus
|
220
cmapi/mcs_node_control/models/dbrm.py
Normal file
220
cmapi/mcs_node_control/models/dbrm.py
Normal file
@ -0,0 +1,220 @@
|
||||
import logging
|
||||
import socket
|
||||
|
||||
from cmapi_server.constants import DEFAULT_MCS_CONF_PATH
|
||||
from mcs_node_control.models.dbrm_socket import (
|
||||
DBRM_COMMAND_BYTES, DEFAULT_HOST, DEFAULT_PORT, DBRMSocketHandler
|
||||
)
|
||||
from mcs_node_control.models.node_config import NodeConfig
|
||||
from mcs_node_control.models.process import Process
|
||||
|
||||
|
||||
# TODO: why we need bitwise shift here? May be constant values?
|
||||
SYSTEM_STATE_FLAGS = {
|
||||
"SS_READY": 1 << 0, # 1
|
||||
"SS_SUSPENDED": 1 << 1, # 2
|
||||
"SS_SUSPEND_PENDING": 1 << 2, # 4
|
||||
"SS_SHUTDOWN_PENDING": 1 << 3, # 8
|
||||
"SS_ROLLBACK": 1 << 4, # 16
|
||||
"SS_FORCE": 1 << 5, # 32
|
||||
"SS_QUERY_READY": 1 << 6, # 64
|
||||
}
|
||||
|
||||
|
||||
module_logger = logging.getLogger()
|
||||
|
||||
|
||||
class DBRM:
|
||||
"""Class DBRM commands"""
|
||||
def __init__(
|
||||
self, root=None, config_filename: str = DEFAULT_MCS_CONF_PATH
|
||||
):
|
||||
self.dbrm_socket = DBRMSocketHandler()
|
||||
self.root = root
|
||||
self.config_filename = config_filename
|
||||
|
||||
def connect(self):
|
||||
node_config = NodeConfig()
|
||||
root = self.root or node_config.get_current_config_root(
|
||||
self.config_filename
|
||||
)
|
||||
master_conn_info = node_config.get_dbrm_conn_info(root)
|
||||
if master_conn_info is None:
|
||||
module_logger.warning(
|
||||
'DBRB.connect: No DBRM info in the Columnstore.xml.'
|
||||
)
|
||||
dbrm_host = master_conn_info['IPAddr'] or DEFAULT_HOST
|
||||
dbrm_port = int(master_conn_info['Port']) or DEFAULT_PORT
|
||||
self.dbrm_socket.connect(dbrm_host, dbrm_port)
|
||||
|
||||
def close(self):
|
||||
self.dbrm_socket.close()
|
||||
|
||||
def __enter__(self):
|
||||
self.connect()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
if exc_type:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _send_command(self, command_name, command_value=None):
|
||||
if command_name not in DBRM_COMMAND_BYTES:
|
||||
module_logger.warning(
|
||||
f'DBRM._send_command: Wrong command requested {command_name}'
|
||||
)
|
||||
return None
|
||||
|
||||
module_logger.info(
|
||||
f'DBRM._send_command: Command {command_name} '
|
||||
f'was requested with value {command_value}'
|
||||
)
|
||||
|
||||
self.dbrm_socket.send(command_name, command_value)
|
||||
response_value_bytes = self.dbrm_socket.receive()
|
||||
|
||||
if command_name == 'readonly':
|
||||
reply = int.from_bytes(response_value_bytes, 'little')
|
||||
else:
|
||||
# get first byte, it's an error message
|
||||
err = int.from_bytes(response_value_bytes[:1], 'little')
|
||||
|
||||
if err != 0:
|
||||
module_logger.warning(
|
||||
f'DBRM._send_command: Command {command_name} '
|
||||
'returned error on server'
|
||||
)
|
||||
raise RuntimeError(
|
||||
f'Controller Node replied error with code {err} '
|
||||
f'for command {command_name}'
|
||||
)
|
||||
|
||||
if len(response_value_bytes) < 2:
|
||||
return None
|
||||
|
||||
reply = int.from_bytes(response_value_bytes[1:], 'little')
|
||||
return reply
|
||||
|
||||
def get_system_state(self):
|
||||
state = self._send_command('get_system_state')
|
||||
return [
|
||||
flag_name for flag_name, flag_value in SYSTEM_STATE_FLAGS.items()
|
||||
# TODO: looks like weird logic? Not readable.
|
||||
if flag_value & state
|
||||
]
|
||||
|
||||
def _edit_system_state(self, states: list, command: str):
|
||||
state = 0
|
||||
# TODO: why we need this? States type is list.
|
||||
# May be str without loop inside is more appropriate here.
|
||||
if isinstance(states, str):
|
||||
states = (states,)
|
||||
|
||||
for state_name in states:
|
||||
if state_name not in SYSTEM_STATE_FLAGS:
|
||||
module_logger.warning(
|
||||
f'DBRM.{command}: Wrong system state requested: '
|
||||
f'{state_name}'
|
||||
)
|
||||
continue
|
||||
# TODO: For that case it's same with simple addition?
|
||||
# So why we need bitwise OR?
|
||||
state |= SYSTEM_STATE_FLAGS[state_name]
|
||||
|
||||
self._send_command(command, state)
|
||||
|
||||
def set_system_state(self, states: list):
|
||||
self._edit_system_state(states, 'set_system_state')
|
||||
|
||||
def clear_system_state(self, states: list):
|
||||
self._edit_system_state(states, 'clear_system_state')
|
||||
|
||||
@staticmethod
|
||||
def get_dbrm_status():
|
||||
"""Reads DBRM status
|
||||
|
||||
DBRM Block Resolution Manager operates in two modes:
|
||||
- master
|
||||
- slave
|
||||
|
||||
This method returns the mode of this DBRM node
|
||||
looking for controllernode process running.
|
||||
|
||||
:return: mode of this DBRM node
|
||||
:rtype: string
|
||||
"""
|
||||
if Process.check_process_alive('controllernode'):
|
||||
return 'master'
|
||||
return 'slave'
|
||||
|
||||
def _get_cluster_mode(self):
|
||||
"""Get DBRM cluster mode for internal usage.
|
||||
|
||||
Returns real DBRM cluster mode from socket response.
|
||||
"""
|
||||
# state can be 1(readonly) or 0(readwrite) or exception raised
|
||||
state = self._send_command('readonly')
|
||||
if state == 1:
|
||||
return 'readonly'
|
||||
elif state == 0:
|
||||
return 'readwrite'
|
||||
|
||||
def get_cluster_mode(self):
|
||||
"""Get DBRM cluster mode for external usage.
|
||||
|
||||
There are some kind of weird logic.
|
||||
It's requested from management.
|
||||
TODO: Here we can cause a logic error.
|
||||
E.g. set non master node to "readwrite" and
|
||||
we got a "readonly" in return value.
|
||||
|
||||
:return: DBRM cluster mode
|
||||
:rtype: str
|
||||
"""
|
||||
real_mode = self._get_cluster_mode()
|
||||
if self.get_dbrm_status() == 'master':
|
||||
return real_mode
|
||||
else:
|
||||
return 'readonly'
|
||||
|
||||
def set_cluster_mode(self, mode):
|
||||
"""Set cluster mode requested
|
||||
|
||||
Connects to the DBRM master's socket and
|
||||
send a command to set cluster mode.
|
||||
|
||||
:rtype: str :error or cluster mode set
|
||||
"""
|
||||
|
||||
if mode == 'readonly':
|
||||
command = 'set_readonly'
|
||||
elif mode == 'readwrite':
|
||||
command = 'set_readwrite'
|
||||
else:
|
||||
return ''
|
||||
|
||||
_ = self._send_command(command)
|
||||
|
||||
return self.get_cluster_mode()
|
||||
|
||||
|
||||
def set_cluster_mode(
|
||||
mode: str, root=None, config_filename: str = DEFAULT_MCS_CONF_PATH
|
||||
):
|
||||
"""Set cluster mode requested
|
||||
|
||||
Connects to the DBRM master's socket and send a command to
|
||||
set cluster mode.
|
||||
|
||||
:rtype: str :error or cluster mode set
|
||||
"""
|
||||
try:
|
||||
with DBRM(root, config_filename) as dbrm:
|
||||
return dbrm.set_cluster_mode(mode)
|
||||
except (ConnectionRefusedError, RuntimeError, socket.error):
|
||||
module_logger.warning(
|
||||
'Cannot establish DBRM connection.', exc_info=True
|
||||
)
|
||||
return 'readonly'
|
248
cmapi/mcs_node_control/models/dbrm_socket.py
Normal file
248
cmapi/mcs_node_control/models/dbrm_socket.py
Normal file
@ -0,0 +1,248 @@
|
||||
import logging
|
||||
import socket
|
||||
|
||||
|
||||
MAGIC_BYTES = 0x14fbc137.to_bytes(4, 'little')
|
||||
# value is tuple(command_bytes, command_value_length)
|
||||
DBRM_COMMAND_BYTES = {
|
||||
'readonly': ((20).to_bytes(1, 'little'), 0),
|
||||
'set_readonly': ((14).to_bytes(1, 'little'), 0),
|
||||
'set_readwrite': ((15).to_bytes(1, 'little'), 0),
|
||||
'set_system_state': ((55).to_bytes(1, 'little'), 4),
|
||||
'get_system_state': ((54).to_bytes(1, 'little'), 4),
|
||||
'clear_system_state': ((57).to_bytes(1, 'little'), 4),
|
||||
}
|
||||
DEFAULT_HOST = 'localhost'
|
||||
DEFAULT_PORT = 8616
|
||||
SOCK_TIMEOUT = 5
|
||||
|
||||
|
||||
class DBRMSocketHandler():
|
||||
"""Class for stream socket operations.
|
||||
|
||||
Include all logic for detecting bytestream protocol version, reading and
|
||||
parsing magic inside, getting command bytes and command value length
|
||||
by command name.
|
||||
|
||||
"""
|
||||
long_strings = None
|
||||
|
||||
def __init__(
|
||||
self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0,
|
||||
fileno=None
|
||||
) -> None:
|
||||
self._socket = None
|
||||
self._family = family
|
||||
self._type = type
|
||||
self._proto = proto
|
||||
self._fileno = fileno
|
||||
self._host = None
|
||||
self._port = None
|
||||
self._recreate_socket()
|
||||
|
||||
@property
|
||||
def _connect_called(self):
|
||||
"""Is connect method called previously.
|
||||
|
||||
This is the instance state to determine if "connect" method called
|
||||
previously. This is not quaranteed that connection still alive.
|
||||
:return: connected state
|
||||
:rtype: bool
|
||||
"""
|
||||
if self._host and self._port:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _recreate_socket(self) -> None:
|
||||
"""Create new internal _socket object.
|
||||
|
||||
Create\recreate new _socket object and connects to it if was already
|
||||
connected.
|
||||
"""
|
||||
if self._socket is not None:
|
||||
self._socket.close()
|
||||
self._socket = socket.socket(
|
||||
family=self._family, type=self._type,
|
||||
proto=self._proto, fileno=self._fileno
|
||||
)
|
||||
if self._connect_called:
|
||||
self.connect(self._host, self._port)
|
||||
|
||||
def _detect_protocol(self) -> None:
|
||||
"""Detect dbrm socket bytestream version.
|
||||
|
||||
This method normally will be called only in first instance
|
||||
at first "send" method call.
|
||||
After that header will be formed and parsed depending on
|
||||
"long_strings" class variable value.
|
||||
|
||||
Sends "readonly" message with "old" protocol version (before MCS 6.2.1)
|
||||
If timeout error raised, sends message with "new" protocol version
|
||||
(after MCS 6.2.1) with extra 4 bytes in header.
|
||||
If both attemts are failed raise RuntimeError and return the
|
||||
"long_strings" variable to initial state - None.
|
||||
|
||||
:raises RuntimeError: [description]
|
||||
"""
|
||||
success = False
|
||||
# check at first old protocol because 5.x.x version got an issue if
|
||||
# we try to send new format packages.
|
||||
for long_strings in (False, True):
|
||||
DBRMSocketHandler.long_strings = long_strings
|
||||
self.send('readonly')
|
||||
try:
|
||||
_ = self.receive()
|
||||
success = True
|
||||
break
|
||||
except (socket.timeout, TimeoutError):
|
||||
# wrong packet sended could cause errors on the mcs engine side
|
||||
self._recreate_socket()
|
||||
continue
|
||||
if not success:
|
||||
# something went wrong so return to unknown protocol state
|
||||
DBRMSocketHandler.long_strings = None
|
||||
raise RuntimeError(
|
||||
'Can\'t detect DBRM bytestream protocol version.'
|
||||
)
|
||||
else:
|
||||
dbrm_protocol_version = (
|
||||
'new' if DBRMSocketHandler.long_strings else 'old'
|
||||
)
|
||||
logging.info(
|
||||
f'Detected "{dbrm_protocol_version}" DBRM bytestream protocol'
|
||||
)
|
||||
|
||||
def _make_msg(self, command_name: str, command_value: int) -> bytes:
|
||||
"""Make bytes msg by command name and value.
|
||||
|
||||
:param command_name: name of a command
|
||||
:type command_name: str
|
||||
:param command_value: command value
|
||||
:type command_value: int or None
|
||||
:return: msg to send throught socket
|
||||
:rtype: bytes
|
||||
"""
|
||||
command_bytes, command_value_length = DBRM_COMMAND_BYTES[command_name]
|
||||
data_length = (
|
||||
command_value_length + len(command_bytes)
|
||||
).to_bytes(4, 'little')
|
||||
# bytestream protocol before MCS 6.2.1 version
|
||||
package_header = MAGIC_BYTES + data_length
|
||||
if DBRMSocketHandler.long_strings:
|
||||
# bytestream protocol after MCS 6.2.1 version
|
||||
long_strings_count = (0).to_bytes(4, 'little')
|
||||
package_header += long_strings_count
|
||||
|
||||
msg_bytes = package_header + command_bytes
|
||||
if command_value is not None:
|
||||
msg_bytes += command_value.to_bytes(
|
||||
command_value_length, 'little'
|
||||
)
|
||||
return msg_bytes
|
||||
|
||||
def _receive_magic(self):
|
||||
"""Reads the stream up to the uncompressed magic.
|
||||
|
||||
The magic is a constant delimeter that occurs at the begging
|
||||
of the stream.
|
||||
"""
|
||||
data: bytes
|
||||
recv_data: bytes = b''
|
||||
while recv_data != MAGIC_BYTES:
|
||||
data = self._socket.recv(1)
|
||||
# TODO: advanced error handling
|
||||
if data == b'':
|
||||
raise RuntimeError(
|
||||
'Socket connection broken while receiving magic'
|
||||
)
|
||||
recv_data += data
|
||||
if not MAGIC_BYTES.startswith(recv_data):
|
||||
recv_data = data
|
||||
continue
|
||||
|
||||
def _receive(self, length: int):
|
||||
"""Receive raw data from socket by length.
|
||||
|
||||
:param length: length in bytes to receive
|
||||
:type length: int
|
||||
:raises RuntimeError: if socket connection is broken while receiving
|
||||
:return: received bytes
|
||||
:rtype: bytes
|
||||
"""
|
||||
chunks = []
|
||||
bytes_recd = 0
|
||||
while bytes_recd < length:
|
||||
chunk = self._socket.recv(min(length - bytes_recd, 2048))
|
||||
if chunk == b'':
|
||||
raise RuntimeError(
|
||||
'Socket connection broken while receiving data.'
|
||||
)
|
||||
chunks.append(chunk)
|
||||
bytes_recd += len(chunk)
|
||||
return b''.join(chunks)
|
||||
|
||||
def _send(self, msg: bytes):
|
||||
"""Send msg in bytes through the socket.
|
||||
|
||||
:param msg: string in bytes to send
|
||||
:type msg: bytes
|
||||
:raises RuntimeError: if connection is broken while sending
|
||||
"""
|
||||
totalsent = 0
|
||||
while totalsent < len(msg):
|
||||
sent = self._socket.send(msg[totalsent:])
|
||||
if sent == 0:
|
||||
raise RuntimeError(
|
||||
'DBRM socket connection broken while sending.'
|
||||
)
|
||||
totalsent = totalsent + sent
|
||||
|
||||
def connect(self, host: str = DEFAULT_HOST, port: int = DEFAULT_PORT):
|
||||
"""Connect to socket.
|
||||
|
||||
By default it connects with DBRM master.
|
||||
"""
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._socket.settimeout(SOCK_TIMEOUT)
|
||||
self._socket.connect((host, port))
|
||||
|
||||
def close(self):
|
||||
"""Closing the socket.
|
||||
|
||||
Set _host and _port instance variables to None to change state to
|
||||
not connected. Then close the _socket.
|
||||
"""
|
||||
self._host = None
|
||||
self._port = None
|
||||
self._socket.close()
|
||||
|
||||
def send(self, command_name: str, command_value: int = None):
|
||||
"""Top level send by command name and value.
|
||||
|
||||
param command_name: name of a command
|
||||
:type command_name: str
|
||||
:param command_value: command value, defaults to None
|
||||
:type command_value: int, optional
|
||||
"""
|
||||
if DBRMSocketHandler.long_strings is None:
|
||||
self._detect_protocol()
|
||||
msg_bytes = self._make_msg(command_name, command_value)
|
||||
self._send(msg_bytes)
|
||||
|
||||
def receive(self):
|
||||
"""Top level method to receive data from socket.
|
||||
|
||||
Automatically reads the magic and data length from data header.
|
||||
|
||||
:return: received bytes without header
|
||||
:rtype: bytes
|
||||
"""
|
||||
self._receive_magic()
|
||||
data_length = int.from_bytes(self._receive(4), 'little')
|
||||
if DBRMSocketHandler.long_strings:
|
||||
# receive long strings count to meet new bytestream protocol
|
||||
# requirements (after MCS 6.2.1 release)
|
||||
long_strings_count_bytes = self._receive(4)
|
||||
data_bytes = self._receive(data_length)
|
||||
return data_bytes
|
114
cmapi/mcs_node_control/models/misc.py
Normal file
114
cmapi/mcs_node_control/models/misc.py
Normal file
@ -0,0 +1,114 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from cmapi_server.constants import (
|
||||
DEFAULT_MCS_CONF_PATH, MCS_DATA_PATH, MCS_MODULE_FILE_PATH,
|
||||
)
|
||||
|
||||
|
||||
module_logger = logging.getLogger()
|
||||
|
||||
|
||||
def read_module_id():
|
||||
"""Retrieves module ID from MCS_MODULE_FILE_PATH.
|
||||
|
||||
:rtype: int : seconds
|
||||
"""
|
||||
module_file = Path(MCS_MODULE_FILE_PATH)
|
||||
return int(module_file.read_text()[2:])
|
||||
|
||||
|
||||
# TODO: Useless for now, newer called in code
|
||||
# Nodeconfig.apply_config doing this.
|
||||
def set_module_id(module_id: int = 1):
|
||||
"""Sets current module ID from MCS_MODULE_FILE_PATH.
|
||||
|
||||
:rtype: int : seconds
|
||||
"""
|
||||
module_file = Path(MCS_MODULE_FILE_PATH)
|
||||
return module_file.write_text(f'pm{module_id}\n')
|
||||
|
||||
|
||||
def get_dbroots_list(path: str = MCS_DATA_PATH):
|
||||
"""searches for services
|
||||
|
||||
The method returns numeric ids of dbroots available.
|
||||
|
||||
:rtype: generator of ints
|
||||
"""
|
||||
func_name = 'get_dbroots_list'
|
||||
path = Path(path)
|
||||
for child in path.glob('data[1-9]*'):
|
||||
dir_list = str(child).split('/') # presume Linux only
|
||||
dbroot_id = int(''.join(list(filter(str.isdigit, dir_list[-1]))))
|
||||
module_logger.debug(f'{func_name} The node has dbroot {dbroot_id}')
|
||||
yield dbroot_id
|
||||
|
||||
|
||||
def get_workernodes() -> dict[dict[str, int]]:
|
||||
"""Get workernodes list.
|
||||
|
||||
Returns a list of network address of all workernodes.
|
||||
This is an equivalent of all nodes.
|
||||
|
||||
:return: workernodes dict
|
||||
:rtype: dict[dict[str, int]]
|
||||
"""
|
||||
# TODO: fix in MCOL-5147, get xml path from class that will handle xml
|
||||
root = current_config_root()
|
||||
workernodes = {}
|
||||
# searches for all tags starts with DBRM_Worker, eg DBRM_Worker1
|
||||
workernodes_elements = root.xpath(
|
||||
"//*[starts-with(local-name(), 'DBRM_Worker')]"
|
||||
)
|
||||
for workernode_el in workernodes_elements:
|
||||
workernode_ip = workernode_el.find('./IPAddr').text
|
||||
if workernode_ip == '0.0.0.0':
|
||||
# skip elements with specific ip
|
||||
continue
|
||||
try:
|
||||
workernode_port = int(workernode_el.find('./Port').text)
|
||||
except (AttributeError, ValueError):
|
||||
# AttributeError for not found Port tag, so got None.text
|
||||
# ValueError for non numeric values in tag text
|
||||
module_logger.error(
|
||||
'No Port tag found or wrong Port value for tag '
|
||||
f'"{workernode_el.tag}".'
|
||||
)
|
||||
workernode_port = 8700
|
||||
workernodes[workernode_el.tag] = {
|
||||
'IPAddr': workernode_ip, 'Port': workernode_port
|
||||
}
|
||||
return workernodes
|
||||
|
||||
|
||||
def get_dbrm_master(config_filename: str = DEFAULT_MCS_CONF_PATH) -> dict:
|
||||
"""Get DBRM master ip and port.
|
||||
|
||||
:param config_filename: path to xml conf, defaults to DEFAULT_MCS_CONF_PATH
|
||||
:type config_filename: str, optional
|
||||
:return: ipaddress and port of DBRM master
|
||||
:rtype: dict
|
||||
"""
|
||||
# TODO: fix in MCOL-5147, get xml path from class that will handle xml
|
||||
# Use NodeConfig class as a template?
|
||||
root = current_config_root(config_filename)
|
||||
return {
|
||||
'IPAddr': root.find("./DBRM_Controller/IPAddr").text,
|
||||
'Port': root.find("./DBRM_Controller/Port").text
|
||||
}
|
||||
|
||||
|
||||
def current_config_root(config_filename: str = DEFAULT_MCS_CONF_PATH):
|
||||
"""Retrievs current configuration
|
||||
|
||||
Read the config and returns Element
|
||||
|
||||
:rtype: lxml.Element
|
||||
"""
|
||||
parser = etree.XMLParser(load_dtd=True)
|
||||
tree = etree.parse(config_filename, parser=parser)
|
||||
return tree.getroot()
|
114
cmapi/mcs_node_control/models/network_ifaces.py
Normal file
114
cmapi/mcs_node_control/models/network_ifaces.py
Normal file
@ -0,0 +1,114 @@
|
||||
# Based on https://gist.github.com/provegard/1536682, which was
|
||||
# Based on getifaddrs.py from pydlnadms [http://code.google.com/p/pydlnadms/].
|
||||
# Only tested on Linux!
|
||||
# WARNING: Not working on Mac OS (tested on 10.12 Sierra)
|
||||
# TODO: move to psutil lib
|
||||
|
||||
|
||||
from socket import AF_INET, AF_INET6, inet_ntop
|
||||
from ctypes import (
|
||||
Structure, Union, POINTER,
|
||||
pointer, get_errno, cast,
|
||||
c_ushort, c_byte, c_void_p, c_char_p, c_uint, c_int, c_uint16, c_uint32
|
||||
)
|
||||
import ctypes.util
|
||||
import ctypes
|
||||
|
||||
|
||||
class struct_sockaddr(Structure):
|
||||
_fields_ = [
|
||||
('sa_family', c_ushort),
|
||||
('sa_data', c_byte * 14),]
|
||||
|
||||
|
||||
class struct_sockaddr_in(Structure):
|
||||
_fields_ = [
|
||||
('sin_family', c_ushort),
|
||||
('sin_port', c_uint16),
|
||||
('sin_addr', c_byte * 4)]
|
||||
|
||||
|
||||
class struct_sockaddr_in6(Structure):
|
||||
_fields_ = [
|
||||
('sin6_family', c_ushort),
|
||||
('sin6_port', c_uint16),
|
||||
('sin6_flowinfo', c_uint32),
|
||||
('sin6_addr', c_byte * 16),
|
||||
('sin6_scope_id', c_uint32)]
|
||||
|
||||
|
||||
class union_ifa_ifu(Union):
|
||||
_fields_ = [
|
||||
('ifu_broadaddr', POINTER(struct_sockaddr)),
|
||||
('ifu_dstaddr', POINTER(struct_sockaddr)),]
|
||||
|
||||
|
||||
class struct_ifaddrs(Structure):
|
||||
pass
|
||||
struct_ifaddrs._fields_ = [
|
||||
('ifa_next', POINTER(struct_ifaddrs)),
|
||||
('ifa_name', c_char_p),
|
||||
('ifa_flags', c_uint),
|
||||
('ifa_addr', POINTER(struct_sockaddr)),
|
||||
('ifa_netmask', POINTER(struct_sockaddr)),
|
||||
('ifa_ifu', union_ifa_ifu),
|
||||
('ifa_data', c_void_p),]
|
||||
|
||||
libc = ctypes.CDLL(ctypes.util.find_library('c'))
|
||||
|
||||
|
||||
def ifap_iter(ifap):
|
||||
ifa = ifap.contents
|
||||
while True:
|
||||
yield ifa
|
||||
if not ifa.ifa_next:
|
||||
break
|
||||
ifa = ifa.ifa_next.contents
|
||||
|
||||
|
||||
def getfamaddr(sa):
|
||||
family = sa.sa_family
|
||||
addr = None
|
||||
if family == AF_INET:
|
||||
sa = cast(pointer(sa), POINTER(struct_sockaddr_in)).contents
|
||||
addr = inet_ntop(family, sa.sin_addr)
|
||||
elif family == AF_INET6:
|
||||
sa = cast(pointer(sa), POINTER(struct_sockaddr_in6)).contents
|
||||
addr = inet_ntop(family, sa.sin6_addr)
|
||||
return family, addr
|
||||
|
||||
|
||||
class NetworkInterface(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.index = libc.if_nametoindex(name)
|
||||
self.addresses = {}
|
||||
|
||||
def __str__(self):
|
||||
return "%s [index=%d, IPv4=%s, IPv6=%s]" % (
|
||||
self.name, self.index,
|
||||
self.addresses.get(AF_INET),
|
||||
self.addresses.get(AF_INET6))
|
||||
|
||||
|
||||
def get_network_interfaces():
|
||||
ifap = POINTER(struct_ifaddrs)()
|
||||
result = libc.getifaddrs(pointer(ifap))
|
||||
if result != 0:
|
||||
raise OSError(get_errno())
|
||||
del result
|
||||
try:
|
||||
retval = {}
|
||||
for ifa in ifap_iter(ifap):
|
||||
name = ifa.ifa_name.decode("UTF-8")
|
||||
i = retval.get(name)
|
||||
if not i:
|
||||
i = retval[name] = NetworkInterface(name)
|
||||
family, addr = getfamaddr(ifa.ifa_addr.contents)
|
||||
if addr:
|
||||
if family not in i.addresses:
|
||||
i.addresses[family] = list()
|
||||
i.addresses[family].append(addr)
|
||||
return retval.values()
|
||||
finally:
|
||||
libc.freeifaddrs(ifap)
|
574
cmapi/mcs_node_control/models/node_config.py
Normal file
574
cmapi/mcs_node_control/models/node_config.py
Normal file
@ -0,0 +1,574 @@
|
||||
import configparser
|
||||
import grp
|
||||
import logging
|
||||
import pwd
|
||||
import re
|
||||
import socket
|
||||
from os import mkdir, replace, chown
|
||||
from pathlib import Path
|
||||
from shutil import copyfile
|
||||
from xml.dom import minidom # to pick up pretty printing functionality
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from cmapi_server.constants import (
|
||||
DEFAULT_MCS_CONF_PATH, DEFAULT_SM_CONF_PATH,
|
||||
MCS_MODULE_FILE_PATH,
|
||||
)
|
||||
# from cmapi_server.managers.process import MCSProcessManager
|
||||
from mcs_node_control.models.misc import (
|
||||
read_module_id, get_dbroots_list
|
||||
)
|
||||
from mcs_node_control.models.network_ifaces import get_network_interfaces
|
||||
|
||||
|
||||
module_logger = logging.getLogger()
|
||||
|
||||
|
||||
class NodeConfig:
|
||||
"""Class to operate with the configuration file.
|
||||
|
||||
The class instance applies new config or retrives current.
|
||||
|
||||
config_filename and output_filename allow tests to override
|
||||
the input & output of this fcn
|
||||
The output in this case may be a config file upgraded to version 1.
|
||||
"""
|
||||
def get_current_config_root(
|
||||
self, config_filename: str = DEFAULT_MCS_CONF_PATH, upgrade=True
|
||||
):
|
||||
"""Retrievs current configuration.
|
||||
|
||||
Read the config and returns Element.
|
||||
TODO: pretty the same function in misc.py - review
|
||||
|
||||
:rtype: lxml.Element
|
||||
"""
|
||||
parser = etree.XMLParser(load_dtd=True)
|
||||
tree = etree.parse(config_filename, parser=parser)
|
||||
self.upgrade_config(tree=tree, upgrade=upgrade)
|
||||
return tree.getroot()
|
||||
|
||||
def get_root_from_string(self, config_string: str):
|
||||
root = etree.fromstring(config_string)
|
||||
self.upgrade_config(root=root)
|
||||
return root
|
||||
|
||||
def upgrade_from_v0(self, root):
|
||||
revision = etree.SubElement(root, 'ConfigRevision')
|
||||
revision.text = '1'
|
||||
cluster_manager = etree.SubElement(root, 'ClusterManager')
|
||||
cluster_manager.text = str(self.get_module_net_address(root=root))
|
||||
cluster_name = etree.SubElement(root, 'ClusterName')
|
||||
cluster_name.text = 'MyCluster'
|
||||
|
||||
# Need to get the addresses/host names of all nodes.
|
||||
# Should all be listed as DBRM_worker nodes
|
||||
addrs = set()
|
||||
num = 1
|
||||
max_node = 1
|
||||
while True:
|
||||
node = root.find(f'./DBRM_Worker{num}/IPAddr')
|
||||
if node is None:
|
||||
break
|
||||
if node.text != '0.0.0.0':
|
||||
addrs.add(node.text)
|
||||
if max_node < num:
|
||||
max_node = num
|
||||
num += 1
|
||||
|
||||
# NextNodeId can be derived from the max DBRM_worker entry with non-0
|
||||
# ip address
|
||||
next_node_id = etree.SubElement(root, 'NextNodeId')
|
||||
next_node_id.text = str(max_node + 1)
|
||||
|
||||
# NextDBRootId is the max current dbroot in use + 1
|
||||
num = 1
|
||||
max_dbroot = 1
|
||||
while num < 100:
|
||||
node = root.find(f'./SystemConfig/DBRoot{num}')
|
||||
if node is not None:
|
||||
max_dbroot = num
|
||||
num += 1
|
||||
next_dbroot_id = etree.SubElement(root, 'NextDBRootId')
|
||||
next_dbroot_id.text = str(max_dbroot + 1)
|
||||
|
||||
# The current primary node is listed under DBRMControllerNode.
|
||||
# Might as well start with that.
|
||||
primary_node_addr = root.find('./DBRM_Controller/IPAddr').text
|
||||
|
||||
# Put them all in the DesiredNodes and ActiveNodes sections
|
||||
desired_nodes = etree.SubElement(root, 'DesiredNodes')
|
||||
active_nodes = etree.SubElement(root, 'ActiveNodes')
|
||||
for addr in addrs:
|
||||
node = etree.SubElement(desired_nodes, 'Node')
|
||||
node.text = addr
|
||||
node = etree.SubElement(active_nodes, 'Node')
|
||||
node.text = addr
|
||||
|
||||
# Add an empty InactiveNodes section and set the primary node addr
|
||||
inactive_nodes = etree.SubElement(root, 'InactiveNodes')
|
||||
primary_node = etree.SubElement(root, 'PrimaryNode')
|
||||
primary_node.text = primary_node_addr
|
||||
|
||||
# Add Maintenance tag and set to False
|
||||
maintenance = etree.SubElement(root, 'Maintenance')
|
||||
maintenance.text = str(False).lower()
|
||||
|
||||
|
||||
def upgrade_config(self, tree=None, root=None, upgrade=True):
|
||||
"""
|
||||
Add the parts that might be missing after an upgrade from an earlier
|
||||
version.
|
||||
|
||||
.. note:: one or the other optional parameter should be specified (?)
|
||||
"""
|
||||
if root is None and tree is not None:
|
||||
root = tree.getroot()
|
||||
|
||||
rev_node = root.find('./ConfigRevision')
|
||||
|
||||
if rev_node is None and upgrade:
|
||||
self.upgrade_from_v0(root)
|
||||
# as we add revisions, add add'l checks on rev_node.text here
|
||||
|
||||
def write_config(self, tree, filename=DEFAULT_MCS_CONF_PATH):
|
||||
tmp_filename = filename + ".cmapi.tmp"
|
||||
with open(tmp_filename, "w") as f:
|
||||
f.write(self.to_string(tree))
|
||||
replace(tmp_filename, filename) # atomic replacement
|
||||
|
||||
def to_string(self, tree):
|
||||
# TODO: try to use lxml to do this to avoid the add'l dependency
|
||||
xmlstr = minidom.parseString(etree.tostring(tree)).toprettyxml(
|
||||
indent=" "
|
||||
)
|
||||
# fix annoying issue of extra newlines added by toprettyxml()
|
||||
xmlstr = '\n'.join([
|
||||
line.rstrip() for line in xmlstr.split('\n') if line.strip() != ""
|
||||
])
|
||||
return xmlstr
|
||||
|
||||
def get_dbrm_conn_info(self, root=None):
|
||||
"""Retrievs current DBRM master IP and port
|
||||
|
||||
Read the config and returns a dict with the connection information.
|
||||
|
||||
:rtype: dict
|
||||
"""
|
||||
if root is None:
|
||||
return None
|
||||
addr = ''
|
||||
port = 0
|
||||
for el in root:
|
||||
if el.tag == 'DBRM_Controller':
|
||||
for subel in el:
|
||||
if subel.tag == 'IPAddr':
|
||||
addr = subel.text
|
||||
elif subel.tag == 'Port':
|
||||
port = subel.text
|
||||
return {'IPAddr': addr, 'Port': port}
|
||||
|
||||
return None
|
||||
|
||||
def apply_config(
|
||||
self, config_filename: str = DEFAULT_MCS_CONF_PATH,
|
||||
xml_string: str = None, sm_config_filename: str = None,
|
||||
sm_config_string: str = None
|
||||
):
|
||||
"""Applies the configuration WIP.
|
||||
|
||||
Instance iterates over the xml nodes.
|
||||
|
||||
: param config_filename: string 4 testing
|
||||
: param xml_string: string
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
if xml_string is None:
|
||||
return
|
||||
|
||||
current_root = self.get_current_config_root(config_filename)
|
||||
parser = etree.XMLParser(load_dtd=True)
|
||||
new_root = etree.fromstring(xml_string, parser=parser)
|
||||
|
||||
try:
|
||||
# We don't change module ids for non-single nodes.
|
||||
# if self.is_single_node(root=current_root):
|
||||
# set_module_id(self.get_new_module_id(new_root))
|
||||
|
||||
# make sure all of the dbroot directories exist on this node
|
||||
for dbroot in self.get_all_dbroots(new_root):
|
||||
try:
|
||||
node = new_root.find(f'./SystemConfig/DBRoot{dbroot}')
|
||||
mkdir(node.text, mode=0o755)
|
||||
|
||||
# if we are using the systemd dispatcher we need to change
|
||||
# ownership on any created dirs to mysql:mysql
|
||||
# TODO: remove conditional once container dispatcher will
|
||||
# use non-root by default
|
||||
# TODO: what happened if we change ownership in container?
|
||||
# check the container installations works as expected
|
||||
# from cmapi_server.managers.process import MCSProcessManager
|
||||
# if MCSProcessManager.dispatcher_name == 'systemd':
|
||||
uid = pwd.getpwnam('mysql').pw_uid
|
||||
gid = grp.getgrnam('mysql').gr_gid
|
||||
chown(node.text, uid, gid)
|
||||
except FileExistsError:
|
||||
pass
|
||||
# Save current config
|
||||
config_file = Path(config_filename)
|
||||
config_dir = config_file.resolve().parent
|
||||
copyfile(
|
||||
config_file, f'{config_dir}/{config_file.name}.cmapi.save'
|
||||
)
|
||||
|
||||
# Save new config
|
||||
self.write_config(tree=new_root, filename=config_filename)
|
||||
|
||||
# Save current and new storagemanager config
|
||||
if sm_config_string and sm_config_filename:
|
||||
sm_config_file = Path(sm_config_filename)
|
||||
sm_config_dir = sm_config_file.resolve().parent
|
||||
copyfile(
|
||||
sm_config_file,
|
||||
f'{sm_config_dir}/{sm_config_file.name}.cmapi.save'
|
||||
)
|
||||
with open(sm_config_filename, 'w') as sm_config_file:
|
||||
sm_config_file.write(sm_config_string)
|
||||
# TODO: review
|
||||
# figure out what to put in the 'module' file to make
|
||||
# the old oam library happy
|
||||
module_file = None
|
||||
try:
|
||||
pm_num = self.get_current_pm_num(new_root)
|
||||
with open(MCS_MODULE_FILE_PATH, 'w') as module_file:
|
||||
module_file.write(f'pm{pm_num}\n')
|
||||
module_logger.info(
|
||||
f'Wrote "pm{pm_num}" to {MCS_MODULE_FILE_PATH}'
|
||||
)
|
||||
except Exception:
|
||||
module_logger.error(
|
||||
'Failed to get or set this node\'s pm number.\n'
|
||||
'You may observe add\'l errors as a result.\n',
|
||||
exc_info=True
|
||||
)
|
||||
except:
|
||||
# Raise an appropriate exception
|
||||
module_logger.error(
|
||||
f'{self.apply_config.__name__} throws an exception.'
|
||||
'The original config must be restored by '
|
||||
'explicit ROLLBACK command or timeout.',
|
||||
exc_info=True
|
||||
)
|
||||
raise
|
||||
|
||||
def in_active_nodes(self, root):
|
||||
my_names = set(self.get_network_addresses_and_names())
|
||||
active_nodes = [
|
||||
node.text for node in root.findall("./ActiveNodes/Node")
|
||||
]
|
||||
for node in active_nodes:
|
||||
if node in my_names:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_current_pm_num(self, root):
|
||||
# Find this node in the Module* tags, return the module number
|
||||
|
||||
my_names = set(self.get_network_addresses_and_names())
|
||||
smc_node = root.find("./SystemModuleConfig")
|
||||
pm_count = int(smc_node.find("./ModuleCount3").text)
|
||||
for pm_num in range(1, pm_count + 1):
|
||||
ip_addr = smc_node.find(f"./ModuleIPAddr{pm_num}-1-3").text
|
||||
name = smc_node.find(f"./ModuleHostName{pm_num}-1-3").text
|
||||
if ip_addr in my_names:
|
||||
module_logger.info(f" -- Matching against ModuleIPAddr{pm_num}-1-3, which says {ip_addr}")
|
||||
return pm_num
|
||||
if name in my_names:
|
||||
module_logger.info(f" -- Matching against ModuleHostName{pm_num}-1-3, which says {name}")
|
||||
return pm_num
|
||||
raise Exception("Did not find my IP addresses or names in the SystemModuleConfig section")
|
||||
|
||||
|
||||
def rollback_config(self, config_filename: str = DEFAULT_MCS_CONF_PATH):
|
||||
"""Rollback the configuration.
|
||||
|
||||
Copyback the copy of the configuration file.
|
||||
|
||||
: param config_filename: Columnstore config file path
|
||||
:rtype: dict
|
||||
"""
|
||||
# TODO: Rollback doesn't restart needed processes?
|
||||
config_file = Path(config_filename)
|
||||
config_dir = config_file.resolve().parent
|
||||
backup_path = f"{config_dir}/{config_file.name}.cmapi.save"
|
||||
config_file_copy = Path(backup_path)
|
||||
if config_file_copy.exists():
|
||||
replace(backup_path, config_file) # atomic replacement
|
||||
|
||||
|
||||
def get_current_config(self, config_filename: str = DEFAULT_MCS_CONF_PATH):
|
||||
"""Retrievs current configuration.
|
||||
|
||||
Read the config and convert it into bytes string.
|
||||
|
||||
:rtype: string
|
||||
|
||||
..TODO: fix using self.get_current_config_root()
|
||||
"""
|
||||
parser = etree.XMLParser(load_dtd=True)
|
||||
tree = etree.parse(config_filename, parser=parser)
|
||||
self.upgrade_config(tree=tree)
|
||||
# TODO: Unicode? UTF-8 may be?
|
||||
return etree.tostring(
|
||||
tree.getroot(), pretty_print=True, encoding='unicode'
|
||||
)
|
||||
|
||||
|
||||
def get_current_sm_config(
|
||||
self, config_filename: str = DEFAULT_SM_CONF_PATH
|
||||
) -> str:
|
||||
"""Retrievs current SM configuration
|
||||
|
||||
Read the config and convert it into a string.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
func_name = 'get_current_sm_config'
|
||||
sm_config_path = Path(config_filename)
|
||||
try:
|
||||
return sm_config_path.read_text(encoding='utf-8')
|
||||
except FileNotFoundError:
|
||||
module_logger.error(f"{func_name} SM config {config_filename} not found.")
|
||||
return ''
|
||||
|
||||
|
||||
def s3_enabled(self, config_filename: str = DEFAULT_SM_CONF_PATH) -> bool:
|
||||
"""Checks if SM is enabled
|
||||
|
||||
Reads SM config and checks if storage set to S3.
|
||||
It also checks for additional settings in the XML that must be set too.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
func_name = 's3_enabled'
|
||||
sm_config = configparser.ConfigParser()
|
||||
if len(sm_config.read(config_filename)) > 0:
|
||||
storage = sm_config.get('ObjectStorage', 'service')
|
||||
if storage is None:
|
||||
storage = 'LocalStorage'
|
||||
|
||||
if storage.lower() == 's3':
|
||||
config_root = self.get_current_config_root()
|
||||
if not config_root.find('./Installation/DBRootStorageType').text.lower() == "storagemanager":
|
||||
module_logger.error(f"{func_name} DBRootStorageType.lower() != storagemanager")
|
||||
if not config_root.find('./StorageManager/Enabled').text.lower() == "y":
|
||||
module_logger.error(f"{func_name} StorageManager/Enabled.lower() != y")
|
||||
if not config_root.find('./SystemConfig/DataFilePlugin').text == "libcloudio.so":
|
||||
module_logger.error(f"{func_name} SystemConfig/DataFilePlugin != libcloudio.so")
|
||||
|
||||
return True
|
||||
else:
|
||||
module_logger.error(f"{func_name} SM config {config_filename} not found.")
|
||||
|
||||
return False
|
||||
|
||||
def get_network_addresses(self):
|
||||
"""Retrievs the list of the network addresses
|
||||
|
||||
Generator that yields network interface addresses.
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
for ni in get_network_interfaces():
|
||||
for fam in [socket.AF_INET, socket.AF_INET6]:
|
||||
addrs = ni.addresses.get(fam)
|
||||
if addrs is not None:
|
||||
for addr in addrs:
|
||||
yield(addr)
|
||||
|
||||
def get_network_addresses_and_names(self):
|
||||
"""Retrievs the list of the network addresses, hostnames, and aliases
|
||||
|
||||
Generator that yields network interface addresses, hostnames, and aliases
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
for ni in get_network_interfaces():
|
||||
for fam in [socket.AF_INET, socket.AF_INET6]:
|
||||
addrs = ni.addresses.get(fam)
|
||||
if addrs is not None:
|
||||
for addr in addrs:
|
||||
yield(addr)
|
||||
try:
|
||||
(host, aliases, _) = socket.gethostbyaddr(addr)
|
||||
except:
|
||||
continue
|
||||
yield host
|
||||
for alias in aliases:
|
||||
yield alias
|
||||
|
||||
def is_primary_node(self, root=None):
|
||||
"""Checks if this node is the primary node.
|
||||
|
||||
Reads the config and compares DBRM_Controller IP or
|
||||
hostname with the this node's IP and hostname.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
if root is None:
|
||||
root = self.get_current_config_root()
|
||||
|
||||
primary_address = self.get_dbrm_conn_info(root)['IPAddr']
|
||||
return primary_address in self.get_network_addresses_and_names()
|
||||
|
||||
def is_single_node(self,
|
||||
root=None):
|
||||
"""Checks if this node is the single node.
|
||||
|
||||
Reads the config and compares DBRMMaster IP with the predefined localhost addresses.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
if root is None:
|
||||
root = self.get_current_config_root()
|
||||
|
||||
master_address = self.get_dbrm_conn_info(root)['IPAddr']
|
||||
if master_address in ['127.0.0.1', 'localhost', '::1']:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_new_module_id(self, new_root=None):
|
||||
"""Retrieves new module id.
|
||||
|
||||
Reads new XML config and searches IP belongs to this host in SystemModuleConfig.ModuleIPAddrX-1-3. X is the new module id.
|
||||
|
||||
:rtype: int
|
||||
"""
|
||||
func_name = 'get_new_module_id'
|
||||
current_module_id = read_module_id()
|
||||
|
||||
if new_root is None:
|
||||
module_logger.error(f'{func_name} Empty new XML tree root.')
|
||||
return current_module_id
|
||||
|
||||
net_address = self.get_module_net_address(new_root, current_module_id)
|
||||
# Use getaddrinfo in case of IPv6
|
||||
if net_address is None:
|
||||
module_logger.error(f'{func_name} Columnstore.xml has unknown value in SystemModuleConfig.\
|
||||
ModuleIPAddr{current_module_id}-1-3.')
|
||||
raise RuntimeError('net_address is None.')
|
||||
if socket.gethostbyname(net_address) in self.get_network_addresses():
|
||||
return current_module_id
|
||||
|
||||
# Use getaddrinfo in case of IPv6
|
||||
# This fires for a added node when node id changes from 1 to something
|
||||
for module_entry in self.get_modules_addresses(new_root):
|
||||
if module_entry['addr'] is not None:
|
||||
net_addr = socket.gethostbyname(module_entry['addr'])
|
||||
if net_addr in self.get_network_addresses():
|
||||
module_logger.debug(f'{func_name} New module id \
|
||||
{module_entry["id"]}')
|
||||
return int(module_entry['id'])
|
||||
|
||||
module_logger.error(f'{func_name} Cannot find new module id for \
|
||||
the node.')
|
||||
raise RuntimeError('Fail to find module id.')
|
||||
|
||||
def get_module_net_address(self, root=None, module_id: int = None):
|
||||
"""Retrieves the module network address.
|
||||
|
||||
Reads new XML config and returns IP or
|
||||
hostname from SystemModuleConfig.ModuleIPAddrX-1-3.
|
||||
|
||||
:rtype: string
|
||||
"""
|
||||
func_name = 'get_module_net_address'
|
||||
if module_id is None:
|
||||
module_id = read_module_id()
|
||||
|
||||
if root is None:
|
||||
module_logger.error(f'{func_name} Empty XML root.')
|
||||
return
|
||||
|
||||
for el in root:
|
||||
if el.tag == 'SystemModuleConfig':
|
||||
for subel in el:
|
||||
if subel.tag == f'ModuleIPAddr{module_id}-1-3':
|
||||
module_logger.debug(
|
||||
f'{func_name} Module {module_id} '
|
||||
f'network address {subel.text}'
|
||||
)
|
||||
return subel.text
|
||||
|
||||
module_logger.error(f'{func_name} Module {module_id} was not found.')
|
||||
return
|
||||
|
||||
def get_modules_addresses(self, root=None):
|
||||
"""Retrieves the modules network addresses.
|
||||
|
||||
Reads new XML config and returns IP or hostname from
|
||||
SystemModuleConfig.ModuleIPAddrX-1-3 with X being a node id.
|
||||
|
||||
:rtype: dict
|
||||
"""
|
||||
func_name = 'get_module_addresses'
|
||||
if root is None:
|
||||
module_logger.error(f'{func_name} Empty XML root.')
|
||||
return None
|
||||
|
||||
regex_string = 'ModuleIPAddr[0-9]+-1-3'
|
||||
for el in root:
|
||||
if el.tag == 'SystemModuleConfig':
|
||||
for subel in el:
|
||||
module_ip_m = re.match(regex_string, subel.tag)
|
||||
if module_ip_m is not None:
|
||||
id_m = re.search('[0-9]+', module_ip_m.group(0))
|
||||
module_id = id_m.group(0)
|
||||
module_logger.debug(
|
||||
f'{func_name} Module {module_id} '
|
||||
f'network address {subel.text}'
|
||||
)
|
||||
yield {'addr': subel.text, 'id': module_id}
|
||||
|
||||
|
||||
module_logger.error(f'{func_name} Module {module_id} was not found.')
|
||||
return None
|
||||
|
||||
def dbroots_to_create(self, root=None, module_id:int=None):
|
||||
"""Generates dbroot ids if there are new dbroots to be created/renamed
|
||||
|
||||
Reads new XML config and generates dbroot ids if on-disk dbroots differs from the config's set.
|
||||
|
||||
:rtype: generator of strings
|
||||
"""
|
||||
func_name = 'dbroots_to_create'
|
||||
if module_id is None:
|
||||
module_id = read_module_id()
|
||||
|
||||
if root is None:
|
||||
module_logger.error(f'{func_name} Empty XML root.')
|
||||
return
|
||||
|
||||
current_dbroot_list = get_dbroots_list()
|
||||
|
||||
regex_string = f'ModuleDBRootID{module_id}-[0-9]+-3'
|
||||
for el in root:
|
||||
if el.tag == 'SystemModuleConfig':
|
||||
for subel in el:
|
||||
if re.match(regex_string, subel.tag) is not None and \
|
||||
int(subel.text) not in current_dbroot_list:
|
||||
module_logger.debug(f'{func_name} Module {module_id} \
|
||||
has dbroot {subel.text}')
|
||||
yield int(subel.text)
|
||||
return
|
||||
|
||||
def get_all_dbroots(self, root):
|
||||
dbroots = []
|
||||
smc_node = root.find("./SystemModuleConfig")
|
||||
mod_count = int(smc_node.find("./ModuleCount3").text)
|
||||
for i in range(1, mod_count+1):
|
||||
for j in range(1, int(smc_node.find(f"./ModuleDBRootCount{i}-3").text) + 1):
|
||||
dbroots.append(smc_node.find(f"./ModuleDBRootID{i}-{j}-3").text)
|
||||
return dbroots
|
91
cmapi/mcs_node_control/models/node_status.py
Normal file
91
cmapi/mcs_node_control/models/node_status.py
Normal file
@ -0,0 +1,91 @@
|
||||
import logging
|
||||
import socket
|
||||
|
||||
from cmapi_server.constants import MCS_DATA_PATH, MCS_MODULE_FILE_PATH
|
||||
from mcs_node_control.models.dbrm import DBRM
|
||||
from mcs_node_control.models.misc import get_dbroots_list, read_module_id
|
||||
from mcs_node_control.models.process import get_host_uptime
|
||||
|
||||
|
||||
PROC_NAMES = ['ExeMgr', 'PrimProc', 'WriteEngine', 'controllernode',
|
||||
'workernode', 'cmagent', 'DMLProc', 'DDLProc']
|
||||
|
||||
|
||||
module_logger = logging.getLogger()
|
||||
|
||||
|
||||
class NodeStatus:
|
||||
"""Class to tell the status of the node.
|
||||
|
||||
Inspects runtime of the cluster and OS and returns its observations.
|
||||
"""
|
||||
def get_cluster_mode(self):
|
||||
"""Reads cluster mode.
|
||||
|
||||
Cluster can be in readwrite or readonly modes. It can be also ready or
|
||||
not ready but it is not important at this point. We pesume if there is
|
||||
no connection with DBRM master then the cluster is readonly.
|
||||
|
||||
TODO:
|
||||
- Is it ok to have those method here in NodeStatus?
|
||||
Move to DBRM.
|
||||
- pass 'root' and config_filename arguments
|
||||
(likewise dbrm.set_cluster_mode)
|
||||
|
||||
:rtype: string
|
||||
"""
|
||||
try:
|
||||
with DBRM() as dbrm:
|
||||
return dbrm.get_cluster_mode()
|
||||
except (ConnectionRefusedError, RuntimeError, socket.error):
|
||||
module_logger.error(
|
||||
'Cannot establish or use DBRM connection.',
|
||||
exc_info=True
|
||||
)
|
||||
return 'readonly'
|
||||
|
||||
|
||||
def get_dbrm_status(self):
|
||||
"""reads DBRM status
|
||||
|
||||
DBRM Block Resolution Manager operates in two modes:
|
||||
master and slave. This m() returns the mode of this node
|
||||
looking for controllernode process running.
|
||||
|
||||
:rtype: string
|
||||
"""
|
||||
return DBRM.get_dbrm_status()
|
||||
|
||||
def get_dbroots(self, path:str = MCS_DATA_PATH):
|
||||
"""searches for services
|
||||
|
||||
The method returns numeric ids of dbroots available.
|
||||
|
||||
:rtype: generator of ints
|
||||
"""
|
||||
for id in get_dbroots_list(path):
|
||||
yield id
|
||||
|
||||
|
||||
def get_host_uptime(self):
|
||||
"""Retrieves uptime in seconds.
|
||||
|
||||
:rtype: int : seconds
|
||||
"""
|
||||
return get_host_uptime()
|
||||
|
||||
|
||||
def get_module_id(self):
|
||||
"""Retrieves module ID from MCS_MODULE_FILE_PATH.
|
||||
|
||||
:rtype: int : seconds
|
||||
"""
|
||||
func_name = 'get_module_id'
|
||||
try:
|
||||
module_id = read_module_id()
|
||||
except FileNotFoundError:
|
||||
module_id = 0
|
||||
module_logger.error(
|
||||
f'{func_name} {MCS_MODULE_FILE_PATH} file is absent.'
|
||||
)
|
||||
return module_id
|
110
cmapi/mcs_node_control/models/process.py
Normal file
110
cmapi/mcs_node_control/models/process.py
Normal file
@ -0,0 +1,110 @@
|
||||
import os
|
||||
import time
|
||||
|
||||
import psutil
|
||||
|
||||
PROCFS_PATH = '/proc/' # Linux only
|
||||
|
||||
|
||||
def open_binary(fname, **kwargs):
|
||||
return open(fname, "rb", **kwargs)
|
||||
|
||||
|
||||
def get_host_uptime():
|
||||
"""
|
||||
Return the system boot time expressed in seconds since the epoch.
|
||||
|
||||
:rtype: int : diff b/w current epoch and boot epoch
|
||||
"""
|
||||
path = f'{PROCFS_PATH}stat'
|
||||
boot_time = 0
|
||||
with open_binary(path) as f:
|
||||
for line in f:
|
||||
if line.startswith(b'btime'):
|
||||
boot_time = float(line.strip().split()[1])
|
||||
return int(time.time() - int(boot_time))
|
||||
return 0
|
||||
|
||||
|
||||
class Process():
|
||||
"""An interface to retrieve data from proc."""
|
||||
def get_proc_iterator(self):
|
||||
for pid in self.pids():
|
||||
yield pid
|
||||
|
||||
|
||||
def pids(self):
|
||||
"""Returns a list of PIDs currently running on the system."""
|
||||
return [int(x) for x in os.listdir(PROCFS_PATH) if x.isdigit()]
|
||||
|
||||
|
||||
def name(self, pid: int):
|
||||
"""Method to retrive name associated with the pid."""
|
||||
return self.parse_stat_file(pid)['name']
|
||||
|
||||
|
||||
def parse_stat_file(self, pid: int):
|
||||
"""Parse /proc/{pid}/stat file and return a dict with various
|
||||
process info.
|
||||
|
||||
Using "man proc" as a reference: where "man proc" refers to
|
||||
position N always substract 3 (e.g ppid position 4 in
|
||||
'man proc' == position 1 in here).
|
||||
"""
|
||||
ret = {}
|
||||
try:
|
||||
with open_binary(f"{PROCFS_PATH}{pid}/stat") as f:
|
||||
data = f.read()
|
||||
# Process name is between parentheses. It can contain spaces and
|
||||
# other parentheses. This is taken into account by looking for
|
||||
# the first occurrence of "(" and the last occurence of ")".
|
||||
rpar = data.rfind(b')')
|
||||
name = data[data.find(b'(') + 1:rpar]
|
||||
fields = data[rpar + 2:].split()
|
||||
|
||||
ret['name'] = name
|
||||
ret['status'] = fields[0]
|
||||
ret['ppid'] = fields[1]
|
||||
ret['ttynr'] = fields[4]
|
||||
ret['utime'] = fields[11]
|
||||
ret['stime'] = fields[12]
|
||||
ret['children_utime'] = fields[13]
|
||||
ret['children_stime'] = fields[14]
|
||||
ret['create_time'] = fields[19]
|
||||
ret['cpu_num'] = fields[36]
|
||||
ret['blkio_ticks'] = fields[39] # aka 'delayacct_blkio_ticks'
|
||||
except (PermissionError, ProcessLookupError, FileNotFoundError):
|
||||
ret['name'] = ''
|
||||
ret['status'] = ''
|
||||
ret['ppid'] = ''
|
||||
ret['ttynr'] = ''
|
||||
ret['utime'] = ''
|
||||
ret['stime'] = ''
|
||||
ret['children_utime'] = ''
|
||||
ret['children_stime'] = ''
|
||||
ret['create_time'] = ''
|
||||
ret['cpu_num'] = ''
|
||||
ret['blkio_ticks'] = '' # aka 'delayacct_blkio_ticks'
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def check_process_alive(proc_name: str) -> bool:
|
||||
"""Check process running.
|
||||
|
||||
:param proc_name: process name
|
||||
:type proc_name: str
|
||||
:return: True if process running, otherwise False
|
||||
:rtype: bool
|
||||
"""
|
||||
# Iterate over the all the running process
|
||||
for proc in psutil.process_iter():
|
||||
try:
|
||||
# Check if process name equals to the given name string.
|
||||
if proc_name.lower() == proc.name().lower():
|
||||
return True
|
||||
except (
|
||||
psutil.NoSuchProcess, psutil.AccessDenied,
|
||||
psutil.ZombieProcess
|
||||
):
|
||||
pass
|
||||
return False
|
587
cmapi/mcs_node_control/test/Columnstore_new.xml
Normal file
587
cmapi/mcs_node_control/test/Columnstore_new.xml
Normal file
@ -0,0 +1,587 @@
|
||||
<Columnstore Version="V1.0.1">
|
||||
<!--
|
||||
WARNING: Do not make changes to this file unless directed to do so by
|
||||
MariaDB service engineers. Incorrect settings can render your system
|
||||
unusable and will require a service call to correct.
|
||||
-->
|
||||
<Manager>MaxScale IP</Manager>
|
||||
<Sequence>42</Sequence>
|
||||
<ExeMgr1>
|
||||
<IPAddr>192.168.0.101</IPAddr>
|
||||
<Port>8601</Port>
|
||||
<Module>um1</Module>
|
||||
</ExeMgr1>
|
||||
<JobProc>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8602</Port>
|
||||
</JobProc>
|
||||
<ProcMgr>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8603</Port>
|
||||
</ProcMgr>
|
||||
<ProcMgr_Alarm>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8606</Port>
|
||||
</ProcMgr_Alarm>
|
||||
<ProcStatusControl>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8604</Port>
|
||||
</ProcStatusControl>
|
||||
<ProcStatusControlStandby>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8605</Port>
|
||||
</ProcStatusControlStandby>
|
||||
<!-- Disabled
|
||||
<ProcHeartbeatControl>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8605</Port>
|
||||
</ProcHeartbeatControl>
|
||||
-->
|
||||
<!-- ProcessMonitor Port: 8800 - 8820 is reserved to support External Modules-->
|
||||
<localhost_ProcessMonitor>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</localhost_ProcessMonitor>
|
||||
<dm1_ProcessMonitor>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</dm1_ProcessMonitor>
|
||||
<um1_ProcessMonitor>
|
||||
<IPAddr>192.168.0.101</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</um1_ProcessMonitor>
|
||||
<pm1_ProcessMonitor>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</pm1_ProcessMonitor>
|
||||
<dm1_ServerMonitor>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</dm1_ServerMonitor>
|
||||
<um1_ServerMonitor>
|
||||
<IPAddr>192.168.0.101</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</um1_ServerMonitor>
|
||||
<pm1_ServerMonitor>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</pm1_ServerMonitor>
|
||||
<pm1_WriteEngineServer>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8630</Port>
|
||||
</pm1_WriteEngineServer>
|
||||
<DDLProc>
|
||||
<IPAddr>192.168.0.101</IPAddr>
|
||||
<Port>8612</Port>
|
||||
</DDLProc>
|
||||
<DMLProc>
|
||||
<IPAddr>192.168.0.101</IPAddr>
|
||||
<Port>8614</Port>
|
||||
</DMLProc>
|
||||
<BatchInsert>
|
||||
<RowsPerBatch>10000</RowsPerBatch>
|
||||
</BatchInsert>
|
||||
<PrimitiveServers>
|
||||
<Count>4</Count>
|
||||
<ConnectionsPerPrimProc>2</ConnectionsPerPrimProc>
|
||||
<ProcessorThreshold>128</ProcessorThreshold>
|
||||
<ProcessorQueueSize>10K</ProcessorQueueSize> <!-- minimum of extent size 8192 -->
|
||||
<DebugLevel>0</DebugLevel>
|
||||
<ColScanBufferSizeBlocks>512</ColScanBufferSizeBlocks>
|
||||
<ColScanReadAheadBlocks>512</ColScanReadAheadBlocks> <!-- s/b factor of extent size 8192 -->
|
||||
<!-- <BPPCount>16</BPPCount> --> <!-- Default num cores * 2. A cap on the number of simultaneous primitives per jobstep -->
|
||||
<PrefetchThreshold>1</PrefetchThreshold>
|
||||
<PTTrace>0</PTTrace>
|
||||
<RotatingDestination>y</RotatingDestination> <!-- Iterate thru UM ports; set to 'n' if UM/PM on same server -->
|
||||
<!-- <HighPriorityPercentage>60</HighPriorityPercentage> -->
|
||||
<!-- <MediumPriorityPercentage>30</MediumPriorityPercentage> -->
|
||||
<!-- <LowPriorityPercentage>10</LowPriorityPercentage> -->
|
||||
<DirectIO>y</DirectIO>
|
||||
<HighPriorityPercentage/>
|
||||
<MediumPriorityPercentage/>
|
||||
<LowPriorityPercentage/>
|
||||
</PrimitiveServers>
|
||||
<PMS1>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS1>
|
||||
<PMS2>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS2>
|
||||
<PMS3>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS3>
|
||||
<PMS4>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS4>
|
||||
<PMS5>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS5>
|
||||
<PMS6>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS6>
|
||||
<PMS7>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS7>
|
||||
<PMS8>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS8>
|
||||
<PMS9>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS9>
|
||||
<PMS10>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS10>
|
||||
<PMS11>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS11>
|
||||
<PMS12>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS12>
|
||||
<PMS13>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS13>
|
||||
<PMS14>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS14>
|
||||
<PMS15>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS15>
|
||||
<PMS16>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS16>
|
||||
<PMS17>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS17>
|
||||
<PMS18>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS18>
|
||||
<PMS19>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS19>
|
||||
<PMS20>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS20>
|
||||
<PMS21>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS21>
|
||||
<PMS22>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS22>
|
||||
<PMS23>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS23>
|
||||
<PMS24>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS24>
|
||||
<PMS25>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS25>
|
||||
<PMS26>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS26>
|
||||
<PMS27>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS27>
|
||||
<PMS28>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS28>
|
||||
<PMS29>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS29>
|
||||
<PMS30>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS30>
|
||||
<PMS31>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS31>
|
||||
<PMS32>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS32>
|
||||
<SystemConfig>
|
||||
<SystemLang>en_US.utf8</SystemLang>
|
||||
<SystemName>columnstore-1</SystemName>
|
||||
<ParentOAMModuleName>pm1</ParentOAMModuleName>
|
||||
<PrimaryUMModuleName>um1</PrimaryUMModuleName>
|
||||
<!-- Warning: Do not change this value once database is built -->
|
||||
<DBRootCount>4</DBRootCount>
|
||||
<DBRoot1>/usr/local/mariadb/columnstore/data1</DBRoot1>
|
||||
<DBRMRoot>$INSTALLDIR/data1/systemFiles/dbrm/BRM_saves</DBRMRoot>
|
||||
<TableLockSaveFile>$INSTALLDIR/data1/systemFiles/dbrm/tablelocks</TableLockSaveFile>
|
||||
<DBRMTimeOut>20</DBRMTimeOut> <!-- in seconds -->
|
||||
<DBRMSnapshotInterval>100000</DBRMSnapshotInterval>
|
||||
<WaitPeriod>10</WaitPeriod> <!-- in seconds -->
|
||||
<CalpontHome>$INSTALLDIR</CalpontHome>
|
||||
<MemoryCheckPercent>95</MemoryCheckPercent> <!-- Max real memory to limit growth of buffers to -->
|
||||
<DataFileLog>OFF</DataFileLog>
|
||||
<!-- enable if you want to limit how much memory may be used for hdfs read/write memory buffers.
|
||||
<hdfsRdwrBufferMaxSize>8G</hdfsRdwrBufferMaxSize>
|
||||
-->
|
||||
<hdfsRdwrScratch>/rdwrscratch</hdfsRdwrScratch> <!-- Do not set to an hdfs file path -->
|
||||
<!-- Be careful modifying SystemTempFileDir! On start, ExeMgr deletes
|
||||
the entire subdirectories "joins" & "aggregates" and recreates it to make sure no
|
||||
files are left behind. -->
|
||||
<SystemTempFileDir>/tmp/columnstore_tmp_files</SystemTempFileDir>
|
||||
<DBRoot2>/usr/local/mariadb/columnstore/data2</DBRoot2>
|
||||
<DBRoot3>/usr/local/mariadb/columnstore/data3</DBRoot3>
|
||||
<DBRoot4>/usr/local/mariadb/columnstore/data4</DBRoot4>
|
||||
</SystemConfig>
|
||||
<SystemModuleConfig>
|
||||
<ModuleType1>dm</ModuleType1>
|
||||
<ModuleDesc1>Director Module</ModuleDesc1>
|
||||
<ModuleCount1>0</ModuleCount1>
|
||||
<ModuleIPAddr1-1-1>0.0.0.0</ModuleIPAddr1-1-1>
|
||||
<ModuleHostName1-1-1>unassigned</ModuleHostName1-1-1>
|
||||
<ModuleDisableState1-1>ENABLED</ModuleDisableState1-1>
|
||||
<ModuleCPUCriticalThreshold1>0</ModuleCPUCriticalThreshold1>
|
||||
<ModuleCPUMajorThreshold1>0</ModuleCPUMajorThreshold1>
|
||||
<ModuleCPUMinorThreshold1>0</ModuleCPUMinorThreshold1>
|
||||
<ModuleCPUMinorClearThreshold1>0</ModuleCPUMinorClearThreshold1>
|
||||
<ModuleDiskCriticalThreshold1>90</ModuleDiskCriticalThreshold1>
|
||||
<ModuleDiskMajorThreshold1>80</ModuleDiskMajorThreshold1>
|
||||
<ModuleDiskMinorThreshold1>70</ModuleDiskMinorThreshold1>
|
||||
<ModuleMemCriticalThreshold1>90</ModuleMemCriticalThreshold1>
|
||||
<ModuleMemMajorThreshold1>0</ModuleMemMajorThreshold1>
|
||||
<ModuleMemMinorThreshold1>0</ModuleMemMinorThreshold1>
|
||||
<ModuleSwapCriticalThreshold1>90</ModuleSwapCriticalThreshold1>
|
||||
<ModuleSwapMajorThreshold1>80</ModuleSwapMajorThreshold1>
|
||||
<ModuleSwapMinorThreshold1>70</ModuleSwapMinorThreshold1>
|
||||
<ModuleDiskMonitorFileSystem1-1>/</ModuleDiskMonitorFileSystem1-1>
|
||||
<ModuleDBRootCount1-1>unassigned</ModuleDBRootCount1-1>
|
||||
<ModuleDBRootID1-1-1>unassigned</ModuleDBRootID1-1-1>
|
||||
<ModuleType2>um</ModuleType2>
|
||||
<ModuleDesc2>User Module</ModuleDesc2>
|
||||
<ModuleCount2>2</ModuleCount2>
|
||||
<ModuleIPAddr1-1-2>192.168.0.101</ModuleIPAddr1-1-2>
|
||||
<ModuleHostName1-1-2>nvm002314</ModuleHostName1-1-2>
|
||||
<ModuleDisableState1-2>ENABLED</ModuleDisableState1-2>
|
||||
<ModuleCPUCriticalThreshold2>0</ModuleCPUCriticalThreshold2>
|
||||
<ModuleCPUMajorThreshold2>0</ModuleCPUMajorThreshold2>
|
||||
<ModuleCPUMinorThreshold2>0</ModuleCPUMinorThreshold2>
|
||||
<ModuleCPUMinorClearThreshold2>0</ModuleCPUMinorClearThreshold2>
|
||||
<ModuleDiskCriticalThreshold2>90</ModuleDiskCriticalThreshold2>
|
||||
<ModuleDiskMajorThreshold2>80</ModuleDiskMajorThreshold2>
|
||||
<ModuleDiskMinorThreshold2>70</ModuleDiskMinorThreshold2>
|
||||
<ModuleMemCriticalThreshold2>90</ModuleMemCriticalThreshold2>
|
||||
<ModuleMemMajorThreshold2>0</ModuleMemMajorThreshold2>
|
||||
<ModuleMemMinorThreshold2>0</ModuleMemMinorThreshold2>
|
||||
<ModuleSwapCriticalThreshold2>90</ModuleSwapCriticalThreshold2>
|
||||
<ModuleSwapMajorThreshold2>80</ModuleSwapMajorThreshold2>
|
||||
<ModuleSwapMinorThreshold2>70</ModuleSwapMinorThreshold2>
|
||||
<ModuleDiskMonitorFileSystem1-2>/</ModuleDiskMonitorFileSystem1-2>
|
||||
<ModuleDBRootCount1-2>unassigned</ModuleDBRootCount1-2>
|
||||
<ModuleDBRootID1-1-2>unassigned</ModuleDBRootID1-1-2>
|
||||
<ModuleType3>pm</ModuleType3>
|
||||
<ModuleDesc3>Performance Module</ModuleDesc3>
|
||||
<ModuleCount3>4</ModuleCount3>
|
||||
<ModuleIPAddr1-1-3>192.168.0.102</ModuleIPAddr1-1-3>
|
||||
<ModuleHostName1-1-3>nvm002315</ModuleHostName1-1-3>
|
||||
<ModuleDisableState1-3>ENABLED</ModuleDisableState1-3>
|
||||
<ModuleCPUCriticalThreshold3>0</ModuleCPUCriticalThreshold3>
|
||||
<ModuleCPUMajorThreshold3>0</ModuleCPUMajorThreshold3>
|
||||
<ModuleCPUMinorThreshold3>0</ModuleCPUMinorThreshold3>
|
||||
<ModuleCPUMinorClearThreshold3>0</ModuleCPUMinorClearThreshold3>
|
||||
<ModuleDiskCriticalThreshold3>90</ModuleDiskCriticalThreshold3>
|
||||
<ModuleDiskMajorThreshold3>80</ModuleDiskMajorThreshold3>
|
||||
<ModuleDiskMinorThreshold3>70</ModuleDiskMinorThreshold3>
|
||||
<ModuleMemCriticalThreshold3>90</ModuleMemCriticalThreshold3>
|
||||
<ModuleMemMajorThreshold3>0</ModuleMemMajorThreshold3>
|
||||
<ModuleMemMinorThreshold3>0</ModuleMemMinorThreshold3>
|
||||
<ModuleSwapCriticalThreshold3>90</ModuleSwapCriticalThreshold3>
|
||||
<ModuleSwapMajorThreshold3>80</ModuleSwapMajorThreshold3>
|
||||
<ModuleSwapMinorThreshold3>70</ModuleSwapMinorThreshold3>
|
||||
<ModuleDiskMonitorFileSystem1-3>/</ModuleDiskMonitorFileSystem1-3>
|
||||
<ModuleDBRootCount1-3>1</ModuleDBRootCount1-3>
|
||||
<ModuleDBRootID1-1-3>1</ModuleDBRootID1-1-3>
|
||||
<ModuleDisableState2-2>ENABLED</ModuleDisableState2-2>
|
||||
<ModuleHostName2-1-2>192.168.0.104</ModuleHostName2-1-2>
|
||||
<ModuleIPAddr2-1-2>192.168.0.104</ModuleIPAddr2-1-2>
|
||||
<ModuleDBRootCount2-3>1</ModuleDBRootCount2-3>
|
||||
<ModuleDBRootID2-1-3>2</ModuleDBRootID2-1-3>
|
||||
<ModuleDBRootCount3-3>1</ModuleDBRootCount3-3>
|
||||
<ModuleDBRootID3-1-3>3</ModuleDBRootID3-1-3>
|
||||
<ModuleDBRootCount4-3>1</ModuleDBRootCount4-3>
|
||||
<ModuleDBRootID4-1-3>4</ModuleDBRootID4-1-3>
|
||||
<ModuleDisableState2-3>ENABLED</ModuleDisableState2-3>
|
||||
<ModuleHostName2-1-3>nvm002316</ModuleHostName2-1-3>
|
||||
<ModuleIPAddr2-1-3>192.168.0.103</ModuleIPAddr2-1-3>
|
||||
<ModuleDisableState3-3>ENABLED</ModuleDisableState3-3>
|
||||
<ModuleHostName3-1-3>nvm002980</ModuleHostName3-1-3>
|
||||
<ModuleIPAddr3-1-3>192.168.0.105</ModuleIPAddr3-1-3>
|
||||
<ModuleDisableState4-3>ENABLED</ModuleDisableState4-3>
|
||||
<ModuleHostName4-1-3>nvm002981</ModuleHostName4-1-3>
|
||||
<ModuleIPAddr4-1-3>192.168.0.106</ModuleIPAddr4-1-3>
|
||||
<ModuleHostName1-2-2>unassigned</ModuleHostName1-2-2>
|
||||
<ModuleIPAddr1-2-2>0.0.0.0</ModuleIPAddr1-2-2>
|
||||
<ModuleHostName2-2-2>unassigned</ModuleHostName2-2-2>
|
||||
<ModuleIPAddr2-2-2>0.0.0.0</ModuleIPAddr2-2-2>
|
||||
<ModuleHostName1-2-3>unassigned</ModuleHostName1-2-3>
|
||||
<ModuleIPAddr1-2-3>0.0.0.0</ModuleIPAddr1-2-3>
|
||||
<ModuleHostName2-2-3>unassigned</ModuleHostName2-2-3>
|
||||
<ModuleIPAddr2-2-3>0.0.0.0</ModuleIPAddr2-2-3>
|
||||
<ModuleHostName3-2-3>unassigned</ModuleHostName3-2-3>
|
||||
<ModuleIPAddr3-2-3>0.0.0.0</ModuleIPAddr3-2-3>
|
||||
<ModuleHostName4-2-3>unassigned</ModuleHostName4-2-3>
|
||||
<ModuleIPAddr4-2-3>0.0.0.0</ModuleIPAddr4-2-3>
|
||||
</SystemModuleConfig>
|
||||
<SystemExtDeviceConfig>
|
||||
<Count>0</Count>
|
||||
<Name1>unassigned</Name1>
|
||||
<IPAddr1>0.0.0.0</IPAddr1>
|
||||
<DisableState1>ENABLED</DisableState1>
|
||||
</SystemExtDeviceConfig>
|
||||
<SessionManager>
|
||||
<MaxConcurrentTransactions>1000</MaxConcurrentTransactions>
|
||||
<TxnIDFile>$INSTALLDIR/data1/systemFiles/dbrm/SMTxnID</TxnIDFile>
|
||||
</SessionManager>
|
||||
<VersionBuffer>
|
||||
<!-- VersionBufferFileSize must be a multiple of 8192.
|
||||
One version buffer file will be put on each DB root. -->
|
||||
<VersionBufferFileSize>1GB</VersionBufferFileSize>
|
||||
</VersionBuffer>
|
||||
<OIDManager>
|
||||
<!-- Do not change this file after database built -->
|
||||
<OIDBitmapFile>$INSTALLDIR/data1/systemFiles/dbrm/oidbitmap</OIDBitmapFile>
|
||||
<!-- Do not change this value after database built -->
|
||||
<FirstOID>3000</FirstOID>
|
||||
</OIDManager>
|
||||
<WriteEngine>
|
||||
<BulkRoot>$INSTALLDIR/data/bulk</BulkRoot>
|
||||
<BulkRollbackDir>$INSTALLDIR/data1/systemFiles/bulkRollback</BulkRollbackDir>
|
||||
<MaxFileSystemDiskUsagePct>98</MaxFileSystemDiskUsagePct>
|
||||
<CompressedPaddingBlocks>1</CompressedPaddingBlocks> <!-- Number of blocks used to pad compressed chunks -->
|
||||
<FastDelete>n</FastDelete>
|
||||
</WriteEngine>
|
||||
<DBRM_Controller>
|
||||
<NumWorkers>6</NumWorkers>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8616</Port>
|
||||
</DBRM_Controller>
|
||||
<!-- Worker Port: 8700 - 8720 is reserved to support External Modules-->
|
||||
<DBRM_Worker1>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>pm1</Module>
|
||||
</DBRM_Worker1>
|
||||
<DBRM_Worker2>
|
||||
<IPAddr>192.168.0.101</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>um1</Module>
|
||||
</DBRM_Worker2>
|
||||
<DBRM_Worker3>
|
||||
<IPAddr>192.168.0.104</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>um2</Module>
|
||||
</DBRM_Worker3>
|
||||
<DBRM_Worker4>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>pm2</Module>
|
||||
</DBRM_Worker4>
|
||||
<DBRM_Worker5>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>pm3</Module>
|
||||
</DBRM_Worker5>
|
||||
<DBRM_Worker6>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>pm4</Module>
|
||||
</DBRM_Worker6>
|
||||
<DBRM_Worker7>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker7>
|
||||
<DBRM_Worker8>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker8>
|
||||
<DBRM_Worker9>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker9>
|
||||
<DBRM_Worker10>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker10>
|
||||
<DBBC>
|
||||
<!-- The percentage of RAM to use for the disk block cache. Defaults to 70% -->
|
||||
<!-- Alternatively, this can be specified in absolute terms using
|
||||
the suffixes 'm' or 'g' to denote size in megabytes or gigabytes.-->
|
||||
<!-- <NumBlocksPct>70</NumBlocksPct> -->
|
||||
<!-- <NumThreads>16</NumThreads> --> <!-- 1-256. Default is 16. -->
|
||||
<NumCaches>1</NumCaches><!-- # of parallel caches to instantiate -->
|
||||
<IOMTracing>0</IOMTracing>
|
||||
<BRPTracing>0</BRPTracing>
|
||||
<ReportFrequency>65536</ReportFrequency>
|
||||
<MaxOpenFiles>2K</MaxOpenFiles>
|
||||
<DecreaseOpenFilesCount>200</DecreaseOpenFilesCount>
|
||||
<FDCacheTrace>0</FDCacheTrace>
|
||||
<NumBlocksPct>65</NumBlocksPct>
|
||||
</DBBC>
|
||||
<Installation>
|
||||
<ServerTypeInstall>1</ServerTypeInstall>
|
||||
<PMwithUM>n</PMwithUM>
|
||||
<MySQLRep>y</MySQLRep>
|
||||
<DBRootStorageType>external</DBRootStorageType>
|
||||
<UMStorageType>internal</UMStorageType>
|
||||
<DistributedInstall>y</DistributedInstall>
|
||||
<ProfileFile>/etc/profile.d/columnstoreAlias.sh</ProfileFile>
|
||||
<DataRedundancyNetworkType/>
|
||||
</Installation>
|
||||
<ExtentMap>
|
||||
<!--
|
||||
WARNING: these can only be changed on an empty system. Once any object has been allocated
|
||||
it cannot be changed!. Extent size is 8M rows.
|
||||
-->
|
||||
<FilesPerColumnPartition>8</FilesPerColumnPartition> <!-- should be multiple of DBRootCount -->
|
||||
<BRM_UID>0x0</BRM_UID>
|
||||
</ExtentMap>
|
||||
<HashJoin>
|
||||
<MaxBuckets>128</MaxBuckets>
|
||||
<MaxElems>128K</MaxElems> <!-- 128 buckets * 128K * 16 = 256 MB -->
|
||||
<PmMaxMemorySmallSide>64M</PmMaxMemorySmallSide><!-- divide by 48 to getapproximate row count -->
|
||||
<TotalUmMemory>25%</TotalUmMemory>
|
||||
<CPUniqueLimit>100</CPUniqueLimit>
|
||||
<AllowDiskBasedJoin>N</AllowDiskBasedJoin>
|
||||
<TempFileCompression>Y</TempFileCompression>
|
||||
<TempFileCompressionType>Snappy</TempFileCompressionType> <!-- LZ4, Snappy -->
|
||||
</HashJoin>
|
||||
<JobList>
|
||||
<FlushInterval>16K</FlushInterval>
|
||||
<FifoSize>32</FifoSize>
|
||||
<RequestSize>1</RequestSize> <!-- Number of extents per request, should be
|
||||
less than MaxOutstandingRequests. Otherwise, default value 1 is used. -->
|
||||
<!-- ProcessorThreadsPerScan is the number of jobs issued to process
|
||||
each extent. The default is 16. MaxOutstandingRequests is the size of
|
||||
the window of work in terms of extents. A value of 20 means there
|
||||
is 20 extents worth of work for the PMs to process at any given time.
|
||||
ProcessorThreadsPerScan * MaxOutstandingRequests should be at least
|
||||
as many threads are available across all PMs. -->
|
||||
<!-- <ProcessorThreadsPerScan>16</ProcessorThreadsPerScan> -->
|
||||
<MaxOutstandingRequests>40</MaxOutstandingRequests>
|
||||
<ThreadPoolSize>100</ThreadPoolSize>
|
||||
</JobList>
|
||||
<RowAggregation>
|
||||
<!-- <RowAggrThreads>4</RowAggrThreads> --> <!-- Default value is 4 or number of cores when less than 4 -->
|
||||
<!-- <RowAggrBuckets>32</RowAggrBuckets> --> <!-- Default value is number of cores * 4 -->
|
||||
<!-- <RowAggrRowGroupsPerThread>20</RowAggrRowGroupsPerThread> --> <!-- Default value is 20 -->
|
||||
<AllowDiskBasedAggregation>N</AllowDiskBasedAggregation>
|
||||
</RowAggregation>
|
||||
<CrossEngineSupport>
|
||||
<Host>127.0.0.1</Host>
|
||||
<Port>3306</Port>
|
||||
<User>root</User>
|
||||
<Password/>
|
||||
<TLSCA/>
|
||||
<TLSClientCert/>
|
||||
<TLSClientKey/>
|
||||
</CrossEngineSupport>
|
||||
<QueryStats>
|
||||
<Enabled>Y</Enabled>
|
||||
</QueryStats>
|
||||
<UserPriority>
|
||||
<Enabled>N</Enabled>
|
||||
</UserPriority>
|
||||
<NetworkCompression>
|
||||
<Enabled>Y</Enabled>
|
||||
<NetworkCompressionType>Snappy</NetworkCompressionType> <!-- LZ4, Snappy -->
|
||||
</NetworkCompression>
|
||||
<QueryTele>
|
||||
<Host>127.0.0.1</Host>
|
||||
<Port>0</Port>
|
||||
</QueryTele>
|
||||
<um2_ProcessMonitor>
|
||||
<IPAddr>192.168.0.104</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</um2_ProcessMonitor>
|
||||
<um2_ServerMonitor>
|
||||
<IPAddr>192.168.0.104</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</um2_ServerMonitor>
|
||||
<ExeMgr2>
|
||||
<IPAddr>192.168.0.104</IPAddr>
|
||||
<Port>8601</Port>
|
||||
<Module>um2</Module>
|
||||
</ExeMgr2>
|
||||
<pm2_ProcessMonitor>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</pm2_ProcessMonitor>
|
||||
<pm2_ServerMonitor>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</pm2_ServerMonitor>
|
||||
<pm2_WriteEngineServer>
|
||||
<IPAddr>192.168.0.103</IPAddr>
|
||||
<Port>8630</Port>
|
||||
</pm2_WriteEngineServer>
|
||||
<pm3_ProcessMonitor>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</pm3_ProcessMonitor>
|
||||
<pm3_ServerMonitor>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</pm3_ServerMonitor>
|
||||
<pm3_WriteEngineServer>
|
||||
<IPAddr>192.168.0.105</IPAddr>
|
||||
<Port>8630</Port>
|
||||
</pm3_WriteEngineServer>
|
||||
<pm4_ProcessMonitor>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</pm4_ProcessMonitor>
|
||||
<pm4_ServerMonitor>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</pm4_ServerMonitor>
|
||||
<pm4_WriteEngineServer>
|
||||
<IPAddr>192.168.0.106</IPAddr>
|
||||
<Port>8630</Port>
|
||||
</pm4_WriteEngineServer>
|
||||
<DataRedundancyConfig>
|
||||
<DBRoot1PMs/>
|
||||
<DBRoot2PMs/>
|
||||
<DBRoot3PMs/>
|
||||
<DBRoot4PMs/>
|
||||
</DataRedundancyConfig>
|
||||
<ProcHeartbeatControl>
|
||||
<IPAddr>192.168.0.102</IPAddr>
|
||||
</ProcHeartbeatControl>
|
||||
</Columnstore>
|
531
cmapi/mcs_node_control/test/Columnstore_old.xml
Normal file
531
cmapi/mcs_node_control/test/Columnstore_old.xml
Normal file
@ -0,0 +1,531 @@
|
||||
<Columnstore Version="V1.0.0">
|
||||
<!--
|
||||
WARNING: Do not make changes to this file unless directed to do so by
|
||||
MariaDB service engineers. Incorrect settings can render your system
|
||||
unusable and will require a service call to correct.
|
||||
-->
|
||||
<ExeMgr1>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8601</Port>
|
||||
<Module>unassigned</Module>
|
||||
</ExeMgr1>
|
||||
<JobProc>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8602</Port>
|
||||
</JobProc>
|
||||
<ProcMgr>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8603</Port>
|
||||
</ProcMgr>
|
||||
<ProcMgr_Alarm>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8606</Port>
|
||||
</ProcMgr_Alarm>
|
||||
<ProcStatusControl>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8604</Port>
|
||||
</ProcStatusControl>
|
||||
<ProcStatusControlStandby>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8605</Port>
|
||||
</ProcStatusControlStandby>
|
||||
<!-- Disabled
|
||||
<ProcHeartbeatControl>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8605</Port>
|
||||
</ProcHeartbeatControl>
|
||||
-->
|
||||
<!-- ProcessMonitor Port: 8800 - 8820 is reserved to support External Modules-->
|
||||
<localhost_ProcessMonitor>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</localhost_ProcessMonitor>
|
||||
<dm1_ProcessMonitor>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</dm1_ProcessMonitor>
|
||||
<um1_ProcessMonitor>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</um1_ProcessMonitor>
|
||||
<pm1_ProcessMonitor>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8800</Port>
|
||||
</pm1_ProcessMonitor>
|
||||
<dm1_ServerMonitor>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</dm1_ServerMonitor>
|
||||
<um1_ServerMonitor>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</um1_ServerMonitor>
|
||||
<pm1_ServerMonitor>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8622</Port>
|
||||
</pm1_ServerMonitor>
|
||||
<pm1_WriteEngineServer>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8630</Port>
|
||||
</pm1_WriteEngineServer>
|
||||
<DDLProc>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8612</Port>
|
||||
</DDLProc>
|
||||
<DMLProc>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8614</Port>
|
||||
</DMLProc>
|
||||
<BatchInsert>
|
||||
<RowsPerBatch>10000</RowsPerBatch>
|
||||
</BatchInsert>
|
||||
<PrimitiveServers>
|
||||
<Count>1</Count>
|
||||
<ConnectionsPerPrimProc>2</ConnectionsPerPrimProc>
|
||||
<ProcessorThreshold>128</ProcessorThreshold>
|
||||
<ProcessorQueueSize>10K</ProcessorQueueSize>
|
||||
<!-- minimum of extent size 8192 -->
|
||||
<DebugLevel>0</DebugLevel>
|
||||
<ColScanBufferSizeBlocks>512</ColScanBufferSizeBlocks>
|
||||
<ColScanReadAheadBlocks>512</ColScanReadAheadBlocks>
|
||||
<!-- s/b factor of extent size 8192 -->
|
||||
<!-- <BPPCount>16</BPPCount> -->
|
||||
<!-- Default num cores * 2. A cap on the number of simultaneous primitives per jobstep -->
|
||||
<PrefetchThreshold>1</PrefetchThreshold>
|
||||
<PTTrace>0</PTTrace>
|
||||
<RotatingDestination>n</RotatingDestination>
|
||||
<!-- Iterate thru UM ports; set to 'n' if UM/PM on same server -->
|
||||
<!-- <HighPriorityPercentage>60</HighPriorityPercentage> -->
|
||||
<!-- <MediumPriorityPercentage>30</MediumPriorityPercentage> -->
|
||||
<!-- <LowPriorityPercentage>10</LowPriorityPercentage> -->
|
||||
<DirectIO>y</DirectIO>
|
||||
<HighPriorityPercentage/>
|
||||
<MediumPriorityPercentage/>
|
||||
<LowPriorityPercentage/>
|
||||
</PrimitiveServers>
|
||||
<PMS1>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS1>
|
||||
<PMS2>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS2>
|
||||
<PMS3>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS3>
|
||||
<PMS4>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS4>
|
||||
<PMS5>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS5>
|
||||
<PMS6>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS6>
|
||||
<PMS7>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS7>
|
||||
<PMS8>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS8>
|
||||
<PMS9>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS9>
|
||||
<PMS10>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS10>
|
||||
<PMS11>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS11>
|
||||
<PMS12>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS12>
|
||||
<PMS13>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS13>
|
||||
<PMS14>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS14>
|
||||
<PMS15>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS15>
|
||||
<PMS16>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS16>
|
||||
<PMS17>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS17>
|
||||
<PMS18>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS18>
|
||||
<PMS19>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS19>
|
||||
<PMS20>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS20>
|
||||
<PMS21>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS21>
|
||||
<PMS22>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS22>
|
||||
<PMS23>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS23>
|
||||
<PMS24>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS24>
|
||||
<PMS25>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS25>
|
||||
<PMS26>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS26>
|
||||
<PMS27>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS27>
|
||||
<PMS28>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS28>
|
||||
<PMS29>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS29>
|
||||
<PMS30>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS30>
|
||||
<PMS31>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS31>
|
||||
<PMS32>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8620</Port>
|
||||
</PMS32>
|
||||
<SystemConfig>
|
||||
<SystemLang>C</SystemLang>
|
||||
<SystemName>columnstore-1</SystemName>
|
||||
<ParentOAMModuleName>pm1</ParentOAMModuleName>
|
||||
<PrimaryUMModuleName>pm1</PrimaryUMModuleName>
|
||||
<!-- Warning: Do not change this value once database is built -->
|
||||
<DBRootCount>1</DBRootCount>
|
||||
<DBRoot1>/var/lib/columnstore/data1</DBRoot1>
|
||||
<DBRMRoot>/var/lib/columnstore/data1/systemFiles/dbrm/BRM_saves</DBRMRoot>
|
||||
<TableLockSaveFile>/var/lib/columnstore/data1/systemFiles/dbrm/tablelocks</TableLockSaveFile>
|
||||
<DBRMTimeOut>20</DBRMTimeOut>
|
||||
<!-- in seconds -->
|
||||
<DBRMSnapshotInterval>100000</DBRMSnapshotInterval>
|
||||
<!-- default SWSDL max element save size -->
|
||||
<WaitPeriod>10</WaitPeriod>
|
||||
<!-- in seconds -->
|
||||
<MemoryCheckPercent>95</MemoryCheckPercent>
|
||||
<!-- Max real memory to limit growth of buffers to -->
|
||||
<DataFileLog>OFF</DataFileLog>
|
||||
<!-- enable if you want to limit how much memory may be used for hdfs read/write memory buffers.
|
||||
<hdfsRdwrBufferMaxSize>8G</hdfsRdwrBufferMaxSize>
|
||||
-->
|
||||
<hdfsRdwrScratch>/rdwrscratch</hdfsRdwrScratch> <!-- Do not set to an hdfs file path -->
|
||||
<!-- Be careful modifying SystemTempFileDir! On start, ExeMgr deletes
|
||||
the entire subdirectories "joins" & "aggregates" and recreates it to make sure no
|
||||
files are left behind. -->
|
||||
<SystemTempFileDir>/tmp/columnstore_tmp_files</SystemTempFileDir>
|
||||
</SystemConfig>
|
||||
<SystemModuleConfig>
|
||||
<ModuleType1>dm</ModuleType1>
|
||||
<ModuleDesc1>Director Module</ModuleDesc1>
|
||||
<ModuleCount1>0</ModuleCount1>
|
||||
<ModuleIPAddr1-1-1>0.0.0.0</ModuleIPAddr1-1-1>
|
||||
<ModuleHostName1-1-1>unassigned</ModuleHostName1-1-1>
|
||||
<ModuleDisableState1-1>ENABLED</ModuleDisableState1-1>
|
||||
<ModuleCPUCriticalThreshold1>0</ModuleCPUCriticalThreshold1>
|
||||
<ModuleCPUMajorThreshold1>0</ModuleCPUMajorThreshold1>
|
||||
<ModuleCPUMinorThreshold1>0</ModuleCPUMinorThreshold1>
|
||||
<ModuleCPUMinorClearThreshold1>0</ModuleCPUMinorClearThreshold1>
|
||||
<ModuleDiskCriticalThreshold1>90</ModuleDiskCriticalThreshold1>
|
||||
<ModuleDiskMajorThreshold1>80</ModuleDiskMajorThreshold1>
|
||||
<ModuleDiskMinorThreshold1>70</ModuleDiskMinorThreshold1>
|
||||
<ModuleMemCriticalThreshold1>90</ModuleMemCriticalThreshold1>
|
||||
<ModuleMemMajorThreshold1>0</ModuleMemMajorThreshold1>
|
||||
<ModuleMemMinorThreshold1>0</ModuleMemMinorThreshold1>
|
||||
<ModuleSwapCriticalThreshold1>90</ModuleSwapCriticalThreshold1>
|
||||
<ModuleSwapMajorThreshold1>80</ModuleSwapMajorThreshold1>
|
||||
<ModuleSwapMinorThreshold1>70</ModuleSwapMinorThreshold1>
|
||||
<ModuleDiskMonitorFileSystem1-1>/</ModuleDiskMonitorFileSystem1-1>
|
||||
<ModuleDBRootCount1-1>unassigned</ModuleDBRootCount1-1>
|
||||
<ModuleDBRootID1-1-1>unassigned</ModuleDBRootID1-1-1>
|
||||
<ModuleType2>um</ModuleType2>
|
||||
<ModuleDesc2>User Module</ModuleDesc2>
|
||||
<ModuleCount2>0</ModuleCount2>
|
||||
<ModuleIPAddr1-1-2>0.0.0.0</ModuleIPAddr1-1-2>
|
||||
<ModuleHostName1-1-2>unassigned</ModuleHostName1-1-2>
|
||||
<ModuleDisableState1-2>ENABLED</ModuleDisableState1-2>
|
||||
<ModuleCPUCriticalThreshold2>0</ModuleCPUCriticalThreshold2>
|
||||
<ModuleCPUMajorThreshold2>0</ModuleCPUMajorThreshold2>
|
||||
<ModuleCPUMinorThreshold2>0</ModuleCPUMinorThreshold2>
|
||||
<ModuleCPUMinorClearThreshold2>0</ModuleCPUMinorClearThreshold2>
|
||||
<ModuleDiskCriticalThreshold2>90</ModuleDiskCriticalThreshold2>
|
||||
<ModuleDiskMajorThreshold2>80</ModuleDiskMajorThreshold2>
|
||||
<ModuleDiskMinorThreshold2>70</ModuleDiskMinorThreshold2>
|
||||
<ModuleMemCriticalThreshold2>90</ModuleMemCriticalThreshold2>
|
||||
<ModuleMemMajorThreshold2>0</ModuleMemMajorThreshold2>
|
||||
<ModuleMemMinorThreshold2>0</ModuleMemMinorThreshold2>
|
||||
<ModuleSwapCriticalThreshold2>90</ModuleSwapCriticalThreshold2>
|
||||
<ModuleSwapMajorThreshold2>80</ModuleSwapMajorThreshold2>
|
||||
<ModuleSwapMinorThreshold2>70</ModuleSwapMinorThreshold2>
|
||||
<ModuleDiskMonitorFileSystem1-2>/</ModuleDiskMonitorFileSystem1-2>
|
||||
<ModuleDBRootCount1-2>unassigned</ModuleDBRootCount1-2>
|
||||
<ModuleDBRootID1-1-2>unassigned</ModuleDBRootID1-1-2>
|
||||
<ModuleType3>pm</ModuleType3>
|
||||
<ModuleDesc3>Performance Module</ModuleDesc3>
|
||||
<ModuleCount3>1</ModuleCount3>
|
||||
<ModuleIPAddr1-1-3>127.0.0.1</ModuleIPAddr1-1-3>
|
||||
<ModuleHostName1-1-3>localhost</ModuleHostName1-1-3>
|
||||
<ModuleDisableState1-3>ENABLED</ModuleDisableState1-3>
|
||||
<ModuleCPUCriticalThreshold3>0</ModuleCPUCriticalThreshold3>
|
||||
<ModuleCPUMajorThreshold3>0</ModuleCPUMajorThreshold3>
|
||||
<ModuleCPUMinorThreshold3>0</ModuleCPUMinorThreshold3>
|
||||
<ModuleCPUMinorClearThreshold3>0</ModuleCPUMinorClearThreshold3>
|
||||
<ModuleDiskCriticalThreshold3>90</ModuleDiskCriticalThreshold3>
|
||||
<ModuleDiskMajorThreshold3>80</ModuleDiskMajorThreshold3>
|
||||
<ModuleDiskMinorThreshold3>70</ModuleDiskMinorThreshold3>
|
||||
<ModuleMemCriticalThreshold3>90</ModuleMemCriticalThreshold3>
|
||||
<ModuleMemMajorThreshold3>0</ModuleMemMajorThreshold3>
|
||||
<ModuleMemMinorThreshold3>0</ModuleMemMinorThreshold3>
|
||||
<ModuleSwapCriticalThreshold3>90</ModuleSwapCriticalThreshold3>
|
||||
<ModuleSwapMajorThreshold3>80</ModuleSwapMajorThreshold3>
|
||||
<ModuleSwapMinorThreshold3>70</ModuleSwapMinorThreshold3>
|
||||
<ModuleDiskMonitorFileSystem1-3>/</ModuleDiskMonitorFileSystem1-3>
|
||||
<ModuleDBRootCount1-3>1</ModuleDBRootCount1-3>
|
||||
<ModuleDBRootID1-1-3>1</ModuleDBRootID1-1-3>
|
||||
</SystemModuleConfig>
|
||||
<SystemExtDeviceConfig>
|
||||
<Count>0</Count>
|
||||
<Name1>unassigned</Name1>
|
||||
<IPAddr1>0.0.0.0</IPAddr1>
|
||||
<DisableState1>ENABLED</DisableState1>
|
||||
</SystemExtDeviceConfig>
|
||||
<SessionManager>
|
||||
<MaxConcurrentTransactions>1000</MaxConcurrentTransactions>
|
||||
<TxnIDFile>/var/lib/columnstore/data1/systemFiles/dbrm/SMTxnID</TxnIDFile>
|
||||
</SessionManager>
|
||||
<VersionBuffer>
|
||||
<!-- VersionBufferFileSize must be a multiple of 8192.
|
||||
One version buffer file will be put on each DB root. -->
|
||||
<VersionBufferFileSize>1GB</VersionBufferFileSize>
|
||||
</VersionBuffer>
|
||||
<OIDManager>
|
||||
<!-- Do not change this file after database built -->
|
||||
<OIDBitmapFile>/var/lib/columnstore/data1/systemFiles/dbrm/oidbitmap</OIDBitmapFile>
|
||||
<!-- Do not change this value after database built -->
|
||||
<FirstOID>3000</FirstOID>
|
||||
</OIDManager>
|
||||
<WriteEngine>
|
||||
<BulkRoot>/var/log/mariadb/columnstore/data/bulk</BulkRoot>
|
||||
<BulkRollbackDir>/var/lib/columnstore/data1/systemFiles/bulkRollback</BulkRollbackDir>
|
||||
<MaxFileSystemDiskUsagePct>98</MaxFileSystemDiskUsagePct>
|
||||
<CompressedPaddingBlocks>1</CompressedPaddingBlocks> <!-- Number of blocks used to pad compressed chunks -->
|
||||
<FastDelete>n</FastDelete>
|
||||
</WriteEngine>
|
||||
<DBRM_Controller>
|
||||
<NumWorkers>1</NumWorkers>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8616</Port>
|
||||
</DBRM_Controller>
|
||||
<!-- Worker Port: 8700 - 8720 is reserved to support External Modules-->
|
||||
<DBRM_Worker1>
|
||||
<IPAddr>127.0.0.1</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>pm1</Module>
|
||||
</DBRM_Worker1>
|
||||
<DBRM_Worker2>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker2>
|
||||
<DBRM_Worker3>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker3>
|
||||
<DBRM_Worker4>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker4>
|
||||
<DBRM_Worker5>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker5>
|
||||
<DBRM_Worker6>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker6>
|
||||
<DBRM_Worker7>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker7>
|
||||
<DBRM_Worker8>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker8>
|
||||
<DBRM_Worker9>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker9>
|
||||
<DBRM_Worker10>
|
||||
<IPAddr>0.0.0.0</IPAddr>
|
||||
<Port>8700</Port>
|
||||
<Module>unassigned</Module>
|
||||
</DBRM_Worker10>
|
||||
<DBBC>
|
||||
<!-- The percentage of RAM to use for the disk block cache. Defaults to 70% -->
|
||||
<!-- Alternatively, this can be specified in absolute terms using
|
||||
the suffixes 'm' or 'g' to denote size in megabytes or gigabytes.-->
|
||||
<!-- <NumBlocksPct>70</NumBlocksPct> -->
|
||||
<!-- <NumThreads>16</NumThreads> -->
|
||||
<!-- 1-256. Default is 16. -->
|
||||
<NumCaches>1</NumCaches>
|
||||
<!-- # of parallel caches to instantiate -->
|
||||
<IOMTracing>0</IOMTracing>
|
||||
<BRPTracing>0</BRPTracing>
|
||||
<ReportFrequency>65536</ReportFrequency>
|
||||
<MaxOpenFiles>2K</MaxOpenFiles>
|
||||
<DecreaseOpenFilesCount>200</DecreaseOpenFilesCount>
|
||||
<FDCacheTrace>0</FDCacheTrace>
|
||||
<NumBlocksPct>50</NumBlocksPct>
|
||||
</DBBC>
|
||||
<Installation>
|
||||
<ServerTypeInstall>2</ServerTypeInstall>
|
||||
<PMwithUM>n</PMwithUM>
|
||||
<MySQLRep>n</MySQLRep>
|
||||
<DBRootStorageType>internal</DBRootStorageType>
|
||||
<UMStorageType>internal</UMStorageType>
|
||||
<ProfileFile>/etc/profile.d/columnstoreAlias.sh</ProfileFile>
|
||||
<DataRedundancyNetworkType/>
|
||||
</Installation>
|
||||
<ExtentMap>
|
||||
<!--
|
||||
WARNING: these can only be changed on an empty system. Once any object has been allocated
|
||||
it cannot be changed!. Extent size is 8M rows.
|
||||
-->
|
||||
<FilesPerColumnPartition>4</FilesPerColumnPartition>
|
||||
<!-- should be multiple of DBRootCount -->
|
||||
<BRM_UID>0x0</BRM_UID>
|
||||
</ExtentMap>
|
||||
<HashJoin>
|
||||
<MaxBuckets>128</MaxBuckets>
|
||||
<MaxElems>128K</MaxElems>
|
||||
<!-- 128 buckets * 128K * 16 = 256 MB -->
|
||||
<PmMaxMemorySmallSide>1G</PmMaxMemorySmallSide>
|
||||
<TotalUmMemory>25%</TotalUmMemory>
|
||||
<CPUniqueLimit>100</CPUniqueLimit>
|
||||
<AllowDiskBasedJoin>N</AllowDiskBasedJoin>
|
||||
<TempFileCompression>Y</TempFileCompression>
|
||||
<TempFileCompressionType>Snappy</TempFileCompressionType> <!-- LZ4, Snappy -->
|
||||
</HashJoin>
|
||||
<JobList>
|
||||
<FlushInterval>16K</FlushInterval>
|
||||
<FifoSize>16</FifoSize>
|
||||
<RequestSize>1</RequestSize>
|
||||
<!-- Number of extents per request, should be
|
||||
less than MaxOutstandingRequests. Otherwise, default value 1 is used. -->
|
||||
<!-- ProcessorThreadsPerScan is the number of jobs issued to process
|
||||
each extent. The default is 16. MaxOutstandingRequests is the size of
|
||||
the window of work in terms of extents. A value of 20 means there
|
||||
is 20 extents worth of work for the PMs to process at any given time.
|
||||
ProcessorThreadsPerScan * MaxOutstandingRequests should be at least
|
||||
as many threads are available across all PMs. -->
|
||||
<!-- <ProcessorThreadsPerScan>16</ProcessorThreadsPerScan> -->
|
||||
<!-- MaxOutstandingRequests is going to default to the num of cores available
|
||||
across all performance modules * 4 divided by the ProcessorThreadsPerScan,
|
||||
but will be lower bounded by 20 -->
|
||||
<!-- <MaxOutstandingRequests>20</MaxOutstandingRequests> -->
|
||||
<ThreadPoolSize>100</ThreadPoolSize>
|
||||
</JobList>
|
||||
<RowAggregation>
|
||||
<!-- <RowAggrThreads>4</RowAggrThreads> -->
|
||||
<!-- Default value is the number of cores -->
|
||||
<!-- <RowAggrBuckets>32</RowAggrBuckets> -->
|
||||
<!-- Default value is number of cores * 4 -->
|
||||
<!-- <RowAggrRowGroupsPerThread>20</RowAggrRowGroupsPerThread> -->
|
||||
<!-- Default value is 20 -->
|
||||
<AllowDiskBasedAggregation>N</AllowDiskBasedAggregation>
|
||||
</RowAggregation>
|
||||
<CrossEngineSupport>
|
||||
<Host>127.0.0.1</Host>
|
||||
<Port>3306</Port>
|
||||
<User>root</User>
|
||||
<Password/>
|
||||
<TLSCA/>
|
||||
<TLSClientCert/>
|
||||
<TLSClientKey/>
|
||||
</CrossEngineSupport>
|
||||
<QueryStats>
|
||||
<Enabled>N</Enabled>
|
||||
</QueryStats>
|
||||
<UserPriority>
|
||||
<Enabled>N</Enabled>
|
||||
</UserPriority>
|
||||
<NetworkCompression>
|
||||
<Enabled>Y</Enabled>
|
||||
<NetworkCompressionType>Snappy</NetworkCompressionType> <!-- LZ4, Snappy -->
|
||||
</NetworkCompression>
|
||||
<QueryTele>
|
||||
<Host>127.0.0.1</Host>
|
||||
<Port>0</Port>
|
||||
</QueryTele>
|
||||
<StorageManager>
|
||||
<MaxSockets>30</MaxSockets>
|
||||
<Enabled>N</Enabled>
|
||||
</StorageManager>
|
||||
<DataRedundancyConfig>
|
||||
<DBRoot1PMs/>
|
||||
</DataRedundancyConfig>
|
||||
<ConfigRevision>1</ConfigRevision>
|
||||
<ClusterManager>127.0.0.1</ClusterManager>
|
||||
<ClusterName>MyCluster</ClusterName>
|
||||
<NextNodeId>2</NextNodeId>
|
||||
<NextDBRootId>2</NextDBRootId>
|
||||
<DesiredNodes>
|
||||
<Node>127.0.0.1</Node>
|
||||
</DesiredNodes>
|
||||
<ActiveNodes>
|
||||
<Node>127.0.0.1</Node>
|
||||
</ActiveNodes>
|
||||
<InactiveNodes/>
|
||||
<PrimaryNode>127.0.0.1</PrimaryNode>
|
||||
</Columnstore>
|
0
cmapi/mcs_node_control/test/__init__.py
Normal file
0
cmapi/mcs_node_control/test/__init__.py
Normal file
7
cmapi/mcs_node_control/test/settings.py
Normal file
7
cmapi/mcs_node_control/test/settings.py
Normal file
@ -0,0 +1,7 @@
|
||||
import os
|
||||
|
||||
from cmapi_server.constants import CMAPI_DEFAULT_CONF_PATH
|
||||
|
||||
|
||||
CONFIG_PATH_NEW = './mcs_node_control/test/Columnstore_new.xml'
|
||||
CONFIG_PATH_OLD = './mcs_node_control/test/Columnstore_old.xml'
|
29
cmapi/mcs_node_control/test/test_dbrm_socket.py
Normal file
29
cmapi/mcs_node_control/test/test_dbrm_socket.py
Normal file
@ -0,0 +1,29 @@
|
||||
import io
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
from mcs_node_control.models.dbrm_socket import MAGIC_BYTES, DBRMSocketHandler
|
||||
|
||||
|
||||
logging.basicConfig(level='DEBUG')
|
||||
|
||||
|
||||
class TestDBRMSocketHandler(unittest.TestCase):
|
||||
|
||||
def test_myreceive_to_magic(self):
|
||||
response_data = b'\x01\x00\x00\x00\x00'
|
||||
valid_magic = b'%s%s' % (MAGIC_BYTES, response_data)
|
||||
first_unknow = b'A%s%s' % (MAGIC_BYTES, response_data)
|
||||
partial_first_magic = b'%s%s%s' % (
|
||||
MAGIC_BYTES[:3], MAGIC_BYTES, response_data
|
||||
)
|
||||
sock_responses = [valid_magic, first_unknow, partial_first_magic]
|
||||
for sock_response in sock_responses:
|
||||
with self.subTest(sock_response=sock_response):
|
||||
data_stream = io.BytesIO(sock_response)
|
||||
data_stream.recv = data_stream.read
|
||||
dbrm_socket = DBRMSocketHandler()
|
||||
# pylint: disable=protected-access
|
||||
dbrm_socket._socket = data_stream
|
||||
dbrm_socket._receive_magic()
|
||||
self.assertEqual(data_stream.read(), response_data)
|
13
cmapi/mcs_node_control/test/test_misc.py
Normal file
13
cmapi/mcs_node_control/test/test_misc.py
Normal file
@ -0,0 +1,13 @@
|
||||
import unittest
|
||||
|
||||
|
||||
class MiscTest(unittest.TestCase):
|
||||
def test_read_module_id(self):
|
||||
pass
|
||||
|
||||
def test_set_module_id(self):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
288
cmapi/mcs_node_control/test/test_node_config.py
Normal file
288
cmapi/mcs_node_control/test/test_node_config.py
Normal file
@ -0,0 +1,288 @@
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import unittest
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
from shutil import copyfile
|
||||
from tempfile import TemporaryDirectory
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from cmapi_server.constants import CMAPI_DEFAULT_CONF_PATH
|
||||
from mcs_node_control.models.dbrm import (
|
||||
DBRM, set_cluster_mode
|
||||
)
|
||||
from mcs_node_control.models.node_config import NodeConfig
|
||||
from mcs_node_control.models.misc import read_module_id
|
||||
from mcs_node_control.models.node_status import NodeStatus
|
||||
from mcs_node_control.test.settings import CONFIG_PATH_NEW, CONFIG_PATH_OLD
|
||||
|
||||
|
||||
MCS_NODE_MODELS = 'mcs_node_control.models'
|
||||
NODE_CONFIG_MODULE = f'{MCS_NODE_MODELS}.node_config'
|
||||
|
||||
|
||||
logging.basicConfig(level='DEBUG')
|
||||
|
||||
|
||||
# These tests needs working DBRM worker.
|
||||
class NodeConfigTest(TestCase):
|
||||
|
||||
@mock.patch(f'{NODE_CONFIG_MODULE}.mkdir')
|
||||
@mock.patch(f'{NODE_CONFIG_MODULE}.chown')
|
||||
@mock.patch(f'{NODE_CONFIG_MODULE}.read_module_id', return_value=1)
|
||||
@mock.patch(
|
||||
f'{NODE_CONFIG_MODULE}.NodeConfig.in_active_nodes',
|
||||
return_value=False
|
||||
)
|
||||
def test_apply_config(self, *_args):
|
||||
"""Test apply configuration file."""
|
||||
with TemporaryDirectory() as tmp_dirname:
|
||||
config_filepath = os.path.join(tmp_dirname, 'Columnstore.xml')
|
||||
|
||||
copyfile(CONFIG_PATH_OLD, config_filepath)
|
||||
# change config
|
||||
parser = etree.XMLParser(load_dtd=True)
|
||||
# new_tree = etree.parse('/etc/columnstore/Columnstore.xml', parser=parser)
|
||||
new_tree = etree.parse(CONFIG_PATH_NEW, parser=parser)
|
||||
|
||||
node_config = NodeConfig()
|
||||
xml_string = node_config.to_string(new_tree)
|
||||
|
||||
node_config.apply_config(config_filepath, xml_string)
|
||||
|
||||
# compare configurations
|
||||
config_file = Path(config_filepath)
|
||||
xml_string_written = config_file.read_text()
|
||||
self.assertEqual(xml_string_written, xml_string)
|
||||
# copy must exists
|
||||
config_file_copy = Path(f"{config_filepath}.cmapi.save")
|
||||
self.assertTrue(config_file_copy.exists())
|
||||
|
||||
@mock.patch(f'{NODE_CONFIG_MODULE}.mkdir')
|
||||
@mock.patch(f'{NODE_CONFIG_MODULE}.chown')
|
||||
@mock.patch(f'{NODE_CONFIG_MODULE}.read_module_id', return_value=1)
|
||||
@mock.patch(
|
||||
f'{NODE_CONFIG_MODULE}.NodeConfig.in_active_nodes',
|
||||
return_value=False
|
||||
)
|
||||
def test_rollback_config(self, *_args):
|
||||
""""Test rollback applied configuration file."""
|
||||
with TemporaryDirectory() as tmp_dirname:
|
||||
config_filepath = os.path.join(tmp_dirname, 'Columnstore.xml')
|
||||
copyfile(CONFIG_PATH_OLD, config_filepath)
|
||||
|
||||
old_config_file = Path(CONFIG_PATH_OLD)
|
||||
old_xml_string = old_config_file.read_text()
|
||||
new_config_file = Path(CONFIG_PATH_NEW)
|
||||
new_xml_string = new_config_file.read_text()
|
||||
|
||||
node_config = NodeConfig()
|
||||
node_config.apply_config(config_filepath, new_xml_string)
|
||||
node_config.rollback_config(config_filepath)
|
||||
|
||||
config_file = Path(config_filepath)
|
||||
xml_string_restored = config_file.read_text()
|
||||
self.assertEqual(xml_string_restored, old_xml_string)
|
||||
|
||||
def test_get_current_config(self):
|
||||
"""Test get current config from file."""
|
||||
config_file = Path(CONFIG_PATH_OLD)
|
||||
node_config = NodeConfig()
|
||||
self.assertEqual(
|
||||
node_config.get_current_config(CONFIG_PATH_OLD),
|
||||
config_file.read_text()
|
||||
)
|
||||
|
||||
def test_set_cluster_mode(self):
|
||||
"""Test set cluster mode.
|
||||
|
||||
TODO:
|
||||
- move from here. There are no set_cluster_mode in NodeConfig
|
||||
- split to unit and integrational tests
|
||||
- make unittests for raising exception
|
||||
"""
|
||||
|
||||
for mode in ['readonly', 'readwrite']:
|
||||
with self.subTest(mode=mode):
|
||||
fake_mode = mode
|
||||
set_cluster_mode(mode)
|
||||
with DBRM() as dbrm:
|
||||
if dbrm.get_dbrm_status() != 'master':
|
||||
fake_mode = 'readonly'
|
||||
self.assertEqual(dbrm.get_cluster_mode(), fake_mode)
|
||||
self.assertEqual(dbrm._get_cluster_mode(), mode)
|
||||
|
||||
def test_get_dbrm_conn_info(self):
|
||||
node_config = NodeConfig()
|
||||
root = node_config.get_current_config_root(CONFIG_PATH_OLD)
|
||||
master_conn_info = node_config.get_dbrm_conn_info(root)
|
||||
|
||||
tree = ET.parse(CONFIG_PATH_OLD)
|
||||
master_ip = tree.find('./DBRM_Controller/IPAddr').text
|
||||
master_port = tree.find('./DBRM_Controller/Port').text
|
||||
|
||||
self.assertEqual(master_conn_info['IPAddr'], master_ip)
|
||||
self.assertEqual(master_conn_info['Port'], master_port)
|
||||
|
||||
def test_is_primary_node(self):
|
||||
try:
|
||||
current_master = None
|
||||
node_config = NodeConfig()
|
||||
root = node_config.get_current_config_root()
|
||||
current_master = node_config.get_dbrm_conn_info(root)['IPAddr']
|
||||
list_ips = "ip -4 -o addr | awk '!/^[0-9]*: ?lo|link\/ether/ {print $4}'"
|
||||
result = subprocess.run(list_ips,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE)
|
||||
local_addresses = result.stdout.decode('ASCII').split('\n')
|
||||
local_addresses = [addr.split('/')[0] for addr in local_addresses if len(addr)]
|
||||
os.system(f"mcsSetConfig DBRM_Controller IPAddr {local_addresses[0]}")
|
||||
self.assertTrue(node_config.is_primary_node())
|
||||
os.system(f"mcsSetConfig DBRM_Controller IPAddr 8.8.8.8")
|
||||
self.assertFalse(node_config.is_primary_node())
|
||||
os.system(f"mcsSetConfig DBRM_Controller IPAddr {current_master}")
|
||||
except AssertionError as e:
|
||||
if current_master is not None:
|
||||
os.system(f"mcsSetConfig DBRM_Controller IPAddr \
|
||||
{current_master}")
|
||||
raise e
|
||||
|
||||
def test_get_network_interfaces(self):
|
||||
node_config = NodeConfig()
|
||||
addresses = list(node_config.get_network_addresses())
|
||||
exemplar_addresses = []
|
||||
list_ips = "ip -4 -o addr | awk '!/^[0-9]*: ?lo|link\/ether/ {print $4}'"
|
||||
result = subprocess.run(list_ips,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE)
|
||||
exemplar_addresses += result.stdout.decode('ASCII').split('\n')
|
||||
list_ips = "ip -6 -o addr | awk '!/^[0-9]*: ?lo|link\/ether/ {print $4}'"
|
||||
result = subprocess.run(list_ips,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE)
|
||||
exemplar_addresses += result.stdout.decode('ASCII').split('\n')
|
||||
golden_addresses = [addr.split('/')[0] for addr in exemplar_addresses if len(addr) > 0]
|
||||
for addr in golden_addresses:
|
||||
self.assertTrue(addr in addresses)
|
||||
|
||||
def test_is_single_node(self):
|
||||
try:
|
||||
current_master = None
|
||||
node_config = NodeConfig()
|
||||
root = node_config.get_current_config_root()
|
||||
current_master = node_config.get_dbrm_conn_info(root)['IPAddr']
|
||||
os.system(f"mcsSetConfig DBRM_Controller IPAddr 127.0.0.1")
|
||||
self.assertTrue(node_config.is_single_node())
|
||||
os.system(f"mcsSetConfig DBRM_Controller IPAddr 8.8.8.8")
|
||||
self.assertFalse(node_config.is_single_node())
|
||||
os.system(f"mcsSetConfig DBRM_Controller IPAddr {current_master}")
|
||||
except AssertionError as e:
|
||||
if current_master is not None:
|
||||
os.system(f"mcsSetConfig DBRM_Controller IPAddr \
|
||||
{current_master}")
|
||||
raise e
|
||||
|
||||
@mock.patch(f'{NODE_CONFIG_MODULE}.read_module_id', return_value=1)
|
||||
def test_get_module_net_address(self, *args):
|
||||
with TemporaryDirectory() as tmp_dirname:
|
||||
config_filepath = os.path.join(tmp_dirname, 'Columnstore.xml')
|
||||
copyfile(CONFIG_PATH_OLD, config_filepath)
|
||||
|
||||
module_address = None
|
||||
node_config = NodeConfig()
|
||||
current_module_id = read_module_id()
|
||||
module_address_sh = (
|
||||
f'mcsGetConfig -c {config_filepath} '
|
||||
f'SystemModuleConfig ModuleIPAddr{current_module_id}-1-3'
|
||||
)
|
||||
result = subprocess.run(
|
||||
module_address_sh, shell=True, stdout=subprocess.PIPE
|
||||
)
|
||||
module_address = result.stdout.decode('ASCII').split('\n')[0]
|
||||
dummy_address = '8.8.8.8'
|
||||
os.system(
|
||||
f'mcsSetConfig -c {config_filepath} '
|
||||
f'SystemModuleConfig ModuleIPAddr{current_module_id}-1-3 '
|
||||
f'{dummy_address}'
|
||||
)
|
||||
root = node_config.get_current_config_root(config_filepath)
|
||||
self.assertEqual(
|
||||
dummy_address, node_config.get_module_net_address(root)
|
||||
)
|
||||
self.assertNotEqual(
|
||||
module_address, node_config.get_module_net_address(root)
|
||||
)
|
||||
os.system(
|
||||
f'mcsSetConfig -c {config_filepath} SystemModuleConfig '
|
||||
f'ModuleIPAddr{current_module_id}-1-3 {module_address}'
|
||||
)
|
||||
root = node_config.get_current_config_root(config_filepath)
|
||||
self.assertEqual(
|
||||
module_address, node_config.get_module_net_address(root)
|
||||
)
|
||||
|
||||
def test_get_new_module_id(self):
|
||||
try:
|
||||
current_module_id = None
|
||||
current_module_address = None
|
||||
node_config = NodeConfig()
|
||||
current_module_id = read_module_id()
|
||||
root = node_config.get_current_config_root()
|
||||
current_module_address = node_config.get_module_net_address(root)
|
||||
os.system(f"mcsSetConfig SystemModuleConfig \
|
||||
ModuleIPAddr{current_module_id}-1-3 8.8.8.8")
|
||||
os.system(f"mcsSetConfig SystemModuleConfig \
|
||||
ModuleIPAddr{current_module_id+42}-1-3 {current_module_address}")
|
||||
root = node_config.get_current_config_root()
|
||||
self.assertEqual(current_module_id+42,
|
||||
node_config.get_new_module_id(root))
|
||||
self.assertNotEqual(current_module_id,
|
||||
node_config.get_new_module_id(root))
|
||||
os.system(f"mcsSetConfig SystemModuleConfig \
|
||||
ModuleIPAddr{current_module_id}-1-3 {current_module_address}")
|
||||
os.system(f"mcsSetConfig -x SystemModuleConfig \
|
||||
ModuleIPAddr{current_module_id+42}-1-3 {current_module_address}")
|
||||
root = node_config.get_current_config_root()
|
||||
self.assertEqual(current_module_id,
|
||||
node_config.get_new_module_id(root))
|
||||
except AssertionError as e:
|
||||
if current_module_id is not None and current_module_address is not None:
|
||||
os.system(f"mcsSetConfig SystemModuleConfig \
|
||||
ModuleIPAddr{current_module_id}-1-3 {current_module_address}")
|
||||
os.system(f"mcsSetConfig -x SystemModuleConfig \
|
||||
ModuleIPAddr{current_module_id+42}-1-3 {current_module_address}")
|
||||
|
||||
def test_dbroots_to_create(self):
|
||||
try:
|
||||
node_config = NodeConfig()
|
||||
current_module_id = read_module_id()
|
||||
dummy_dbroots = [42, 43]
|
||||
dbroot_seq_id = 2
|
||||
for d in dummy_dbroots:
|
||||
os.system(f"mcsSetConfig SystemModuleConfig \
|
||||
ModuleDBRootID{current_module_id}-{dbroot_seq_id}-3 {d}")
|
||||
dbroot_seq_id += 1
|
||||
root = node_config.get_current_config_root()
|
||||
dbroots_to_create = list(node_config.dbroots_to_create(root=root, module_id=current_module_id))
|
||||
for d in dbroots_to_create:
|
||||
self.assertTrue(d in dummy_dbroots)
|
||||
except AssertionError as e:
|
||||
dbroot_seq_id = 2
|
||||
for d in dummy_dbroots:
|
||||
os.system(f"mcsSetConfig -x SystemModuleConfig \
|
||||
ModuleDBRootID{current_module_id}-{dbroot_seq_id}-3 {d}")
|
||||
dbroot_seq_id += 1
|
||||
raise e
|
||||
|
||||
dbroot_seq_id = 2
|
||||
for d in dummy_dbroots:
|
||||
os.system(f"mcsSetConfig -x SystemModuleConfig \
|
||||
ModuleDBRootID{current_module_id}-{dbroot_seq_id}-3 {d}")
|
||||
dbroot_seq_id += 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
50
cmapi/mcs_node_control/test/test_node_status.py
Normal file
50
cmapi/mcs_node_control/test/test_node_status.py
Normal file
@ -0,0 +1,50 @@
|
||||
import logging
|
||||
import os
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
|
||||
from cmapi_server.constants import MCS_MODULE_FILE_PATH
|
||||
from mcs_node_control.models.node_status import NodeStatus
|
||||
|
||||
|
||||
logging.basicConfig(level='DEBUG')
|
||||
|
||||
|
||||
class NodeStatusTest(unittest.TestCase):
|
||||
def test_dbrm_cluster_mode(self):
|
||||
node_status = NodeStatus()
|
||||
# use subprocess.run to capture stdout
|
||||
os.system('/usr/bin/dbrmctl readwrite')
|
||||
self.assertEqual(node_status.get_cluster_mode(), 'readwrite')
|
||||
os.system('/usr/bin/dbrmctl readonly')
|
||||
self.assertEqual(node_status.get_cluster_mode(), 'readonly')
|
||||
# kill controllernode and test it
|
||||
|
||||
def test_dbrm_status(self):
|
||||
node_status = NodeStatus()
|
||||
self.assertEqual(node_status.get_dbrm_status(), 'master')
|
||||
|
||||
def test_dbroots(self):
|
||||
try:
|
||||
node_status = NodeStatus()
|
||||
dbroot_ids = [1, 2, 3]
|
||||
path = '/tmp/dbroots/'
|
||||
for e in dbroot_ids:
|
||||
p = Path(path + 'data' + str(e))
|
||||
p.mkdir(parents = True, exist_ok = True)
|
||||
for e in node_status.get_dbroots(path=path):
|
||||
self.assertEqual(e in dbroot_ids, True)
|
||||
except AssertionError as e:
|
||||
rmtree(path)
|
||||
raise e
|
||||
|
||||
def test_module_id(self):
|
||||
node_status = NodeStatus()
|
||||
module_file = Path(MCS_MODULE_FILE_PATH)
|
||||
examplar_id = int(module_file.read_text()[2:])
|
||||
self.assertEqual(examplar_id, node_status.get_module_id())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Reference in New Issue
Block a user