Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DNM] MCOL-5019: distributing cskeys secrets file, move cskeys amd cspasswd functions to mcs cli. #3427

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmapi/cmapi_server/constants.py
Original file line number Diff line number Diff line change
@@ -20,9 +20,11 @@
MCS_EM_PATH = os.path.join(MCS_DATA_PATH, EM_PATH_SUFFIX)
MCS_BRM_CURRENT_PATH = os.path.join(MCS_EM_PATH, 'BRM_saves_current')
S3_BRM_CURRENT_PATH = os.path.join(EM_PATH_SUFFIX, 'BRM_saves_current')

# keys file for CEJ password encryption\decryption
# (CrossEngineSupport section in Columnstore.xml)
MCS_SECRETS_FILE_PATH = os.path.join(MCS_DATA_PATH, '.secrets')
MCS_SECRETS_FILENAME = '.secrets'
MCS_SECRETS_FILE_PATH = os.path.join(MCS_DATA_PATH, MCS_SECRETS_FILENAME)

# CMAPI SERVER
CMAPI_CONFIG_FILENAME = 'cmapi_server.conf'
34 changes: 28 additions & 6 deletions cmapi/cmapi_server/controllers/api_clients.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import requests
from typing import Any, Dict, Optional, Union

import pyotp

from cmapi_server.controllers.dispatcher import _version
from cmapi_server.constants import CURRENT_NODE_CMAPI_URL
from cmapi_server.constants import (
CMAPI_CONF_PATH, CURRENT_NODE_CMAPI_URL, SECRET_KEY,
)
from cmapi_server.helpers import get_config_parser, get_current_key


class ClusterControllerClient:
@@ -11,6 +17,10 @@ def __init__(
):
"""Initialize the ClusterControllerClient with the base URL.

WARNING: This class only handles the API requests, it does not
handle the transaction management. So it should be started
at level above using TransactionManager (decorator or context manager).

:param base_url: The base URL for the API endpoints,
defaults to CURRENT_NODE_CMAPI_URL
:type base_url: str, optional
@@ -59,14 +69,14 @@ def add_node(
return self._request('PUT', 'node', {**node_info, **extra})

def remove_node(
self, node_id: str, extra: Dict[str, Any] = dict()
self, node: str, extra: Dict[str, Any] = dict()
) -> Union[Dict[str, Any], Dict[str, str]]:
"""Remove a node from the cluster.

:param node_id: The ID of the node to remove.
:param node: node IP, name or FQDN.
:return: The response from the API.
"""
return self._request('DELETE', 'node', {'node_id': node_id})
return self._request('DELETE', 'node', {'node': node, **extra})

def get_status(self) -> Union[Dict[str, Any], Dict[str, str]]:
"""Get the status of the cluster.
@@ -83,7 +93,12 @@ def set_api_key(
:param api_key: The API key to set.
:return: The response from the API.
"""
return self._request('put', 'apikey-set', {'api_key': api_key})
totp = pyotp.TOTP(SECRET_KEY)
payload = {
'api_key': api_key,
'verification_key': totp.now()
}
return self._request('put', 'apikey-set', payload)

def set_log_level(
self, log_level: str
@@ -117,9 +132,16 @@ def _request(
:return: The response from the API.
"""
url = f'{self.base_url}/cmapi/{_version}/cluster/{endpoint}'
cmapi_cfg_parser = get_config_parser(CMAPI_CONF_PATH)
key = get_current_key(cmapi_cfg_parser)
headers = {'x-api-key': key}
if method in ['PUT', 'POST', 'DELETE']:
headers['Content-Type'] = 'application/json'
data = {'in_transaction': True, **(data or {})}
try:
response = requests.request(
method, url, json=data, timeout=self.request_timeout
method, url, headers=headers, json=data,
timeout=self.request_timeout, verify=False
)
response.raise_for_status()
return response.json()
49 changes: 39 additions & 10 deletions cmapi/cmapi_server/controllers/endpoints.py
Original file line number Diff line number Diff line change
@@ -14,15 +14,17 @@

from cmapi_server.exceptions import CMAPIBasicError
from cmapi_server.constants import (
DEFAULT_SM_CONF_PATH, EM_PATH_SUFFIX, DEFAULT_MCS_CONF_PATH, MCS_EM_PATH,
MCS_BRM_CURRENT_PATH, S3_BRM_CURRENT_PATH, CMAPI_CONF_PATH, SECRET_KEY,
DEFAULT_MCS_CONF_PATH, DEFAULT_SM_CONF_PATH, EM_PATH_SUFFIX,
MCS_BRM_CURRENT_PATH, MCS_EM_PATH, S3_BRM_CURRENT_PATH, SECRET_KEY,
)
from cmapi_server.controllers.error import APIError
from cmapi_server.handlers.cej import CEJError
from cmapi_server.handlers.cej import CEJPasswordHandler
from cmapi_server.handlers.cluster import ClusterHandler
from cmapi_server.helpers import (
cmapi_config_check, get_config_parser, get_current_key, get_dbroots,
system_ready, save_cmapi_conf_file, dequote, in_maintenance_state,
cmapi_config_check, dequote, get_active_nodes, get_config_parser,
get_current_key, get_dbroots, in_maintenance_state, save_cmapi_conf_file,
system_ready,
)
from cmapi_server.logging_management import change_loggers_level
from cmapi_server.managers.application import AppManager
@@ -60,6 +62,9 @@ def raise_422_error(
:type exc_info: bool
:raises APIError: everytime with custom error message
"""
# TODO: change:
# - func name to inspect.stack(0)[1][3]
# - make something to logger, seems passing here is useless
logger.error(f'{func_name} {err_msg}', exc_info=exc_info)
raise APIError(422, err_msg)

@@ -146,7 +151,21 @@ def active_operation():
if txn_section is not None:
txn_manager_address = app.config['txn'].get('manager_address', None)
if txn_manager_address is not None and len(txn_manager_address) > 0:
raise APIError(422, "There is an active operation.")
raise_422_error(
module_logger, 'active_operation', 'There is an active operation.'
)


@cherrypy.tools.register('before_handler', priority=82)
def has_active_nodes():
"""Check if there are any active nodes in the cluster."""
active_nodes = get_active_nodes()

if len(active_nodes) == 0:
raise_422_error(
module_logger, 'has_active_nodes',
'No active nodes in the cluster.'
)


class TimingTool(cherrypy.Tool):
@@ -339,6 +358,7 @@ def put_config(self):
sm_config_filename = request_body.get(
'sm_config_filename', DEFAULT_SM_CONF_PATH
)
secrets = request_body.get('secrets', None)

if request_mode is None and request_config is None:
raise_422_error(
@@ -367,6 +387,9 @@ def put_config(self):
)
request_response = {'timestamp': str(datetime.now())}

if secrets:
CEJPasswordHandler().save_secrets(secrets)

node_config = NodeConfig()
xml_config = request_body.get('config', None)
sm_config = request_body.get('sm_config', None)
@@ -816,19 +839,22 @@ def put_start(self):
@cherrypy.tools.json_in()
@cherrypy.tools.json_out()
@cherrypy.tools.validate_api_key() # pylint: disable=no-member
@cherrypy.tools.has_active_nodes() # pylint: disable=no-member
def put_shutdown(self):
func_name = 'put_shutdown'
log_begin(module_logger, func_name)

request = cherrypy.request
request_body = request.json
timeout = request_body.get('timeout', None)
force = request_body.get('force', False)
config = request_body.get('config', DEFAULT_MCS_CONF_PATH)
in_transaction = request_body.get('in_transaction', False)

try:
if not in_transaction:
with TransactionManager():
response = ClusterHandler.shutdown(config)
response = ClusterHandler.shutdown(config, timeout)
else:
response = ClusterHandler.shutdown(config)
except CMAPIBasicError as err:
@@ -882,7 +908,7 @@ def put_add_node(self):

try:
if not in_transaction:
with TransactionManager():
with TransactionManager(extra_nodes=[node]):
response = ClusterHandler.add_node(node, config)
else:
response = ClusterHandler.add_node(node, config)
@@ -903,15 +929,18 @@ def delete_remove_node(self):
request_body = request.json
node = request_body.get('node', None)
config = request_body.get('config', DEFAULT_MCS_CONF_PATH)
#TODO: for next release
in_transaction = request_body.get('in_transaction', False)

#TODO: add arguments verification decorator
if node is None:
raise_422_error(module_logger, func_name, 'missing node argument')

try:
response = ClusterHandler.remove_node(node, config)
if not in_transaction:
with TransactionManager(remove_nodes=[node]):
response = ClusterHandler.remove_node(node, config)
else:
response = ClusterHandler.remove_node(node, config)
except CMAPIBasicError as err:
raise_422_error(module_logger, func_name, err.message)

@@ -1021,7 +1050,7 @@ def set_api_key(self):

if not totp_key or not new_api_key:
# not show which arguments in error message because endpoint for
# internal usage only
# cli tool or internal usage only
raise_422_error(
module_logger, func_name, 'Missing required arguments.'
)
Loading