frontend/common/new_server_instance_wizard.cpp (1,186 lines of code) (raw):
/*
* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is designed to work with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms, as
* designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have either included with
* the program or referenced in the documentation.
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "grtdb/db_helpers.h"
#include "base/string_utilities.h"
#include "new_server_instance_wizard.h"
#include "grt/grt_manager.h"
#include "grtui/grtdb_connection_editor.h"
#include "mforms/uistyle.h"
#include "base/log.h"
DEFAULT_LOG_DOMAIN(DOMAIN_WB_CONTEXT_UI)
#define INTRO_TEXT \
"This wizard will guide you through the creation of a Server Profile to manage a MySQL server. " \
"To fully support management of a remote MySQL server, an SSH daemon must be running " \
"on the target machine. Alternatively, if you are going to manage a Windows server from a " \
"Windows computer, you can also use native Windows management tools. " \
"Remote management is used to start and stop a server and do server configuration. " \
"You may create a Profile without remote management if you do not need that functionality."
static struct {
const char *pattern;
const char *os_name;
} platform_strings[] = {
{"apple-darwin", "macOS"}, // For macOS there's an additional check.
{"-linux", "Linux"},
{"win64", "Windows"}, {"win32", "Windows"},
{NULL, NULL},
};
using namespace base;
using namespace mforms;
//----------------- NewServerInstancePage ----------------------------------------------------------
NewServerInstancePage::NewServerInstancePage(WizardForm *form, const std::string &pageid) : WizardPage(form, pageid) {
}
//--------------------------------------------------------------------------------------------------
NewServerInstanceWizard *NewServerInstancePage::wizard() {
return dynamic_cast<NewServerInstanceWizard *>(_form);
}
//----------------- IntroductionPage ---------------------------------------------------------------
IntroductionPage::IntroductionPage(WizardForm *form) : WizardPage(form, "introduction-page") {
set_short_title(_("Introduction"));
set_title(_("Introduction"));
mforms::Label *text = mforms::manage(new mforms::Label());
text->set_text(
_("This dialog will help you to set up remote management for your connection. At the start "
"a connection attempt is made to determine server version and operating system of the target "
"machine. This allows you to validate the connection settings and allows the wizard to pick "
"a meaningful configuration preset. If this attempt fails you can still continue, however.\n\n"
"Continue to the next page to start the connection. This might take a few moments."));
text->set_wrap_text(true);
add(text, false, true);
}
//----------------- TestDatabaseSettingsPage -------------------------------------------------------
TestDatabaseSettingsPage::TestDatabaseSettingsPage(WizardForm *host)
: WizardProgressPage(host, "test database settings page", true) {
set_short_title(_("Test DB Connection"));
set_title(_("Testing the Database Connection"));
set_heading(
_("The database connection information is being tested. This might take a few "
"moments depending on your network connection."));
add_task(_("Open Database Connection"), std::bind(&TestDatabaseSettingsPage::open_connection, this),
_("Connecting to database server..."));
add_task(_("Get Server Version"), std::bind(&TestDatabaseSettingsPage::get_server_version, this),
_("Querying server version..."));
add_task(_("Get Server OS"), std::bind(&TestDatabaseSettingsPage::get_server_platform, this),
_("Querying server OS type..."));
/*XXX Improve rights checks.
add_task(_("Check Account Privileges"),
std::bind(&TestDatabaseSettingsPage::get_server_platform, this),
_("Checking if account being used has enough privileges..."));
- check the privs the account has and warn if it can't do something:
- creating and modifying user accts
- querying for tables
- check if it has global schema rights and if not, warn dumps will only work for databases it has access to
- other stuff
*/
end_adding_tasks(_("Database connection tested successfully."));
set_status_text("");
}
//--------------------------------------------------------------------------------------------------
bool TestDatabaseSettingsPage::open_connection() {
try {
db_mgmt_ConnectionRef conn(db_mgmt_ConnectionRef::cast_from(values().get("connection")));
add_log_text(strfmt("Connecting to MySQL server %s...", conn->name().c_str()));
_dbc_conn = sql::DriverManager::getDriverManager()->getConnection(conn);
add_log_text("Connected.");
} catch (std::exception &exc) {
_message = exc.what();
add_log_text(_message.c_str());
throw;
}
return true;
}
//--------------------------------------------------------------------------------------------------
void TestDatabaseSettingsPage::tasks_finished(bool success) {
if (!success)
set_status_text(
strfmt("Could not connect to MySQL server:\n %s\nYou may continue if the server is simply not running.",
_message.c_str()),
true);
}
//--------------------------------------------------------------------------------------------------
bool TestDatabaseSettingsPage::get_server_version() {
sql::Statement *pstmt = _dbc_conn->createStatement();
sql::ResultSet *res = pstmt->executeQuery("SELECT VERSION() as VERSION");
std::string version;
if (res && res->next()) {
version = res->getString("VERSION");
}
delete res;
delete pstmt;
if (version.empty()) {
current_task()->label.set_text("Server Version: unknown");
throw std::runtime_error("Error querying version of MySQL server");
}
values().gset("server_version", version);
current_task()->label.set_text("Server Version: " + version);
add_log_text(strfmt("MySQL server version is %s", version.c_str()));
// check that we're connecting to a known and supported version of the server
if (!bec::is_supported_mysql_version(version)) {
current_task()->label.set_text("Get Server Version: Unsupported Server Version");
std::string msg = strfmt(
"Unknown/unsupported server version or connection protocol detected (%s).\nMySQL Workbench is developed and "
"tested for MySQL Server versions 5.6 and newer.\nA connection can be established but some MySQL Workbench "
"features may not work properly.\nFor MySQL Server version older than 5.6, please use MySQL Workbench 6.3.",
version.c_str());
add_log_text(msg);
throw std::runtime_error(msg);
}
return true;
}
//--------------------------------------------------------------------------------------------------
/**
* This functions attempts to find a clue on which OS this server is running by examining
* on which is was built. There's usually a good correlation, even though it may not be very precise.
*/
bool TestDatabaseSettingsPage::get_server_platform() {
sql::Statement *pstmt = _dbc_conn->createStatement();
sql::ResultSet *res = pstmt->executeQuery("SHOW VARIABLES LIKE 'version_compile_%'");
std::string name, value;
std::string machine, os;
while (res && res->next()) {
name = res->getString("Variable_name");
value = res->getString("Value");
if (name == "version_compile_machine")
machine = value;
if (name == "version_compile_os")
os = value;
}
delete res;
delete pstmt;
_dbc_conn.reset();
os = base::tolower(os);
std::string os_type = "";
if (base::hasPrefix(os, "macos"))
os_type = "macOS";
if (os.empty()) {
for (int i = 0; platform_strings[i].pattern; i++) {
if (strstr(os.c_str(), platform_strings[i].pattern)) {
os_type = platform_strings[i].os_name;
values().gset("detected_os_type", os_type);
break;
}
}
}
if (os_type.empty())
os_type = "unknown";
current_task()->label.set_text("Server OS: " + os_type);
add_log_text(strfmt("MySQL server architecture is %s", machine.empty() ? "unknown" : machine.c_str()));
add_log_text(strfmt("MySQL server OS is %s", os.empty() ? "unknown" : os.c_str()));
return true;
}
//--------------------------------------------------------------------------------------------------
void TestDatabaseSettingsPage::enter(bool advancing) {
if (advancing) {
values().remove("server_version");
values().remove("detected_os_type");
}
WizardProgressPage::enter(advancing);
}
//--------------------------------------------------------------------------------------------------
NewServerInstanceWizard *TestDatabaseSettingsPage::wizard() {
return dynamic_cast<NewServerInstanceWizard *>(_form);
}
//----------------- HostAndRemoteTypePage ----------------------------------------------------------
HostAndRemoteTypePage::HostAndRemoteTypePage(WizardForm *host)
: NewServerInstancePage(host, "os + remote page"),
_management_type_panel(TitledBoxPanel),
_management_type_box(false),
_os_panel(TitledBoxPanel),
_os_box(false),
_win_remote_admin(RadioButton::new_id()),
_ssh_remote_admin(_win_remote_admin.group_id()) {
set_short_title(_("Management and OS"));
// Remote management.
_management_type_panel.set_title(_("Select the type of remote management you want to use:"));
_management_type_panel.add(&_management_type_box);
_win_remote_admin.set_text(_("Native Windows remote management (only available on Windows)"));
scoped_connect(_win_remote_admin.signal_clicked(), std::bind(&HostAndRemoteTypePage::toggle_remote_admin, this));
#ifndef _MSC_VER
_win_remote_admin.set_enabled(false);
#endif
_ssh_remote_admin.set_text(_("SSH login based management"));
scoped_connect(_ssh_remote_admin.signal_clicked(), std::bind(&HostAndRemoteTypePage::toggle_remote_admin, this));
_management_type_box.add(&_win_remote_admin, false, true);
_management_type_box.add(&_ssh_remote_admin, false, true);
#ifdef _MSC_VER
_win_remote_admin.set_active(true);
#else
_ssh_remote_admin.set_active(true);
#endif
_management_type_box.set_spacing(8);
_management_type_box.set_padding(10);
add(&_management_type_panel, false, true);
// OS selection.
_os_panel.set_title(_("Operating System Selection"));
_os_panel.add(&_os_box);
_os_description.set_wrap_text(true);
_os_description.set_text(
_("Select the operating system and the type of database installation "
"on the target machine. If you configure a Linux target and you are unsure about the type of "
"database installation select the (Vendor Package) variant. If your specific operating system is not in this "
"list, select "
"a related variant. It can later be customized, if needed."));
_os_box.add(&_os_description, false, true);
_params.set_row_count(2);
_params.set_column_count(2);
_params.set_row_spacing(MF_TABLE_ROW_SPACING);
_params.set_column_spacing(MF_TABLE_COLUMN_SPACING);
_os_label.set_text_align(mforms::MiddleRight);
_os_label.set_text(_("Operating System:"));
_params.add(&_os_label, 0, 1, 0, 1, mforms::HFillFlag);
_params.add(&_os_selector, 1, 2, 0, 1, mforms::HFillFlag | mforms::HExpandFlag);
scoped_connect(_os_selector.signal_changed(), std::bind(&HostAndRemoteTypePage::refresh_profile_list, this));
_type_label.set_text_align(mforms::MiddleRight);
_type_label.set_text(_("MySQL Installation Type:"));
_params.add(&_type_label, 0, 1, 1, 2, mforms::HFillFlag);
_params.add(&_type_selector, 1, 2, 1, 2, mforms::HFillFlag | mforms::HExpandFlag);
_os_box.add(&_params, true, true);
_os_box.set_spacing(10);
_os_panel.set_padding(8);
add(&_os_panel, false, true);
}
//--------------------------------------------------------------------------------------------------
void HostAndRemoteTypePage::enter(bool advancing) {
if (!advancing)
return;
// force check of admin type
toggle_remote_admin();
if (wizard()->is_local())
set_title(_("Specify the installation type for your target operation system"));
else
set_title(_("Specify remote management type and target operation system"));
int last_selected = _os_selector.get_selected_index();
suspend_layout();
_os_selector.suspend_layout();
// Refresh platform list on each enter. This way a change on disk can be reflected without
// restarting the wizard.
std::string path = bec::GRTManager::get()->get_data_file_path("mysql.profiles");
GDir *dir = g_dir_open(path.c_str(), 0, NULL);
if (dir) {
const gchar *file;
_presets.clear();
while ((file = g_dir_read_name(dir))) {
if (g_str_has_suffix(file, ".xml")) {
std::string fname = std::string(file, strlen(file) - 4);
std::string label = base::replaceString(fname, "_", " ");
grt::DictRef dict;
try {
dict = grt::DictRef::cast_from(grt::GRT::get()->unserialize(path + "/" + file));
} catch (std::exception &exc) {
logWarning("Profile %s contains invalid data: %s\n", path.c_str(), exc.what());
continue;
}
_presets[dict.get_string("sys.system")].push_back(std::make_pair(label, path + "/" + file));
}
}
g_dir_close(dir);
} else {
logError("Opening profiles folder failed.");
}
// we need to sort the preset list
for (std::map<std::string, std::vector<std::pair<std::string, std::string> > >::const_iterator it = _presets.begin();
it != _presets.end(); it++) {
std::sort(_presets[it->first].begin(), _presets[it->first].end());
}
std::string detected_os_type = values().get_string("detected_os_type");
if (wizard()->is_local()) {
_management_type_panel.show(false);
if (detected_os_type.empty()) {
#ifdef _MSC_VER
detected_os_type = "Windows";
#elif defined(__APPLE__)
detected_os_type = "macOS";
#else
detected_os_type = "Linux";
#endif
}
} else
_management_type_panel.show(true);
_os_selector.clear();
int i = 0;
for (std::map<std::string, std::vector<std::pair<std::string, std::string> > >::const_iterator it = _presets.begin();
it != _presets.end(); ++it, ++i) {
_os_selector.add_item(it->first);
if (advancing) {
if (detected_os_type == it->first)
last_selected = i;
}
}
if (last_selected < 0)
last_selected = 0;
_os_selector.set_selected(last_selected);
_os_selector.resume_layout();
resume_layout();
refresh_profile_list();
}
//--------------------------------------------------------------------------------------------------
void HostAndRemoteTypePage::refresh_profile_list() {
wizard()->clear_problem();
std::string system = _os_selector.get_string_value();
_type_selector.clear();
std::list<std::string> profiles;
for (std::vector<std::pair<std::string, std::string> >::const_iterator iter = _presets[system].begin();
iter != _presets[system].end(); ++iter)
profiles.push_back(iter->first);
_type_selector.add_items(profiles);
}
//--------------------------------------------------------------------------------------------------
void HostAndRemoteTypePage::toggle_remote_admin() {
wizard()->clear_problem();
std::string detected_os_type = values().get_string("detected_os_type");
bool refresh_profiles = false;
// Hard code Windows for remote Windows management. There is also no profile to choose from
// as we base the config file location on the managed service.
if (_win_remote_admin.get_active() && !wizard()->is_local()) {
detected_os_type = "Windows";
_os_panel.show(false);
_type_selector.set_selected(-1);
} else {
refresh_profiles = true;
_os_panel.show(true);
_os_panel.relayout();
if (detected_os_type.empty() && wizard()->is_local()) {
#ifdef _MSC_VER
detected_os_type = "Windows";
#elif defined(__APPLE__)
detected_os_type = "macOS";
#else
detected_os_type = "Linux";
#endif
}
int i = 0;
for (std::map<std::string, std::vector<std::pair<std::string, std::string> > >::const_iterator
it = _presets.begin();
it != _presets.end(); ++it, ++i) {
if (detected_os_type == it->first) {
if (_os_selector.get_selected_index() != i) {
_os_selector.set_selected(i);
if (refresh_profiles)
refresh_profile_list();
}
break;
}
}
}
}
//--------------------------------------------------------------------------------------------------
bool HostAndRemoteTypePage::advance() {
std::string system = _os_selector.get_string_value();
values().gset("os", system);
bool need_templates = false;
if (wizard()->is_local()) {
values().gset("remoteAdmin", 0);
#ifdef _MSC_VER
values().gset("windowsAdmin", 1);
#else
need_templates = true;
values().remove("windowsAdmin");
#endif
} else {
if (_ssh_remote_admin.get_active()) {
need_templates = true;
values().remove("windowsAdmin");
values().gset("remoteAdmin", 1);
} else {
values().gset("windowsAdmin", 1);
values().gset("remoteAdmin", 0);
}
}
if (need_templates) {
int selected_index = _type_selector.get_selected_index();
if (selected_index == -1) {
wizard()->set_problem(_("MySQL installation type not selected"));
return false;
}
values().gset("template_path", _presets[system][selected_index].second);
values().gset("template", _presets[system][selected_index].first);
}
wizard()->load_defaults();
return true;
}
//--------------------------------------------------------------------------------------------------
bool HostAndRemoteTypePage::skip_page() {
// Skip this page if this is a local Windows installation.
#ifdef _MSC_VER
if (wizard()->is_local()) {
values().gset("remoteAdmin", 0);
values().gset("windowsAdmin", 1);
values().remove("template_path");
values().remove("template");
values().gset("os", "Windows");
return true;
}
return false;
#else
return false;
#endif
}
//----------------- SSHManagementPage --------------------------------------------------------------
SSHConfigurationPage::SSHConfigurationPage(WizardForm *host)
: NewServerInstancePage(host, "ssh configuration page"), _indent(false) {
set_short_title(_("SSH Configuration"));
set_title(_("Set remote SSH configuration parameters"));
set_spacing(10);
_main_description1.set_wrap_text(true);
_main_description1.set_text(
_("In order to remotely configure this database instance an SSH account on this "
"host with appropriate privileges is required. This account needs write access "
"to the database configuration file, read access to the database logs and "
"privileges to start/stop the database service/daemon."));
add(&_main_description1, false, true);
_ssh_settings_table.set_row_count(6);
_ssh_settings_table.set_row_spacing(5);
_ssh_settings_table.set_column_count(5);
_ssh_settings_table.set_column_spacing(5);
_indent.set_size(20, -1);
_ssh_settings_table.add(&_indent, 0, 1, 0, 1, HFillFlag);
_host_name_label.set_text(_("Host Name:"));
_ssh_settings_table.add(&_host_name_label, 1, 2, 0, 1, HFillFlag);
_ssh_settings_table.add(&_host_name, 2, 3, 0, 1, HFillFlag | HExpandFlag);
_port_label.set_text(_("Port:"));
_ssh_settings_table.add(&_port_label, 3, 4, 0, 1, HFillFlag);
_port.set_size(50, -1);
_port.set_value("22");
_ssh_settings_table.add(&_port, 4, 5, 0, 1, HFillFlag);
_username_label.set_text(_("User Name:"));
_ssh_settings_table.add(&_username_label, 1, 2, 1, 2, HFillFlag);
_ssh_settings_table.add(&_username, 2, 3, 1, 2, HFillFlag | HExpandFlag);
_use_ssh_key.set_text(_("Authenticate Using SSH Key"));
scoped_connect(_use_ssh_key.signal_clicked(), std::bind(&SSHConfigurationPage::use_ssh_key_changed, this));
_ssh_settings_table.add(&_use_ssh_key, 1, 5, 4, 5, HFillFlag);
_ssh_path_label.set_text(_("SSH Private Key Path:"));
_ssh_settings_table.add(&_ssh_path_label, 1, 2, 5, 6, HFillFlag);
_ssh_settings_table.add(&_ssh_key_path, 2, 3, 5, 6, HFillFlag | HExpandFlag);
_ssh_settings_table.add(&_ssh_key_browse_button, 3, 4, 5, 6, 0);
_file_selector = mforms::manage(new FsObjectSelector(&_ssh_key_browse_button, &_ssh_key_path));
std::string homedir =
#ifdef _MSC_VER
mforms::Utilities::get_special_folder(mforms::ApplicationData);
#else
"~";
#endif
_file_selector->initialize(homedir + "/.ssh/id_rsa", mforms::OpenFile, "", true,
std::bind(&WizardPage::validate, this));
use_ssh_key_changed();
add(&_ssh_settings_table, false, true);
}
//--------------------------------------------------------------------------------------------------
void SSHConfigurationPage::use_ssh_key_changed() {
bool value = _use_ssh_key.get_active();
_ssh_path_label.set_enabled(value);
_ssh_key_path.set_enabled(value);
_ssh_key_browse_button.set_enabled(value);
}
//--------------------------------------------------------------------------------------------------
void SSHConfigurationPage::enter(bool advancing) {
if (advancing) {
_host_name.set_value(values().get_string("host_name"));
std::string value = values().get_string("ssh_user_name");
if (value.empty() && g_get_user_name() != NULL)
value = g_get_user_name();
_username.set_value(value.empty() ? "" : value);
value = values().get_string("ssh_port");
if (!value.empty())
_port.set_value(value);
value = values().get_string("ssh_key_path");
if (!value.empty()) {
_use_ssh_key.set_active(true);
use_ssh_key_changed();
_file_selector->set_filename(value);
}
}
}
//--------------------------------------------------------------------------------------------------
bool SSHConfigurationPage::advance() {
db_mgmt_ServerInstanceRef instance(wizard()->assemble_server_instance());
// Check if we have valid SSH credentials.
std::string value = base::trim(_host_name.get_string_value());
if (value.empty()) {
Utilities::show_error(_("SSH Host Needed"), _("Please specify the host name or address."), _("OK"));
return false;
}
value = base::trim(_username.get_string_value());
if (value.empty()) {
Utilities::show_error(_("SSH User Name Needed"), _("Please specify the user name for the SSH account to be used."),
_("OK"));
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
void SSHConfigurationPage::leave(bool advancing) {
if (advancing) {
values().gset("host_name", _host_name.get_string_value());
values().gset("ssh_port", _port.get_string_value());
values().gset("ssh_user_name", _username.get_string_value());
if (_use_ssh_key.get_active())
values().gset("ssh_key_path", _ssh_key_path.get_string_value());
else
values().remove("ssh_key_path");
}
}
//--------------------------------------------------------------------------------------------------
bool SSHConfigurationPage::skip_page() {
return values().get_int("remoteAdmin", 0) != 1;
}
//----------------- WindowsManagementPage ----------------------------------------------------------
WindowsManagementPage::WindowsManagementPage(WizardForm *host, wb::WBContext *context)
: NewServerInstancePage(host, "windows management page"), _indent(false) {
_context = context;
set_short_title(_("Windows Management"));
// set_spacing(10);
_layout_table.set_row_count(6);
_layout_table.set_column_count(5);
_layout_table.set_row_spacing(6);
_layout_table.set_column_spacing(4);
_indent.set_size(10, -1);
_layout_table.add(&_indent, 0, 1, 1, 2, 0);
_main_description1.set_wrap_text(true);
_main_description1.set_text(
_("Remote Windows management requires a user account on the remote machine "
"which is allowed to connect remotely and also has the required privileges to query system status and "
"to control services. For configuration file manipulation read/write access is needed to the file. "
"Depending on your environment several ways of accessing that file are possible.\n\nExamples are mapped drives, "
"network shares and administrative shares:"));
_layout_table.add(&_main_description1, 0, 4, 0, 1, HFillFlag | VFillFlag);
_main_description2.set_wrap_text(true);
_main_description2.set_style(BoldStyle);
_main_description2.set_text(
_("M:\\<path to file>\\my.ini\n"
"\\\\<server>\\<share>\\<path to file>\\my.ini\n"
"\\\\<server>\\C$\\Program Files\\MySQL\\MySQL Server 5.1\\my.ini\n"));
_layout_table.add(&_main_description2, 1, 4, 1, 2, HFillFlag | VFillFlag);
_progress_label.set_text(_("Initializing WMI, please wait..."));
_layout_table.add(&_progress_label, 0, 4, 2, 3, HFillFlag | VFillFlag);
_service_label.set_wrap_text(true);
_service_label.set_text(
_("Select the service to manage from the list below. It will also help to "
"find the configuration file."));
_layout_table.add(&_service_label, 0, 4, 3, 4, HFillFlag | VFillFlag);
scoped_connect(_service_selector.signal_changed(), std::bind(&WindowsManagementPage::refresh_config_path, this));
_layout_table.add(&_service_selector, 1, 4, 4, 5, HFillFlag);
_config_path_label.set_text(_("Path to Configuration File:"));
_config_path_label.set_text_align(MiddleRight);
_layout_table.add(&_config_path_label, 1, 2, 5, 6, HFillFlag);
_layout_table.add(&_config_path, 2, 4, 5, 6, HFillFlag | HExpandFlag);
_layout_table.add(&_browse_button, 4, 5, 5, 6, HFillFlag);
// Setup for configuration file browsing.
_file_selector = mforms::manage(new FsObjectSelector(&_browse_button, &_config_path));
_file_selector->initialize("", mforms::OpenFile, "", true, std::bind(&WizardPage::validate, this));
add(&_layout_table, false, true);
}
//--------------------------------------------------------------------------------------------------
void WindowsManagementPage::refresh_config_path() {
if (_service_selector.get_selected_index() >= 0 && _service_selector.get_selected_index() < (int)_config_paths.size())
_config_path.set_value(_config_paths[_service_selector.get_selected_index()]);
else
_config_path.set_value("");
}
//--------------------------------------------------------------------------------------------------
void WindowsManagementPage::leave(bool advancing) {
// If we're going back, reset the progress label. We can't set it right before
// performing the slow operation, because it blocks the UI.
if (!advancing) {
_progress_label.set_text(_("Initializing WMI, please wait..."));
}
}
//--------------------------------------------------------------------------------------------------
void WindowsManagementPage::enter(bool advancing) {
if (advancing) {
wizard()->clear_problem();
_config_paths.clear();
_service_names.clear();
_service_selector.clear();
std::string host = values().get_string("host_name");
std::string user;
std::string password;
bool local_connection = wizard()->is_local();
if (!local_connection) {
set_title(_("Set remote Windows configuration parameters for host " + host));
_main_description1.set_text(
_("Remote Windows management requires a user account on the remote machine "
"which is allowed to connect remotely and also has the required privileges to query system status and "
"to control services. For configuration file manipulation read/write access is needed to the file. "
"Depending on your environment several ways of accessing that file are possible.\n\nExamples are mapped "
"drives, "
"network shares and administrative shares:"));
_main_description2.show(true);
user = values().get_string("wmi_user_name", "");
std::string title = strfmt(_("Remote Windows Login (%s)"), host.c_str()) + "|";
title += _("Please enter a Windows user login and password for the remote server with rights to WMI");
bool result = false;
try {
result = Utilities::credentials_for_service(title, "wmi@" + host, user, false, password);
} catch (std::exception &exc) {
logWarning("Exception caught when clearning the password: %s\n", exc.what());
mforms::Utilities::show_error("Clear Password", base::strfmt("Could not clear password: %s", exc.what()), "OK");
}
if (!result) {
_progress_label.set_text(_("Need valid user credentials to connect to server."));
wizard()->set_problem(_("Need valid user credentials to connect to server."));
return;
}
values().gset("wmi_user_name", user);
} else {
host = ""; // Empty host name for local machine.
_main_description1.set_text(
_("Windows management requires a user account on this machine "
"which has the required privileges to query system status and to control services. "
"For configuration file manipulation read/write access to the file is needed. "));
_main_description2.show(false);
set_title(_("Set Windows configuration parameters for this machine"));
}
grt::Module *module = grt::GRT::get()->get_module("Workbench");
try {
grt::ValueRef wmi_session;
{
grt::StringListRef arguments(grt::Initialized);
arguments.ginsert(grt::StringRef(host));
arguments.ginsert(grt::StringRef(user));
arguments.ginsert(grt::StringRef(password));
// This can take a few seconds.
wmi_session = module->call_function("wmiOpenSession", arguments);
}
grt::ValueRef wmi_result;
{
grt::BaseListRef arguments(true);
arguments.ginsert(wmi_session);
arguments.ginsert(
grt::StringRef("select * from Win32_Service where (Name like \"%mysql%\" or DisplayName like \"%mysql%\")"));
wmi_result = module->call_function("wmiQuery", arguments);
module->call_function("wmiCloseSession", arguments); // Only the first parameter of arguments is used here.
}
grt::DictListRef entries = grt::DictListRef::cast_from(wmi_result);
size_t count = entries->count();
if (count > 0) {
for (size_t i = 0; i < count; i++) {
grt::DictRef entry(entries[i]);
std::string service_name = entry.get_string("Name", "invalid");
std::string service_display_name = entry.get_string("DisplayName", "invalid");
std::string path = entry.get_string("PathName", "invalid");
std::string state = entry.get_string("State", "unknown");
std::string start_mode = entry.get_string("StartMode", "unknown");
std::string config_file = base::extract_option_from_command_line("--defaults-file", path);
if (!local_connection) {
if (config_file.empty())
config_file = "C$\\";
config_file = "\\\\" + host + "\\" + config_file;
base::replaceStringInplace(config_file, ":", "$");
}
_config_paths.push_back(config_file);
_service_names.push_back(service_name);
std::string selector_text = service_display_name + " (" + state + ", Start mode: " + start_mode + ")";
_service_selector.add_item(selector_text);
}
_progress_label.set_text("");
} else {
_progress_label.set_text("No MySQL service found.");
wizard()->set_problem(
_("In order to manage a MySQL server it must be installed as a service. "
"The wizard could not find any MySQL service on the target machine, hence the server instance "
"cannot be created."));
}
refresh_config_path();
} catch (std::runtime_error &e) {
_progress_label.set_text(base::strfmt(_("Could not set up connection: %s"), e.what()));
wizard()->set_problem(e.what());
// In case this error was caused by wrong credentials (we cannot be sure from the error message)
// we remove the already stored password to make it possible to (re) enter the current one.
try {
Utilities::forget_password("wmi@" + host, user);
} catch (std::exception &exc) {
logWarning("Exception caught when clearning the password: %s\n", exc.what());
mforms::Utilities::show_error("Clear Password", base::strfmt("Could not clear password: %s", exc.what()), "OK");
}
values().gset("wmi_user_name", "");
}
}
}
//--------------------------------------------------------------------------------------------------
bool WindowsManagementPage::advance() {
if (_service_names.size() == 0 || _service_selector.get_selected_index() < 0)
return false;
values().gset("ini_path", _config_path.get_string_value());
values().gset("ini_section", "mysqld");
values().gset("service_name", _service_names[_service_selector.get_selected_index()]);
return true;
}
//--------------------------------------------------------------------------------------------------
bool WindowsManagementPage::skip_page() {
// Provide native Windows (WMI) management for local and remote Windows boxes.
// Remote Windows boxes which are managed via SSH use the SSH config page instead, though.
bool local_wmi;
#ifdef _MSC_VER
local_wmi = true;
#else
local_wmi = false;
#endif
bool remote_wmi = values().get_int("windowsAdmin", 0) != 0;
if (dynamic_cast<NewServerInstanceWizard *>(_form)->is_local())
return !local_wmi;
else
return !remote_wmi;
}
//----------------- TestHostMachineSettingsPage ----------------------------------------------------
TestHostMachineSettingsPage::TestHostMachineSettingsPage(WizardForm *host)
: WizardProgressPage(host, "test host machine settings page", true) {
set_short_title(_("Test Settings"));
set_title(_("Testing Host Machine Settings"));
set_heading(
_("The connection to the host machine is being tested. This might take a few "
"moments depending on your network connection."));
_connect_task = add_task(_("Connect to host machine"), std::bind(&TestHostMachineSettingsPage::connect_to_host, this),
_("Trying to find host machine and connecting to it..."));
_commands_task = add_async_task(_("Check location of start/stop commands"),
std::bind(&TestHostMachineSettingsPage::check_admin_commands, this),
_("Checking if commands to start and stop server are in the expected location..."));
add_async_task(_("Check MySQL configuration file"), std::bind(&TestHostMachineSettingsPage::find_config_file, this),
_("Looking for the configuration file of the database server..."));
end_adding_tasks(_("Testing host machine settings is done."));
set_status_text("");
}
//--------------------------------------------------------------------------------------------------
void TestHostMachineSettingsPage::enter(bool advance) {
reset_tasks();
db_mgmt_ServerInstanceRef instance(wizard()->assemble_server_instance());
_connect_task->set_enabled(values().get_int("remoteAdmin", 0) == 1);
_commands_task->set_enabled(values().get_int("windowsAdmin", 0) == 0);
WizardProgressPage::enter(advance);
}
//--------------------------------------------------------------------------------------------------
bool TestHostMachineSettingsPage::connect_to_host() {
// This will require the ssh or SSH key password, so it needs to be called from main thread.
wizard()->test_setting_grt("connect_to_host");
return true;
}
//--------------------------------------------------------------------------------------------------
bool TestHostMachineSettingsPage::find_config_file() {
// Native remote Windows management uses a direct URI for the files.
bool use_local = wizard()->is_local() || values().get_int("windowsAdmin", 0) == 1;
execute_grt_task(std::bind(&NewServerInstanceWizard::test_setting_grt, wizard(),
use_local ? "find_config_file/local" : "find_config_file"),
false);
return true;
}
//--------------------------------------------------------------------------------------------------
bool TestHostMachineSettingsPage::find_error_files() {
bool use_local = wizard()->is_local() || values().get_int("windowsAdmin", 0) == 1;
execute_grt_task(std::bind(&NewServerInstanceWizard::test_setting_grt, wizard(),
use_local ? "find_error_files/local" : "find_error_files"),
false);
return true;
}
//--------------------------------------------------------------------------------------------------
bool TestHostMachineSettingsPage::check_admin_commands() {
execute_grt_task(std::bind(&NewServerInstanceWizard::test_setting_grt, wizard(),
wizard()->is_local() ? "check_admin_commands/local" : "check_admin_commands"),
false);
return true;
}
//--------------------------------------------------------------------------------------------------
bool TestHostMachineSettingsPage::skip_page() {
return !(wizard()->is_admin_enabled());
}
//--------------------------------------------------------------------------------------------------
void TestHostMachineSettingsPage::tasks_finished(bool success) {
values().gset("host_tests_succeeded", success);
}
//--------------------------------------------------------------------------------------------------
void TestHostMachineSettingsPage::leave(bool advancing) {
if (advancing) {
bool require_review = false;
if (values().get_int("host_tests_succeeded") == 1) {
require_review = Utilities::show_message(
_("Review settings"),
_("Checks succeeded for Connection and Configuration Settings for this new Server Instance."),
_("Continue"), "", _("I'd like to review the settings again")) == mforms::ResultOther;
} else
require_review = true;
values().gset("review_required", require_review);
if (!require_review)
wizard()->create_instance();
}
}
//--------------------------------------------------------------------------------------------------
NewServerInstanceWizard *TestHostMachineSettingsPage::wizard() {
return dynamic_cast<NewServerInstanceWizard *>(_form);
}
//----------------- ReviewPage ----------------------------------------------------------------------
ReviewPage::ReviewPage(WizardForm *host) : NewServerInstancePage(host, "review"), _text(VerticalScrollBar) {
set_short_title(_("Review Settings"));
set_title(_("Review Remote Management Settings"));
_label.set_text(
_("Below is a list of all settings collected so far. This includes also values taken "
"from templates or default values. Check if they match your actual settings and toggle 'Change Parameters' "
"if you need to make any changes to default values. For any other change go back to the appropriate wizard "
"page.\n\n"
"Pay special attention if you run more than one instance of MySQL on the same machine."));
_label.set_wrap_text(true);
_text.set_read_only(true);
add(&_label, false, true);
add(&_text, true, true);
_customize_check.set_text("Change Parameters");
scoped_connect(_customize_check.signal_clicked(), std::bind(&ReviewPage::customize_changed, this));
add(&_customize_check, false, true);
}
//--------------------------------------------------------------------------------------------------
void ReviewPage::customize_changed() {
values().gset("customize", _customize_check.get_active());
wizard()->update_buttons();
}
//--------------------------------------------------------------------------------------------------
void ReviewPage::enter(bool advancing) {
if (advancing) {
std::string summary;
grt::DictRef serverInfo(wizard()->assemble_server_instance()->serverInfo());
bool ssh_management = values().get_int("remoteAdmin") != 0;
bool wmi_management = values().get_int("windowsAdmin") != 0;
std::string host_name = values().get_string("host_name", "localhost");
if (ssh_management) {
std::string ssh_port = values().get_string("ssh_port", "22");
std::string ssh_user_name = values().get_string("ssh_user_name");
std::string ssh_key_path = values().get_string("ssh_key_path");
summary.append(_("SSH Based Adminstration enabled\n"));
summary.append(strfmt(_(" SSH host: %s:%s\n"), host_name.c_str(), ssh_port.c_str()));
summary.append(strfmt(_(" SSH user: %s\n"), ssh_user_name.c_str()));
summary.append(strfmt(_(" SSH key file: %s\n"), ssh_key_path.empty() ? "not set" : ssh_key_path.c_str()));
} else if (wmi_management) {
std::string user_name = values().get_string("wmi_user_name");
std::string service_name = values().get_string("service_name");
summary.append(_("Native Windows Adminstration enabled\n"));
summary.append(strfmt(_(" Windows host: %s\n"), host_name.c_str()));
if (!wizard()->is_local())
summary.append(strfmt(_(" Windows user name: %s\n"), user_name.c_str()));
summary.append(strfmt(_(" MySQL service name: %s\n"), service_name.c_str()));
}
summary.append("\n");
std::string os_title = serverInfo.get_string("sys.system", "Unknown");
std::string ini_path = serverInfo.get_string("sys.config.path");
std::string ini_section = serverInfo.get_string("sys.config.section");
std::string mysql_version = serverInfo.get_string("serverVersion");
summary.append(_("MySQL Configuration\n"));
summary.append(strfmt(_(" MySQL Version: %s\n"), mysql_version.empty() ? "Unknown" : mysql_version.c_str()));
summary.append(strfmt(_(" Settings Template: %s\n"), serverInfo.get_string("sys.preset").c_str()));
summary.append(strfmt(_(" Path to Configuration File: %s\n"), ini_path.c_str()));
summary.append(strfmt(_(" Instance Name in Configuration File: %s\n"), ini_section.c_str()));
summary.append("\n");
if (!wmi_management) {
std::string start = serverInfo.get_string("sys.mysqld.start");
std::string stop = serverInfo.get_string("sys.mysqld.stop");
bool use_sudo = serverInfo.get_int("sys.usesudo") != 0;
summary.append(_("Commands for MySQL Management\n"));
summary.append(strfmt(_(" Start MySQL: %s\n"), start.c_str()));
summary.append(strfmt(_(" Stop MySQL: %s\n"), stop.c_str()));
if (os_title != "Windows")
summary.append(strfmt(_(" Use sudo: %s\n"), use_sudo ? _("Yes") : _("No")));
}
_text.set_value(summary);
}
}
//--------------------------------------------------------------------------------------------------
bool ReviewPage::skip_page() {
return values().get_int("review_required", 0) == 0;
}
//--------------------------------------------------------------------------------------------------
bool ReviewPage::next_closes_wizard() {
return !_customize_check.get_active();
}
//--------------------------------------------------------------------------------------------------
void ReviewPage::leave(bool advancing) {
if (advancing && !_customize_check.get_active())
wizard()->create_instance();
}
//----------------- PathsPage ----------------------------------------------------------------------
PathsPage::PathsPage(WizardForm *host, wb::WBContext *context) : NewServerInstancePage(host, "paths page") {
_context = context;
set_short_title(_("MySQL Config File"));
set_title(_("Information about MySQL configuration"));
set_padding(10);
set_spacing(20);
_description.set_text(
_("In order to manage the settings of the MySQL Server it is necessary to "
"know where its configuration file resides.\n\n"
"The configuration file may consist of several sections, each of them "
"belonging to a different tool or server instance. Hence it is also "
"necessary to know which section belongs to the server we are managing.\n\n"
"Please specify this information below."));
_description.set_wrap_text(true);
add(&_description, false, true);
_content.set_column_count(4);
_content.set_column_spacing(8);
_content.set_row_count(5);
_content.set_row_spacing(8);
_version_label.set_text(_("MySQL Server Version:"));
_version_label.set_text_align(MiddleRight);
_content.add(&_version_label, 0, 1, 0, 1, HFillFlag);
_content.add(&_version, 1, 2, 0, 1, HFillFlag);
_config_path_label.set_text(_("Path to Configuration File:"));
_config_path_label.set_text_align(MiddleRight);
_content.add(&_config_path_label, 0, 1, 1, 2, HFillFlag);
_content.add(&_config_path, 1, 3, 1, 2, HFillFlag);
_content.add(&_browse_button, 3, 4, 1, 2, HFillFlag);
// Setup for local config file browsing. This will be adjusted if we are at a remote location.
_file_selector = mforms::manage(new FsObjectSelector(&_browse_button, &_config_path));
_file_selector->initialize("", mforms::OpenFile, "", true, std::bind(&WizardPage::validate, this));
_test_config_path_button.set_text(_("Check Path"));
scoped_connect(_test_config_path_button.signal_clicked(), std::bind(&PathsPage::test_path, this));
_content.add(&_test_config_path_button, 1, 2, 2, 3, HFillFlag);
_test_config_path_description.set_text(_("Click to test if your path is correct."));
_content.add(&_test_config_path_description, 2, 3, 2, 3, HFillFlag | HExpandFlag);
_section_name_label.set_text(_("Section of the Server Instance:"));
_section_name_label.set_text_align(MiddleRight);
_content.add(&_section_name_label, 0, 1, 3, 4, HFillFlag);
_content.add(&_section_name, 1, 3, 3, 4, HFillFlag);
_test_section_button.set_text(_("Check Name"));
scoped_connect(_test_section_button.signal_clicked(), std::bind(&PathsPage::test_section, this));
_content.add(&_test_section_button, 1, 2, 4, 5, HFillFlag);
_test_section_description.set_text(_("Click to test if your instance name is correct."));
_content.add(&_test_section_description, 2, 3, 4, 5, HFillFlag | HExpandFlag);
add(&_content, true, true);
}
//--------------------------------------------------------------------------------------------------
bool PathsPage::skip_page() {
return !(wizard()->is_admin_enabled()) || !values().get_int("customize");
}
//--------------------------------------------------------------------------------------------------
void PathsPage::enter(bool advancing) {
_test_config_path_description.set_color(base::Color::getSystemColor(base::TextColor).to_html());
_test_config_path_description.set_text(_("Click to test if your path is correct."));
_test_section_description.set_color(base::Color::getSystemColor(base::TextColor).to_html());
_test_section_description.set_text(_("Click to test if your section is correct."));
if (advancing) {
// Prefill values from defaults.
_version.set_value(wizard()->get_server_info("serverVersion"));
_config_path.set_value(wizard()->get_server_info("sys.config.path"));
_section_name.set_value(wizard()->get_server_info("sys.config.section"));
}
bool ssh_management = values().get_int("remoteAdmin", 0) != 0;
if (ssh_management) {
// Setup for remote browsing.
_file_selector->set_browse_callback(std::bind(&PathsPage::browse_remote_config_file, this));
}
}
//--------------------------------------------------------------------------------------------------
bool PathsPage::advance() {
std::string version = base::trim(_version.get_string_value());
int a, b, c;
if (version.empty() || sscanf(version.c_str(), "%i.%i.%i", &a, &b, &c) < 2 || a < 4) {
Utilities::show_error(_("Invalid version"), _("The MySQL server version number provided appears to be invalid."),
_("OK"));
return false;
}
std::string path = base::trim(_config_path.get_string_value());
if (path.empty()) {
Utilities::show_error(_("Empty path"), _("The path to the configuration must not be empty."), _("OK"));
return false;
}
std::string section = base::trim(_section_name.get_string_value());
if (section.empty()) {
Utilities::show_error(_("Empty section"), _("A section must be given which belongs to the given server."), _("OK"));
return false;
}
values().gset("server_version", version);
values().gset("ini_path", path);
values().gset("ini_section", section);
return true;
}
//--------------------------------------------------------------------------------------------------
/**
* Triggers the remote file open dialog.
*/
void PathsPage::browse_remote_config_file() {
db_mgmt_ServerInstanceRef instance(wizard()->assemble_server_instance());
grt::BaseListRef args(true);
args.ginsert(values().get("connection"));
args.ginsert(instance);
try {
grt::StringRef selection =
grt::StringRef::cast_from(grt::GRT::get()->call_module_function("WbAdmin", "openRemoteFileSelector", args));
if (selection.is_valid() && !selection.empty())
_config_path.set_value(selection);
} catch (const std::exception &exc) {
grt::GRT::get()->send_error("Error in remote file browser", exc.what());
}
}
//--------------------------------------------------------------------------------------------------
void PathsPage::test_path() {
std::string detail;
values().gset("ini_path", _config_path.get_string_value());
bool success;
try {
if (values().get_int("windowsAdmin", 0) != 0 || wizard()->is_local())
success = wizard()->test_setting("check_config_path/local", detail);
else
success = wizard()->test_setting("check_config_path", detail);
} catch (std::exception &) {
success = false;
}
if (success) {
_test_config_path_description.set_color("#00A000");
_test_config_path_description.set_text(_("The config file path is valid."));
} else {
_test_config_path_description.set_color("#A00000");
_test_config_path_description.set_text(_("The config file could not be found."));
}
}
//--------------------------------------------------------------------------------------------------
void PathsPage::test_section() {
std::string detail;
values().gset("ini_path", _config_path.get_string_value());
values().gset("ini_section", _section_name.get_string_value());
bool success;
try {
if (values().get_int("windowsAdmin", 0) != 0 || wizard()->is_local())
success = wizard()->test_setting("check_config_section/local", detail);
else
success = wizard()->test_setting("check_config_section", detail);
} catch (std::exception &) {
success = false;
}
if (success) {
_test_section_description.set_color("#00A000");
_test_section_description.set_text(_("The config file section is valid."));
} else {
_test_section_description.set_color("#A00000");
_test_section_description.set_text(_("The config file section is invalid."));
}
}
//----------------- CommandsPage ----------------------------------------------------------------------
CommandsPage::CommandsPage(WizardForm *host) : NewServerInstancePage(host, "commands page") {
set_short_title(_("Specify Commands"));
set_title(_("Specify commands to be used to manage the MySQL server."));
set_spacing(20);
set_padding(8);
_description.set_text(
_("The values on this page comprise rather low level commands, which are used "
"to control the MySQL server instance (start or stop it) and others.\n\n"
"If you are unsure what these values mean leave them untouched. The defaults "
"are usually a good choice already (for single server machines)."));
_description.set_wrap_text(true);
add(&_description, false, true);
_content.set_column_count(2);
_content.set_column_spacing(8);
_content.set_row_count(4);
_content.set_row_spacing(5);
_start_label.set_text(_("Command to start the MySQL server:"));
_start_label.set_text_align(MiddleRight);
_content.add(&_start_label, 0, 1, 0, 1, HFillFlag);
_content.add(&_start_command, 1, 2, 0, 1, HFillFlag | HExpandFlag);
_stop_label.set_text(_("Command to stop the MySQL server:"));
_stop_label.set_text_align(MiddleRight);
_content.add(&_stop_label, 0, 1, 1, 2, HFillFlag);
_content.add(&_stop_command, 1, 2, 1, 2, HFillFlag | HExpandFlag);
_use_sudo.set_text(
_("Check this box if you want or need the above commands \n"
"to be executed with elevated Operating System Privileges."));
_content.add(&_use_sudo, 1, 2, 3, 4, HFillFlag);
add(&_content, false, true);
}
//--------------------------------------------------------------------------------------------------
bool CommandsPage::skip_page() {
return !(wizard()->is_admin_enabled()) || !values().get_int("customize");
}
//--------------------------------------------------------------------------------------------------
void CommandsPage::enter(bool advancing) {
if (advancing) {
// Prefill values from defaults.
_start_command.set_value(wizard()->get_server_info("sys.mysqld.start"));
_stop_command.set_value(wizard()->get_server_info("sys.mysqld.stop"));
_use_sudo.set_active(wizard()->get_server_info("sys.usesudo") != "0");
}
}
//--------------------------------------------------------------------------------------------------
bool CommandsPage::advance() {
values().gset("command_start", base::trim(_start_command.get_string_value()));
values().gset("command_stop", base::trim(_stop_command.get_string_value()));
values().gset("use_sudo", _use_sudo.get_active());
return true;
}
//--------------------------------------------------------------------------------------------------
void CommandsPage::leave(bool advancing) {
if (advancing)
wizard()->create_instance();
}
//----------------- NewServerInstanceWizard ---------------------------------------------------------
NewServerInstanceWizard::NewServerInstanceWizard(wb::WBContext *context, db_mgmt_ConnectionRef connection)
: WizardForm(), _instance(grt::Initialized) {
set_name("New Instance Wizard");
setInternalName("new_instance_wizard");
_context = context;
_connection = connection;
values().set("connection", connection);
if (is_local())
set_title(_("Configure Local Management"));
else
set_title(_("Configure Remote Management"));
// Fill in some values from the connection that are used by the wizard pages.
grt::DictRef parameter_values = _connection->parameterValues();
std::string host = parameter_values.get_string("sshHost"); // SSH tunnel host name.
if (host.empty())
host = parameter_values.get_string("hostName"); // MySQL server host name.
std::vector<std::string> host_parts = base::split(host, ":"); // Separate host name and port.
if (host_parts.size() > 1) {
// We come here usually only for SSH connection.
values().gset("host_name", host_parts[0]);
values().gset("ssh_port", host_parts[1]);
values().gset("ssh_user_name", parameter_values.get_string("sshUserName"));
std::string key_path = parameter_values.get_string("sshKeyFile");
if (!key_path.empty())
values().gset("ssh_key_path", key_path);
} else
values().gset("host_name", host);
// Set up page structure of the wizard.
_introduction_page = new IntroductionPage(this);
add_page(manage(_introduction_page));
_test_database_settings_page = new TestDatabaseSettingsPage(this);
add_page(manage(_test_database_settings_page));
_os_page = new HostAndRemoteTypePage(this);
add_page(manage(_os_page));
_ssh_configuration_page = new SSHConfigurationPage(this);
add_page(manage(_ssh_configuration_page));
_windows_connection_page = new WindowsManagementPage(this, _context);
add_page(manage(_windows_connection_page));
_test_host_machine_settings_page = new TestHostMachineSettingsPage(this);
add_page(manage(_test_host_machine_settings_page));
_review_page = new ReviewPage(this);
add_page(manage(_review_page));
_paths_page = new PathsPage(this, _context);
add_page(manage(_paths_page));
_commands_page = new CommandsPage(this);
add_page(manage(_commands_page));
}
//--------------------------------------------------------------------------------------------------
NewServerInstanceWizard::~NewServerInstanceWizard() {
// Pages are freed by the WizardForm ancestor.
// disconnect the test SSH session.
std::string s;
test_setting("disconnect", s);
}
//--------------------------------------------------------------------------------------------------
/**
* Creates a server instance object from the current values.
*/
db_mgmt_ServerInstanceRef NewServerInstanceWizard::assemble_server_instance() {
db_mgmt_ConnectionRef conn(db_mgmt_ConnectionRef::cast_from(values().get("connection")));
_instance->owner(_context->get_root()->rdbmsMgmt());
std::string os = values().get_string("os");
_instance->serverInfo().gset("sys.system", os);
bool ssh_management = values().get_int("remoteAdmin", 0) != 0;
_instance->serverInfo().gset("remoteAdmin", ssh_management);
if (ssh_management) {
_instance->loginInfo().gset("ssh.userName", values().get_string("ssh_user_name", ""));
_instance->loginInfo().gset("ssh.hostName", values().get_string("host_name", "localhost"));
}
bool win_management = values().get_int("windowsAdmin", 0) != 0;
_instance->serverInfo().gset("windowsAdmin", win_management);
if (win_management) {
_instance->loginInfo().gset("wmi.userName", values().get_string("wmi_user_name", ""));
_instance->loginInfo().gset("wmi.hostName", values().get_string("host_name", "localhost"));
}
std::string instance_name = *conn->name() + " instance";
std::string ssh_port = values().get_string("ssh_port", "22");
std::string ssh_key_path = values().get_string("ssh_key_path");
_instance->name(instance_name);
_instance->loginInfo().gset("ssh.port", ssh_port);
_instance->loginInfo().gset("ssh.useKey", !ssh_key_path.empty());
if (!ssh_key_path.empty())
_instance->loginInfo().gset("ssh.key", ssh_key_path);
std::string version = values().get_string("server_version", "");
if (!version.empty())
_instance->serverInfo().gset("serverVersion", version);
if (values().get_int("customize", 0))
_instance->serverInfo().gset("sys.preset", "Custom");
else
_instance->serverInfo().gset("sys.preset", values().get_string("template"));
if (win_management ||
(get_active_page_number() >= 7 &&
values().get_int("customize"))) // don't overwrite defaults unless user has had time to enter them
{
std::string ini_path = values().get_string("ini_path");
std::string ini_section = values().get_string("ini_section");
std::string service_name = values().get_string("service_name");
_instance->serverInfo().gset("sys.config.path", ini_path);
_instance->serverInfo().gset("sys.config.section", ini_section);
_instance->serverInfo().gset("sys.mysqld.service_name", service_name);
}
if (get_active_page_number() >= 8 &&
values().get_int("customize")) // don't overwrite defaults unless user has had time to enter them
{
std::string start = values().get_string("command_start");
std::string stop = values().get_string("command_stop");
bool use_sudo = values().get_int("use_sudo") != 0;
_instance->serverInfo().gset("sys.mysqld.start", start);
_instance->serverInfo().gset("sys.mysqld.stop", stop);
_instance->serverInfo().gset("sys.usesudo", use_sudo);
}
_instance->connection(db_mgmt_ConnectionRef::cast_from(values().get("connection")));
return _instance;
}
//--------------------------------------------------------------------------------------------------
grt::ValueRef NewServerInstanceWizard::test_setting_grt(const std::string &name) {
std::string detail;
if (!test_setting(name, detail))
throw std::runtime_error(detail);
return grt::ValueRef();
}
//--------------------------------------------------------------------------------------------------
bool NewServerInstanceWizard::test_setting(const std::string &name, std::string &detail) {
grt::Module *module = grt::GRT::get()->get_module("WbAdmin");
if (module) {
grt::BaseListRef args(true);
grt::ValueRef ret;
args.ginsert(grt::StringRef(name));
args.ginsert(values().get("connection"));
args.ginsert(assemble_server_instance());
try {
ret = module->call_function("testInstanceSettingByName", args);
if (ret.is_valid() && grt::StringRef::can_wrap(ret)) {
std::string s = *grt::StringRef::cast_from(ret);
if (g_str_has_prefix(s.c_str(), "OK")) {
if (s.size() > 3 && s[2] == ' ')
detail = s.substr(3);
return true;
}
// ERROR
if (s.size() > 6 && s[5] == ' ')
detail = s.substr(6);
return false;
}
} catch (std::exception &exc) {
detail = exc.what();
return false;
}
}
return false;
}
//--------------------------------------------------------------------------------------------------
/**
* Loads all default values for a given instance.
*/
void NewServerInstanceWizard::load_defaults() {
std::string template_file = values().get_string("template_path");
if (!template_file.empty()) {
grt::DictRef dict;
try {
dict = grt::DictRef::cast_from(grt::GRT::get()->unserialize(template_file));
} catch (std::exception &exc) {
logWarning("Instance %s contains invalid data: %s\n", template_file.c_str(), exc.what());
return;
}
grt::merge_contents(_instance->serverInfo(), dict, true);
_instance->serverInfo().gset("sys.preset", values().get_string("template"));
}
}
//--------------------------------------------------------------------------------------------------
/**
* Returns the current value for the server info at the specified key. Might be the default
* value or one set by assemble_server_instance().
*/
std::string NewServerInstanceWizard::get_server_info(const std::string &key) {
grt::ValueRef value = _instance->serverInfo().get(key);
if (!value.is_valid())
return "";
if (grt::StringRef::can_wrap(value))
return grt::StringRef::cast_from(value);
return value.debugDescription();
}
//--------------------------------------------------------------------------------------------------
bool NewServerInstanceWizard::is_admin_enabled() {
return (values().get_int("remoteAdmin", 0) == 1) || (values().get_int("windowsAdmin", 0) == 1) || is_local();
}
//--------------------------------------------------------------------------------------------------
/**
* Returns true if the currently selected host is the local machine.
*/
bool NewServerInstanceWizard::is_local() {
std::string driver = _connection->driver().is_valid() ? _connection->driver()->name() : "";
if (driver != "MysqlNativeSSH") {
std::string hostname = _connection->parameterValues().get_string("hostName");
if (hostname == "localhost" || hostname.empty() || hostname == "127.0.0.1")
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
void NewServerInstanceWizard::create_instance() {
db_mgmt_ManagementRef rdbms(_context->get_root()->rdbmsMgmt());
grt::ListRef<db_mgmt_ServerInstance> instances = rdbms->storedInstances();
// Remove any previously defined instance for the given connection and set the new one.
db_mgmt_ServerInstanceRef instance = assemble_server_instance();
for (grt::ListRef<db_mgmt_ServerInstance>::const_iterator iterator = instances.begin(); iterator != instances.end();
iterator++) {
if ((*iterator)->connection() == _connection) {
instances->remove(*iterator);
break;
}
}
instances.insert(instance);
}
//--------------------------------------------------------------------------------------------------