1
0
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:
mariadb-AlanMologorsky
2022-11-18 15:18:40 +02:00
committed by Alan Mologorsky
parent 77eedd1756
commit a079a2c944
93 changed files with 15218 additions and 0 deletions

View File

View 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

View File

@ -0,0 +1,2 @@
from mcs_node_control.models.node_status import NodeStatus

View 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'

View 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

View 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()

View 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)

View 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

View 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

View 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

View 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>

View 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>

View File

View 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'

View 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)

View 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()

View 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()

View 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()