File indexing completed on 2025-02-16 14:23:01
0001 /* 0002 SPDX-FileCopyrightText: 2013 Lukas Tinkl <ltinkl@redhat.com> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "bondwidget.h" 0008 #include "connectioneditordialog.h" 0009 #include "plasma_nm_editor.h" 0010 #include "ui_bond.h" 0011 0012 #include <QDBusPendingReply> 0013 0014 #include <NetworkManagerQt/Connection> 0015 #include <NetworkManagerQt/ConnectionSettings> 0016 #include <NetworkManagerQt/GenericTypes> 0017 #include <NetworkManagerQt/Settings> 0018 0019 #include <KLocalizedString> 0020 #include <KMessageBox> 0021 #include <kwidgetsaddons_version.h> 0022 0023 #define NM_SETTING_BOND_OPTION_MII_MONITOR "mii" 0024 #define NM_SETTING_BOND_OPTION_ARP_MONITOR "arp" 0025 0026 BondWidget::BondWidget(const QString &masterUuid, const QString &masterId, const NetworkManager::Setting::Ptr &setting, QWidget *parent, Qt::WindowFlags f) 0027 : SettingWidget(setting, parent, f) 0028 , m_uuid(masterUuid) 0029 , m_id(masterId) 0030 , m_ui(new Ui::BondWidget) 0031 , m_menu(new QMenu(this)) 0032 { 0033 m_ui->setupUi(this); 0034 0035 // Action buttons and menu 0036 auto action = new QAction(i18n("Ethernet"), this); 0037 action->setData(NetworkManager::ConnectionSettings::Wired); 0038 m_menu->addAction(action); 0039 action = new QAction(i18n("InfiniBand"), this); 0040 action->setData(NetworkManager::ConnectionSettings::Infiniband); 0041 m_menu->addAction(action); 0042 m_ui->btnAdd->setMenu(m_menu); 0043 connect(m_menu, &QMenu::triggered, this, &BondWidget::addBond); 0044 connect(m_ui->btnEdit, &QPushButton::clicked, this, &BondWidget::editBond); 0045 connect(m_ui->btnDelete, &QPushButton::clicked, this, &BondWidget::deleteBond); 0046 0047 // mode 0048 m_ui->mode->addItem(i18nc("bond mode", "Round-robin"), QLatin1String("balance-rr")); 0049 m_ui->mode->addItem(i18nc("bond mode", "Active backup"), QLatin1String("active-backup")); 0050 m_ui->mode->addItem(i18nc("bond mode", "Broadcast"), QLatin1String("broadcast")); 0051 m_ui->mode->addItem(i18nc("bond mode", "802.3ad"), QLatin1String("802.3ad")); 0052 m_ui->mode->addItem(i18nc("bond mode", "Adaptive transmit load balancing"), QLatin1String("balance-tlb")); 0053 m_ui->mode->addItem(i18nc("bond mode", "Adaptive load balancing"), QLatin1String("balance-alb")); 0054 0055 // link monitor 0056 m_ui->linkMonitoring->addItem(i18nc("bond link monitoring", "MII (recommended)"), NM_SETTING_BOND_OPTION_MII_MONITOR); 0057 m_ui->linkMonitoring->addItem(i18nc("bond link monitoring", "ARP"), NM_SETTING_BOND_OPTION_ARP_MONITOR); 0058 0059 // bonds 0060 populateBonds(); 0061 connect(m_ui->bonds, &QListWidget::currentItemChanged, this, &BondWidget::currentBondChanged); 0062 connect(m_ui->bonds, &QListWidget::itemDoubleClicked, this, &BondWidget::editBond); 0063 0064 connect(m_ui->ifaceName, &KLineEdit::textChanged, this, &BondWidget::slotWidgetChanged); 0065 connect(m_ui->arpTargets, &KLineEdit::textChanged, this, &BondWidget::slotWidgetChanged); 0066 connect(m_ui->linkMonitoring, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &BondWidget::slotWidgetChanged); 0067 0068 // Connect for setting check 0069 watchChangedSetting(); 0070 0071 KAcceleratorManager::manage(this); 0072 KAcceleratorManager::manage(m_menu); 0073 0074 if (setting) { 0075 loadConfig(setting); 0076 } 0077 } 0078 0079 BondWidget::~BondWidget() 0080 { 0081 delete m_ui; 0082 } 0083 0084 void BondWidget::loadConfig(const NetworkManager::Setting::Ptr &setting) 0085 { 0086 NetworkManager::BondSetting::Ptr bondSetting = setting.staticCast<NetworkManager::BondSetting>(); 0087 0088 m_ui->ifaceName->setText(bondSetting->interfaceName()); 0089 0090 const NMStringMap options = bondSetting->options(); 0091 0092 // mode 0093 int modeIndex = m_ui->mode->findData(options.value(NM_SETTING_BOND_OPTION_MODE)); 0094 if (modeIndex == -1) 0095 modeIndex = 0; 0096 m_ui->mode->setCurrentIndex(modeIndex); 0097 0098 const QString arpTargets = options.value(NM_SETTING_BOND_OPTION_ARP_IP_TARGET); 0099 if (!arpTargets.isEmpty()) { // ARP 0100 m_ui->linkMonitoring->setCurrentIndex(m_ui->linkMonitoring->findData(NM_SETTING_BOND_OPTION_ARP_MONITOR)); 0101 0102 bool ok = false; 0103 const int arpMonFreq = options.value(NM_SETTING_BOND_OPTION_ARP_INTERVAL).toInt(&ok); 0104 if (ok && arpMonFreq > 0) 0105 m_ui->monitorFreq->setValue(arpMonFreq); 0106 0107 m_ui->arpTargets->setText(arpTargets); 0108 } else { // MII 0109 m_ui->linkMonitoring->setCurrentIndex(m_ui->linkMonitoring->findData(NM_SETTING_BOND_OPTION_MII_MONITOR)); 0110 0111 bool ok = false; 0112 const int miiMonFreq = options.value(NM_SETTING_BOND_OPTION_MIIMON).toInt(&ok); 0113 if (ok && miiMonFreq > 0) 0114 m_ui->monitorFreq->setValue(miiMonFreq); 0115 0116 ok = false; 0117 const int upDelay = options.value(NM_SETTING_BOND_OPTION_UPDELAY).toInt(&ok); 0118 if (ok && upDelay > 0) 0119 m_ui->upDelay->setValue(upDelay); 0120 0121 ok = false; 0122 const int downDelay = options.value(NM_SETTING_BOND_OPTION_DOWNDELAY).toInt(&ok); 0123 if (ok && downDelay > 0) 0124 m_ui->upDelay->setValue(downDelay); 0125 } 0126 } 0127 0128 QVariantMap BondWidget::setting() const 0129 { 0130 NetworkManager::BondSetting setting; 0131 setting.setInterfaceName(m_ui->ifaceName->text()); 0132 0133 NMStringMap options; 0134 options.insert(NM_SETTING_BOND_OPTION_MODE, m_ui->mode->itemData(m_ui->mode->currentIndex()).toString()); 0135 0136 if (m_ui->linkMonitoring->itemData(m_ui->linkMonitoring->currentIndex()).toString() == NM_SETTING_BOND_OPTION_MII_MONITOR) { // MII 0137 options.insert(NM_SETTING_BOND_OPTION_MIIMON, QString::number(m_ui->monitorFreq->value())); 0138 const int upDelay = m_ui->upDelay->value(); 0139 if (upDelay) 0140 options.insert(NM_SETTING_BOND_OPTION_UPDELAY, QString::number(upDelay)); 0141 const int downDelay = m_ui->downDelay->value(); 0142 if (downDelay) 0143 options.insert(NM_SETTING_BOND_OPTION_DOWNDELAY, QString::number(downDelay)); 0144 } else { // ARP 0145 options.insert(NM_SETTING_BOND_OPTION_ARP_INTERVAL, QString::number(m_ui->monitorFreq->value())); 0146 const QString arpTargets = m_ui->arpTargets->text(); 0147 if (!arpTargets.isEmpty()) 0148 options.insert(NM_SETTING_BOND_OPTION_ARP_IP_TARGET, arpTargets); 0149 } 0150 0151 setting.setOptions(options); 0152 return setting.toMap(); 0153 } 0154 0155 void BondWidget::addBond(QAction *action) 0156 { 0157 qCDebug(PLASMA_NM_EDITOR_LOG) << "Adding bonded connection:" << action->data(); 0158 qCDebug(PLASMA_NM_EDITOR_LOG) << "Master UUID:" << m_uuid; 0159 qCDebug(PLASMA_NM_EDITOR_LOG) << "Slave type:" << type(); 0160 0161 NetworkManager::ConnectionSettings::ConnectionType connectionType = static_cast<NetworkManager::ConnectionSettings::ConnectionType>(action->data().toInt()); 0162 NetworkManager::ConnectionSettings::Ptr connectionSettings = 0163 NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(connectionType)); 0164 connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); 0165 connectionSettings->setMaster(m_uuid); 0166 connectionSettings->setSlaveType(type()); 0167 connectionSettings->setAutoconnect(false); 0168 0169 QPointer<ConnectionEditorDialog> bondEditor = new ConnectionEditorDialog(connectionSettings); 0170 bondEditor->setAttribute(Qt::WA_DeleteOnClose); 0171 connect(bondEditor.data(), &ConnectionEditorDialog::accepted, [bondEditor, this]() { 0172 qCDebug(PLASMA_NM_EDITOR_LOG) << "Saving slave connection"; 0173 // qCDebug(PLASMA_NM_EDITOR_LOG) << bondEditor->setting(); 0174 QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addConnection(bondEditor->setting()); 0175 auto watcher = new QDBusPendingCallWatcher(reply, this); 0176 connect(watcher, &QDBusPendingCallWatcher::finished, this, &BondWidget::bondAddComplete); 0177 }); 0178 bondEditor->setModal(true); 0179 bondEditor->show(); 0180 } 0181 0182 void BondWidget::currentBondChanged(QListWidgetItem *current, QListWidgetItem *previous) 0183 { 0184 Q_UNUSED(previous) 0185 0186 m_ui->btnEdit->setEnabled(current); 0187 m_ui->btnDelete->setEnabled(current); 0188 } 0189 0190 void BondWidget::bondAddComplete(QDBusPendingCallWatcher *watcher) 0191 { 0192 QDBusPendingReply<QDBusObjectPath> reply = *watcher; 0193 0194 if (reply.isValid()) { 0195 // find the slave connection with matching UUID 0196 NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(reply.value().path()); 0197 if (connection && connection->settings()->master() == m_uuid) { 0198 const QString label = 0199 QStringLiteral("%1 (%2)").arg(connection->name(), connection->settings()->typeAsString(connection->settings()->connectionType())); 0200 auto slaveItem = new QListWidgetItem(label, m_ui->bonds); 0201 slaveItem->setData(Qt::UserRole, connection->uuid()); 0202 slotWidgetChanged(); 0203 } 0204 } else { 0205 qCWarning(PLASMA_NM_EDITOR_LOG) << "Bonded connection not added:" << reply.error().message(); 0206 } 0207 } 0208 0209 void BondWidget::editBond() 0210 { 0211 QListWidgetItem *currentItem = m_ui->bonds->currentItem(); 0212 if (!currentItem) 0213 return; 0214 0215 const QString uuid = currentItem->data(Qt::UserRole).toString(); 0216 NetworkManager::Connection::Ptr connection = NetworkManager::findConnectionByUuid(uuid); 0217 0218 if (connection) { 0219 // qCDebug(PLASMA_NM_EDITOR_LOG) << "Editing bonded connection" << currentItem->text() << uuid; 0220 QPointer<ConnectionEditorDialog> bondEditor = new ConnectionEditorDialog(connection->settings()); 0221 bondEditor->setAttribute(Qt::WA_DeleteOnClose); 0222 connect(bondEditor.data(), &ConnectionEditorDialog::accepted, [connection, bondEditor, this]() { 0223 connection->update(bondEditor->setting()); 0224 connect(connection.data(), &NetworkManager::Connection::updated, this, &BondWidget::populateBonds); 0225 }); 0226 bondEditor->setModal(true); 0227 bondEditor->show(); 0228 } 0229 } 0230 0231 void BondWidget::deleteBond() 0232 { 0233 QListWidgetItem *currentItem = m_ui->bonds->currentItem(); 0234 if (!currentItem) 0235 return; 0236 0237 const QString uuid = currentItem->data(Qt::UserRole).toString(); 0238 NetworkManager::Connection::Ptr connection = NetworkManager::findConnectionByUuid(uuid); 0239 0240 if (connection) { 0241 // qCDebug(PLASMA_NM_EDITOR_LOG) << "About to delete bonded connection" << currentItem->text() << uuid; 0242 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) 0243 if (KMessageBox::questionTwoActions(this, 0244 #else 0245 if (KMessageBox::questionYesNo(this, 0246 0247 #endif 0248 i18n("Do you want to remove the connection '%1'?", connection->name()), 0249 i18n("Remove Connection"), 0250 KStandardGuiItem::remove(), 0251 KStandardGuiItem::cancel(), 0252 QString(), 0253 KMessageBox::Dangerous) 0254 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) 0255 == KMessageBox::ButtonCode::PrimaryAction) { 0256 #else 0257 == KMessageBox::Yes) { 0258 #endif 0259 connection->remove(); 0260 delete currentItem; 0261 slotWidgetChanged(); 0262 } 0263 } 0264 } 0265 0266 void BondWidget::populateBonds() 0267 { 0268 m_ui->bonds->clear(); 0269 0270 for (const NetworkManager::Connection::Ptr &connection : NetworkManager::listConnections()) { 0271 NetworkManager::ConnectionSettings::Ptr settings = connection->settings(); 0272 // The mapping from slave to master may be by uuid or name, try our best to 0273 // figure out if we are master to the slave. 0274 const QString master = settings->master(); 0275 bool isSlave = ((master == m_uuid) || // by-uuid 0276 (!m_id.isEmpty() && master == m_id)); // by-name 0277 if (isSlave && (settings->slaveType() == type())) { 0278 const QString label = 0279 QStringLiteral("%1 (%2)").arg(connection->name(), connection->settings()->typeAsString(connection->settings()->connectionType())); 0280 auto slaveItem = new QListWidgetItem(label, m_ui->bonds); 0281 slaveItem->setData(Qt::UserRole, connection->uuid()); 0282 } 0283 } 0284 } 0285 0286 bool BondWidget::isValid() const 0287 { 0288 if (m_ui->linkMonitoring->itemData(m_ui->linkMonitoring->currentIndex()).toString() == NM_SETTING_BOND_OPTION_ARP_MONITOR) { 0289 const QStringList ipAddresses = m_ui->arpTargets->text().split(QLatin1Char(',')); 0290 if (ipAddresses.isEmpty()) { 0291 return false; 0292 } 0293 0294 for (const QString &ip : ipAddresses) { 0295 QHostAddress ipAddress(ip); 0296 if (ipAddress.isNull()) { 0297 return false; 0298 } 0299 } 0300 } 0301 0302 return !m_ui->ifaceName->text().isEmpty() && m_ui->bonds->count() > 0; 0303 }