diff --git a/engine/enginerpc/engine.proto b/engine/enginerpc/engine.proto index f11b867dda0..2280e53f9d1 100644 --- a/engine/enginerpc/engine.proto +++ b/engine/enginerpc/engine.proto @@ -356,6 +356,9 @@ message EngineHost { UNREACHABLE = 2; } State current_state = 6; + string display_name = 7; + repeated string parent_hosts = 8; + repeated string child_hosts = 9; } message ContactIdentifier { diff --git a/engine/enginerpc/engine_impl.cc b/engine/enginerpc/engine_impl.cc index 0476f9dc9d0..f32a0aefc5e 100644 --- a/engine/enginerpc/engine_impl.cc +++ b/engine/enginerpc/engine_impl.cc @@ -245,9 +245,19 @@ grpc::Status engine_impl::GetHost(grpc::ServerContext* context [[maybe_unused]], host->set_alias(selectedhost->get_alias()); host->set_address(selectedhost->get_address()); host->set_check_period(selectedhost->check_period()); + host->set_id(selectedhost->host_id()); host->set_current_state( static_cast(selectedhost->get_current_state())); - host->set_id(selectedhost->host_id()); + host->set_display_name(selectedhost->get_display_name()); + + if (!selectedhost->parent_hosts.empty()) + for (const auto& [key, _] : selectedhost->parent_hosts) + host->add_parent_hosts(key); + + if (!selectedhost->child_hosts.empty()) + for (const auto& [key, _] : selectedhost->child_hosts) + host->add_child_hosts(key); + return 0; }); diff --git a/tests/broker-engine/parent_child_relation.robot b/tests/broker-engine/parent_child_relation.robot new file mode 100644 index 00000000000..2a123df2f5e --- /dev/null +++ b/tests/broker-engine/parent_child_relation.robot @@ -0,0 +1,262 @@ +*** Settings *** +Documentation Centreon Engine/Broker verify relation parent child host. + +Resource ../resources/import.resource + +Suite Setup Ctn Clean Before Suite +Suite Teardown Ctn Clean After Suite +Test Setup Ctn Stop Processes +Test Teardown Ctn Save Logs If Failed + + +*** Test Cases *** + +EBPN0 + [Documentation] Verify if child is in queue when parent is down. + [Tags] broker engine MON-151686 + + Ctn Config Engine ${1} ${5} ${1} + Ctn Config Broker rrd + Ctn Config Broker central + Ctn Config Broker module + Ctn Config BBDO3 1 + + Ctn Broker Config Log rrd rrd trace + Ctn Broker Config Log central sql debug + Ctn Broker Config Log rrd core error + Ctn Engine Config Set Value 0 log_level_checks debug + Ctn Config Broker Sql Output central unified_sql 10 + Ctn Broker Config Flush Log central 0 + Ctn Broker Config Flush Log rrd 0 + + Ctn Clear Retention + Ctn Clear Db resources + + # force the check result to 2 + Ctn Config Host Command Status ${0} checkh1 2 + + # host_1 is parent of host_2 + Ctn Add Parent To Host 0 host_2 host_1 + + ${start} Get Current Date + Ctn Start Broker + Ctn Start Engine + Ctn Wait For Engine To Be Ready ${start} ${1} + + # check if host_2 is child of host_1 + Connect To Database pymysql ${DBName} ${DBUser} ${DBPass} ${DBHost} ${DBPort} + + FOR ${index} IN RANGE 30 + ${output} Query + ... SELECT child_id, parent_id FROM hosts_hosts_parents + Log To Console ${output} + Sleep 1s + IF "${output}" == "((2, 1),)" BREAK + END + Should Be Equal As Strings ${output} ((2, 1),) host parent not inserted + + # check if host_1 is pending + ${result} Ctn Check Host Status host_1 4 1 True + Should Be True ${result} host_1 should be pending + + ${result} Ctn Check Host Status host_2 4 1 True + Should Be True ${result} host_2 should be pending + + ${content} Create List INITIAL HOST STATE: host_1; + ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 60 + Should Be True + ... ${result} + ... An Initial host state on host_1 should be raised before we can start our external commands. + + Ctn Process Host Check Result host_1 0 host_1 UP + + FOR ${i} IN RANGE ${4} + Ctn Schedule Forced Host Check host_1 ${VarRoot}/lib/centreon-engine/config0/rw/centengine.cmd + Sleep 1s + END + + ${content} Create List + ... EXTERNAL COMMAND: SCHEDULE_FORCED_HOST_CHECK;host_1; + ... HOST ALERT: host_1;DOWN;HARD; + + ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 60 + Should Be True ${result} Message about SCHEDULE HOST should be down in log. + + ${result} Ctn Check Host Status host_1 1 1 True + Should Be True ${result} host_1 should be down/hard + + ${content} Create List + ... Check of child host 'host_2' queued. + ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 60 + Should Be True ${result} Check of child host 'host_2' should be queued. + + Disconnect From Database + Ctn Stop Engine + Ctn Kindly Stop Broker + +EBPN1 + [Documentation] verify relation parent child when delete parent. + [Tags] broker engine MON-151686 + + Ctn Config Engine ${1} ${5} ${1} + Ctn Config Broker rrd + Ctn Config Broker central + Ctn Config Broker module + Ctn Config BBDO3 1 + + Ctn Broker Config Log rrd rrd error + Ctn Broker Config Log rrd core error + Ctn Broker Config Log module0 core error + + Ctn Broker Config Log central sql debug + Ctn Engine Config Set Value 0 log_level_checks error + Ctn Config Broker Sql Output central unified_sql 10 + Ctn Broker Config Flush Log central 0 + Ctn Broker Config Flush Log rrd 0 + + Ctn Clear Retention + + + # host_1 is parent of host_2 + Ctn Add Parent To Host 0 host_2 host_1 + + ${start} Get Current Date + Ctn Start Broker + Ctn Start Engine + Ctn Wait For Engine To Be Ready ${start} ${1} + + ${output} Ctn Get Host Info Grpc ${2} + Log To Console parents:${output}[parentHosts] + Should Contain ${output}[parentHosts] host_1 parentHosts + + ${output} Ctn Get Host Info Grpc ${1} + Log To Console childs:${output}[childHosts] + Should Contain ${output}[childHosts] host_2 childHosts + + Connect To Database pymysql ${DBName} ${DBUser} ${DBPass} ${DBHost} ${DBPort} + + FOR ${index} IN RANGE 30 + ${output} Query + ... SELECT child_id, parent_id FROM hosts_hosts_parents + Log To Console ${output} + Sleep 1s + IF "${output}" == "((2, 1),)" BREAK + END + Should Be Equal As Strings ${output} ((2, 1),) the parent link not inserted + + Ctn Engine Config Del Block In Cfg ${0} host host_1 hosts.cfg + Ctn Engine Config Del Block In Cfg ${0} service host_1 services.cfg + Ctn Engine Config Delete Value In Hosts ${0} host_2 parents + + ${start} Get Current Date + Ctn Reload Engine + Ctn Wait For Engine To Be Ready ${start} ${1} + ${content} Create List Reload configuration finished + ${result} Ctn Find In Log With Timeout + ... ${ENGINE_LOG}/config0/centengine.log + ... ${start} + ... ${content} + ... 60 + ... verbose=False + Should Be True ${result} Engine is Not Ready after 60s!! + + + ${output} Ctn Get Host Info Grpc ${2} + Log To Console parents:${output}[parentHosts] + Should Be Empty ${output}[parentHosts] + + FOR ${index} IN RANGE 30 + ${output} Query + ... SELECT child_id, parent_id FROM hosts_hosts_parents + Log To Console ${output} + Sleep 1s + IF "${output}" == "()" BREAK + END + Should Be Equal As Strings ${output} () the parent link should be deleted + + Disconnect From Database + Ctn Stop Engine + Ctn Kindly Stop Broker + +EBPN2 + [Documentation] verify relation parent child when delete child. + [Tags] broker engine MON-151686 + + Ctn Config Engine ${1} ${5} ${1} + Ctn Config Broker rrd + Ctn Config Broker central + Ctn Config Broker module + Ctn Config BBDO3 1 + + Ctn Broker Config Log rrd rrd error + Ctn Broker Config Log rrd core error + Ctn Broker Config Log module0 core error + + Ctn Broker Config Log central sql debug + Ctn Engine Config Set Value 0 log_level_checks error + Ctn Config Broker Sql Output central unified_sql 10 + Ctn Broker Config Flush Log central 0 + Ctn Broker Config Flush Log rrd 0 + + Ctn Clear Retention + + # host_1 is parent of host_2 + Ctn Add Parent To Host 0 host_2 host_1 + + ${start} Get Current Date + + Ctn Start Broker + Ctn Start Engine + Ctn Wait For Engine To Be Ready ${start} ${1} + + ${output} Ctn Get Host Info Grpc ${2} + Log To Console parents:${output}[parentHosts] + Should Contain ${output}[parentHosts] host_1 parentHosts + + ${output} Ctn Get Host Info Grpc ${1} + Log To Console childs:${output}[childHosts] + Should Contain ${output}[childHosts] host_2 childHosts + + Connect To Database pymysql ${DBName} ${DBUser} ${DBPass} ${DBHost} ${DBPort} + + FOR ${index} IN RANGE 30 + ${output} Query + ... SELECT child_id, parent_id FROM hosts_hosts_parents + Log To Console ${output} + Sleep 1s + IF "${output}" == "((2, 1),)" BREAK + END + Should Be Equal As Strings ${output} ((2, 1),) the parent link not inserted + + Ctn Engine Config Del Block In Cfg ${0} host host_2 hosts.cfg + Ctn Engine Config Del Block In Cfg ${0} service host_2 services.cfg + Ctn Engine Config Delete Value In Hosts ${0} host_2 parents + + ${start} Get Current Date + Ctn Reload Engine + Ctn Wait For Engine To Be Ready ${start} ${1} + ${content} Create List Reload configuration finished + ${result} Ctn Find In Log With Timeout + ... ${ENGINE_LOG}/config0/centengine.log + ... ${start} + ... ${content} + ... 60 + ... verbose=False + Should Be True ${result} Engine is Not Ready after 60s!! + + ${output} Ctn Get Host Info Grpc ${1} + Log To Console childs:${output}[childHosts] + Should Be Empty ${output}[childHosts] + + FOR ${index} IN RANGE 30 + ${output} Query + ... SELECT child_id, parent_id FROM hosts_hosts_parents + Log To Console ${output} + Sleep 1s + IF "${output}" == "()" BREAK + END + Should Be Equal As Strings ${output} () the parent link should be deleted + + Disconnect From Database + Ctn Stop Engine + Ctn Kindly Stop Broker \ No newline at end of file diff --git a/tests/resources/Engine.py b/tests/resources/Engine.py index 3df8fddf7db..2def5e7ec3c 100755 --- a/tests/resources/Engine.py +++ b/tests/resources/Engine.py @@ -32,6 +32,7 @@ from array import array from dateutil import parser import datetime +from google.protobuf.json_format import MessageToDict from os import makedirs, chmod from os.path import exists, dirname from robot.api import logger @@ -3648,3 +3649,102 @@ def ctn_send_otl_to_engine(port: int, resource_metrics: list): logger.console("gRPC server not ready") +def ctn_engine_config_del_block_in_cfg(idx: int, type: str, key: str, file): + """ + Delete a element in the file given for the Engine configuration idx. + + Args: + idx (int): Index of the Engine configuration (from 0) + type (str): The type (host/service/...). + key (str): The parameter that will be deleted. + file (str): The file to delete the key from. + """ + filename = f"{ETC_ROOT}/centreon-engine/config{idx}/{file}" + + with open(filename, "r") as f: + content = f.read() + + if type == "host": + pattern = rf"define host \{{\s*host_name\s+{re.escape(key)}\b.*?\}}" + elif type == "service": + pattern = rf"define service \{{\s*host_name\s+{re.escape(key)}\b.*?\}}" + + # Use re.sub to remove the matched block + new_content = re.sub(pattern, '', content, flags=re.DOTALL) + new_content = re.sub(r'\n\s*\n', '\n', new_content) + + if content != new_content: + with open(filename, "w") as f: + f.write(new_content) + else: + logger.console(f'\n\033[91mFailed : Cannot delete the block with the type : {type} and the key : {key} in {file}\033[0m') + +def ctn_get_host_info_grpc(id:int): + """ + Retrieve host information via a gRPC call. + + Args: + id: The identifier of the host to retrieve. + + Returns: + A dictionary containing the host informations, if successfully retrieved. + """ + if id is not None: + limit = time.time() + 30 + while time.time() < limit: + time.sleep(1) + with grpc.insecure_channel("127.0.0.1:50001") as channel: + stub = engine_pb2_grpc.EngineStub(channel) + request = engine_pb2.HostIdentifier(id=id) + try: + host = stub.GetHost(request) + host_dict = MessageToDict(host, always_print_fields_with_no_presence=True) + return host_dict + except Exception as e: + logger.console(f"gRPC server not ready {e}") + return {} + +def ctn_engine_config_delete_value_in_hosts(idx: int, desc: str, key: str, file: str = 'hosts.cfg'): + """ + Delete a parameter in the hosts.cfg for the Engine configuration idx. + + Args: + idx (int): Index of the Engine configuration (from 0) + desc (str): host name of the host to modify. + key (str): the parameter that will be deleted. + file (str): The file to modify, default value 'hosts.cfg' + """ + + + filename = f"{ETC_ROOT}/centreon-engine/config{idx}/{file}" + with open(filename, "r") as f: + lines = f.readlines() + + r = re.compile(r"^\s*host_name\s+" + desc + "\s*$") + rbis = re.compile(r"^\s*name\s+" + desc + "\s*$") + found = False + for i in range(len(lines)): + if r.match(lines[i]): + print("here" + lines[i]) + for j in range(i + 1, len(lines)): + if '}' in lines[j]: + break + if key in lines[j]: + del lines[j] + found = True + break + break + + if not found: + for i in range(len(lines)): + if rbis.match(lines[i]): + for j in range(i + 1, len(lines)): + if '}' in lines[j]: + break + if key in lines[j]: + del lines[j] + found = True + break + break + with open(filename, "w") as f: + f.writelines(lines) \ No newline at end of file