handle blank rspecs without faulting
[sface.git] / sface / sfirenew.py
1 import calendar
2 import datetime
3 import os
4 import re
5 import sys
6 import time
7
8 from PyQt4.QtCore import *
9 from PyQt4.QtGui import *
10 from sface.config import config
11 from sface.sfiprocess import SfiProcess
12 #from sface.sfithread import SfiThread
13 from sface.sliceview import SliceView, SliceModel
14
15 class SfiRenewer(QObject):
16     def __init__(self, hrn, newExpiration, parent=None):
17         QObject.__init__(self, parent)
18         self.hrn = hrn
19         self.newExpiration = newExpiration
20         self.faultString = None
21
22         self.renewProcess = SfiProcess(self)
23         self.connect(self.renewProcess, SIGNAL('finished()'), self.finishedGetRecord)
24         self.renewProcess.getRecord(hrn=hrn, filename="/tmp/slicerecord")
25
26     def finishedGetRecord(self):
27         self.faultString = self.renewProcess.getFaultString()
28         if self.faultString:
29             self.emitFinished("fault", self.faultString)
30             return
31
32         f = open("/tmp/slicerecord", "r")
33         data = f.read()
34         f.close()
35
36         # find the expiration time
37         exp = re.compile('expires="[^"]*"')
38         if exp.search(data)==None:
39             # didn't find it
40             self.faultString = "failed to find expiration in slice record"
41             self.emitFinished("failure", self.faultString)
42             return
43
44         # change the expiration time
45         delta = 24*60*60 # always extend the slice by one extra day to cover slop for time zone differences
46         data = exp.sub('expires="' + str(calendar.timegm(self.newExpiration.timetuple())+delta) + '"', data)
47
48         open("/tmp/slicerecord", "w").write(data)
49
50         self.disconnect(self.renewProcess, SIGNAL('finished()'), self.finishedGetRecord)
51         self.connect(self.renewProcess, SIGNAL('finished()'), self.finishedUpdateRecord)
52
53         self.renewProcess.updateRecord("/tmp/slicerecord")
54
55     def finishedUpdateRecord(self):
56         self.faultString = self.renewProcess.getFaultString()
57         if self.faultString:
58             self.emitFinished("fault", self.faultString)
59             return
60
61         # we have to force sfi.py to download an updated slice credential
62         sliceCredName = config.fullpath("slice_" + self.hrn.split(".")[-1] + ".cred")
63         if os.path.exists(sliceCredName):
64             os.remove(sliceCredName)
65
66         # newer SFA has a slightly different slice credential name
67         sliceCredName = config.fullpath(self.hrn + ".slice.cred")
68         if os.path.exists(sliceCredName):
69             os.remove(sliceCredName)
70
71         open("/tmp/expiration", "w").write(self.newExpiration.strftime("%Y-%m-%dT%H:%M:%SZ"))
72
73         # call renewSlivers on the aggregate
74         self.disconnect(self.renewProcess, SIGNAL('finished()'), self.finishedUpdateRecord)
75         self.connect(self.renewProcess, SIGNAL('finished()'), self.finishedRenewSlivers)
76         self.renewProcess.renewSlivers(self.newExpiration.strftime("%Y-%m-%dT%H:%M:%SZ"), slice = self.hrn)
77
78     def finishedRenewSlivers(self):
79         self.faultString = self.renewProcess.getFaultString()
80         if self.faultString:
81             self.emitFinished("fault", self.faultString)
82             return
83
84         self.emitFinished("success")
85
86     def emitFinished(self, status, statusMsg=None):
87         self.status = status
88         self.statusMsg = statusMsg
89         self.emit(SIGNAL("finished()"))
90
91     def getFaultString(self):
92         return self.faultString
93
94 class RenewWindow(QDialog):
95     def __init__(self, batch=False, parent=None):
96         super(RenewWindow, self).__init__(parent)
97         self.setWindowTitle("Renew Slivers")
98
99         self.batch = batch
100
101         self.renewProcess = None
102
103         durationLabel = QLabel("Duration:")
104         self.duration = QComboBox()
105
106         self.expirations = []
107
108         durations = ( (1, "One Week"), (2, "Two Weeks"), (3, "Three Weeks"), (4, "One Month") )
109
110         now = datetime.datetime.utcnow()
111         for (weeks, desc) in durations:
112             exp = now + datetime.timedelta(days = weeks * 7)
113             desc = desc + " " + exp.strftime("%Y-%m-%d %H:%M:%S")
114             self.expirations.append(exp)
115             self.duration.addItem(desc)
116
117         self.duration.setCurrentIndex(0)
118
119         if self.batch:
120             sliceLabel = QLabel("Slices:")
121             self.sliceView = SliceView()
122
123         self.status = QLabel("")
124         self.status.setMaximumWidth(640)
125
126         self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
127         self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True)
128
129         if not self.batch:
130             self.buttonBox.addButton("Batch", QDialogButtonBox.ActionRole)
131
132         layout = QVBoxLayout()
133         layout.addWidget(durationLabel)
134         layout.addWidget(self.duration)
135         if self.batch:
136             layout.addWidget(sliceLabel)
137             layout.addWidget(self.sliceView)
138         layout.addWidget(self.status)
139         layout.addWidget(self.buttonBox)
140         self.setLayout(layout)
141
142         self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()"))
143         self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()"))
144         self.connect(self.buttonBox, SIGNAL("clicked(QAbstractButton *)"), self.clicked)
145
146         if self.batch:
147             self.sliceModel = SliceModel()
148             self.refreshAuthority()
149
150     def clicked(self, button):
151         if button.text() == "Batch":
152             # close ourself, and reopen the batch renew window
153             self.close()
154             dlg = RenewWindow(batch=True, parent=self.parent())
155             dlg.exec_()
156
157     def accept(self):
158         if self.batch:
159             self.slicesToRenew = self.sliceModel.getSelectedSlices()
160             if self.slicesToRenew == []:
161                 QMessageBox.warning(self, "No Slices", "Please add at least on slice by double-clicking on a slice name")
162                 return
163         else:
164             self.slicesToRenew = [config.getSlice()]
165
166         self.duration.setEnabled(False)
167         self.buttonBox.setEnabled(False)
168         if self.batch:
169             self.sliceView.setEnabled(False)
170
171         self.renewNextSlice()
172
173     def setStatus(self, x):
174         self.status.setText(x)
175
176     def renewNextSlice(self):
177         self.sliceName = self.slicesToRenew.pop(0)
178         self.renewProcess = SfiRenewer(self.sliceName, self.get_new_expiration(), self)
179         self.connect(self.renewProcess, SIGNAL('finished()'), self.renewFinished)
180         self.setStatus("<font color='green'>Renewing: %s.</font>" % self.sliceName)
181
182     def renewFinished(self):
183         self.disconnect(self.renewProcess, SIGNAL('finished()'), self.renewFinished)
184
185         faultString = self.renewProcess.getFaultString()
186         if faultString:
187             self.setStatus("<font color='red'>Renew %s Error: %s</font>" % (self.sliceName, faultString))
188             self.buttonBox.setEnabled(True)
189         else:
190             self.setStatus("<font color='green'>Renew %s Success</font>" % self.sliceName)
191
192             if (self.slicesToRenew != []):
193                 self.renewNextSlice()
194             else:
195                 # give the user the <close> button
196                 self.buttonBox.clear()
197                 self.buttonBox.addButton(QDialogButtonBox.Close)
198                 self.buttonBox.setEnabled(True)
199
200     def get_new_expiration(self):
201         index = self.duration.currentIndex()
202         return self.expirations[index]
203
204     def refreshAuthority(self):
205         self.process = SfiProcess(self)
206         self.connect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
207
208         self.process.listRecords(config.getAuthority(), None)
209         self.setStatus("Refreshing slice list. This will take a moment...")
210
211     def getAuthorityRecordFinished(self):
212         self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
213
214         faultString = self.process.getFaultString()
215         if not faultString:
216             self.setStatus("<font color='green'>Slice list refreshed.</font>")
217             self.updateSliceView()
218         else:
219             self.setStatus("<font color='red'>Authority rec refresh error: %s</font>" % (faultString))
220
221     def updateSliceView(self):
222         self.sliceModel.updateModel()
223
224         self.sliceView.setModel(self.sliceModel)
225         self.sliceView.resizeColumnToContents(0)
226