1 /*
2 * ClusterGroupDialog.cpp - ClusterGroup view implementation
3 *
4 * Copyright (c) 2008 secunet Security Networks AG
5 * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger@swiss-it.ch>
6 * Copyright (c) 2008 Reto Buerki <buerki@swiss-it.ch>
7 *
8 * This work is dual-licensed under:
9 *
10 * o The terms of the GNU General Public License as published by the Free
11 * Software Foundation, either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * o The terms of NetCitadel End User License Agreement
15 */
16
17 #include "ClusterGroupDialog.h"
18
19 #include "utils.h"
20 #include "platforms.h"
21 #include "events.h"
22
23 #include "ObjectListViewItem.h"
24 #include "FWWindow.h"
25 #include "ProjectPanel.h"
26 #include "DialogFactory.h"
27 #include "vrrpOptionsDialog.h"
28 #include "FWCmdChange.h"
29
30 #include "fwbuilder/Cluster.h"
31 #include "fwbuilder/StateSyncClusterGroup.h"
32 #include "fwbuilder/FailoverClusterGroup.h"
33 #include "fwbuilder/Resources.h"
34 #include "fwbuilder/Interface.h"
35
36 #include <memory>
37
38 #include <qpixmapcache.h>
39 #include <qmessagebox.h>
40 #include <qdialog.h>
41 #include <QCoreApplication>
42 #include <QtDebug>
43 #include <QUndoStack>
44
45 #include <algorithm>
46 #include <iostream>
47
48 using namespace std;
49 using namespace libfwbuilder;
50
~ClusterGroupDialog()51 ClusterGroupDialog::~ClusterGroupDialog()
52 {
53 delete m_dialog;
54 }
55
ClusterGroupDialog(QWidget * parent)56 ClusterGroupDialog::ClusterGroupDialog(QWidget *parent)
57 : BaseObjectDialog(parent)
58 {
59 m_dialog = new Ui::ClusterGroupDialog_q;
60 m_dialog->setupUi(this);
61 obj = NULL;
62 reload = false;
63
64 connectSignalsOfAllWidgetsToSlotChange();
65 }
66
loadFWObject(FWObject * o)67 void ClusterGroupDialog::loadFWObject(FWObject *o)
68 {
69 obj = o;
70 ClusterGroup *g = dynamic_cast<ClusterGroup*>(obj);
71 assert(g != NULL);
72
73 init = true;
74
75 // disable manage members if host OS does not support clustering.
76 // Parent is either 'Cluster' or 'Interface', call getParent() approprietly
77 FWObject *parent = obj;
78 while (parent && !Cluster::isA(parent)) parent = parent->getParent();
79 if (parent == NULL)
80 {
81 throw FWException("ClusterGroupDialog: parent is NULL!");
82 }
83 cluster = Cluster::cast(parent);
84 string host_os = cluster->getStr("host_OS");
85
86 // Sanity check
87 // Failover type could be wrong if user changed host OS of the cluster
88 string type = obj->getStr("type");
89
90 list<QStringPair> possible_cluster_group_types;
91 if (StateSyncClusterGroup::isA(o))
92 getStateSyncTypesForOS(host_os.c_str(), possible_cluster_group_types);
93 if (FailoverClusterGroup::isA(o))
94 getFailoverTypesForOS(host_os.c_str(), possible_cluster_group_types);
95
96 enable_master_column = Resources::os_res[host_os]->getResourceBool(
97 "/FWBuilderResources/Target/protocols/" + type + "/needs_master");
98
99 if (enable_master_column) m_dialog->fwMemberTree->showColumn(2);
100 else m_dialog->fwMemberTree->hideColumn(2);
101
102 bool acceptable_failover_type = false;
103 for (list<QStringPair>::iterator it=possible_cluster_group_types.begin();
104 it!=possible_cluster_group_types.end(); ++it)
105 {
106 QString t = it->first;
107 if (t == QString(type.c_str()))
108 {
109 acceptable_failover_type = true;
110 break;
111 }
112 }
113 if (!acceptable_failover_type && possible_cluster_group_types.size())
114 obj->setStr(
115 "type", possible_cluster_group_types.front().first.toStdString());
116
117 m_dialog->obj_name->setText(QString::fromUtf8(g->getName().c_str()));
118 m_dialog->commentKeywords->loadFWObject(o);
119 m_dialog->obj_name->setEnabled(!o->isReadOnly());
120 setDisabledPalette(m_dialog->obj_name);
121
122 QString grp_type = obj->getStr("type").c_str();
123 m_dialog->type->clear();
124 int cp = 0;
125 for (list<QStringPair>::iterator i1=possible_cluster_group_types.begin();
126 i1!=possible_cluster_group_types.end(); i1++,cp++)
127 {
128 m_dialog->type->addItem( i1->second );
129 if ( grp_type == i1->first ) m_dialog->type->setCurrentIndex(cp);
130 }
131
132 // init link icons, master firewall is colored
133 m_dialog->fwMemberTree->clear();
134
135 string master_iface = g->getStr("master_iface");
136 for (FWObject::iterator it = g->begin(); it != g->end(); it++)
137 {
138 FWObject *o = FWObjectReference::getObject(*it);
139 if (Interface::isA(o))
140 {
141 if (master_iface == FWObjectDatabase::getStringId(o->getId()))
142 {
143 addIcon(o, true);
144 }
145 else
146 {
147 addIcon(o);
148 }
149 }
150 }
151
152 if (!Resources::getTargetCapabilityBool(host_os, "supports_cluster"))
153 {
154 m_dialog->manageMembers->setEnabled(false);
155 m_dialog->manageMembers->setToolTip(
156 QObject::tr("Feature not supported by host OS '%1'").arg(host_os.c_str()));
157 }
158 else
159 {
160 m_dialog->manageMembers->setEnabled(true);
161 m_dialog->manageMembers->setToolTip(
162 QObject::tr("Click here to manage member firewalls of this cluster group."));
163 }
164
165 m_dialog->fwMemberTree->resizeColumnToContents(0);
166 m_dialog->fwMemberTree->resizeColumnToContents(1);
167 m_dialog->fwMemberTree->resizeColumnToContents(2);
168 m_dialog->fwMemberTree->resizeColumnToContents(3);
169
170 QString dlgname = DialogFactory::getClusterGroupOptionsDialogName(
171 ClusterGroup::cast(obj)->getOptionsObject());
172
173 if (fwbdebug)
174 qDebug() << "ClusterGroupDialog::loadFWObject dlgname=" << dlgname;
175
176 m_dialog->editParameters->setEnabled(!dlgname.isEmpty());
177
178 init = false;
179 }
180
saveGroupType(FWObject * group)181 void ClusterGroupDialog::saveGroupType(FWObject *group)
182 {
183 QString host_os = cluster->getStr("host_OS").c_str();
184 list<QStringPair> possible_cluster_group_types;
185 if (StateSyncClusterGroup::isA(obj))
186 getStateSyncTypesForOS(host_os, possible_cluster_group_types);
187 if (FailoverClusterGroup::isA(obj))
188 getFailoverTypesForOS(host_os, possible_cluster_group_types);
189
190 QString grp_type = m_dialog->type->currentText();
191 list<QStringPair>::iterator li =
192 std::find_if(possible_cluster_group_types.begin(),
193 possible_cluster_group_types.end(),
194 findSecondInQStringPair(grp_type));
195 if (li != possible_cluster_group_types.end())
196 group->setStr("type", li->first.toLatin1().constData() );
197 }
198
addIcon(FWObject * o,bool master)199 void ClusterGroupDialog::addIcon(FWObject *o, bool master)
200 {
201 FWObject *iface = o;
202 assert(Interface::cast(iface)!=NULL);
203 FWObject *fw = Host::getParentHost(iface);
204 // FWObject *fw = Interface::cast(iface)->getParentHost(); // because iface can be subinterface
205 bool valid = cluster->validateMember(Firewall::cast(fw));
206 QString iface_name = QString::fromUtf8(iface->getName().c_str());
207 QString fw_name = QString::fromUtf8(fw->getName().c_str());
208
209 QString iface_icn_file = (":/Icons/" + iface->getTypeName() +
210 "/icon-ref").c_str();
211 QString fw_icn_file = (":/Icons/" + fw->getTypeName() +
212 "/icon-ref").c_str();
213
214 QPixmap iface_pm;
215 if (!QPixmapCache::find(iface_icn_file, iface_pm))
216 {
217 iface_pm.load(iface_icn_file);
218 QPixmapCache::insert(iface_icn_file, iface_pm);
219 }
220 QPixmap fw_pm;
221 if (!QPixmapCache::find(fw_icn_file, fw_pm))
222 {
223 fw_pm.load(fw_icn_file);
224 QPixmapCache::insert(fw_icn_file, fw_pm);
225 }
226
227 ObjectListViewItem *item = new ObjectListViewItem(m_dialog->fwMemberTree);
228 int col = 0;
229
230 item->setText(col, fw_name);
231 item->setIcon(col, QIcon(fw_pm));
232 col++;
233
234 item->setText(col, iface_name);
235 item->setIcon(col, QIcon(iface_pm));
236 col++;
237
238 // note that if enable_master_column==false, this column is hidden
239 // but we still need to create an item in this column.
240 if (master) item->setText(col, tr("Master"));
241 else item->setText(col, tr(""));
242 col++;
243
244 if (valid)
245 {
246 item->setText(col, "OK");
247 item->setToolTip(
248 col, tr("Firewall %1 can be used as a member of this cluster").arg(fw->getName().c_str()));
249 } else
250 {
251 item->setText(col, tr("Invalid"));
252 item->setToolTip(
253 col, tr("Firewall %1 can not be used as a member of this cluster\n because its host OS or platform does not match those of the cluster.").arg(fw->getName().c_str()));
254 item->setBackgroundColor(col, QColor(255, 0, 0, 100));
255 }
256
257 item->setProperty("type", iface->getTypeName().c_str());
258 item->setFWObject(iface);
259 }
260
changed()261 void ClusterGroupDialog::changed()
262 {
263 if (fwbdebug)
264 qDebug() << "ClusterGroupDialog::changed()";
265 if (!reload) BaseObjectDialog::changed();
266 }
267
validate(bool * res)268 void ClusterGroupDialog::validate(bool *res)
269 {
270 *res = true;
271 if (!validateName(this, obj, m_dialog->obj_name->text()))
272 {
273 *res = false;
274 }
275 }
276
applyChanges()277 void ClusterGroupDialog::applyChanges()
278 {
279 std::auto_ptr<FWCmdChange> cmd( new FWCmdChange(m_project, obj));
280 FWObject* new_state = cmd->getNewState();
281
282 ClusterGroup *g = dynamic_cast<ClusterGroup*>(new_state);
283 assert(g != NULL);
284
285 QString oldname = obj->getName().c_str();
286 new_state->setName(string(m_dialog->obj_name->text().toUtf8().constData()));
287 m_dialog->commentKeywords->applyChanges(new_state);
288
289 saveGroupType(new_state);
290
291 if (!cmd->getOldState()->cmp(new_state, true))
292 {
293 if (obj->isReadOnly()) return;
294 m_project->undoStack->push(cmd.release());
295 }
296 }
297
298 /*
299 * This method is connected to the "Edit members" button and opens dialog
300 * where user chooses cluster member firewalls and interfaces
301 */
openClusterConfDialog()302 void ClusterGroupDialog::openClusterConfDialog()
303 {
304 try
305 {
306 QWidget *w = DialogFactory::createClusterConfDialog(this, obj);
307 if (w == NULL)
308 {
309 return; // some dialogs may not be implemented yet
310 }
311 QDialog *d = dynamic_cast<QDialog*>(w);
312 assert(d != NULL);
313
314 // connect obj changed signal
315 //connect(d, SIGNAL(membersChanged()), this, SLOT(objectChanged()));
316
317 if (d->exec() == QDialog::Accepted)
318 {
319 // modal dialog, dialog saves data into the object
320
321 // update object tree (if members have changed, the object
322 // properties summary text may have to change too)
323 mw->activeProject()->updateObjectInTree(obj, true);
324
325 // reload object to reflect changes in members
326 loadFWObject(obj);
327
328 // mark as modified
329 changed();
330 }
331 delete d;
332 }
333 catch (FWException &ex)
334 {
335 QMessageBox::critical(
336 this, "Firewall Builder",
337 tr("FWBuilder API error: %1").arg(ex.toString().c_str()),
338 tr("&Continue"), QString::null, QString::null, 0, 1);
339 return;
340 }
341 }
342
openObject(QTreeWidgetItem * item)343 void ClusterGroupDialog::openObject(QTreeWidgetItem *item)
344 {
345 ObjectListViewItem *otvi = dynamic_cast<ObjectListViewItem*>(item);
346 assert(otvi != NULL);
347
348 FWObject *o = otvi->getFWObject();
349 if (o != NULL)
350 {
351 QCoreApplication::postEvent(
352 mw, new showObjectInTreeEvent(o->getRoot()->getFileName().c_str(),
353 o->getId()));
354 }
355 }
356
objectChanged()357 void ClusterGroupDialog::objectChanged()
358 {
359 reload = true;
360 loadFWObject(obj);
361 reload = false;
362 }
363
364 /*
365 * this method is connected to the "Edit protocol parameters" button
366 * and opens dialog where user edits state sync and failover
367 * (heartbeat/openais/vrrp/carp/conntrack/etc) protocol parameters.
368 */
openParametersEditor()369 void ClusterGroupDialog::openParametersEditor()
370 {
371 FWOptions *gr_opt = ClusterGroup::cast(obj)->getOptionsObject();
372
373 QDialog *dlg = dynamic_cast<QDialog*>(
374 DialogFactory::createClusterGroupOptionsDialog(this, gr_opt));
375
376 if (dlg)
377 {
378 if (dlg->exec() == QDialog::Accepted)
379 {
380 // modal dialog, dialog saves data into the object
381
382 // update object tree (if protocol type has changed, the
383 // object properties summary text may have to change too)
384 mw->activeProject()->updateObjectInTree(obj, true);
385 changed();
386 }
387 delete dlg;
388 }
389 }
390
391