From: smbaker Date: Tue, 20 Sep 2011 04:57:06 +0000 (-0700) Subject: add batch mode for renewing slices X-Git-Tag: sface-0.1-20~30 X-Git-Url: http://git.onelab.eu/?p=sface.git;a=commitdiff_plain;h=1123a48de43e859c145f1489257b92ad79323e14 add batch mode for renewing slices --- diff --git a/sface/sfirenew.py b/sface/sfirenew.py index 47ddf2a..a4dd1b3 100644 --- a/sface/sfirenew.py +++ b/sface/sfirenew.py @@ -10,6 +10,7 @@ from PyQt4.QtGui import * from sface.config import config from sface.sfiprocess import SfiProcess #from sface.sfithread import SfiThread +from sface.sliceview import SliceView, SliceModel class SfiRenewer(QObject): def __init__(self, hrn, newExpiration, parent=None): @@ -20,12 +21,12 @@ class SfiRenewer(QObject): self.renewProcess = SfiProcess(self) self.connect(self.renewProcess, SIGNAL('finished()'), self.finishedGetRecord) - self.renewProcess.getRecord(hrn=config.getSlice(), filename="/tmp/slicerecord") + self.renewProcess.getRecord(hrn=hrn, filename="/tmp/slicerecord") def finishedGetRecord(self): - faultString = self.renewProcess.getFaultString() - if faultString: - self.emitFinished("fault", faultString) + self.faultString = self.renewProcess.getFaultString() + if self.faultString: + self.emitFinished("fault", self.faultString) return f = open("/tmp/slicerecord", "r") @@ -36,7 +37,8 @@ class SfiRenewer(QObject): exp = re.compile('expires="[^"]*"') if exp.search(data)==None: # didn't find it - self.emitFinished("failure", "failed to find expiration in slice record") + self.faultString = "failed to find expiration in slice record" + self.emitFinished("failure", self.faultString) return # change the expiration time @@ -51,9 +53,9 @@ class SfiRenewer(QObject): self.renewProcess.updateRecord("/tmp/slicerecord") def finishedUpdateRecord(self): - faultString = self.renewProcess.getFaultString() - if faultString: - self.emitFinished("fault", faultString) + self.faultString = self.renewProcess.getFaultString() + if self.faultString: + self.emitFinished("fault", self.faultString) return # we have to force sfi.py to download an updated slice credential @@ -69,9 +71,9 @@ class SfiRenewer(QObject): self.renewProcess.renewSlivers(self.newExpiration.strftime("%Y-%m-%dT%H:%M:%SZ")) def finishedRenewSlivers(self): - faultString = self.renewProcess.getFaultString() - if faultString: - self.emitFinished("fault", faultString) + self.faultString = self.renewProcess.getFaultString() + if self.faultString: + self.emitFinished("fault", self.faultString) return self.emitFinished("success") @@ -81,13 +83,19 @@ class SfiRenewer(QObject): self.statusMsg = statusMsg self.emit(SIGNAL("finished()")) + def getFaultString(self): + return self.faultString + class RenewWindow(QDialog): - def __init__(self, parent=None): + def __init__(self, batch=False, parent=None): super(RenewWindow, self).__init__(parent) self.setWindowTitle("Renew Slivers") + self.batch = batch + self.renewProcess = None + durationLabel = QLabel("Duration:") self.duration = QComboBox() self.expirations = [] @@ -103,54 +111,111 @@ class RenewWindow(QDialog): self.duration.setCurrentIndex(0) + if self.batch: + sliceLabel = QLabel("Slices:") + self.sliceView = SliceView() + self.status = QLabel("") self.status.setMaximumWidth(640) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True) + if not self.batch: + self.buttonBox.addButton("Batch", QDialogButtonBox.ActionRole) + layout = QVBoxLayout() + layout.addWidget(durationLabel) layout.addWidget(self.duration) + if self.batch: + layout.addWidget(sliceLabel) + layout.addWidget(self.sliceView) layout.addWidget(self.status) layout.addWidget(self.buttonBox) self.setLayout(layout) - #self.status.hide() - self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + self.connect(self.buttonBox, SIGNAL("clicked(QAbstractButton *)"), self.clicked) - def accept(self): - self.setStatus("Renewing Slice...") + if self.batch: + self.sliceModel = SliceModel() + self.refreshAuthority() - self.renewProcess = SfiRenewer(config.getSlice(), self.get_new_expiration(), self) - self.connect(self.renewProcess, SIGNAL('finished()'), self.renewFinished) + def clicked(self, button): + if button.text() == "Batch": + # close ourself, and reopen the batch renew window + self.close() + dlg = RenewWindow(batch=True, parent=self.parent()) + dlg.exec_() + + def accept(self): + if self.batch: + self.slicesToRenew = self.sliceModel.getSelectedSlices() + if self.slicesToRenew == []: + QMessageBox.warning(self, "No Slices", "Please add at least on slice by double-clicking on a slice name") + return + else: + self.slicesToRenew = [config.getSlice()] self.duration.setEnabled(False) self.buttonBox.setEnabled(False) + if self.batch: + self.sliceView.setEnabled(False) + + self.renewNextSlice() def setStatus(self, x): self.status.setText(x) + def renewNextSlice(self): + self.sliceName = self.slicesToRenew.pop(0) + self.renewProcess = SfiRenewer(self.sliceName, self.get_new_expiration(), self) + self.connect(self.renewProcess, SIGNAL('finished()'), self.renewFinished) + self.setStatus("Renewing: %s." % self.sliceName) + def renewFinished(self): - if self.renewProcess.status == "success": - color = "green" - # give the user the button - self.buttonBox.clear() - self.buttonBox.addButton(QDialogButtonBox.Close) - else: - color = "red" + self.disconnect(self.renewProcess, SIGNAL('finished()'), self.renewFinished) - if self.renewProcess.statusMsg: - self.setStatus("Renew %s: %s" % (color, self.renewProcess.status, self.renewProcess.statusMsg)) + faultString = self.renewProcess.getFaultString() + if faultString: + self.setStatus("Renew %s Error: %s" % (self.sliceName, faultString)) + self.buttonBox.setEnabled(True) else: - self.setStatus("Renew %s" % (color, self.renewProcess.status)) - - self.buttonBox.setEnabled(True) + self.setStatus("Renew %s Success" % self.sliceName) - self.disconnect(self.renewProcess, SIGNAL('finished()'), self.renewFinished) - self.renewProcess = None + if (self.slicesToRenew != []): + self.renewNextSlice() + else: + # give the user the button + self.buttonBox.clear() + self.buttonBox.addButton(QDialogButtonBox.Close) + self.buttonBox.setEnabled(True) def get_new_expiration(self): index = self.duration.currentIndex() return self.expirations[index] + + def refreshAuthority(self): + self.process = SfiProcess(self) + self.connect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished) + + self.process.listRecords(config.getAuthority(), None, config.getAuthorityListFile()) + self.setStatus("Refreshing slice list. This will take a moment...") + + def getAuthorityRecordFinished(self): + self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished) + + faultString = self.process.getFaultString() + if not faultString: + self.setStatus("Slice list refreshed.") + self.updateSliceView() + else: + self.setStatus("Authority rec refresh error: %s" % (faultString)) + + def updateSliceView(self): + self.sliceModel.updateModel() + + self.sliceView.setModel(self.sliceModel) + self.sliceView.resizeColumnToContents(0) + diff --git a/sface/sliceview.py b/sface/sliceview.py new file mode 100644 index 0000000..92dd844 --- /dev/null +++ b/sface/sliceview.py @@ -0,0 +1,167 @@ +import datetime +import os +import pickle +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from sfa.util.record import SfaRecord, SliceRecord, AuthorityRecord +from sface.config import config + +NAME_COLUMN = 0 +MEMBERSHIP_STATUS_COLUMN = 1 + +# maximum length of a name to display before clipping +NAME_MAX_LEN = 48 + +slice_status = { "in": "Selected", + "out": "Not Selected"} + +color_status = { "in": QColor.fromRgb(0, 250, 250), } + +class SliceNameDelegate(QStyledItemDelegate): + def __init__(self, parent): + QStyledItemDelegate.__init__(self, parent) + + def displayText(self, value, locale): + data = str(QStyledItemDelegate.displayText(self, value, locale)) + if (len(data)>NAME_MAX_LEN): + data = data[:(NAME_MAX_LEN-3)] + "..." + return QString(data) + + def paint(self, painter, option, index): + model = index.model() + data = str(self.displayText(index.data(), QLocale())) + status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent()) + status_data = status_index.data().toString() + + fm = QFontMetrics(option.font) + rect = QRect(option.rect) + + rect.setHeight(rect.height() - 2) + rect.setWidth(fm.width(QString(data)) + 6) + rect.setX(rect.x() + 5) + rect.setY(rect.y() - 1) + + textRect = QRect(option.rect) + textRect.setWidth(fm.width(QString(data)) + 6) + textRect.setX(rect.x()) + + x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width() + + path = QPainterPath() + path.addRoundedRect(x - 1, y + 1, w, h, 4, 4) + + painter.save() + painter.setRenderHint(QPainter.Antialiasing) + + if option.state & QStyle.State_Selected: + painter.fillRect(option.rect, option.palette.color(QPalette.Active, QPalette.Highlight)) + + color = None + for x in slice_status.keys(): + if (slice_status[x] == status_data) and (x in color_status): + color = color_status[x] + + if color != None: + painter.fillPath(path, color) + painter.setPen(QColor.fromRgb(0, 0, 0)) + painter.drawText(textRect, Qt.AlignVCenter, QString(data)) + + painter.restore() + +class SliceView(QTableView): + def __init__(self, parent=None): + QTableView.__init__(self, parent) + + self.setSelectionBehavior(QAbstractItemView.SelectRows) + self.setSelectionMode(QAbstractItemView.SingleSelection) + self.setAlternatingRowColors(True) + self.setAttribute(Qt.WA_MacShowFocusRect, 0) + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.setToolTip("Double click on a row to change its status.") + + self.setItemDelegateForColumn(0, SliceNameDelegate(self)) + + def keyPressEvent(self, event): + if (event.key() == Qt.Key_Space): + self.toggleSelection() + else: + QTableView.keyPressEvent(self, event) + + def mouseDoubleClickEvent(self, event): + self.toggleSelection() + + def toggleSelection(self): + index = self.currentIndex() + model = index.model() + status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent()) + status_data = status_index.data().toString() + node_index = model.index(index.row(), NAME_COLUMN, index.parent()) + node_data = node_index.data().toString() + + if status_data == slice_status['in']: + model.setData(status_index, QString(slice_status['out'])) + else: + model.setData(status_index, QString(slice_status['in'])) + + model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index) + + def currentChanged(self, current, previous): + model = current.model() + node_index = model.index(current.row(), 0, current.parent()) + node_data = node_index.data().toString() + +class SliceModel(QStandardItemModel): + def __init__(self, rows=0, columns=4, parent=None): + QStandardItemModel.__init__(self, rows, columns, parent) + + def updateModel(self): + self.clear() + + slice_names = [] + + i=0 + while (os.path.exists(config.getAuthorityListFile(i))): + rec = self.readSliceRecord(i) + if rec: + name = str(rec.get_name()) + if (rec.get_type() == "slice"): + slice_names.append(name) + i=i+1 + + rootItem = self.invisibleRootItem() + + for name in slice_names: + rootItem.appendRow([self.readOnlyItem(name), + self.readOnlyItem(slice_status["out"])]) + + headers = QStringList() << "Slice Name" << "Status" + self.setHorizontalHeaderLabels(headers) + + def readOnlyItem(self, x): + item = QStandardItem(QString(x)) + item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) + return item + + def getSelectedSlices(self): + slices = [] + item = self.invisibleRootItem() + children = item.rowCount() + for row in range(0, children): + childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString()) + childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString()) + + if (childStatus == slice_status['in']): + slices.append(childName) + + return slices + + def readSliceRecord(self, i): + rec_file = config.getAuthorityListFile(i) + if os.path.exists(rec_file): + xml = open(rec_file).read() + rec = SliceRecord() + rec.load_from_string(xml) + return rec + return None +