From b0caada215c3ca0bcdd40e5b9735e9f26f454b83 Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Wed, 29 Jun 2016 14:18:33 +0200 Subject: [PATCH] implemented the Main Controller's 'reconf' command (artf468488) Change-Id: Ie84819f7e01d73b0735aaab24207117e4adef28b Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- core/Communication.cc | 23 ++++++-- core/Communication.hh | 2 +- core/Message_types.hh | 12 +++- core/Runtime.hh | 8 +-- mctr2/cli/Cli.cc | 45 +++++++++++--- mctr2/cli/Cli.h | 1 + mctr2/mctr/MainController.cc | 110 +++++++++++++++++++++++++++++++---- mctr2/mctr/MainController.h | 9 ++- usrguide/userguide.doc | Bin 968192 -> 966656 bytes 9 files changed, 174 insertions(+), 36 deletions(-) diff --git a/core/Communication.cc b/core/Communication.cc index 44352b934..6c4df1c2b 100644 --- a/core/Communication.cc +++ b/core/Communication.cc @@ -572,7 +572,7 @@ void TTCN_Communication::process_all_messages_hc() process_error(); break; case MSG_CONFIGURE: - process_configure(msg_end); + process_configure(msg_end, false); break; case MSG_CREATE_MTC: process_create_mtc(); @@ -704,6 +704,9 @@ void TTCN_Communication::process_all_messages_tc() case MSG_EXIT_MTC: process_exit_mtc(); break; + case MSG_CONFIGURE: + process_configure(msg_end, true); + break; default: process_unsupported_message(msg_type, msg_end); } @@ -1263,20 +1266,28 @@ void TTCN_Communication::send_message(Text_Buf& text_buf) } } -void TTCN_Communication::process_configure(int msg_end) +void TTCN_Communication::process_configure(int msg_end, bool to_mtc) { switch (TTCN_Runtime::get_state()) { case TTCN_Runtime::HC_IDLE: case TTCN_Runtime::HC_ACTIVE: case TTCN_Runtime::HC_OVERLOADED: - break; + if (!to_mtc) { + break; + } + // no break + case TTCN_Runtime::MTC_IDLE: + if (to_mtc) { + break; + } + // no break default: incoming_buf.cut_message(); send_error("Message CONFIGURE arrived in invalid state."); return; } - TTCN_Runtime::set_state(TTCN_Runtime::HC_CONFIGURING); + TTCN_Runtime::set_state(to_mtc ? TTCN_Runtime::MTC_CONFIGURING : TTCN_Runtime::HC_CONFIGURING); TTCN_Logger::log_configdata(TitanLoggerApiSimple::ExecutorConfigdata_reason::received__from__mc); // take the config string directly from the buffer for efficiency reasons @@ -1311,12 +1322,12 @@ void TTCN_Communication::process_configure(int msg_end) if (success) { send_configure_ack(); - TTCN_Runtime::set_state(TTCN_Runtime::HC_ACTIVE); + TTCN_Runtime::set_state(to_mtc ? TTCN_Runtime::MTC_IDLE : TTCN_Runtime::HC_ACTIVE); TTCN_Logger::log_configdata( TitanLoggerApiSimple::ExecutorConfigdata_reason::processing__succeeded); } else { send_configure_nak(); - TTCN_Runtime::set_state(TTCN_Runtime::HC_IDLE); + TTCN_Runtime::set_state(to_mtc ? TTCN_Runtime::MTC_IDLE : TTCN_Runtime::HC_IDLE); } incoming_buf.cut_message(); diff --git a/core/Communication.hh b/core/Communication.hh index 3cf058ebf..1cf41e27c 100644 --- a/core/Communication.hh +++ b/core/Communication.hh @@ -182,7 +182,7 @@ private: /** @name Handlers of various messages * @{ */ - static void process_configure(int msg_end); + static void process_configure(int msg_end, bool to_mtc); static void process_create_mtc(); static void process_create_ptc(); static void process_kill_process(); diff --git a/core/Message_types.hh b/core/Message_types.hh index b8873d663..cb718983b 100644 --- a/core/Message_types.hh +++ b/core/Message_types.hh @@ -34,7 +34,6 @@ /* Messages from MC to HC (down) */ -#define MSG_CONFIGURE 1 #define MSG_CREATE_MTC 2 #define MSG_CREATE_PTC 3 #define MSG_KILL_PROCESS 4 @@ -42,8 +41,6 @@ /* Messages from HC to MC (up) */ -#define MSG_CONFIGURE_ACK 2 -#define MSG_CONFIGURE_NAK 3 #define MSG_CREATE_NAK 4 #define MSG_HC_READY 5 @@ -128,4 +125,13 @@ #define MSG_DEBUG_RETURN_VALUE 100 +/* Messages from MC to HC or MTC (down) */ + +#define MSG_CONFIGURE 200 + +/* Messages from HC or MTC to MC (up) */ + +#define MSG_CONFIGURE_ACK 200 +#define MSG_CONFIGURE_NAK 201 + #endif diff --git a/core/Runtime.hh b/core/Runtime.hh index 282a531a5..10e67fcc6 100644 --- a/core/Runtime.hh +++ b/core/Runtime.hh @@ -50,11 +50,11 @@ public: MTC_TERMINATING_TESTCASE, MTC_TERMINATING_EXECUTION, MTC_PAUSED, // 14-16 MTC_CREATE, MTC_START, MTC_STOP, MTC_KILL, MTC_RUNNING, MTC_ALIVE, // 17-22 MTC_DONE, MTC_KILLED, MTC_CONNECT, MTC_DISCONNECT, MTC_MAP, MTC_UNMAP, // 23-28 - MTC_EXIT, // 29 + MTC_CONFIGURING, MTC_EXIT, // 30 - PTC_INITIAL, PTC_IDLE, PTC_FUNCTION, PTC_CREATE, PTC_START, PTC_STOP, // 30-35 - PTC_KILL, PTC_RUNNING, PTC_ALIVE, PTC_DONE, PTC_KILLED, PTC_CONNECT, // 36-41 - PTC_DISCONNECT, PTC_MAP, PTC_UNMAP, PTC_STOPPED, PTC_EXIT // 42-46 + PTC_INITIAL, PTC_IDLE, PTC_FUNCTION, PTC_CREATE, PTC_START, PTC_STOP, // 31-36 + PTC_KILL, PTC_RUNNING, PTC_ALIVE, PTC_DONE, PTC_KILLED, PTC_CONNECT, // 37-42 + PTC_DISCONNECT, PTC_MAP, PTC_UNMAP, PTC_STOPPED, PTC_EXIT // 43-47 }; private: static executor_state_enum executor_state; diff --git a/mctr2/cli/Cli.cc b/mctr2/cli/Cli.cc index b4818ac18..6a4886ab9 100644 --- a/mctr2/cli/Cli.cc +++ b/mctr2/cli/Cli.cc @@ -195,12 +195,14 @@ Cli::Cli() perror("Cli::Cli: pthread_cond_init failed."); exit(EXIT_FAILURE); } + cfg_file_name = NULL; } Cli::~Cli() { pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); + Free(cfg_file_name); } //---------------------------------------------------------------------------- @@ -217,8 +219,9 @@ int Cli::enterLoop(int argc, char *argv[]) printWelcome(); if (argc == 2) { - printf("Using configuration file: %s\n", argv[1]); - if (process_config_read_file(argv[1], &mycfg)) { + cfg_file_name = mcopystr(argv[1]); + printf("Using configuration file: %s\n", cfg_file_name); + if (process_config_read_file(cfg_file_name, &mycfg)) { puts("Error was found in the configuration file. Exiting."); cleanUp(); return EXIT_FAILURE; @@ -728,12 +731,37 @@ void Cli::infoCallback(const char *arguments) void Cli::reconfCallback(const char *arguments) { - if(*arguments == 0) { // reconf called without its optional argument - puts("Reconfiguration of MC and HCs using original configuration " - "data\n -- not supported, yet."); - } else { // reconf called with config_file argument - puts("Reconfiguration of MC and HCs using configuration file" - "specified in\ncommand line argument -- not supported, yet."); + if (!MainController::start_reconfiguring()) { + return; + } + if (*arguments != 0) { + Free(cfg_file_name); + cfg_file_name = mcopystr(arguments); + } + + printf("Using configuration file: %s\n", cfg_file_name); + if (process_config_read_file(cfg_file_name, &mycfg)) { + puts("Error was found in the configuration file. Exiting."); + cleanUp(); + puts("exit"); + exitCallback(""); + } + else { + MainController::set_kill_timer(mycfg.kill_timer); + + for (int i = 0; i < mycfg.group_list_len; ++i) { + MainController::add_host(mycfg.group_list[i].group_name, + mycfg.group_list[i].host_name); + } + + for (int i = 0; i < mycfg.component_list_len; ++i) { + MainController::assign_component(mycfg.component_list[i].host_or_group, + mycfg.component_list[i].component); + } + + if (MainController::get_state() == mctr::MC_RECONFIGURING) { + MainController::configure(mycfg.config_read_buffer); + } } } @@ -800,6 +828,7 @@ void Cli::exitCallback(const char *arguments) if (*arguments == 0) { switch (MainController::get_state()) { case mctr::MC_READY: + case mctr::MC_RECONFIGURING: MainController::exit_mtc(); waitMCState(WAIT_MTC_TERMINATED); case mctr::MC_LISTENING: diff --git a/mctr2/cli/Cli.h b/mctr2/cli/Cli.h index 190de8f7e..5485f0e18 100644 --- a/mctr2/cli/Cli.h +++ b/mctr2/cli/Cli.h @@ -191,6 +191,7 @@ private: */ int executeListIndex; + char* cfg_file_name; config_data mycfg; }; diff --git a/mctr2/mctr/MainController.cc b/mctr2/mctr/MainController.cc index 0af653164..8ff32588c 100644 --- a/mctr2/mctr/MainController.cc +++ b/mctr2/mctr/MainController.cc @@ -688,20 +688,37 @@ void MainController::configure_host(host_struct *host, boolean should_notify) host->hostname); } send_configure(host, config_str); - send_debug_setup(host); + if (mc_state != MC_RECONFIGURING) { + send_debug_setup(host); + } + } +} + +void MainController::configure_mtc() +{ + if (config_str == NULL) { + fatal_error("MainController::configure_mtc: no config file"); + } + if (mtc->tc_state != TC_IDLE) { + error("MainController::configure_mtc(): MTC is in wrong state."); + } + else { + mtc->tc_state = MTC_CONFIGURING; + send_configure_mtc(config_str); } } void MainController::check_all_hc_configured() { + bool reconf = mc_state == MC_RECONFIGURING; if (is_hc_in_state(HC_CONFIGURING) || is_hc_in_state(HC_CONFIGURING_OVERLOADED)) return; if (is_hc_in_state(HC_IDLE)) { error("There were errors during configuring HCs."); - mc_state = MC_HC_CONNECTED; + mc_state = reconf ? MC_READY : MC_HC_CONNECTED; } else if (is_hc_in_state(HC_ACTIVE) || is_hc_in_state(HC_OVERLOADED)) { notify("Configuration file was processed on all HCs."); - mc_state = MC_ACTIVE; + mc_state = reconf ? MC_READY : MC_ACTIVE; } else { error("There is no HC connection after processing the configuration " "file."); @@ -2782,6 +2799,7 @@ void MainController::handle_hc_data(host_struct *hc, boolean recv_from_socket) if (all_hc_in_state(HC_DOWN)) mc_state = MC_LISTENING; break; case MC_CONFIGURING: + case MC_RECONFIGURING: check_all_hc_configured(); break; case MC_ACTIVE: @@ -2901,6 +2919,12 @@ void MainController::handle_tc_data(component_struct *tc, case MSG_MTC_READY: process_mtc_ready(); break; + case MSG_CONFIGURE_ACK: + process_configure_ack_mtc(); + break; + case MSG_CONFIGURE_NAK: + process_configure_nak_mtc(); + break; default: error("Invalid message type (%d) was received " "from the MTC at %s [%s].", message_type, @@ -3538,6 +3562,15 @@ void MainController::send_exit_mtc() send_message(mtc->tc_fd, text_buf); } +void MainController::send_configure_mtc(const char* config_file) +{ + Text_Buf text_buf; + text_buf.push_int(MSG_CONFIGURE); + text_buf.push_string(config_file); + send_message(mtc->tc_fd, text_buf); +} + + void MainController::send_cancel_done_ptc(component_struct *tc, component component_reference) { @@ -3840,7 +3873,8 @@ void MainController::process_configure_ack(host_struct *hc) "received."); return; } - if (mc_state == MC_CONFIGURING) check_all_hc_configured(); + if (mc_state == MC_CONFIGURING || mc_state == MC_RECONFIGURING) + check_all_hc_configured(); else notify("Host %s was configured successfully.", hc->hostname); status_change(); } @@ -3857,7 +3891,8 @@ void MainController::process_configure_nak(host_struct *hc) "received."); return; } - if (mc_state == MC_CONFIGURING) check_all_hc_configured(); + if (mc_state == MC_CONFIGURING || mc_state == MC_RECONFIGURING) + check_all_hc_configured(); else notify("Processing of configuration file failed on host %s.", hc->hostname); status_change(); @@ -5766,6 +5801,26 @@ void MainController::process_mtc_ready() status_change(); } +void MainController::process_configure_ack_mtc() +{ + if (mtc->tc_state != MTC_CONFIGURING) { + send_error_str(mtc->tc_fd, "Unexpected message CONFIGURE_ACK was received."); + return; + } + mtc->tc_state = TC_IDLE; + notify("Configuration file was processed on the MTC."); +} + +void MainController::process_configure_nak_mtc() +{ + if (mtc->tc_state != MTC_CONFIGURING) { + send_error_str(mtc->tc_fd, "Unexpected message CONFIGURE_NAK was received."); + return; + } + mtc->tc_state = TC_IDLE; + notify("Processing of configuration file failed on the MTC."); +} + void MainController::process_stopped(component_struct *tc, int message_end) { switch (tc->tc_state) { @@ -6049,12 +6104,20 @@ void MainController::destroy_host_groups() void MainController::set_kill_timer(double timer_val) { lock(); - if (mc_state != MC_INACTIVE) + switch (mc_state) { + case MC_INACTIVE: + case MC_LISTENING: + case MC_HC_CONNECTED: + case MC_RECONFIGURING: + if (timer_val < 0.0) + error("MainController::set_kill_timer: setting a negative kill timer " + "value."); + else kill_timer = timer_val; + break; + default: error("MainController::set_kill_timer: called in wrong state."); - else if (timer_val < 0.0) - error("MainController::set_kill_timer: setting a negative kill timer " - "value."); - else kill_timer = timer_val; + break; + } unlock(); } @@ -6309,6 +6372,8 @@ void MainController::configure(const char *config_file) case MC_LISTENING_CONFIGURED: mc_state = MC_LISTENING_CONFIGURED; break; + case MC_RECONFIGURING: + break; default: error("MainController::configure: called in wrong state."); unlock(); @@ -6316,14 +6381,35 @@ void MainController::configure(const char *config_file) } Free(config_str); config_str = mcopystr(config_file); - if(mc_state == MC_CONFIGURING) { + if (mc_state == MC_CONFIGURING || mc_state == MC_RECONFIGURING) { notify("Downloading configuration file to all HCs."); for (int i = 0; i < n_hosts; i++) configure_host(hosts[i], FALSE); } + if (mc_state == MC_RECONFIGURING) { + notify("Downloading configuration file to the MTC."); + configure_mtc(); + } status_change(); unlock(); } +bool MainController::start_reconfiguring() +{ + switch (mc_state) { + case MC_READY: + mc_state = MC_RECONFIGURING; + return true; + case MC_LISTENING: + case MC_HC_CONNECTED: + return true; + default: + lock(); + error("MainController::start_reconfiguring: called in wrong state."); + unlock(); + return false; + } +} + void MainController::create_mtc(int host_index) { lock(); @@ -6415,7 +6501,7 @@ void MainController::create_mtc(int host_index) void MainController::exit_mtc() { lock(); - if (mc_state != MC_READY) { + if (mc_state != MC_READY && mc_state != MC_RECONFIGURING) { error("MainController::exit_mtc: called in wrong state."); unlock(); return; diff --git a/mctr2/mctr/MainController.h b/mctr2/mctr/MainController.h index 561c665a3..c670f01f6 100644 --- a/mctr2/mctr/MainController.h +++ b/mctr2/mctr/MainController.h @@ -63,7 +63,7 @@ enum mc_state_enum { MC_INACTIVE, MC_LISTENING, MC_LISTENING_CONFIGURED, MC_HC_CONNECTED, MC_CONFIGURING, MC_ACTIVE, MC_SHUTDOWN, MC_CREATING_MTC, MC_READY, MC_TERMINATING_MTC, MC_EXECUTING_CONTROL, MC_EXECUTING_TESTCASE, - MC_TERMINATING_TESTCASE, MC_PAUSED + MC_TERMINATING_TESTCASE, MC_PAUSED, MC_RECONFIGURING }; /** Data structure for unknown incoming connections (before receiving @@ -164,7 +164,7 @@ enum tc_state_enum { TC_INITIAL, TC_IDLE, TC_CREATE, TC_START, TC_STOP, TC_KILL, MTC_CONTROLPART, MTC_TESTCASE, MTC_ALL_COMPONENT_STOP, MTC_ALL_COMPONENT_KILL, MTC_TERMINATING_TESTCASE, MTC_PAUSED, PTC_FUNCTION, PTC_STARTING, PTC_STOPPED, PTC_KILLING, PTC_STOPPING_KILLING, - PTC_STALE, TC_SYSTEM }; + PTC_STALE, TC_SYSTEM, MTC_CONFIGURING }; /** Data structure for each TC */ struct component_struct { @@ -345,6 +345,7 @@ class MainController { static boolean is_hc_in_state(hc_state_enum checked_state); static boolean all_hc_in_state(hc_state_enum checked_state); static void configure_host(host_struct *host, boolean should_notify); + static void configure_mtc(); static void check_all_hc_configured(); static void add_component_to_host(host_struct *host, component_struct *comp); @@ -544,6 +545,7 @@ private: static void send_ptc_verdict(boolean continue_execution); static void send_continue(); static void send_exit_mtc(); + static void send_configure_mtc(const char *config_file); /** Messages to PTCs */ static void send_cancel_done_ptc(component_struct *tc, @@ -607,6 +609,8 @@ private: static void process_testcase_started(); static void process_testcase_finished(); static void process_mtc_ready(); + static void process_configure_ack_mtc(); + static void process_configure_nak_mtc(); /* Incoming messages from PTCs */ static void process_stopped(component_struct *tc, int message_end); @@ -629,6 +633,7 @@ public: static void shutdown_session(); static void configure(const char *config_file); + static bool start_reconfiguring(); static void create_mtc(int host_index); static void exit_mtc(); diff --git a/usrguide/userguide.doc b/usrguide/userguide.doc index a440e5d62ec66b7e9d4ddd52d11b68c603476180..430e53215fdb5c788ae9dae09b4307ef7ff58387 100644 GIT binary patch delta 16436 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 Wq?FXG#EjJR#2!7($9C&aYX1+!i^4Ae delta 17478 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 -- GitLab