File indexing completed on 2024-07-21 07:58:03

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 (KMessageBox::questionTwoActions(this,
0243                                             i18n("Do you want to remove the connection '%1'?", connection->name()),
0244                                             i18n("Remove Connection"),
0245                                             KStandardGuiItem::remove(),
0246                                             KStandardGuiItem::cancel(),
0247                                             QString(),
0248                                             KMessageBox::Dangerous)
0249             == KMessageBox::ButtonCode::PrimaryAction) {
0250             connection->remove();
0251             delete currentItem;
0252             slotWidgetChanged();
0253         }
0254     }
0255 }
0256 
0257 void BondWidget::populateBonds()
0258 {
0259     m_ui->bonds->clear();
0260 
0261     for (const NetworkManager::Connection::Ptr &connection : NetworkManager::listConnections()) {
0262         NetworkManager::ConnectionSettings::Ptr settings = connection->settings();
0263         // The mapping from slave to master may be by uuid or name, try our best to
0264         // figure out if we are master to the slave.
0265         const QString master = settings->master();
0266         bool isSlave = ((master == m_uuid) || // by-uuid
0267                         (!m_id.isEmpty() && master == m_id)); // by-name
0268         if (isSlave && (settings->slaveType() == type())) {
0269             const QString label =
0270                 QStringLiteral("%1 (%2)").arg(connection->name(), connection->settings()->typeAsString(connection->settings()->connectionType()));
0271             auto slaveItem = new QListWidgetItem(label, m_ui->bonds);
0272             slaveItem->setData(Qt::UserRole, connection->uuid());
0273         }
0274     }
0275 }
0276 
0277 bool BondWidget::isValid() const
0278 {
0279     if (m_ui->linkMonitoring->itemData(m_ui->linkMonitoring->currentIndex()).toString() == NM_SETTING_BOND_OPTION_ARP_MONITOR) {
0280         const QStringList ipAddresses = m_ui->arpTargets->text().split(QLatin1Char(','));
0281         if (ipAddresses.isEmpty()) {
0282             return false;
0283         }
0284 
0285         for (const QString &ip : ipAddresses) {
0286             QHostAddress ipAddress(ip);
0287             if (ipAddress.isNull()) {
0288                 return false;
0289             }
0290         }
0291     }
0292 
0293     return !m_ui->ifaceName->text().isEmpty() && m_ui->bonds->count() > 0;
0294 }
0295 
0296 #include "moc_bondwidget.cpp"