source: src-qt4/pc-zmanager/zmanagerwindow.cpp @ 6a5c6a1

releng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 6a5c6a1 was 6a5c6a1, checked in by Ken Moore <ken@…>, 10 months ago

Fix typo's and corrupt translation strings in pc-zmanager. Thanks to Jun Yamashiro for discovering them.

  • Property mode set to 100644
File size: 90.2 KB
Line 
1#include <QTimer>
2#include "zmanagerwindow.h"
3#include "ui_zmanagerwindow.h"
4#include "dialogpartition.h"
5#include "dialogmount.h"
6#include "dialognewpool.h"
7#include "dialogname.h"
8#include "dialogprop.h"
9#include "dialogfsprop.h"
10#include "dialogfscreate.h"
11#include <pcbsd-utils.h>
12#include <QDebug>
13#include <QIcon>
14#include <QString>
15#include <QStringList>
16#include <QMenu>
17#include <QAction>
18#include <QMessageBox>
19#include <QPushButton>
20#include <QTreeWidgetItemIterator>
21#include <QToolButton>
22
23ZManagerWindow::ZManagerWindow(QWidget *parent) :
24    QMainWindow(parent),
25    ui(new Ui::ZManagerWindow)
26{
27
28    lastSelectedPool=NULL;
29    lastSelectedVdev=NULL;
30    lastSelectedFileSystem=NULL;
31
32    ui->setupUi(this);
33
34    ui->zpoolList->setIconSize(QSize(48,48));
35    ui->zpoolList->header()->setStretchLastSection(true);
36    ui->zpoolList->header()->setResizeMode(0,QHeaderView::Interactive);
37    ui->zpoolList->header()->setResizeMode(1,QHeaderView::Stretch);
38    ui->zpoolList->header()->resizeSection(0,ui->deviceList->width()*2/3);
39
40    ui->deviceList->setIconSize(QSize(48,48));
41    ui->deviceList->header()->setStretchLastSection(true);
42    ui->deviceList->header()->setResizeMode(0,QHeaderView::Interactive);
43    ui->deviceList->header()->setResizeMode(1,QHeaderView::Stretch);
44    ui->deviceList->header()->resizeSection(0,ui->deviceList->width()*2/3);
45
46
47    connect(ui->zpoolList,SIGNAL(customContextMenuRequested(QPoint)),SLOT(zpoolContextMenu(QPoint)));
48    connect(ui->deviceList,SIGNAL(customContextMenuRequested(QPoint)),SLOT(deviceContextMenu(QPoint)));
49    connect(ui->fsList,SIGNAL(customContextMenuRequested(QPoint)),SLOT(filesystemContextMenu(QPoint)));
50
51    ui->frameStatus->setVisible(false);
52
53
54    ui->fspoolList->setIconSize(QSize(48,48));
55    ui->tabZFS->layout()->setAlignment(ui->dropDownButton,Qt::AlignTop);
56
57    ui->fsList->setIconSize(QSize(48,48));
58    ui->fsList->header()->setStretchLastSection(true);
59    ui->fsList->header()->setResizeMode(0,QHeaderView::Interactive);
60    ui->fsList->header()->setResizeMode(1,QHeaderView::Stretch);
61    ui->fsList->header()->resizeSection(0,ui->deviceList->width()*2/3);
62
63
64}
65
66ZManagerWindow::~ZManagerWindow()
67{
68    delete ui;
69}
70
71
72void ZManagerWindow::ProgramInit()
73{
74    // Perform initialization
75    QTimer::singleShot(300,this,SLOT(refreshState()));
76}
77
78void ZManagerWindow::slotSingleInstance()
79{
80    activateWindow();
81}
82
83
84// THIS IS THE MAIN FUNCTION THAT GATHERS ALL INFORMATION FROM THE BACKEND
85
86void ZManagerWindow::GetCurrentTopology()
87{
88
89    // RUN ALL REQUIRED PROCESSES AND GET THE RESULTS
90
91    QStringList a=pcbsd::Utils::runShellCommand("zpool status");    // GET ALL ACTIVE POOLS
92    QStringList i=pcbsd::Utils::runShellCommand("zpool import");    // GET ALL EXPORTED POOLS AVAILABLE
93    QStringList d=pcbsd::Utils::runShellCommand("zpool import -D");    // GET ALL DESTROYED POOLS READY TO RECOVER
94    QStringList g=pcbsd::Utils::runShellCommand("geom disk list");
95    QStringList h=pcbsd::Utils::runShellCommand("gpart list");
96    QStringList h2=pcbsd::Utils::runShellCommand("gpart show -p");
97    QStringList lbl=pcbsd::Utils::runShellCommand("glabel status");
98    QStringList m=pcbsd::Utils::runShellCommand("mount");
99    QStringList prop;   // GET PROPERTIES FOR ALL POOLS ONCE WE HAVE A LIST OF POOLS
100    QStringList zfsl=pcbsd::Utils::runShellCommand("zfs list -H -t all");
101    QStringList zfspr=pcbsd::Utils::runShellCommand("zfs get -H all");
102
103
104    // CLEAR ALL EXISTING TOPOLOGY
105    this->Pools.clear();
106    this->Disks.clear();
107    this->Errors.clear();
108    this->FileSystems.clear();
109
110    QStringList::const_iterator idx;
111    int state;
112
113
114
115    // BEGIN PROCESSING DISKS FROM GEOM
116
117    idx=g.constBegin();
118    vdev_t dsk,part;
119    state=0;
120
121    while(idx!=g.constEnd()) {
122
123        QString str=*idx;
124
125
126        if(str.startsWith("Geom name: ")) {
127
128            if(state) {
129                this->Disks.append(dsk);
130                state=0;
131            }
132            dsk.Name=str.remove(0,11);
133            dsk.SectorSize=0;
134            dsk.SectorStart=0;
135            dsk.SectorsCount=0;
136            dsk.Description.clear();
137            dsk.Level=0;
138            dsk.Status=0;
139            dsk.InPool.clear();
140            dsk.Index=0;
141            dsk.Size=0LL;
142            dsk.PartType.clear();
143            dsk.Partitions.clear();
144            ++state;
145        }
146
147        if(str.startsWith("   Mediasize: ") && (state==1)) {
148
149            QStringList l=str.remove(0,14).split(" ");
150            QStringList::const_iterator lit=l.constBegin();
151            while( (*lit).isEmpty() && lit!=l.constEnd()) ++lit;
152            if(lit!=l.constEnd()) dsk.Size=(*lit).toULongLong();
153            ++state;
154        }
155
156        if(str.startsWith("   Sectorsize: ") && (state==2)) {
157
158            dsk.SectorSize=str.remove(0,15).toInt();
159            ++state;
160        }
161
162
163        if(str.startsWith("   descr: ") && (state==3)) {
164
165            dsk.Description=str.remove(0,10);
166            ++state;
167        }
168
169
170        ++idx;
171    }
172
173    if(state==4) {
174        this->Disks.append(dsk);
175    }
176
177
178
179
180    // BEGIN PROCESSING PARTITIONS FROM GPART
181
182    idx=h.constBegin();
183    state=0;
184    QString ParentGeom;
185
186    while(idx!=h.constEnd()) {
187
188        QString str=*idx;
189
190
191        if(str.startsWith("Geom name: ")) {
192
193            if(state) {
194                // APPEND SLICES AND PARTITIONS
195                QList<vdev_t>::iterator dskidx=this->Disks.begin();
196
197                while(dskidx!=this->Disks.end()) {
198                    if((*dskidx).Name==dsk.Name) {
199                        // ADD SLICES
200                        (*dskidx).PartType=dsk.PartType;
201                        (*dskidx).Partitions=dsk.Partitions;
202
203                        break;
204                    }
205                    if(dsk.Name.startsWith((*dskidx).Name)) {
206                        // THE GEOM IS ACTUALLY A SLICE
207                        QList<vdev_t>::iterator partidx=(*dskidx).Partitions.begin();
208
209                        while(partidx!=(*dskidx).Partitions.end()) {
210                            if((*partidx).Name==dsk.Name) {
211                                // ADD PARTITIONS
212                                (*partidx).PartType=dsk.PartType;
213                                (*partidx).Partitions=dsk.Partitions;
214
215                                break;
216                            }
217
218                            ++partidx;
219
220                        }
221                    }
222
223                    ++dskidx;
224
225                }
226                if(dskidx==this->Disks.constEnd()) {
227                    // THIS SHOULDN'T HAPPEN, DISK HAD TO BE IN THE GEOM LIST!!!
228                    // SIMPLY DISCARD
229
230
231                }
232
233                state=0;
234            }
235
236
237            // DETERMINE IF THE GEOM IS A DISK OR A SLICE
238            QList<vdev_t>::iterator dskidx=this->Disks.begin();
239
240
241
242            dsk.Name=str.remove(0,11);
243            dsk.Level=0;
244            while(dskidx!=this->Disks.end()) {
245                if((*dskidx).Name==dsk.Name) {
246                    // GEOM IS A DISK
247                    break;
248                }
249                if(dsk.Name.startsWith((*dskidx).Name)) {
250                    // GEOM IS A SLICE
251                    dsk.Level=1;
252                }
253                ++dskidx;
254            }
255
256            dsk.Description.clear();
257            dsk.PartType.clear();
258            dsk.Partitions.clear();
259            dsk.SectorSize=0;
260            dsk.SectorStart=0;
261            dsk.SectorsCount=0;
262            dsk.Index=0;
263            dsk.Status=0;
264            dsk.Size=0LL;
265            ++state;
266        }
267
268        if(str.startsWith("scheme: ") && (state==1)) {
269
270            dsk.PartType=str.remove(0,8);
271            ++state;
272        }
273
274
275        if(str.startsWith("Providers:") &&(state==2)) {
276            ++state;
277        }
278
279        if(str.contains(". Name: ") && (state>=3)) {
280            if(state>3) {
281                // ADD PREVIOUS PARTITION TO MAIN GEOM
282                // WE MUST INSERT IN THE LIST, SORTED BY SECTOR START
283               QList<vdev_t>::iterator partit=dsk.Partitions.begin();
284
285                while(partit!=dsk.Partitions.end())
286                {
287                    if( (*partit).SectorStart>part.SectorStart) break;
288                    ++partit;
289                }
290
291                if(partit==dsk.Partitions.end()) dsk.Partitions.append(part);
292                else dsk.Partitions.insert(partit,part);
293                state=3;
294            }
295            part.Name=str.remove(0,9);
296            part.Description.clear();
297            part.Partitions.clear();
298            part.PartType.clear();
299            part.Level=dsk.Level+1;
300            part.SectorSize=0;
301            part.SectorStart=0;
302            part.SectorsCount=0;
303            part.Status=0;
304            part.InPool.clear();
305            part.Index=0;
306            part.Size=0LL;
307            ++state;
308        }
309
310        if(str.startsWith("   Mediasize: ") &&(state==4)) {
311            QStringList l=str.remove(0,14).split(" ");
312            QStringList::const_iterator lit=l.constBegin();
313            while( (*lit).isEmpty() && lit!=l.constEnd()) ++lit;
314            if(lit!=l.constEnd()) part.Size=(*lit).toULongLong();
315            ++state;
316        }
317
318        if(str.startsWith("   Sectorsize: ") && (state==5)) {
319
320            part.SectorSize=str.remove(0,15).toInt();
321            ++state;
322        }
323
324
325        if(str.startsWith("   type: ") &&(state==6)) {
326            part.PartType=str.remove(0,9);
327            ++state;
328        }
329
330        if(str.startsWith("   index: ") && (state==7)) {
331
332            part.Index=str.remove(0,10).toInt();
333            ++state;
334        }
335
336        if(str.startsWith("   end: ") &&(state==8)) {
337            part.SectorsCount=str.remove(0,8).toULongLong();
338            ++state;
339        }
340
341        if(str.startsWith("   start: ") &&(state==9)) {
342            part.SectorStart=str.remove(0,10).toULongLong();
343            part.SectorsCount-=(part.SectorStart-1);
344            ++state;
345        }
346
347        if(str.startsWith("Consumers:") &&(state==10)) {
348            // WE MUST INSERT IN THE LIST, SORTED BY SECTOR START
349           QList<vdev_t>::iterator partit=dsk.Partitions.begin();
350
351            while(partit!=dsk.Partitions.end())
352            {
353                if( (*partit).SectorStart>part.SectorStart) break;
354                ++partit;
355            }
356
357            if(partit==dsk.Partitions.end()) dsk.Partitions.append(part);
358            else dsk.Partitions.insert(partit,part);
359            state=-1;
360        }
361
362
363        ++idx;
364    }
365
366    if(state==3 || state==-1) {
367            // APPEND SLICES AND PARTITIONS
368            QList<vdev_t>::iterator dskidx=this->Disks.begin();
369
370            while(dskidx!=this->Disks.end()) {
371                if((*dskidx).Name==dsk.Name) {
372                    // ADD SLICES
373                    (*dskidx).PartType=dsk.PartType;
374                    (*dskidx).Partitions=dsk.Partitions;
375
376                    break;
377                }
378                if(dsk.Name.startsWith((*dskidx).Name)) {
379                    // THE GEOM IS ACTUALLY A SLICE
380                    QList<vdev_t>::iterator partidx=(*dskidx).Partitions.begin();
381                    while(partidx!=(*dskidx).Partitions.end()) {
382                        if((*partidx).Name==dsk.Name) {
383                            // ADD PARTITIONS
384                            (*partidx).PartType=dsk.PartType;
385                            (*partidx).Partitions=dsk.Partitions;
386
387                            break;
388                        }
389
390                        ++partidx;
391
392                    }
393                }
394
395                ++dskidx;
396
397            }
398            if(dskidx==this->Disks.constEnd()) {
399                // THIS SHOULDN'T HAPPEN, DISK HAD TO BE IN THE GEOM LIST!!!
400                // SIMPLY DISCARD
401
402
403            }
404
405            state=0;
406        }
407
408    // GPART SHOW IS ONLY NEEDED BECAUSE GPART LIST
409    // DOESN'T SHOW IF A DISK HAS A PARTITION TABLE
410    // BUT NO PARTITIONS IN IT
411    // IN ANY CASE, WE READ THE SECTORS AGAIN FOR EACH PARTITION
412
413    vdev_t *exist_disk;
414
415    bool mainitem;
416    idx=h2.constBegin();
417
418    while(idx!=h2.constEnd()) {
419        QStringList tokens=(*idx).split(" ", QString::SkipEmptyParts);
420
421        if(tokens.count()>=4)
422        {
423
424            if(tokens.at(0)=="=>") { mainitem=true; tokens.removeAt(0); }
425            else mainitem=false;
426
427            exist_disk=getDiskbyName(tokens.at(2));
428                if(exist_disk!=NULL) {
429                    if(!mainitem || !exist_disk->SectorStart) exist_disk->SectorStart=tokens.at(0).toULongLong();
430                    exist_disk->SectorsCount=tokens.at(1).toULongLong();
431                    exist_disk->PartType=tokens.at(3);
432                }
433
434        }
435
436
437        ++idx;
438    }
439
440
441
442
443
444
445
446
447    // GET LABELS FROM GLABEL
448
449    idx=lbl.constBegin();
450
451    while(idx!=lbl.constEnd()) {
452        QStringList tokens=(*idx).split(" ", QString::SkipEmptyParts);
453
454        if(tokens.count()>=2) {
455        QList<vdev_t>::iterator dskit=this->Disks.begin();
456
457        while(dskit!=this->Disks.end())
458        {
459            QList<vdev_t>::iterator sliceit=(*dskit).Partitions.begin();
460            while(sliceit!=(*dskit).Partitions.end()) {
461                QList<vdev_t>::Iterator partit=(*sliceit).Partitions.begin();
462                while(partit!=(*sliceit).Partitions.end()) {
463                    if(tokens.at(2)==(*partit).Name) { (*partit).Alias=tokens.at(0); break; }
464                    ++partit;
465                }
466                    if(partit!=(*sliceit).Partitions.end()) break;
467                    if(tokens.at(2)==(*sliceit).Name) { (*sliceit).Alias=tokens.at(0); break; }
468                    ++sliceit;
469            }
470
471            if(sliceit!=(*dskit).Partitions.end()) break;
472
473            if(tokens.at(2)==(*dskit).Name) { (*dskit).Alias=tokens.at(0); break; }
474
475            ++dskit;
476        }
477        }
478
479        ++idx;
480    }
481
482    // GET MOUNT LOCATIONS FROM MOUNT
483
484    idx=m.constBegin();
485
486    while(idx!=m.constEnd()) {
487        QString MountString=(*idx);
488        QStringList tokens=(*idx).split(" ",QString::SkipEmptyParts);
489        int startpos=MountString.indexOf(" "+tokens.at(2)+" ");
490        int endpos=MountString.lastIndexOf(" (");
491
492        MountString=MountString.mid(startpos+1,endpos-startpos);
493
494        if(tokens.count()<3) { ++ idx; continue; }
495        // FIND DISK OR PARTITION WITH GIVEN NAME
496
497
498        QList<vdev_t>::iterator dskit=this->Disks.begin();
499
500        while(dskit!=this->Disks.end())
501        {
502            QList<vdev_t>::iterator sliceit=(*dskit).Partitions.begin();
503            while(sliceit!=(*dskit).Partitions.end()) {
504                QList<vdev_t>::Iterator partit=(*sliceit).Partitions.begin();
505                while(partit!=(*sliceit).Partitions.end()) {
506                    if(tokens.at(0)=="/dev/"+(*partit).Name) { (*partit).MountPoint=MountString; break; }
507                    if(!(*partit).Alias.isEmpty()) {
508                        if(tokens.at(0)=="/dev/"+(*partit).Alias) { (*partit).MountPoint=MountString; break; }
509                    }
510                    ++partit;
511                }
512                    if(partit!=(*sliceit).Partitions.end()) break;
513                    if(tokens.at(0)=="/dev/"+(*sliceit).Name) { (*sliceit).MountPoint=MountString; break; }
514                    if(!(*sliceit).Alias.isEmpty()) {
515                        if(tokens.at(0)=="/dev/"+(*sliceit).Alias) { (*sliceit).MountPoint=MountString; break; }
516                    }
517                    ++sliceit;
518            }
519
520            if(sliceit!=(*dskit).Partitions.end()) break;
521
522            if(tokens.at(0)=="/dev/"+(*dskit).Name) { (*dskit).MountPoint=MountString; break; }
523            if(!(*dskit).Alias.isEmpty()) {
524                if(tokens.at(0)=="/dev/"+(*dskit).Alias) { (*dskit).MountPoint=MountString; break; }
525            }
526
527            ++dskit;
528        }
529
530
531
532
533        ++idx;
534    }
535
536
537    // FINISHED PROCESSING DEVICES
538
539    // PROCESS THE POOL LIST
540
541    idx=a.constBegin();
542    zpool_t pool;
543    zerror_t err;
544
545    state=0;
546
547    while(idx!=a.constEnd()) {
548
549        QString str=*idx;
550
551        if(str.startsWith("  pool: ")) {
552
553            if(state) {
554                // PREVIOUS POOL FINISHED, STORE
555                this->Pools.append(pool);
556                state=0;
557            }
558            pool.Name=str.remove(0,8);
559            pool.Size=0;
560            pool.Free=0;
561            pool.Status=0;
562            pool.VDevs.clear();
563            pool.Type.clear();
564            ++state;
565        }
566
567        if(str.startsWith(" state: ")&& (state==1)) {
568            QString tmp=*idx;
569            tmp=tmp.remove(0,8);
570            pool.Status=STATE_UNKNOWN;
571            if(tmp.startsWith("ONLINE")) pool.Status=STATE_ONLINE;
572            if(tmp.startsWith("OFFLINE")) pool.Status=STATE_OFFLINE;
573            if(tmp.startsWith("DEGRADED")) pool.Status=STATE_DEGRADED;
574            if(tmp.startsWith("FAULTED")) pool.Status=STATE_FAULTED;
575            if(tmp.startsWith("REMOVED")) pool.Status=STATE_REMOVED;
576            if(tmp.startsWith("UNAVAIL")) pool.Status=STATE_UNAVAIL;
577            ++state;
578        }
579
580        if(str.startsWith("status: ")&& (state==2)) {
581            QString tmp=*idx;
582            tmp.remove(0,8);
583            err.PoolName=pool.Name;
584            err.Error=tmp;
585            ++state;
586        }
587        else {
588        if(state==3) {
589            if(str.startsWith(QString("\t"))) {
590                // ERROR MESSAGE CONTINUES
591                QString tmp=*idx;
592                tmp.remove(0,1);
593                err.Error+=" "+tmp;
594            }
595            else {
596                // ERROR MESSAGE FINISHED
597                this->Errors.append(err);
598                ++state;
599            }
600        }
601        }
602
603
604
605        if(str.startsWith(QString("\t")+pool.Name) && ((state==2)||(state==4)) ) {
606        // START DETAILED LIST OF THE POOL
607            state=5;
608        }
609
610        if(str.startsWith("\tlogs") && (state==5)) {
611            vdev_t vdev;
612
613            vdev.Name=vdev.PartType="logs";
614            vdev.Level=0;
615            vdev.Status=STATE_NOTAPPLICABLE;
616            vdev.InPool=pool.Name;
617            pool.VDevs.append(vdev);
618
619        }
620
621        if(str.startsWith("\tspares") && (state==5)) {
622            vdev_t vdev;
623
624            vdev.Name=vdev.PartType="spares";
625            vdev.Level=0;
626            vdev.Status=STATE_NOTAPPLICABLE;
627            vdev.InPool=pool.Name;
628            pool.VDevs.append(vdev);
629
630        }
631
632        if(str.startsWith("\tcache") && (state==5)) {
633            vdev_t vdev;
634
635            vdev.Name=vdev.PartType="cache";
636            vdev.Level=0;
637            vdev.Status=STATE_NOTAPPLICABLE;
638            vdev.InPool=pool.Name;
639            pool.VDevs.append(vdev);
640
641        }
642
643
644        if(str.startsWith("\t  ")&& (state==5)) {
645            QString tmp=*idx;
646            int level=0;
647            tmp=tmp.remove(0,3);
648            if(tmp.startsWith("  ")) {
649                // THIS IS A DISK INSIDE A MIRROR/RAID, ETC.
650                ++level;
651            }
652            else {
653                if( (pool.VDevs.count()>0) && ((pool.VDevs.last().PartType=="logs")||(pool.VDevs.last().PartType=="cache")||(pool.VDevs.last().PartType=="spares")))
654                {
655                    // THIS DEVICE IS PART OF A SPECIAL GROUP
656                    ++level;
657                }
658            }
659            QStringList tokens=tmp.split(" ",QString::SkipEmptyParts);
660
661            QStringList::const_iterator it=tokens.constBegin();
662            vdev_t vdev;
663
664            if(tokens.count()>0)
665            {
666                QString tok=*it;
667
668                vdev.Name=tok;
669                vdev.Level=level;
670                vdev.Status=STATE_UNKNOWN;
671                ++it;
672                if(it!=tokens.constEnd()) {
673
674
675                QString tmp=*it;
676
677
678                if(tmp=="ONLINE") vdev.Status=STATE_ONLINE;
679                if(tmp=="OFFLINE") vdev.Status=STATE_OFFLINE;
680                if(tmp=="DEGRADED") vdev.Status=STATE_DEGRADED;
681                if(tmp=="FAULTED") vdev.Status=STATE_FAULTED;
682                if(tmp=="REMOVED") vdev.Status=STATE_REMOVED;
683                if(tmp=="UNAVAIL") vdev.Status=STATE_UNAVAIL;
684                if(tmp=="AVAIL") vdev.Status=STATE_AVAIL;
685
686                }
687
688                if(vdev.Status==STATE_UNKNOWN) vdev.Name.clear();
689
690                // CHECK IF VDEV IS A MIRROR OR RAID ARRAY
691
692                if(vdev.Name.startsWith("mirror")) vdev.PartType="mirror";
693                if(vdev.Name.startsWith("raidz3")) vdev.PartType="raidz3";
694                else {
695                if(vdev.Name.startsWith("raidz2")) vdev.PartType="raidz2";
696                else {
697                if(vdev.Name.startsWith("raidz")) vdev.PartType="raidz";
698                }
699                }
700
701                vdev.Partitions.clear();
702
703
704            }
705            if(!vdev.Name.isEmpty()) {
706                vdev.InPool=pool.Name;
707                if(vdev.Level) pool.VDevs.last().Partitions.append(vdev);
708                else pool.VDevs.append(vdev);
709
710                vdev_t *dsk=getDiskbyName(vdev.Name);
711                if(dsk!=NULL) dsk->InPool = pool.Name;
712                if(dsk!=NULL && level==0) {
713                 // THE POOL IS STRIPED ONLY
714                    pool.Type="striped";
715                }
716                if(pool.Type.isEmpty() && !vdev.PartType.isEmpty()) pool.Type=vdev.PartType;
717
718            }
719        }
720
721        if(str.startsWith("errors: ")&& (state==5)) ++state;
722
723
724        idx++;
725    }
726    if(state) {
727        // PREVIOUS POOL FINISHED, STORE
728        this->Pools.append(pool);
729        state=0;
730    }
731
732    // NOW THAT WE HAVE A LIST OF POOLS, GET PROPERTIES FOR ALL ACTIVE ZPOOLS
733
734    QString cmdline="zpool get all";
735    zpool_t n;
736
737    foreach(n,Pools) {
738        cmdline+= " \""+n.Name+"\"";
739    }
740
741    prop=pcbsd::Utils::runShellCommand(cmdline);
742
743
744
745
746
747
748    // PROCESS THE EXPORTED POOL LIST
749
750    idx=i.constBegin();
751
752    state=0;
753
754    while(idx!=i.constEnd()) {
755
756        QString str=*idx;
757
758        if(str.startsWith("   pool: ")) {
759
760            if(state) {
761                // PREVIOUS POOL FINISHED, STORE
762                this->Pools.append(pool);
763                state=0;
764            }
765            pool.Name=str.remove(0,9);
766            pool.Size=0;
767            pool.Free=0;
768            pool.Status=0;
769            pool.VDevs.clear();
770            pool.Type.clear();
771            ++state;
772        }
773
774        if(str.startsWith("     id: ")&& (state==1)) {
775            QString tmp=*idx;
776            tmp=tmp.remove(0,9);
777            pool.Type=tmp;
778        }
779
780
781        if(str.startsWith("  state: ")&& (state==1)) {
782            QString tmp=*idx;
783            tmp=tmp.remove(0,9);
784            pool.Status=STATE_UNKNOWN;
785            if(tmp.startsWith("ONLINE")) pool.Status=STATE_ONLINE;
786            if(tmp.startsWith("OFFLINE")) pool.Status=STATE_OFFLINE;
787            if(tmp.startsWith("DEGRADED")) pool.Status=STATE_DEGRADED;
788            if(tmp.startsWith("FAULTED")) pool.Status=STATE_FAULTED;
789            if(tmp.startsWith("REMOVED")) pool.Status=STATE_REMOVED;
790            if(tmp.startsWith("UNAVAIL")) pool.Status=STATE_UNAVAIL;
791            pool.Status|=STATE_EXPORTED;
792            ++state;
793        }
794
795        /*
796        if(str.startsWith(" status: ")&& (state==2)) {
797            QString tmp=*idx;
798            tmp.remove(0,9);
799            err.PoolName=pool.Name;
800            err.Error=tmp;
801            ++state;
802        }
803        else {
804        if(state==3) {
805            if(str.startsWith(QString("\t"))) {
806                // ERROR MESSAGE CONTINUES
807                QString tmp=*idx;
808                tmp.remove(0,1);
809                err.Error+=" "+tmp;
810            }
811            else {
812                // ERROR MESSAGE FINISHED
813                this->Errors.append(err);
814                ++state;
815            }
816        }
817        }
818        */
819
820
821        if(str.startsWith(QString("\t")+pool.Name) && ((state==2)||(state==4)) ) {
822        // START DETAILED LIST OF THE POOL
823            state=5;
824        }
825
826        if(str.startsWith("\tlogs") && (state==5)) {
827            vdev_t vdev;
828
829            vdev.Name=vdev.PartType="logs";
830            vdev.Level=0;
831            vdev.Status=STATE_NOTAPPLICABLE;
832            vdev.InPool=pool.Name;
833            pool.VDevs.append(vdev);
834
835        }
836
837        if(str.startsWith("\tspares") && (state==5)) {
838            vdev_t vdev;
839
840            vdev.Name=vdev.PartType="spares";
841            vdev.Level=0;
842            vdev.Status=STATE_NOTAPPLICABLE;
843            vdev.InPool=pool.Name;
844            pool.VDevs.append(vdev);
845
846        }
847
848        if(str.startsWith("\tcache") && (state==5)) {
849            vdev_t vdev;
850
851            vdev.Name=vdev.PartType="cache";
852            vdev.Level=0;
853            vdev.Status=STATE_NOTAPPLICABLE;
854            vdev.InPool=pool.Name;
855            pool.VDevs.append(vdev);
856
857        }
858
859
860        if(str.startsWith("\t  ")&& (state==5)) {
861            QString tmp=*idx;
862            int level=0;
863            tmp=tmp.remove(0,3);
864            if(tmp.startsWith("  ")) {
865                // THIS IS A DISK INSIDE A MIRROR/RAID, ETC.
866                ++level;
867            }
868            else {
869                if( (pool.VDevs.count()>0) && ((pool.VDevs.last().PartType=="logs")||(pool.VDevs.last().PartType=="cache")||(pool.VDevs.last().PartType=="spares")))
870                {
871                    // THIS DEVICE IS PART OF A SPECIAL GROUP
872                    ++level;
873                }
874            }
875            QStringList tokens=tmp.split(" ",QString::SkipEmptyParts);
876
877            QStringList::const_iterator it=tokens.constBegin();
878            vdev_t vdev;
879
880            if(tokens.count()>0)
881            {
882                QString tok=*it;
883
884                vdev.Name=tok;
885                vdev.Level=level;
886                vdev.Status=STATE_UNKNOWN;
887                ++it;
888                if(it!=tokens.constEnd()) {
889
890                QString tmp=*it;
891
892
893                if(tmp=="ONLINE") vdev.Status=STATE_ONLINE;
894                if(tmp=="OFFLINE") vdev.Status=STATE_OFFLINE;
895                if(tmp=="DEGRADED") vdev.Status=STATE_DEGRADED;
896                if(tmp=="FAULTED") vdev.Status=STATE_FAULTED;
897                if(tmp=="REMOVED") vdev.Status=STATE_REMOVED;
898                if(tmp=="UNAVAIL") vdev.Status=STATE_UNAVAIL;
899                if(tmp=="AVAIL") vdev.Status=STATE_AVAIL;
900                }
901
902                if(vdev.Status==STATE_UNKNOWN) vdev.Name.clear();
903
904                // CHECK IF VDEV IS A MIRROR OR RAID ARRAY
905
906                if(vdev.Name.startsWith("mirror")) vdev.PartType="mirror";
907                if(vdev.Name.startsWith("raidz3")) vdev.PartType="raidz3";
908                else {
909                if(vdev.Name.startsWith("raidz2")) vdev.PartType="raidz2";
910                else {
911                if(vdev.Name.startsWith("raidz")) vdev.PartType="raidz";
912                }
913                }
914
915                vdev.Partitions.clear();
916
917
918            }
919            if(!vdev.Name.isEmpty()) {
920                vdev.InPool=pool.Name;
921                if(vdev.Level) pool.VDevs.last().Partitions.append(vdev);
922                else pool.VDevs.append(vdev);
923
924                vdev_t *dsk=getDiskbyName(vdev.Name);
925                if(dsk!=NULL) dsk->InPool = pool.Name;
926                //if(dsk!=NULL && level==0) {
927                 // THE POOL IS STRIPED ONLY
928                //    pool.Type="striped";
929                //}
930                //if(pool.Type.isEmpty() && !vdev.PartType.isEmpty()) pool.Type=vdev.PartType;
931
932            }
933        }
934
935        if(str.startsWith("errors: ")&& (state==5)) ++state;
936
937
938        idx++;
939    }
940    if(state) {
941        // PREVIOUS POOL FINISHED, STORE
942        this->Pools.append(pool);
943        state=0;
944    }
945
946
947
948    // PROCESS THE DESTROYED POOL LIST
949
950    idx=d.constBegin();
951
952    state=0;
953
954    while(idx!=d.constEnd()) {
955
956        QString str=*idx;
957
958        if(str.startsWith("   pool: ")) {
959
960            if(state) {
961                // PREVIOUS POOL FINISHED, STORE
962                if(pool.Status!=(STATE_FAULTED|STATE_DESTROYED)) {
963                    // IF POOL IS DAMAGED, CANNOT BE RECOVERED. DON'T SHOW IN THE LIST
964                this->Pools.append(pool);
965                }
966                state=0;
967
968            }
969            pool.Name=str.remove(0,9);
970            pool.Size=0;
971            pool.Free=0;
972            pool.Status=0;
973            pool.VDevs.clear();
974            pool.Type.clear();
975            ++state;
976        }
977
978        if(str.startsWith("     id: ")&& (state==1)) {
979            QString tmp=*idx;
980            tmp=tmp.remove(0,9);
981            pool.Type=tmp;
982        }
983
984
985        if(str.startsWith("  state: ")&& (state==1)) {
986            QString tmp=*idx;
987            tmp=tmp.remove(0,9);
988            pool.Status=STATE_UNKNOWN;
989            if(tmp.startsWith("ONLINE")) pool.Status=STATE_ONLINE;
990            if(tmp.startsWith("OFFLINE")) pool.Status=STATE_OFFLINE;
991            if(tmp.startsWith("DEGRADED")) pool.Status=STATE_DEGRADED;
992            if(tmp.startsWith("FAULTED")) pool.Status=STATE_FAULTED;
993            if(tmp.startsWith("REMOVED")) pool.Status=STATE_REMOVED;
994            if(tmp.startsWith("UNAVAIL")) pool.Status=STATE_UNAVAIL;
995            pool.Status|=STATE_DESTROYED;
996            ++state;
997        }
998
999
1000
1001
1002        if(str.startsWith(QString("\t")+pool.Name) && ((state==2)||(state==4)) ) {
1003        // START DETAILED LIST OF THE POOL
1004            state=5;
1005        }
1006
1007        if(str.startsWith("\tlogs") && (state==5)) {
1008            vdev_t vdev;
1009
1010            vdev.Name=vdev.PartType="logs";
1011            vdev.Level=0;
1012            vdev.Status=STATE_NOTAPPLICABLE;
1013            //vdev.InPool=pool.Name;
1014            pool.VDevs.append(vdev);
1015
1016        }
1017
1018        if(str.startsWith("\tspares") && (state==5)) {
1019            vdev_t vdev;
1020
1021            vdev.Name=vdev.PartType="spares";
1022            vdev.Level=0;
1023            vdev.Status=STATE_NOTAPPLICABLE;
1024            //vdev.InPool=pool.Name;
1025            pool.VDevs.append(vdev);
1026
1027        }
1028
1029        if(str.startsWith("\tcache") && (state==5)) {
1030            vdev_t vdev;
1031
1032            vdev.Name=vdev.PartType="cache";
1033            vdev.Level=0;
1034            vdev.Status=STATE_NOTAPPLICABLE;
1035            //vdev.InPool=pool.Name;
1036            pool.VDevs.append(vdev);
1037
1038        }
1039
1040
1041        if(str.startsWith("\t  ")&& (state==5)) {
1042            QString tmp=*idx;
1043            int level=0;
1044            tmp=tmp.remove(0,3);
1045            if(tmp.startsWith("  ")) {
1046                // THIS IS A DISK INSIDE A MIRROR/RAID, ETC.
1047                ++level;
1048            }
1049            else {
1050                if( (pool.VDevs.count()>0) && ((pool.VDevs.last().PartType=="logs")||(pool.VDevs.last().PartType=="cache")||(pool.VDevs.last().PartType=="spares")))
1051                {
1052                    // THIS DEVICE IS PART OF A SPECIAL GROUP
1053                    ++level;
1054                }
1055            }
1056            QStringList tokens=tmp.split(" ",QString::SkipEmptyParts);
1057
1058            QStringList::const_iterator it=tokens.constBegin();
1059            vdev_t vdev;
1060
1061            if(tokens.count()>0)
1062            {
1063                QString tok=*it;
1064
1065                vdev.Name=tok;
1066                vdev.Level=level;
1067                vdev.Status=STATE_UNKNOWN;
1068
1069                ++it;
1070                if(it!=tokens.constEnd()) {
1071
1072                QString tmp=*it;
1073
1074
1075                if(tmp=="ONLINE") vdev.Status=STATE_ONLINE;
1076                if(tmp=="OFFLINE") vdev.Status=STATE_OFFLINE;
1077                if(tmp=="DEGRADED") vdev.Status=STATE_DEGRADED;
1078                if(tmp=="FAULTED") vdev.Status=STATE_FAULTED;
1079                if(tmp=="REMOVED") vdev.Status=STATE_REMOVED;
1080                if(tmp=="UNAVAIL") vdev.Status=STATE_UNAVAIL;
1081                if(tmp=="AVAIL") vdev.Status=STATE_AVAIL;
1082
1083                }
1084                if(vdev.Status==STATE_UNKNOWN) vdev.Name.clear();
1085
1086                // CHECK IF VDEV IS A MIRROR OR RAID ARRAY
1087
1088                if(vdev.Name.startsWith("mirror")) vdev.PartType="mirror";
1089                if(vdev.Name.startsWith("raidz3")) vdev.PartType="raidz3";
1090                else {
1091                if(vdev.Name.startsWith("raidz2")) vdev.PartType="raidz2";
1092                else {
1093                if(vdev.Name.startsWith("raidz")) vdev.PartType="raidz";
1094                }
1095                }
1096
1097                vdev.Partitions.clear();
1098
1099
1100            }
1101            if(!vdev.Name.isEmpty()) {
1102                //vdev.InPool=pool.Name;
1103                if(vdev.Level) pool.VDevs.last().Partitions.append(vdev);
1104                else pool.VDevs.append(vdev);
1105
1106                //vdev_t *dsk=getDiskbyName(vdev.Name);
1107                //if(dsk!=NULL) dsk->InPool = pool.Name;
1108                //if(dsk!=NULL && level==0) {
1109                 // THE POOL IS STRIPED ONLY
1110                //    pool.Type="striped";
1111                //}
1112                //if(pool.Type.isEmpty() && !vdev.PartType.isEmpty()) pool.Type=vdev.PartType;
1113
1114            }
1115        }
1116
1117        if(str.startsWith("errors: ")&& (state==5)) ++state;
1118
1119
1120        idx++;
1121    }
1122    if(state) {
1123        // PREVIOUS POOL FINISHED, STORE
1124        if(pool.Status!=(STATE_FAULTED|STATE_DESTROYED)) {
1125            // IF POOL IS DAMAGED, CANNOT BE RECOVERED. DON'T SHOW IN THE LIST
1126        this->Pools.append(pool);
1127        state=0;
1128        }
1129    }
1130
1131
1132// EXTRACT PROPERTIES
1133
1134QStringList::const_iterator pit=prop.constBegin();
1135
1136while(pit!=prop.constEnd())
1137{
1138    // SEARCH THROUGH ALL CURRENT POOLS TO SEE IF NAMES MATCH
1139    QList<zpool_t>::const_iterator poolit=Pools.constBegin();
1140    QList<zprop_t> *proplist=NULL;
1141    int matchinglength=0;
1142    while(poolit!=Pools.constEnd())
1143    {
1144        if(!((*poolit).Status&(STATE_DESTROYED|STATE_EXPORTED)) )
1145        {
1146            // THE POOL IS ACTIVE
1147            if((*pit).startsWith((*poolit).Name)) {
1148                if(matchinglength<(*poolit).Name.length()) { proplist=(QList<zprop_t> *)&(*poolit).Properties; matchinglength=(*poolit).Name.length(); }
1149            }
1150        }
1151
1152     ++poolit;
1153    }
1154    if(proplist!=NULL)
1155    {
1156        QString tmpline=(*pit);
1157        QStringList a=tmpline.remove(0,matchinglength).split(" ",QString::SkipEmptyParts);
1158        if(a.count()>=2) {
1159        zprop_t proptmp;
1160        proptmp.Name=a[0];
1161        QList<QString> val=a.mid(1,a.count()-2);
1162        proptmp.Value.clear();
1163        while(val.count()) { proptmp.Value+=val.first(); val.removeFirst(); if(val.count()) proptmp.Value+=" "; }
1164
1165        proplist->append(proptmp);
1166        }
1167    }
1168
1169
1170
1171    ++pit;
1172}
1173
1174
1175
1176// BEGIN PROCESSING FILESYSTEMS
1177
1178
1179QStringList::const_iterator fsit=zfsl.constBegin();
1180
1181while(fsit!=zfsl.constEnd())
1182{
1183
1184    QString tmpline=(*fsit);
1185
1186    QStringList line=tmpline.split("\t",QString::SkipEmptyParts);
1187
1188    if(line.count()>=4) {
1189     zfs_t tmp;
1190     tmp.FullPath=line[0];
1191     tmp.Properties.clear();
1192     FileSystems.append(tmp);
1193    }
1194
1195    ++fsit;
1196}
1197
1198
1199// GET ALL PROPERTIES FOR FILESYSTEMS
1200
1201
1202QStringList::const_iterator fspr=zfspr.constBegin();
1203
1204while(fspr!=zfspr.constEnd())
1205{
1206
1207    QStringList line=(*fspr).split("\t",QString::SkipEmptyParts);
1208
1209    if(line.count()>=4) {
1210     zprop_t tmp;
1211     zfs_t *zptr=getFileSystembyPath(line[0]);
1212     if(zptr) {
1213
1214         tmp.Name=line[1];
1215         tmp.Value=line[2];
1216         if(tmp.Value=="-") tmp.Value.clear();
1217         tmp.From=line[3];
1218         if(tmp.From=="-") tmp.Source=ZFS_SRCNONE;
1219         if(tmp.From=="default") tmp.Source=ZFS_SRCDEFAULT;
1220         if(tmp.From=="local")  tmp.Source=ZFS_SRCLOCAL;
1221         if(tmp.From.startsWith("inherited")) tmp.Source=ZFS_SRCINHERIT;
1222
1223         zptr->Properties.append(tmp);
1224    }
1225    }
1226
1227    ++fspr;
1228}
1229
1230
1231
1232}
1233
1234void ZManagerWindow::refreshState()
1235{
1236    QToolButton splash;
1237    splash.setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
1238    splash.setMinimumHeight(100);
1239    splash.setMinimumWidth(100);
1240    splash.setMaximumHeight(100);
1241    splash.setMaximumWidth(100);
1242
1243    splash.setText(tr("Refreshing..."));
1244    splash.setIcon(QIcon(":/icons/server-database.png"));
1245    splash.setIconSize(QSize(48,48));
1246    splash.setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
1247    splash.setWindowFlags(Qt::SplashScreen);
1248
1249    int x=this->pos().x();
1250    int y=this->pos().y();
1251
1252    x+=this->width()/2;
1253    y+=this->height()/2;
1254
1255    x-=50;
1256    y-=50;
1257
1258    splash.move(x,y);
1259
1260    splash.show();
1261
1262//    ui->zpoolList->header()->setStretchLastSection(false);
1263//    ui->zpoolList->header()->setResizeMode(0,QHeaderView::Stretch);
1264//    ui->zpoolList->header()->setResizeMode(1,QHeaderView::ResizeToContents);
1265
1266//    ui->deviceList->header()->setStretchLastSection(false);
1267//    ui->deviceList->header()->setResizeMode(0,QHeaderView::Stretch);
1268//    ui->deviceList->header()->setResizeMode(1,QHeaderView::ResizeToContents);
1269
1270
1271
1272    ui->zpoolList->clear();
1273    ui->deviceList->clear();
1274    ui->fspoolList->clear();
1275    ui->fsList->clear();
1276
1277
1278    GetCurrentTopology();
1279
1280
1281    // SHOW ERRORS
1282    if(Errors.count()>0) {
1283
1284        ui->statusLabel->setText(Errors.at(0).Error);
1285        ui->poolLabel->setText(Errors.at(0).PoolName);
1286        ui->frameStatus->setVisible(true);
1287    }
1288    else     ui->frameStatus->setVisible(false);
1289
1290
1291
1292    // ADD ALL POOLS
1293
1294    if(Pools.count()!=0) {
1295
1296    int itindex=0;
1297
1298    QList<zpool_t>::iterator it=Pools.begin();
1299
1300    while(it!=Pools.end()) {
1301        // ADD POOL TO THE POOL LIST
1302    QTreeWidgetItem *item=new QTreeWidgetItem(ui->zpoolList);
1303    item->setText(0,(*it).Name);
1304    item->setIcon(0,QIcon(":/icons/server-database.png"));
1305    item->setText(1,getStatusString((*it).Status));
1306    item->setData(0,Qt::UserRole,QVariant(itindex));
1307    if((*it).Status&(STATE_DESTROYED|STATE_EXPORTED)) { item->setIcon(1,QIcon(":/icons/task-reject.png")); item->setDisabled(true); }
1308    else {
1309    if((*it).Status==STATE_ONLINE) item->setIcon(1,QIcon(":/icons/task-complete.png"));
1310    else if( ((*it).Status==STATE_FAULTED)|| ((*it).Status==STATE_UNAVAIL)|| ((*it).Status==STATE_REMOVED)) item->setIcon(1,QIcon(":/icons/task-reject.png"));
1311        else item->setIcon(1,QIcon(":/icons/task-attention.png"));
1312
1313    // ALSO ADD IT TO THE FILESYSTEMS TAB, BUT ONLY IF IT'S A VALID POOL (NOT DESTROYED OR EXPORTED)
1314    QTreeWidgetItem *fsitem=new QTreeWidgetItem(ui->fspoolList);
1315    fsitem->setText(0,(*it).Name);
1316    fsitem->setIcon(0,QIcon(":/icons/server-database.png"));
1317    fsitem->setData(0,Qt::UserRole,QVariant(itindex));
1318    }
1319
1320
1321
1322
1323
1324    QList<vdev_t>::const_iterator devit=(*it).VDevs.constBegin();
1325
1326    while(devit!=(*it).VDevs.constEnd())
1327    {
1328        QTreeWidgetItem *subitem=new QTreeWidgetItem(item);
1329
1330        // TODO: ADD MORE DESCRIPTION HERE FROM THE GEOM INFO OF THE DEVICE
1331        subitem->setText(0,(*devit).Name);
1332        // TODO: USE APPROPRIATE ICONS FOR DISKS, SLICES, FILES, ETC.
1333        subitem->setIcon(0,QIcon(":/icons/drive-harddisk.png"));
1334        subitem->setData(0,Qt::UserRole,QVariant(-1));
1335        subitem->setText(1,getStatusString((*devit).Status));
1336        if((*devit).Status==STATE_ONLINE) subitem->setIcon(1,QIcon(":/icons/task-complete.png"));
1337        else if( ((*devit).Status==STATE_FAULTED)|| ((*devit).Status==STATE_UNAVAIL)|| ((*devit).Status==STATE_REMOVED)) subitem->setIcon(1,QIcon(":/icons/task-reject.png"));
1338            else if((*devit).Status!=STATE_NOTAPPLICABLE) subitem->setIcon(1,QIcon(":/icons/task-attention.png"));
1339
1340        // ADD SUB-VDEVS IN CASE THIS IS A MIRROR OR RAIDZ VIRTUAL DEVICE
1341
1342        QList<vdev_t>::const_iterator level2it=(*devit).Partitions.constBegin();
1343
1344        while(level2it!=(*devit).Partitions.constEnd())
1345        {
1346            QTreeWidgetItem *subsubitem=new QTreeWidgetItem(subitem);
1347
1348            // TODO: ADD MORE DESCRIPTION HERE FROM THE GEOM INFO OF THE DEVICE
1349            subsubitem->setText(0,(*level2it).Name);
1350            // TODO: USE APPROPRIATE ICONS FOR DISKS, SLICES, FILES, ETC.
1351            subsubitem->setIcon(0,QIcon(":/icons/drive-harddisk.png"));
1352            subsubitem->setData(0,Qt::UserRole,QVariant(-1));
1353            subsubitem->setText(1,getStatusString((*level2it).Status));
1354            if((*level2it).Status==STATE_ONLINE || (*level2it).Status==STATE_AVAIL) subsubitem->setIcon(1,QIcon(":/icons/task-complete.png"));
1355            else if( ((*level2it).Status==STATE_FAULTED)|| ((*level2it).Status==STATE_UNAVAIL)|| ((*level2it).Status==STATE_REMOVED)) subsubitem->setIcon(1,QIcon(":/icons/task-reject.png"));
1356                else subsubitem->setIcon(1,QIcon(":/icons/task-attention.png"));
1357
1358
1359            ++level2it;
1360
1361        }
1362
1363
1364        ++devit;
1365    }
1366
1367    ++it;
1368    ++itindex;
1369    }
1370
1371    }
1372
1373    else {
1374        QTreeWidgetItem *item=new QTreeWidgetItem(ui->zpoolList);
1375        item->setText(0,tr("No pools available, right click to create a new one..."));
1376   }
1377
1378
1379
1380    // ADD ALL DISK DEVICES
1381
1382
1383    QList<vdev_t>::const_iterator idx=Disks.constBegin();
1384
1385
1386    while(idx!=Disks.constEnd()) {
1387        QTreeWidgetItem *item=new QTreeWidgetItem(ui->deviceList);
1388
1389        QString sz;
1390        if((*idx).Size!=0) sz=" ("+printBytes((*idx).Size)+")";
1391        else sz=tr(" (No media in drive)");
1392        if(!(*idx).PartType.isEmpty()) { sz+=" [" + (*idx).PartType + "]"; }
1393        sz+="\n";
1394        item->setText(0,(*idx).Name + sz + (*idx).Description);
1395        item->setIcon(0,QIcon(":/icons/drive-harddisk.png"));
1396        if((*idx).MountPoint.isEmpty()) {
1397            // IS NOT MOUNTED, CHECK IF IT HAS ANY PARTITIONS
1398            if((*idx).Partitions.count()==0) {
1399                // NO PARTITIONS, IT'S EITHER UNUSED OR PART OF A POOL (DEDICATED)
1400                if((*idx).InPool.isEmpty())  { if((*idx).Size!=0) item->setText(1,tr("Avaliable")); else item->setText(1,tr("No disk")); }
1401                else item->setText(1,tr("ZPool: ")+(*idx).InPool);
1402            } else item->setText(1,tr("Sliced")); }
1403        else item->setText(1,tr("Mounted: ")+(*idx).MountPoint);
1404
1405        // ADD SLICES HERE AS SUBITEMS
1406        if((*idx).Partitions.count()>0) {
1407            QList<vdev_t>::const_iterator partidx=(*idx).Partitions.constBegin();
1408
1409
1410            while(partidx!=(*idx).Partitions.constEnd()) {
1411
1412            QTreeWidgetItem *subitem=new QTreeWidgetItem(item);
1413
1414            QString sz;
1415
1416            sz=" ("+printBytes((*partidx).Size)+")\n";
1417            subitem->setText(0,(*partidx).Name + sz + " ["+(*partidx).PartType+"]");
1418            subitem->setIcon(0,QIcon(":/icons/partitionmanager.png"));
1419
1420            if((*partidx).MountPoint.isEmpty()) {
1421                if((*partidx).Partitions.count()==0) {
1422                    if( (*partidx).InPool.isEmpty()) {
1423                        if((*partidx).PartType.isEmpty() || ((*partidx).PartType=="BSD")) subitem->setText(1,tr("Available")); else subitem->setText(1,tr("Unmounted"));
1424                    } else subitem->setText(1,tr("ZPool: ") + (*partidx).InPool);
1425                } else subitem->setText(1,tr("Partitioned")); }
1426            else subitem->setText(1,tr("Mounted: ")+(*partidx).MountPoint);
1427
1428            if((*partidx).Partitions.count()>0) {
1429                QList<vdev_t>::const_iterator part2idx=(*partidx).Partitions.constBegin();
1430
1431
1432                while(part2idx!=(*partidx).Partitions.constEnd()) {
1433
1434                QTreeWidgetItem *subsubitem=new QTreeWidgetItem(subitem);
1435
1436                QString sz;
1437
1438                sz=" ("+printBytes((*part2idx).Size)+")\n";
1439                subsubitem->setText(0,(*part2idx).Name + sz + " ["+(*part2idx).PartType+"]");
1440                subsubitem->setIcon(0,QIcon(":/icons/kdf.png"));
1441
1442                if((*part2idx).MountPoint.isEmpty()) {
1443
1444                    if((*part2idx).InPool.isEmpty()) {
1445                        if( (*part2idx).PartType.isEmpty()) subsubitem->setText(1,tr("Available")); else subsubitem->setText(1,tr("Unmounted"));
1446                    } else subsubitem->setText(1,tr("ZPool: ") + (*part2idx).InPool);
1447                }
1448                else subsubitem->setText(1,tr("Mounted: ")+(*part2idx).MountPoint);
1449
1450
1451                ++part2idx;
1452                }
1453
1454
1455            }
1456
1457
1458
1459
1460            ++partidx;
1461            }
1462
1463
1464        }
1465
1466    ++idx;
1467    }
1468
1469ui->zpoolList->expandAll();
1470ui->deviceList->expandAll();
1471if(ui->fspoolList->topLevelItem(0)) ui->fspoolList->setCurrentItem(ui->fspoolList->topLevelItem(0));
1472
1473
1474// CHECK SECTION SIZES FOR ALL LISTS AND ADJUST ACCORDINGLY
1475
1476/*
1477if(ui->deviceList->header()->sectionSize(1)>ui->deviceList->width()/2) {
1478    ui->deviceList->header()->setResizeMode(1,QHeaderView::Interactive);
1479    ui->deviceList->header()->resizeSection(1,ui->deviceList->width()/3);
1480
1481}
1482else     {
1483    ui->deviceList->header()->setResizeMode(1,QHeaderView::Interactive);
1484
1485}
1486*/
1487/*
1488if(ui->zpoolList->header()->sectionSize(1)>ui->zpoolList->width()/2) {
1489    ui->zpoolList->header()->setResizeMode(1,QHeaderView::Interactive);
1490    ui->zpoolList->header()->resizeSection(1,ui->zpoolList->width()/3);
1491
1492}
1493else     {
1494    ui->zpoolList->header()->setResizeMode(1,QHeaderView::Interactive);
1495}
1496*/
1497
1498}
1499
1500
1501bool ZManagerWindow::close()
1502{
1503    return QMainWindow::close();
1504}
1505
1506
1507const QString ZManagerWindow::getStatusString(int status)
1508{
1509    QString result;
1510
1511    if(status&STATE_EXPORTED) {
1512        result=tr("(Exported)\n");
1513        status&=~STATE_EXPORTED;
1514    }
1515
1516    if(status&STATE_DESTROYED) {
1517        result=tr("(Destroyed)\n");
1518        status&=~STATE_DESTROYED;
1519    }
1520
1521    switch(status)
1522    {
1523    case STATE_NOTAPPLICABLE:
1524        return result;
1525    case STATE_OFFLINE:
1526        return result+tr("Offline");
1527    case STATE_ONLINE:
1528        return result+tr("Online");
1529    case STATE_DEGRADED:
1530        return result+tr("Degraded");
1531    case STATE_FAULTED:
1532        return result+tr("Faulted");
1533    case STATE_REMOVED:
1534        return result+tr("Removed");
1535    case STATE_UNAVAIL:
1536        return result+tr("Unavailable");
1537    case STATE_AVAIL:
1538        return result+tr("Available");
1539    default:
1540    case STATE_UNKNOWN:
1541        return result+tr("Unknown");
1542
1543    }
1544}
1545
1546
1547
1548
1549void ZManagerWindow::zpoolContextMenu(QPoint p)
1550{
1551    struct zactions zpoolmenu[]={
1552        /*  QString menutext        ,   int triggermask, triggerflags    ,  action_func slot */
1553        { QString(tr("Create new pool"))    ,     ~ITEM_ISPOOL, ITEM_NONE         ,  SLOT(zpoolCreate(bool)) },
1554        { QString(tr("Rename pool"))    ,     ~ITEM_ISPOOL, ITEM_NONE         ,  SLOT(zpoolRename(bool)) },
1555        { QString(tr("Destroy pool"))       ,    ITEM_TYPE|ITEM_ISEXPORTED|ITEM_ISDESTROYED, ITEM_ISPOOL         ,  SLOT(zpoolDestroy(bool)) },
1556        { QString(tr("Add devices..."))       ,    ITEM_TYPE|ITEM_ISEXPORTED|ITEM_ISDESTROYED, ITEM_ISPOOL         ,  SLOT(zpoolAdd(bool)) },
1557        { QString(tr("Add log devices..."))       ,    ITEM_TYPE|ITEM_ISEXPORTED|ITEM_ISDESTROYED, ITEM_ISPOOL         ,  SLOT(zpoolAddLog(bool)) },
1558        { QString(tr("Add cache devices..."))       ,    ITEM_TYPE|ITEM_ISEXPORTED|ITEM_ISDESTROYED, ITEM_ISPOOL         ,  SLOT(zpoolAddCache(bool)) },
1559        { QString(tr("Add spare devices..."))       ,    ITEM_TYPE|ITEM_ISEXPORTED|ITEM_ISDESTROYED, ITEM_ISPOOL         ,  SLOT(zpoolAddSpare(bool)) },
1560        { QString(tr("Scrub"))      ,   ITEM_TYPE|ITEM_ISEXPORTED|ITEM_ISDESTROYED, ITEM_ISPOOL         ,  SLOT(zpoolScrub(bool)) },
1561        { QString(tr("Export pool"))      ,   ITEM_TYPE|ITEM_ISEXPORTED|ITEM_ISDESTROYED, ITEM_ISPOOL         ,  SLOT(zpoolExport(bool)) },
1562        { QString(tr("Import pool"))    ,     ITEM_ALL, ITEM_ISPOOL|ITEM_ISEXPORTED         ,  SLOT(zpoolImport(bool)) },
1563        { QString(tr("Recover destroyed pool"))    ,     ITEM_ALL, ITEM_ISPOOL|ITEM_ISDESTROYED         ,  SLOT(zpoolImport(bool)) },
1564        { QString(tr("Properties..."))      ,   ITEM_TYPE|ITEM_ISEXPORTED|ITEM_ISDESTROYED, ITEM_ISPOOL         ,  SLOT(zpoolEditProperties(bool)) },
1565
1566        { QString(tr("Attach (mirror) device..."))      ,   ITEM_TYPE|PARENT(ITEM_TYPE&~(ITEM_ISMIRROR|ITEM_ISPOOL)), ITEM_ISDISK    ,  SLOT(zpoolAttachDevice(bool)) },
1567        { QString(tr("Detach from mirror"))      ,   ITEM_TYPE|PARENT(ITEM_TYPE), ITEM_ISDISK|PARENT(ITEM_ISMIRROR)    ,  SLOT(zpoolDetachDevice(bool)) },
1568        { QString(tr("Take offline"))      ,   ITEM_ALL|PARENT(ITEM_TYPE&~(ITEM_ISMIRROR|ITEM_ISPOOL|ITEM_ISRAIDZ)), ITEM_ISDISK   ,  SLOT(zpoolOfflineDevice(bool)) },
1569        { QString(tr("Bring online"))      ,   ITEM_STATE, ITEM_ISOFFLINE   ,  SLOT(zpoolOnlineDevice(bool)) },
1570
1571        { QString(tr("Remove"))      ,   ITEM_TYPE|PARENT(ITEM_TYPE&~(ITEM_ISLOG|ITEM_ISCACHE|ITEM_ISSPARE)), ITEM_ISDISK    ,  SLOT(zpoolRemoveDevice(bool)) },
1572
1573
1574
1575
1576        { QString()       ,   0, 0      ,  NULL }
1577
1578    };
1579
1580    int flags=0;
1581    int idx;
1582
1583    QMenu m(tr("zpool Menu"),this);
1584
1585    QTreeWidgetItem *item=ui->zpoolList->itemAt(p);
1586
1587    if(item!=NULL) {
1588        // FIRST DETERMINE IF THE ITEM IS A POOL OR A DEVICE
1589        qDebug() << item->text(0);
1590        flags=0;
1591        if( (lastSelectedPool=getZpoolbyName(item->text(0),item->data(0,Qt::UserRole).toInt()) )) {
1592            flags=ITEM_ISPOOL;
1593            lastSelectedVdev=NULL;
1594
1595            if(lastSelectedPool->Status&STATE_EXPORTED) flags|=ITEM_ISEXPORTED;
1596            if(lastSelectedPool->Status&STATE_DESTROYED) flags|=ITEM_ISDESTROYED;
1597
1598        }
1599        else {
1600        // IT MUST BE A DEVICE, GET MORE INFORMATION
1601            if(!item->isDisabled()) {
1602            if( (lastSelectedVdev=getVDevbyName(item->text(0)))) flags=zpoolGetVDEVType(lastSelectedVdev);
1603            else flags=ITEM_NONE;   // NO ITEM WAS SELECTED, USER MUST'VE CLICKED IN THE BLANK SPACE
1604            }
1605            else flags=ITEM_NONE;
1606        }
1607    }
1608
1609    for(idx=0;zpoolmenu[idx].slot!=NULL;++idx)
1610    {
1611        if( (flags & zpoolmenu[idx].triggermask) == zpoolmenu[idx].triggerflags) {
1612
1613            QAction *act=m.addAction(zpoolmenu[idx].menutext);
1614            connect(act,SIGNAL(triggered(bool)),this,zpoolmenu[idx].slot);
1615
1616        }
1617    }
1618
1619
1620    needRefresh=false;
1621    m.exec(ui->zpoolList->viewport()->mapToGlobal(p));
1622
1623    if(needRefresh) refreshState();
1624}
1625
1626void ZManagerWindow::deviceContextMenu(QPoint p)
1627{
1628    QMenu m(tr("Device Menu"),this);
1629    QTreeWidgetItem *item=ui->deviceList->itemAt(p);
1630
1631    vdev_t *dev=getDiskbyName(item->text(0).split(" ",QString::SkipEmptyParts).at(0));
1632
1633    if(dev==NULL) return;
1634
1635    if(dev->Name.startsWith("cd")) return;  // NOTHING TO DO ON A CD/DVD DRIVE
1636
1637    if(!dev->InPool.isEmpty()) {
1638        // DISK IS IN A POOL, NOTHING TO DO UNTIL IT'S REMOVED
1639        return;
1640    }
1641
1642    if(!dev->MountPoint.isEmpty()) m.addAction(tr("Unmount"))->setData(QVariant(1));  // OFFER TO UNMOUNT IF PARTITION IS MOUNTED
1643    else {
1644        if( (!dev->PartType.isEmpty()) && (dev->Partitions.count()==0) && (dev->PartType!="MBR") && (dev->PartType!="GPT") && (dev->PartType!="BSD") && (dev->PartType!="freebsd") )
1645        {
1646            // THIS IS A PARTITION WITH A FILE SYSTEM BUT NOT MOUNTED
1647            m.addAction(tr("Mount"))->setData(QVariant(2));
1648        }
1649
1650        if(dev->PartType.isEmpty() && dev->Level<2) {
1651            // THIS IS NOT PARTITIONED, OFFER TO CREATE A PARTITION TABLE
1652            m.addAction(tr("Create MBR partition table"))->setData(QVariant(3));
1653            m.addAction(tr("Create GPT partition table"))->setData(QVariant(4));
1654        }
1655        if(dev->PartType=="freebsd" && dev->Level<2) {
1656            // THIS IS NOT PARTITIONED, OFFER TO CREATE A PARTITION TABLE
1657            m.addAction(tr("Create BSD partition table"))->setData(QVariant(5));
1658        }
1659
1660        else if( (dev->Level<2) && ((dev->PartType=="MBR") || (dev->PartType=="GPT") || (dev->PartType=="BSD"))) {
1661            // THIS DISK HAS PARTITIONS, OFFER TO ADD A NEW ONE
1662            if(dev->Partitions.count()==0) m.addAction(tr("Delete Partition Table"))->setData(QVariant(6));
1663            if(dev->Level==0) m.addAction(tr("Add new slice"))->setData(QVariant(7));
1664            else m.addAction(tr("Add new partition"))->setData(QVariant(7));
1665        }
1666
1667        if( (dev->Partitions.count()==0) && (dev->Level>0)) {
1668            // OFFER TO DELETE THE PARTITION
1669            if(dev->Level==1) m.addAction(tr("Destroy this slice"))->setData(QVariant(8));
1670            else m.addAction(tr("Destroy this partition"))->setData(QVariant(8));
1671
1672        }
1673
1674
1675    }
1676
1677
1678
1679    QAction *result=m.exec(ui->deviceList->viewport()->mapToGlobal(p));
1680
1681    if(result!=NULL) {
1682        int selected=result->data().toInt();
1683        bool result;
1684        switch(selected)
1685        {
1686        case 1:
1687            result=deviceUnmount(dev);
1688            break;
1689        case 2:
1690            result=deviceMount(dev);
1691            break;
1692        case 3:
1693        case 4:
1694        case 5:
1695            result=deviceCreatePartitionTable(dev,selected-3);
1696            break;
1697        case 6:
1698            result=deviceDestroyPartitionTable(dev);
1699            break;
1700        case 7:
1701            result=deviceAddPartition(dev);
1702            break;
1703        case 8:
1704            result=deviceDestroyPartition(dev);
1705            break;
1706        default:
1707            result=false;
1708            break;
1709        }
1710
1711
1712        if(result) this->refreshState();
1713
1714
1715    }
1716
1717
1718}
1719
1720
1721
1722vdev_t *ZManagerWindow::getDiskbyName(QString name)
1723{
1724    QList<vdev_t>::const_iterator it=this->Disks.constBegin();
1725
1726
1727    while(it!=this->Disks.constEnd())
1728    {
1729        if((*it).Name==name) return (vdev_t *)&(*it);
1730        if((*it).Partitions.count()!=0) {
1731            // PROCESS ALL SLICES
1732            QList<vdev_t>::const_iterator sliceit=(*it).Partitions.constBegin();
1733
1734            while(sliceit!=(*it).Partitions.constEnd())
1735            {
1736                if((*sliceit).Name==name) return (vdev_t *)&(*sliceit);
1737                if((*it).Partitions.count()!=0) {
1738                    // PROCESS ALL BSD LABEL PARTITIONS
1739                    QList<vdev_t>::const_iterator partit=(*sliceit).Partitions.constBegin();
1740
1741                    while(partit!=(*sliceit).Partitions.constEnd())
1742                    {
1743                        if((*partit).Name==name) return (vdev_t *)&(*partit);
1744
1745                    ++partit;
1746                    }
1747
1748                }
1749
1750            ++sliceit;
1751            }
1752
1753        }
1754
1755        ++it;
1756    }
1757
1758    return NULL;
1759
1760}
1761
1762
1763vdev_t *ZManagerWindow::getContainerDisk(vdev_t* device)
1764{
1765
1766    QList<vdev_t>::const_iterator it=this->Disks.constBegin();
1767
1768
1769    while(it!=this->Disks.constEnd())
1770    {
1771        if((*it).Partitions.count()!=0) {
1772            // PROCESS ALL SLICES
1773
1774
1775
1776            if((*it).Partitions.contains(*device)) return (vdev_t *)&(*it);
1777
1778            QList<vdev_t>::const_iterator sliceit=(*it).Partitions.constBegin();
1779
1780            while(sliceit!=(*it).Partitions.constEnd())
1781            {
1782
1783                if((*sliceit).Partitions.contains(*device)) return (vdev_t *)&(*sliceit);
1784
1785            ++sliceit;
1786            }
1787
1788        }
1789
1790        ++it;
1791    }
1792
1793    return NULL;
1794
1795
1796
1797}
1798
1799
1800vdev_t *ZManagerWindow::getContainerGroup(vdev_t* device)
1801{
1802
1803    QList<zpool_t>::const_iterator it=this->Pools.constBegin();
1804
1805
1806    while(it!=this->Pools.constEnd())
1807    {
1808        if((*it).VDevs.count()!=0) {
1809            // PROCESS ALL SLICES
1810
1811
1812
1813            if((*it).VDevs.contains(*device)) return NULL;  // THERE IS NO GROUP, THIS DEVICE WAS DIRECTLY IN THE POOL
1814
1815            QList<vdev_t>::const_iterator sliceit=(*it).VDevs.constBegin();
1816
1817            while(sliceit!=(*it).VDevs.constEnd())
1818            {
1819
1820                if((*sliceit).Partitions.contains(*device)) return (vdev_t *)&(*sliceit);
1821
1822            ++sliceit;
1823            }
1824
1825        }
1826
1827        ++it;
1828    }
1829
1830    return NULL;
1831
1832
1833
1834}
1835
1836
1837
1838
1839
1840zpool_t *ZManagerWindow::getZpoolbyName(QString name,int index)
1841{
1842
1843    if(index>=0) return (zpool_t *)&(this->Pools.at(index));
1844
1845QList<zpool_t>::const_iterator it=this->Pools.constBegin();
1846
1847while(it!=this->Pools.constEnd())
1848{
1849    if((*it).Name==name) {
1850        return (zpool_t *)&(*it);
1851    }
1852    ++it;
1853}
1854
1855return NULL;
1856
1857}
1858vdev_t *ZManagerWindow::getVDevbyName(QString name)
1859{
1860QList<zpool_t>::const_iterator it=this->Pools.constBegin();
1861
1862while(it!=this->Pools.constEnd())
1863{
1864    QList<vdev_t>::const_iterator vdit=(*it).VDevs.constBegin();
1865    while(vdit!=(*it).VDevs.constEnd()) {
1866        vdev_t *ptr=(vdev_t *)&(*vdit);
1867        if((*vdit).Name==name) return ptr;
1868        if((*vdit).Partitions.count()!=0) {
1869            QList<vdev_t>::const_iterator vdev2=(*vdit).Partitions.constBegin();
1870            while(vdev2!=(*vdit).Partitions.constEnd()) {
1871                if((*vdev2).Name==name) return (vdev_t *)&(*vdev2);
1872            ++vdev2;
1873            }
1874        }
1875        ++vdit;
1876    }
1877
1878    ++it;
1879}
1880
1881return NULL;
1882
1883}
1884
1885
1886
1887
1888bool ZManagerWindow::deviceCreatePartitionTable(vdev_t *device,int type)
1889{
1890    QString cmd;
1891    if(type==0) cmd="gpart create -s mbr ";
1892    if(type==1) cmd="gpart create -s gpt ";
1893    if(type==2) cmd="gpart create -s bsd ";
1894    cmd+=device->Name;
1895
1896    QStringList a=pcbsd::Utils::runShellCommand(cmd);
1897
1898    if(processErrors(a,"gpart")) return false;
1899
1900    return true;
1901}
1902
1903bool ZManagerWindow::deviceDestroyPartitionTable(vdev_t *device)
1904{
1905    QString cmd;
1906    cmd="gpart destroy ";
1907    cmd+=device->Name;
1908
1909    QStringList a=pcbsd::Utils::runShellCommand(cmd);
1910
1911    if(processErrors(a,"gpart")) return false;
1912
1913    return true;
1914}
1915bool ZManagerWindow::deviceUnmount(vdev_t * device)
1916{
1917    QString cmdline="umount ";
1918
1919    if(device->Alias.isEmpty())  cmdline += "/dev/"+device->Name;
1920    else cmdline+="/dev/"+device->Alias;
1921
1922    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
1923
1924
1925    if(processErrors(a,"umount")) return false;
1926
1927
1928    return true;
1929}
1930bool ZManagerWindow::deviceMount(vdev_t * device)
1931{
1932
1933DialogMount mnt;
1934
1935mnt.setDevice(device);
1936
1937int result=mnt.exec();
1938
1939if(result) {
1940
1941    QString cmdline="mount ";
1942
1943    if(device->Alias.isEmpty())  cmdline += "/dev/"+device->Name;
1944    else cmdline+="/dev/"+device->Alias;
1945
1946    cmdline += " \"" + mnt.getMountLocation()+"\"";
1947
1948    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
1949
1950
1951    if(processErrors(a,"mount")) return false;
1952
1953
1954}
1955
1956    return true;
1957}
1958bool ZManagerWindow::deviceAddPartition(vdev_t *device)
1959{
1960    DialogPartition part;
1961
1962    part.setDevice(device);
1963
1964    int result=part.exec();
1965
1966    if(result) {
1967
1968        long long startsector=part.getStartSector();
1969        unsigned long long sectorcount=part.getSectorCount();
1970
1971        if(!sectorcount) return false;
1972
1973        QString type=part.getPartType();
1974
1975        QString cmdline,tmp;
1976        cmdline="gpart add";
1977        cmdline += " -t " + type;
1978        if(startsector>=0) { tmp.sprintf(" -b %lld",startsector); cmdline += tmp; }
1979        tmp.sprintf(" -s %llu",sectorcount);
1980        cmdline += tmp;
1981
1982        cmdline += " " + device->Name;
1983
1984        QStringList a=pcbsd::Utils::runShellCommand(cmdline);
1985
1986        if(processErrors(a,"gpart")) return false;
1987
1988
1989        if(part.isnewfsChecked()) {
1990            // WE NEED TO INITIALIZE A FILE SYSTEM IN THE DEVICE
1991
1992            // FOR NOW ONLY UFS FILE SYSTEM IS SUPPORTED, SO...
1993
1994            // SINCE GPART SUCCEEDED, IT MUST'VE OUTPUT THE NAME OF THE NEW DEVICE
1995
1996            QStringList tmp=a.at(0).split(" ",QString::SkipEmptyParts);
1997
1998            cmdline="newfs /dev/"+ tmp.at(0);
1999
2000            QStringList b=pcbsd::Utils::runShellCommand(cmdline);
2001
2002            if(processErrors(b,"newfs")) return false;
2003
2004        }
2005
2006
2007        return true;
2008
2009    }
2010
2011    return false;
2012}
2013bool ZManagerWindow::deviceDestroyPartition(vdev_t* device)
2014{
2015
2016    QMessageBox msg(QMessageBox::Warning,tr("Warning"),tr("This operation cannot be undone.\nOK to destroy the slice/partition?"),QMessageBox::Yes|QMessageBox::No);
2017    int result=msg.exec();
2018
2019    if(result!=QMessageBox::Yes) return false;
2020
2021
2022
2023   QString cmdline;
2024   QString tmp;
2025   cmdline="gpart delete ";
2026   tmp.sprintf(" -i %d",device->Index);
2027   cmdline+=tmp;
2028   cmdline += " " + getContainerDisk(device)->Name;
2029
2030   QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2031
2032   if(processErrors(a,"gpart")) return false;
2033
2034   return true;
2035}
2036
2037
2038bool ZManagerWindow::processErrors(QStringList& output,QString command)
2039{
2040    QStringList::const_iterator it=output.constBegin();
2041    QString start;
2042    QString errormsg;
2043    bool errorfound=false;
2044
2045    start=command+":";
2046
2047    while(it!=output.constEnd())
2048    {
2049
2050        if((*it).startsWith(command)) {
2051            QString tmp=(*it);
2052            tmp.remove(0,command.length()+2);
2053            errormsg+= tmp + "\n";
2054            errorfound=true;
2055        }
2056        ++it;
2057    }
2058
2059    if(errorfound) {
2060        QString msg(tr("An error was detected while executing '%1':\n\n"));
2061        msg=msg.arg(command);
2062        msg+=errormsg;
2063        QMessageBox err(QMessageBox::Warning,tr("Error report"),msg,QMessageBox::Ok,this);
2064
2065        err.exec();
2066
2067        return true;
2068
2069    }
2070
2071    return false;
2072}
2073
2074bool ZManagerWindow::processzpoolErrors(QStringList& output)
2075{
2076    QStringList::const_iterator it=output.constBegin();
2077    QString errormsg;
2078    bool errorfound=false;
2079
2080    while(it!=output.constEnd())
2081    {
2082
2083        if(!(*it).isEmpty()) {
2084            QString tmp=(*it);
2085            errormsg+= tmp + "\n";
2086            errorfound=true;
2087        }
2088        ++it;
2089    }
2090
2091    if(errorfound) {
2092        QString msg(tr("An error was detected while executing 'zpool':\n\n"));
2093        msg+=errormsg;
2094        QMessageBox err(QMessageBox::Warning,tr("Error report"),msg,QMessageBox::Ok,this);
2095
2096        err.exec();
2097
2098        return true;
2099
2100    }
2101
2102    return false;
2103
2104}
2105
2106bool ZManagerWindow::processzfsErrors(QStringList& output)
2107{
2108    QStringList::const_iterator it=output.constBegin();
2109    QString errormsg;
2110    bool errorfound=false;
2111
2112    while(it!=output.constEnd())
2113    {
2114
2115        if(!(*it).isEmpty()) {
2116            QString tmp=(*it);
2117            errormsg+= tmp + "\n";
2118            errorfound=true;
2119        }
2120        ++it;
2121    }
2122
2123    if(errorfound) {
2124        QString msg(tr("An error was detected while executing 'zfs':\n\n"));
2125        msg+=errormsg;
2126        QMessageBox err(QMessageBox::Warning,tr("Error report"),msg,QMessageBox::Ok,this);
2127
2128        err.exec();
2129
2130        return true;
2131
2132    }
2133
2134    return false;
2135
2136}
2137
2138
2139QString ZManagerWindow::getPoolProperty(zpool_t *pool,QString Property)
2140{
2141    zprop_t tmp;
2142
2143    foreach(tmp,pool->Properties) {
2144        if(tmp.Name==Property) return tmp.Value;
2145    }
2146
2147    return "";
2148
2149}
2150
2151void    ZManagerWindow::setPoolProperty(zpool_t *pool,QString Property,QString Value)
2152{
2153    QString cmdline="zpool set ";
2154
2155    cmdline+=Property+"=\""+Value+"\"";
2156
2157    cmdline+=" \""+pool->Name+"\"";
2158
2159    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2160
2161    if(!processzpoolErrors(a)) needRefresh=true;
2162
2163
2164}
2165
2166
2167void ZManagerWindow::zpoolCreate(bool b)
2168{
2169
2170    Q_UNUSED(b);
2171    DialogNewPool dlg;
2172
2173    dlg.setDevices(&Disks);
2174    dlg.setTitle(tr("Create new zpool"));
2175
2176    int result=dlg.exec();
2177
2178    if(result) {
2179    QString cmdline="zpool create ";
2180
2181    cmdline+="\""+dlg.getName()+"\" ";
2182
2183    cmdline+=dlg.getRaidType();
2184
2185    QStringList vdev=dlg.getVdevList();
2186
2187    QString arg;
2188
2189    foreach( arg, vdev) cmdline+=" "+arg;
2190
2191    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2192
2193    if(!processzpoolErrors(a)) needRefresh=true;
2194
2195    }
2196}
2197
2198void ZManagerWindow::zpoolDestroy(bool b)
2199{
2200    Q_UNUSED(b);
2201
2202    if(getPoolProperty(lastSelectedPool,"readonly")=="on") {
2203        QMessageBox msg(QMessageBox::Information,tr("Important information"),tr("The pool was imported in read-only mode, therefore attempting to destroy the pool will leave the pool in the state it was when imported, not necessarily destroyed."),QMessageBox::Ok);
2204        msg.exec();
2205    }
2206
2207    QString cmdline="zpool destroy ";
2208
2209
2210    cmdline+="\""+lastSelectedPool->Name+"\"";
2211
2212    // TODO: ASK USER FOR CONFIRMATION BEFORE DESTROYING ANYTHING!
2213
2214
2215    qDebug() << cmdline;
2216
2217    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2218
2219    if(!processzpoolErrors(a)) needRefresh=true;
2220
2221}
2222
2223void ZManagerWindow::zpoolClear(bool b)
2224{
2225    Q_UNUSED(b);
2226
2227    zpool_t *ptr=lastSelectedPool;
2228    QString cmdline;
2229    cmdline="zpool clear \""+ptr->Name+"\"";
2230
2231    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2232
2233    if(processzpoolErrors(a)) needRefresh=false;
2234    else needRefresh=true;
2235
2236
2237}
2238
2239
2240
2241void ZManagerWindow::on_toolButton_clicked()
2242{
2243    lastSelectedPool=getZpoolbyName(ui->poolLabel->text());
2244
2245    if(lastSelectedPool) {
2246        needRefresh=false;
2247        zpoolClear(false);
2248        if(needRefresh) refreshState();
2249    }
2250
2251}
2252
2253
2254
2255
2256// ANALYZE THE VDEV, AND RETURN A SERIES OF FLAGS (ITEM_XXXXX) TO BE USED
2257// FOR EASIER HANDLING OF THE DIFFERENT KINDS OF VDEVS
2258
2259int ZManagerWindow::zpoolGetVDEVType(vdev_t *dev)
2260{
2261    int result=0;
2262    if(getDiskbyName(dev->Name)!=NULL) result|=ITEM_ISDISK;
2263
2264    if(dev->Name.startsWith("mirror")) result|=ITEM_ISMIRROR;
2265    if(dev->Name.startsWith("raidz")) result|=ITEM_ISRAIDZ;
2266    if(dev->Name.startsWith("logs")) result|=ITEM_ISLOG;
2267    if(dev->Name.startsWith("cache")) result|=ITEM_ISCACHE;
2268    if(dev->Name.startsWith("spares")) result|=ITEM_ISSPARE;
2269
2270
2271    if(dev->Status==STATE_OFFLINE) result |=ITEM_ISOFFLINE;
2272    if(dev->Status==STATE_DEGRADED) result |=ITEM_ISDEGRADED;
2273    if(dev->Status==STATE_FAULTED) result |=ITEM_ISFAULTED;
2274    if(dev->Status==STATE_REMOVED) result |=ITEM_ISREMOVED;
2275    if(dev->Status==STATE_UNAVAIL) result |=ITEM_ISUNAVAIL;
2276
2277
2278
2279    vdev_t *parent=getContainerGroup(dev);
2280
2281
2282    // RETURN THE FLAGS FOR THIS DEVICE IN THE LOW BYTE, AND ITS IMMEDIATE PARENT
2283    if(parent==NULL) { return result | PARENT(ITEM_ISPOOL); }
2284    else return result | PARENT((zpoolGetVDEVType(parent) & ITEM_ALL) );
2285}
2286
2287
2288void ZManagerWindow::zpoolEditProperties(bool)
2289{
2290    DialogProp dlg;
2291
2292    dlg.refreshList(lastSelectedPool);
2293
2294    int result=dlg.exec();
2295
2296    if(result==QDialog::Accepted) {
2297        // TODO: UPDATE ALL MODIFIED PROPERTIES
2298        QStringList props;
2299        QStringList vals;
2300
2301        props=dlg.getAllChangedProps();
2302
2303        vals=dlg.getAllChangedValues();
2304
2305        QStringList::const_iterator itp=props.constBegin(),itv=vals.constBegin();
2306
2307        while(itp!=props.constEnd()&&itv!=vals.constEnd()) {
2308            setPoolProperty(lastSelectedPool,(*itp),(*itv));
2309
2310            ++itp;
2311            ++itv;
2312        }
2313
2314        needRefresh=true;
2315    }
2316    return;
2317}
2318
2319void ZManagerWindow::zpoolRemoveDevice(bool b)
2320{
2321    Q_UNUSED(b);
2322
2323    QString cmdline="zpool remove ";
2324
2325    cmdline+="\""+lastSelectedVdev->InPool+"\" "+lastSelectedVdev->Name;
2326
2327    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2328
2329    if(!processzpoolErrors(a)) needRefresh=true;
2330
2331
2332}
2333
2334void ZManagerWindow::zpoolAttachDevice(bool b)
2335{
2336    Q_UNUSED(b);
2337
2338    DialogNewPool dlg;
2339
2340    dlg.setDevices(&Disks);
2341
2342    dlg.setTitle(tr("Attach mirror devices to ")+lastSelectedVdev->Name);
2343
2344    dlg.setName(lastSelectedVdev->InPool);
2345
2346    dlg.setType(DialogNewPool::DISK_MIRROR);
2347
2348    dlg.setNumDisks(1);
2349
2350    int result=dlg.exec();
2351
2352    if(result) {
2353    QString cmdline="zpool attach ";
2354
2355    cmdline+="\""+dlg.getName()+"\" "+lastSelectedVdev->Name+" ";
2356
2357    QStringList vdev=dlg.getVdevList();
2358
2359    QString arg;
2360
2361    foreach( arg, vdev) cmdline+=" "+arg;
2362
2363    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2364
2365    if(!processzpoolErrors(a)) needRefresh=true;
2366
2367    }
2368
2369
2370
2371    return;
2372
2373}
2374
2375void ZManagerWindow::zpoolDetachDevice(bool b)
2376{
2377    Q_UNUSED(b);
2378
2379    QString cmdline="zpool detach ";
2380
2381    cmdline+="\""+lastSelectedVdev->InPool+"\" "+lastSelectedVdev->Name;
2382
2383    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2384
2385    if(!processzpoolErrors(a)) needRefresh=true;
2386
2387
2388}
2389
2390
2391
2392void ZManagerWindow::zpoolOfflineDevice(bool b)
2393{
2394    Q_UNUSED(b);
2395
2396    QString cmdline="zpool offline ";
2397
2398    cmdline+="\""+lastSelectedVdev->InPool+"\" "+lastSelectedVdev->Name;
2399
2400    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2401
2402    if(!processzpoolErrors(a)) needRefresh=true;
2403
2404}
2405
2406void ZManagerWindow::zpoolOnlineDevice(bool b)
2407{
2408    Q_UNUSED(b);
2409
2410    QString cmdline="zpool online ";
2411
2412    cmdline+="\""+lastSelectedVdev->InPool+"\" "+lastSelectedVdev->Name;
2413
2414    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2415
2416    if(!processzpoolErrors(a)) needRefresh=true;
2417}
2418
2419void ZManagerWindow::zpoolScrub(bool b)
2420{
2421    Q_UNUSED(b);
2422
2423    QString cmdline="zpool scrub ";
2424
2425    cmdline+="\""+lastSelectedPool->Name+"\"";
2426
2427    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2428
2429    if(!processzpoolErrors(a)) needRefresh=true;
2430
2431}
2432
2433void ZManagerWindow::zpoolExport(bool b)
2434{
2435    Q_UNUSED(b);
2436
2437    if(getPoolProperty(lastSelectedPool,"readonly")=="on") {
2438        QMessageBox msg(QMessageBox::Information,tr("Important information"),tr("The pool was imported in read-only mode, therefore attempting to export the pool will leave the pool in the state it was when imported, not necessarily exported."),QMessageBox::Ok);
2439        msg.exec();
2440    }
2441
2442    QString cmdline="zpool export ";
2443
2444    cmdline+="\""+lastSelectedPool->Name+"\"";
2445
2446    qDebug() << cmdline;
2447
2448    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2449
2450    if(!processzpoolErrors(a)) needRefresh=true;
2451
2452}
2453
2454void ZManagerWindow::zpoolImport(bool b)
2455{
2456    Q_UNUSED(b);
2457
2458    DialogName dlg;
2459
2460    if(lastSelectedPool->Status&STATE_DESTROYED)    dlg.setTitle(tr("Recover destroyed pool as..."));
2461        else dlg.setTitle(tr("Import pool as..."));
2462
2463
2464    QStringList usednames;
2465
2466    zpool_t tmp;
2467
2468    foreach(tmp, Pools) {
2469        if(!(tmp.Status&(STATE_DESTROYED|STATE_EXPORTED))) usednames.append(tmp.Name);
2470    }
2471
2472    dlg.setForbiddenList(usednames);
2473    dlg.setName(lastSelectedPool->Name);
2474
2475    dlg.showOptions(true);
2476
2477    int result=dlg.exec();
2478
2479    if(result==QDialog::Accepted) {
2480
2481        if(lastSelectedPool->Status&STATE_DESTROYED) {
2482            // SHOW A WARNING
2483
2484            QMessageBox msg(QMessageBox::Warning,tr("Warning"),tr("This pool had been destroyed, and its disks may have been reused. Attempting to recover will destroy any new data that could've been stored in the devices that were reused and cannot be recovered.\nOK to proceed with recovery?"),QMessageBox::Yes|QMessageBox::No);
2485            int result=msg.exec();
2486
2487            if(result!=QMessageBox::Yes) return;
2488
2489        }
2490
2491    QString cmdline="zpool import ";
2492
2493    if(dlg.importReadOnly()) cmdline+=" -o readonly=on ";
2494
2495    if(dlg.importSetAltRoot()) cmdline+=" -R \""+dlg.getAltRoot()+"\" ";
2496
2497    if(lastSelectedPool->Status&STATE_DESTROYED) cmdline+="-D ";
2498
2499    cmdline+=lastSelectedPool->Type; //"\""+lastSelectedPool->Name+"\"";        // Type CONTAINS THE ID OF THE POOL, IT'S BETTER TO USE THE ID TO PREVENT PROBLEMS
2500
2501    cmdline+=" \""+ dlg.getName()+"\"";
2502
2503    qDebug() << cmdline;
2504
2505
2506    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2507
2508    if(!processzpoolErrors(a)) needRefresh=true;
2509
2510    }
2511
2512}
2513
2514void ZManagerWindow::zpoolRename(bool b)
2515{
2516    Q_UNUSED(b);
2517
2518    if(getPoolProperty(lastSelectedPool,"readonly")=="on") {
2519        QMessageBox msg(QMessageBox::Information,tr("Important information"),tr("The pool was imported in read-only mode, it cannot be renamed."),QMessageBox::Ok);
2520        msg.exec();
2521        return;
2522    }
2523
2524
2525    DialogName dlg;
2526
2527    dlg.setTitle(tr("Rename pool"));
2528
2529    dlg.setName(lastSelectedPool->Name);
2530
2531    dlg.showOptions(false);
2532
2533    QStringList usednames;
2534
2535    zpool_t tmp;
2536
2537    foreach(tmp, Pools) {
2538        if(!(tmp.Status&(STATE_DESTROYED|STATE_EXPORTED))) usednames.append(tmp.Name);
2539    }
2540
2541    dlg.setForbiddenList(usednames);
2542
2543
2544    int result=dlg.exec();
2545
2546
2547    if(result==QDialog::Accepted) {
2548
2549        QString id=getPoolProperty(lastSelectedPool,"guid");
2550
2551        QString cmdline="zpool export ";
2552
2553        cmdline+="\""+lastSelectedPool->Name+"\"";
2554
2555        qDebug() << cmdline;
2556    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2557
2558    if(!processzpoolErrors(a)) {
2559        cmdline="zpool import "+id+" \""+dlg.getName()+"\"";
2560
2561        qDebug() << cmdline;
2562        a=pcbsd::Utils::runShellCommand(cmdline);
2563
2564        if(!processzpoolErrors(a)) needRefresh=true;
2565
2566
2567    }
2568
2569    }
2570}
2571
2572
2573void ZManagerWindow::zpoolAdd(bool b)
2574{
2575    Q_UNUSED(b);
2576
2577    DialogNewPool dlg;
2578
2579    dlg.setDevices(&Disks);
2580
2581    dlg.setTitle(tr("Add more devices to zpool"));
2582    dlg.setName(lastSelectedPool->Name);
2583
2584    if(lastSelectedPool->Type=="striped") dlg.setType(DialogNewPool::DISK_STRIPE);
2585    if(lastSelectedPool->Type=="mirror") {
2586        dlg.setType(DialogNewPool::DISK_MIRROR);
2587        dlg.setNumDisks(lastSelectedPool->VDevs[0].Partitions.count());
2588        }
2589    if(lastSelectedPool->Type=="raidz") {
2590        dlg.setType(DialogNewPool::DISK_RAIDZ);
2591        dlg.setNumDisks(lastSelectedPool->VDevs[0].Partitions.count());
2592
2593    }
2594    if(lastSelectedPool->Type=="raidz2") {
2595        dlg.setType(DialogNewPool::DISK_RAIDZ2);
2596        dlg.setNumDisks(lastSelectedPool->VDevs[0].Partitions.count());
2597    }
2598    if(lastSelectedPool->Type=="raidz3") {
2599        dlg.setType(DialogNewPool::DISK_RAIDZ3);
2600        dlg.setNumDisks(lastSelectedPool->VDevs[0].Partitions.count());
2601    }
2602
2603
2604    int result=dlg.exec();
2605
2606    if(result) {
2607    QString cmdline="zpool add ";
2608
2609    cmdline+="\""+dlg.getName()+"\" ";
2610
2611    cmdline+=dlg.getRaidType();
2612
2613    QStringList vdev=dlg.getVdevList();
2614
2615    QString arg;
2616
2617    foreach( arg, vdev) cmdline+=" "+arg;
2618
2619    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2620
2621    if(!processzpoolErrors(a)) needRefresh=true;
2622
2623    }
2624
2625
2626
2627    return;
2628}
2629
2630void ZManagerWindow::zpoolAddLog(bool b)
2631{
2632    Q_UNUSED(b);
2633
2634    DialogNewPool dlg;
2635
2636    dlg.setDevices(&Disks);
2637
2638    dlg.setTitle(tr("Add log devices to zpool"));
2639    dlg.setName(lastSelectedPool->Name);
2640
2641    dlg.setType(DialogNewPool::DISK_LOG);
2642
2643    int result=dlg.exec();
2644
2645    if(result) {
2646    QString cmdline="zpool add ";
2647
2648    cmdline+="\""+dlg.getName()+"\" ";
2649
2650    cmdline+=dlg.getRaidType();
2651
2652    QStringList vdev=dlg.getVdevList();
2653
2654    QString arg;
2655
2656    foreach( arg, vdev) cmdline+=" "+arg;
2657
2658    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2659
2660    if(!processzpoolErrors(a)) needRefresh=true;
2661
2662    }
2663
2664
2665
2666    return;
2667
2668}
2669void ZManagerWindow::zpoolAddCache(bool b)
2670{
2671    Q_UNUSED(b);
2672
2673    DialogNewPool dlg;
2674
2675    dlg.setDevices(&Disks);
2676
2677    dlg.setTitle(tr("Add cache devices to zpool"));
2678    dlg.setName(lastSelectedPool->Name);
2679
2680    dlg.setType(DialogNewPool::DISK_CACHE);
2681
2682    int result=dlg.exec();
2683
2684    if(result) {
2685    QString cmdline="zpool add ";
2686
2687    cmdline+="\""+dlg.getName()+"\" ";
2688
2689    cmdline+=dlg.getRaidType();
2690
2691    QStringList vdev=dlg.getVdevList();
2692
2693    QString arg;
2694
2695    foreach( arg, vdev) cmdline+=" "+arg;
2696
2697    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2698
2699    if(!processzpoolErrors(a)) needRefresh=true;
2700
2701    }
2702
2703
2704
2705    return;
2706
2707}
2708void ZManagerWindow::zpoolAddSpare(bool b)
2709{
2710    Q_UNUSED(b);
2711
2712    DialogNewPool dlg;
2713
2714    dlg.setDevices(&Disks);
2715
2716    dlg.setTitle(tr("Add spare devices to zpool"));
2717    dlg.setName(lastSelectedPool->Name);
2718
2719    dlg.setType(DialogNewPool::DISK_SPARE);
2720
2721    int result=dlg.exec();
2722
2723    if(result) {
2724    QString cmdline="zpool add ";
2725
2726    cmdline+="\""+dlg.getName()+"\" ";
2727
2728    cmdline+=dlg.getRaidType();
2729
2730    QStringList vdev=dlg.getVdevList();
2731
2732    QString arg;
2733
2734    foreach( arg, vdev) cmdline+=" "+arg;
2735
2736    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
2737
2738    if(!processzpoolErrors(a)) needRefresh=true;
2739
2740    }
2741
2742
2743
2744    return;
2745
2746}
2747
2748
2749// PRINT SIZE IN BYTES, KBYTES , GBYTES OR TBYTES
2750QString printBytes(unsigned long long bytes,int unit)
2751{
2752    QString a;
2753
2754    if(unit>4) unit=-1;
2755
2756    // UNIT == -1 FOR AUTOMATIC SELECTION OF UNITS
2757    if(unit==-1) {
2758    if(bytes<2*1024) return a.sprintf("%llu bytes",bytes);
2759    if(bytes<2*1024*1024) return a.sprintf("%.2lf kB",((double)bytes)/1024);
2760    if(bytes<1*1024*1024*1024) return a.sprintf("%.2lf MB",((double)bytes)/(1024*1024));
2761    if(bytes<(1LL*1024LL*1024LL*1024LL*1024LL)) return a.sprintf("%.2lf GB",((double)bytes)/(1024*1024*1024));
2762    return a.sprintf("%.2lf TB",((double)bytes)/((double)1024.0*1024.0*1024.0*1024.0));
2763    }
2764
2765    switch(unit)
2766    {
2767    case 1:
2768        return a.sprintf("%.2lf",((double)bytes)/1024);
2769    case 2:
2770        return a.sprintf("%.2lf",((double)bytes)/(1024*1024));
2771    case 3:
2772        return a.sprintf("%.2lf",((double)bytes)/(1024*1024*1024));
2773    case 4:
2774        return a.sprintf("%.2lf",((double)bytes)/((double)1024.0*1024.0*1024.0*1024.0));
2775    default:
2776        return a.sprintf("%llu",bytes);
2777
2778    }
2779
2780}
2781
2782
2783// GET PREFERRED UNITS FOR PRINTING
2784int printUnits(unsigned long long bytes)
2785{
2786    if(bytes<2*1024) return 0;
2787    if(bytes<2*1024*1024) return 1;
2788    if(bytes<1*1024*1024*1024) return 2;
2789    if(bytes<(1LL*1024LL*1024LL*1024LL*1024LL)) return 3;
2790    return 4;
2791}
2792
2793
2794
2795void ZManagerWindow::on_dropDownButton_clicked()
2796{
2797    ui->fspoolList->setMaximumHeight(1<<24);
2798    ui->fspoolList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2799    ui->dropDownButton->setEnabled(false);
2800    updateGeometry();
2801}
2802
2803void ZManagerWindow::on_fspoolList_clicked(const QModelIndex &index)
2804{
2805    Q_UNUSED(index);
2806
2807    if(ui->fspoolList->maximumHeight()!=ui->fspoolList->minimumHeight()) {
2808    ui->fspoolList->setMaximumHeight(ui->fspoolList->minimumHeight());
2809    ui->fspoolList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2810    updateGeometry();
2811    ui->dropDownButton->setEnabled(true);
2812    }
2813    else on_dropDownButton_clicked();
2814
2815}
2816
2817
2818zfs_t *ZManagerWindow::getFileSystembyPath(QString path, int index)
2819{
2820
2821    if(index>=0) return (zfs_t *)&(this->FileSystems.at(index));
2822
2823    QList<zfs_t>::const_iterator it=this->FileSystems.constBegin();
2824
2825    while(it!=this->FileSystems.constEnd())
2826    {
2827        if((*it).FullPath==path) {
2828            return (zfs_t *)&(*it);
2829        }
2830        ++it;
2831    }
2832
2833    return NULL;
2834
2835}
2836
2837
2838QTreeWidgetItem *ZManagerWindow::getParentFileSystem(QString path)
2839{
2840    QTreeWidgetItemIterator it(ui->fsList);
2841    int len=-1;
2842    QTreeWidgetItem *ptr=NULL;
2843
2844    while(*it) {
2845        if(path.startsWith((*it)->text(0))) {
2846            if((*it)->text(0).length()>len) {
2847                len=(*it)->text(0).length();
2848                ptr=(*it);
2849            }
2850        }
2851        ++it;
2852    }
2853
2854    if(ptr) return ptr;
2855    return NULL;
2856}
2857
2858void ZManagerWindow::on_fspoolList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
2859{
2860    Q_UNUSED(previous);
2861
2862
2863
2864//    ui->fsList->header()->setStretchLastSection(false);
2865//    ui->fsList->header()->setResizeMode(0,QHeaderView::Stretch);
2866//    ui->fsList->header()->setResizeMode(1,QHeaderView::ResizeToContents);
2867
2868
2869
2870    ui->fsList->clear();
2871    if(current==NULL) return;
2872    QString pool=current->text(0);
2873    QTreeWidgetItem *item,*parent;
2874    zprop_t *prop;
2875
2876    QList<zfs_t>::const_iterator it=FileSystems.constBegin();
2877
2878    while(it!=FileSystems.constEnd())
2879    {
2880
2881        if((*it).FullPath.startsWith(pool)) {
2882
2883            zprop_t *origin=getFileSystemProperty((zfs_t *)&(*it),"origin");
2884            if(origin && !origin->Value.isEmpty()) parent=getParentFileSystem(origin->Value);
2885            else parent=getParentFileSystem((*it).FullPath);
2886            if(parent) item=new QTreeWidgetItem(parent);
2887            else item=new QTreeWidgetItem(ui->fsList);
2888
2889        item->setText(0,(*it).FullPath);
2890        if((*it).FullPath==pool) item->setIcon(0,QIcon(":/icons/server-database.png"));
2891            else {
2892        prop=getFileSystemProperty((zfs_t *)&(*it),"type");
2893        if(prop)
2894        {
2895            if(prop->Value=="filesystem")  item->setIcon(0,QIcon(":/icons/kexi.png"));
2896            else if(prop->Value=="snapshot") item->setIcon(0,QIcon(":/icons/camera-photo.png"));
2897            else item->setIcon(0,QIcon(":/icons/kdf.png"));
2898        }
2899        }
2900
2901        QString state;
2902        prop=getFileSystemProperty((zfs_t *)&(*it),"mounted");
2903
2904        if(prop && prop->Value=="yes")  {
2905            state=tr("[Mounted]");
2906            prop=getFileSystemProperty((zfs_t *)&(*it),"mountpoint");
2907            if(prop) state+=" "+prop->Value;
2908        }
2909        else state=tr("[Not Mounted]");
2910
2911        state+="\n";
2912
2913        prop=getFileSystemProperty((zfs_t *)&(*it),"used");
2914
2915        if(prop) state+=prop->Value;
2916
2917        prop=getFileSystemProperty((zfs_t *)&(*it),"available");
2918
2919        if(prop) state+=tr(" of ")+prop->Value;
2920
2921
2922        item->setText(1,state);
2923
2924
2925
2926        // TODO: SHOW OTHER STATE INFO
2927        }
2928        ++it;
2929    }
2930
2931
2932    // NEED TO DO A SECOND PASS DUE TO ITEMS NOT BEING IN PROPER DEPENDENCY ORDER
2933
2934    it=FileSystems.constBegin();
2935
2936    while(it!=FileSystems.constEnd())
2937    {
2938
2939        if((*it).FullPath.startsWith(pool)) {
2940
2941            zprop_t *origin=getFileSystemProperty((zfs_t *)&(*it),"origin");
2942            if(origin && !origin->Value.isEmpty()) parent=getParentFileSystem(origin->Value);
2943            else parent=NULL;
2944            if(parent) {
2945                QTreeWidgetItem *item=getParentFileSystem((*it).FullPath);
2946                item->parent()->takeChild(item->parent()->indexOfChild(item));
2947                parent->addChild(item);
2948            }
2949        }
2950        ++it;
2951    }
2952
2953
2954    ui->fsList->expandAll();
2955
2956
2957
2958
2959}
2960
2961
2962zprop_t *ZManagerWindow::getFileSystemProperty(zfs_t *fs,QString prop)
2963{
2964    QList<zprop_t>::iterator it=fs->Properties.begin();
2965
2966    while(it!=fs->Properties.end())
2967    {
2968        if((*it).Name==prop) return &(*it);
2969        ++it;
2970    }
2971
2972    return NULL;
2973}
2974
2975
2976void ZManagerWindow::filesystemContextMenu(QPoint p)
2977{
2978    Q_UNUSED(p);
2979
2980    struct zactions zfsmenu[]={
2981        /*  QString menutext        ,   int triggermask, triggerflags    ,  action_func slot */
2982        { QString(tr("Mount"))       ,    FSITEM_TYPEFS|FSITEM_ISMOUNTED, FSITEM_TYPEFS       ,  SLOT(fsMount(bool)) },
2983        { QString(tr("Unmount"))       ,    FSITEM_TYPEFS|FSITEM_ISMOUNTED, FSITEM_TYPEFS|FSITEM_ISMOUNTED       ,  SLOT(fsUnmount(bool)) },
2984        { QString(tr("Rename dataset"))    ,     FSITEM_NONE | FSITEM_ISROOTFS, 0         ,  SLOT(fsRename(bool)) },
2985        { QString(tr("Create new dataset"))    ,    FSITEM_TYPESNAP|FSITEM_TYPEVOL, 0         ,  SLOT(fsCreate(bool)) },
2986        { QString(tr("Create a clone dataset"))       ,    ~(FSITEM_TYPEFS|FSITEM_TYPESNAP|FSITEM_ISROOTFS|FSITEM_ISMOUNTED), 0         ,  SLOT(fsClone(bool)) },
2987        { QString(tr("Destroy dataset"))       ,    FSITEM_TYPEFS|FSITEM_NONE|FSITEM_ISROOTFS, FSITEM_TYPEFS         ,  SLOT(fsDestroy(bool)) },
2988        { QString(tr("Promote filesystem"))       ,    FSITEM_ALL, FSITEM_TYPEFS|FSITEM_ISCLONE         ,  SLOT(fsPromote(bool)) },
2989        { QString(tr("Take a snapshot"))       ,    FSITEM_TYPEFS, FSITEM_TYPEFS         ,  SLOT(fsSnapshot(bool)) },
2990        { QString(tr("Destroy snapshot"))       ,    FSITEM_TYPESNAP, FSITEM_TYPESNAP         ,  SLOT(fsDestroy(bool)) },
2991        { QString(tr("Rollback to this snapshot"))       ,    FSITEM_ALL, FSITEM_TYPESNAP       ,  SLOT(fsRollback(bool)) },
2992        { QString(tr("Edit properties"))       ,    FSITEM_NONE, 0       ,  SLOT(fsEditProps(bool)) },
2993
2994
2995// TODO: ADD MORE COMMANDS HERE
2996        { QString()       ,   0, 0      ,  NULL }
2997
2998    };
2999
3000    int flags=0;
3001    int idx;
3002
3003    QMenu m(tr("zfs Menu"),this);
3004
3005    QTreeWidgetItem *item=ui->fsList->itemAt(p);
3006
3007    if(item!=NULL) {
3008        // FIRST DETERMINE THE FILESYSTEM OF THE ITEM
3009        qDebug() << item->text(0);
3010        flags=0;
3011        if( (lastSelectedFileSystem=getFileSystembyPath(item->text(0)) )) {
3012            // YES, IT'S A VALID FILESYSTEM
3013            flags=getFileSystemFlags(lastSelectedFileSystem);
3014        }
3015        else {
3016            // THIS SHOULD NEVER HAPPEN!
3017            return;
3018        }
3019    } else { flags=FSITEM_NONE; lastSelectedFileSystem=NULL; }
3020
3021    for(idx=0;zfsmenu[idx].slot!=NULL;++idx)
3022    {
3023        if( (flags & zfsmenu[idx].triggermask) == zfsmenu[idx].triggerflags) {
3024
3025            QAction *act=m.addAction(zfsmenu[idx].menutext);
3026            connect(act,SIGNAL(triggered(bool)),this,zfsmenu[idx].slot);
3027
3028        }
3029    }
3030
3031
3032    needRefresh=false;
3033    m.exec(ui->fsList->viewport()->mapToGlobal(p));
3034
3035    if(needRefresh) {
3036        // REFRESH STATE BUT KEEP THE CURRENT POOL SELECTED
3037        QString currentPool=ui->fspoolList->currentItem()->text(0);
3038        refreshState();
3039
3040        QTreeWidgetItemIterator it(ui->fspoolList);
3041
3042        while(*it) {
3043            if((*it)->text(0)==currentPool) { ui->fspoolList->setCurrentItem((*it)); break; }
3044            ++it;
3045        }
3046
3047    }
3048
3049
3050}
3051
3052int ZManagerWindow::getFileSystemFlags(zfs_t *fs)
3053{
3054    int flags=0;
3055    zprop_t *prop;
3056
3057    if(fs==NULL) return 0;
3058
3059    if(getZpoolbyName(fs->FullPath)) flags|=FSITEM_ISROOTFS;
3060
3061    prop=getFileSystemProperty(fs,"origin");
3062    if(prop && !prop->Value.isEmpty()) flags|=FSITEM_ISCLONE;
3063
3064    prop=getFileSystemProperty(fs,"type");
3065    if(prop && prop->Value=="filesystem") flags|=FSITEM_TYPEFS;
3066    if(prop && prop->Value=="snapshot") flags|=FSITEM_TYPESNAP;
3067    if(prop && prop->Value=="volume") flags|=FSITEM_TYPEVOL;
3068
3069    prop=getFileSystemProperty(fs,"mounted");
3070    if(prop && prop->Value=="yes") flags|=FSITEM_ISMOUNTED;
3071
3072
3073    // TODO: ADD MORE CASES HERE
3074
3075
3076    return flags;
3077
3078}
3079
3080
3081
3082void ZManagerWindow::fsCreate(bool b)
3083{
3084    Q_UNUSED(b);
3085
3086    DialogfsCreate dlg;
3087
3088    if(lastSelectedFileSystem) dlg.setRootPath(lastSelectedFileSystem->FullPath+"/",QString());
3089    else dlg.setRootPath(ui->fspoolList->currentItem()->text(0)+"/",QString());
3090
3091    int result=dlg.exec();
3092
3093    if(result==QDialog::Accepted) {
3094            // TODO: DO THE ACTUAL FILESYSTEM CREATION
3095
3096        QString cmdline="zfs create ";
3097
3098
3099        QStringList optlist=dlg.getOptions();
3100
3101        QString opt;
3102
3103        foreach(opt,optlist) {
3104            cmdline+="-o "+opt+" ";
3105        }
3106
3107
3108        cmdline+=" \""+dlg.getPath()+"\"";
3109
3110        QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3111
3112        if(!processzfsErrors(a)) needRefresh=true;
3113
3114        }
3115        return;
3116
3117
3118}
3119
3120void ZManagerWindow::fsDestroy(bool b)
3121{
3122    Q_UNUSED(b);
3123
3124    if(!lastSelectedFileSystem)  return;
3125
3126
3127    QString cmdline="zfs destroy -n -v -R";
3128
3129    cmdline+=" \""+lastSelectedFileSystem->FullPath+"\"";
3130
3131    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3132
3133    QString msg(tr("This operation cannot be undone and will cause data loss.\n\nYou are about to perform the following operation(s):\n\n"));
3134
3135    QString str;
3136
3137    foreach(str,a) {
3138        msg+=str.right(str.length()-6)+"\n";
3139    }
3140
3141    msg+=tr("\n\nAre you sure you want to proceed?\n\n");
3142
3143    QMessageBox err(QMessageBox::Warning,tr("Confirmation"),msg,QMessageBox::Yes | QMessageBox::No,this);
3144    err.setDefaultButton(QMessageBox::No);
3145
3146    int result=err.exec();
3147
3148    if(result==QMessageBox::Yes) {
3149    QString cmdline="zfs destroy -R";
3150
3151    cmdline+=" \""+lastSelectedFileSystem->FullPath+"\"";
3152
3153    QStringList b=pcbsd::Utils::runShellCommand(cmdline);
3154
3155
3156    if(!processzfsErrors(b)) needRefresh=true;
3157    }
3158}
3159
3160
3161
3162
3163
3164void ZManagerWindow::fsSnapshot(bool b)
3165{
3166    Q_UNUSED(b);
3167
3168    DialogfsCreate dlg;
3169
3170    if(lastSelectedFileSystem) dlg.setRootPath(lastSelectedFileSystem->FullPath+"@",QString());
3171    else dlg.setRootPath(ui->fspoolList->currentItem()->text(0)+"@",QString());
3172
3173    dlg.nameOnlyMode();
3174    dlg.changeTitle(tr("Take a new snapshot"));
3175
3176    int result=dlg.exec();
3177
3178    if(result==QDialog::Accepted) {
3179
3180        QString cmdline="zfs snapshot ";
3181
3182        cmdline+=" \""+dlg.getPath()+"\"";
3183
3184        QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3185
3186        if(!processzfsErrors(a)) needRefresh=true;
3187
3188        }
3189        return;
3190
3191
3192
3193}
3194void ZManagerWindow::fsRename(bool b)
3195{
3196    Q_UNUSED(b);
3197
3198    if(!lastSelectedFileSystem) return;
3199
3200    DialogfsCreate dlg;
3201
3202    bool isSnapshot=lastSelectedFileSystem->FullPath.contains("@");
3203
3204    if(isSnapshot) {
3205        int pos=lastSelectedFileSystem->FullPath.lastIndexOf(QChar('@'));
3206        dlg.setRootPath(lastSelectedFileSystem->FullPath.left(pos+1),lastSelectedFileSystem->FullPath);
3207
3208    } else {
3209        int pos=lastSelectedFileSystem->FullPath.lastIndexOf(QChar('/'));
3210        dlg.setRootPath(lastSelectedFileSystem->FullPath.left(pos+1),lastSelectedFileSystem->FullPath);
3211    }
3212
3213
3214
3215    dlg.nameOnlyMode();
3216    dlg.changeTitle(tr("New name"));
3217
3218    int result=dlg.exec();
3219
3220    if(result==QDialog::Accepted) {
3221
3222        QString cmdline="zfs rename ";
3223
3224        cmdline+=" \""+lastSelectedFileSystem->FullPath+"\"";
3225        cmdline+=" \""+dlg.getPath()+"\"";
3226
3227        QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3228
3229        if(!processzfsErrors(a)) needRefresh=true;
3230
3231        }
3232        return;
3233
3234
3235
3236}
3237void ZManagerWindow::fsPromote(bool b)
3238{
3239    Q_UNUSED(b);
3240
3241    if(!lastSelectedFileSystem) return;
3242
3243    QString cmdline="zfs promote ";
3244
3245    cmdline+="\""+lastSelectedFileSystem->FullPath+"\"";
3246
3247    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3248
3249    if(!processzfsErrors(a)) needRefresh=true;
3250
3251}
3252void ZManagerWindow::fsClone(bool b)
3253{
3254    Q_UNUSED(b);
3255
3256    DialogfsCreate dlg;
3257
3258    if(!lastSelectedFileSystem) return;
3259
3260    bool isSnapshot=lastSelectedFileSystem->FullPath.contains("@");
3261
3262    int pos=lastSelectedFileSystem->FullPath.lastIndexOf(QChar('/'));
3263
3264    dlg.setRootPath(lastSelectedFileSystem->FullPath.left(pos+1),QString());
3265
3266    int result=dlg.exec();
3267
3268    if(result==QDialog::Accepted) {
3269            // TODO: DO THE ACTUAL FILESYSTEM CREATION
3270
3271        QString cmdline="zfs clone ";
3272
3273
3274        QStringList optlist=dlg.getOptions();
3275
3276        QString opt;
3277
3278        foreach(opt,optlist) {
3279            cmdline+="-o "+opt+" ";
3280        }
3281
3282        if(isSnapshot) cmdline+=" \""+lastSelectedFileSystem->FullPath+"\"";
3283        else {
3284            // IF THIS ISN'T A SNAPSHOT, WE NEED TO TAKE ONE NOW
3285            QString newname=dlg.getPath();
3286            QString snapname=lastSelectedFileSystem->FullPath+"@"+newname.right(newname.size()-pos-1)+"_base";
3287            QString cmd2="zfs snapshot \""+snapname+"\"";
3288            QStringList a=pcbsd::Utils::runShellCommand(cmd2);
3289            if(processzfsErrors(a)) return;
3290
3291            cmdline+=" \""+snapname+"\"";
3292        }
3293
3294        cmdline+=" \""+dlg.getPath()+"\"";
3295
3296        QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3297
3298        if(!processzfsErrors(a)) needRefresh=true;
3299
3300        }
3301        return;
3302
3303
3304
3305}
3306
3307
3308
3309void ZManagerWindow::fsRollback(bool b)
3310{
3311    Q_UNUSED(b);
3312
3313    if(!lastSelectedFileSystem)  return;
3314
3315
3316    QString cmdline="zfs rollback -r -R";
3317
3318    cmdline+=" \""+lastSelectedFileSystem->FullPath+"\"";
3319
3320    QString msg(tr("This operation cannot be undone and will cause all data added after the snapshot to be lost.\n"
3321                   "Any snapshots created after this one will be deleted, along with any clone filesystems that depend on them.\n"));
3322
3323    msg+=tr("\n\nAre you sure you want to proceed?\n\n");
3324
3325    QMessageBox err(QMessageBox::Warning,tr("Confirmation"),msg,QMessageBox::Yes | QMessageBox::No,this);
3326    err.setDefaultButton(QMessageBox::No);
3327
3328    int result=err.exec();
3329
3330    if(result==QMessageBox::Yes) {
3331
3332    QStringList b=pcbsd::Utils::runShellCommand(cmdline);
3333
3334
3335    if(!processzfsErrors(b)) needRefresh=true;
3336    }
3337}
3338
3339
3340void ZManagerWindow::fsMount(bool b)
3341{
3342    Q_UNUSED(b);
3343
3344    if(!lastSelectedFileSystem)  return;
3345
3346
3347    QString cmdline="zfs mount";
3348
3349    cmdline+=" \""+lastSelectedFileSystem->FullPath+"\"";
3350
3351    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3352
3353
3354    if(!processzfsErrors(a)) needRefresh=true;
3355
3356
3357}
3358
3359void ZManagerWindow::fsUnmount(bool b)
3360{
3361    Q_UNUSED(b);
3362    if(!lastSelectedFileSystem)  return;
3363
3364
3365    QString cmdline="zfs unmount";
3366
3367    cmdline+=" \""+lastSelectedFileSystem->FullPath+"\"";
3368
3369    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3370
3371
3372    if(!processzfsErrors(a)) needRefresh=true;
3373
3374}
3375
3376
3377void ZManagerWindow::fsEditProps(bool)
3378{
3379    DialogFSProp dlg;
3380
3381    dlg.setDataset(lastSelectedFileSystem);
3382
3383    int result=dlg.exec();
3384
3385    if(result==QDialog::Accepted) {
3386        // TODO: UPDATE ALL MODIFIED PROPERTIES
3387        QStringList props;
3388        QStringList vals;
3389        QList<int> flags;
3390
3391        props=dlg.getAllChangedProps();
3392
3393        flags=dlg.getAllChangedFlags();
3394
3395        vals=dlg.getAllChangedValues();
3396
3397        QStringList::const_iterator itp=props.constBegin(),itv=vals.constBegin();
3398        QList<int>::const_iterator itf=flags.constBegin();
3399
3400        while(itp!=props.constEnd()&&itv!=vals.constEnd() && itf!=flags.constEnd()) {
3401            if((*itf)&PROP_INHERIT) inheritFSProperty(lastSelectedFileSystem,(*itp),dlg.applyRecursively());
3402            else setFSProperty(lastSelectedFileSystem,(*itp),(*itv));
3403
3404            ++itp;
3405            ++itv;
3406            ++itf;
3407        }
3408
3409        needRefresh=true;
3410    }
3411    return;
3412}
3413
3414
3415void    ZManagerWindow::setFSProperty(zfs_t *fs, QString Property, QString Value)
3416{
3417    QString cmdline="zfs set ";
3418
3419    cmdline+=Property+"=\""+Value+"\"";
3420
3421    cmdline+=" \""+fs->FullPath+"\"";
3422
3423    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3424
3425    if(!processzfsErrors(a)) needRefresh=true;
3426
3427
3428}
3429
3430
3431void    ZManagerWindow::inheritFSProperty(zfs_t *fs,QString Property,bool recursive)
3432{
3433    QString cmdline="zfs inherit ";
3434
3435    if(recursive) cmdline+=" -r ";
3436
3437    cmdline+=Property;
3438
3439    cmdline+=" \""+fs->FullPath+"\"";
3440
3441    QStringList a=pcbsd::Utils::runShellCommand(cmdline);
3442
3443    if(!processzfsErrors(a)) needRefresh=true;
3444
3445
3446}
3447
Note: See TracBrowser for help on using the repository browser.