def fillTable(self): if self.__disabled: self.table.clear() return services = list(self.access_cfg.permissions) self.table.clear() # (interface (str), network (IPy), ip version (int)) => row (int) # Don't use (interface, network) because of a bug in IPy < 0.70: # IP('0.0.0.0/0') and IP('::/0') are considered as equal self.net_to_row = {} component_to_name = ComponentToName() self.table.setSortingEnabled(False) self.table.setRowCount(len(self.networks)) self.setVerticalHeaders() self.table.setColumnCount(len(services)) self.table.setHorizontalHeaderLabels([component_to_name.display_name(service) for service in services]) self.table.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) for irow, interface_network in enumerate(self.networks): self.setRow(interface_network, irow) for icol, service in enumerate(self.access_cfg.permissions): allow = interface_network in self.access_cfg.permissions[service] self.createService(irow, icol, service, interface_network, allow)
def __init__(self, mainwindow): self.mainwindow = mainwindow self._interval = _BASE_INTERVAL self.addToInfoArea = mainwindow.addToInfoArea self.client = mainwindow.client self.splash = SplashScreen() self._component_to_name = ComponentToName() self.reset()
def __init__(self, component_name, on_off, parent): component_to_name = ComponentToName() ServiceStatusItem.__init__(self, component_to_name.display_name(component_name), on_off, parent) self.component_name = component_name
class Applier(object): def __init__(self, mainwindow): self.mainwindow = mainwindow self._interval = _BASE_INTERVAL self.addToInfoArea = mainwindow.addToInfoArea self.client = mainwindow.client self.splash = SplashScreen() self._component_to_name = ComponentToName() self.reset() def setPhase(self, phase): self._last_fired_component = '' self._last_fired_component_formatted = '' def reset(self): self._last_read_index = 0 self._apply_error = None self._applied_component_list = [] self._rolled_back_component_list = [] self._rollback_occurred = False self._component_counter = 0 self._final_success_msg = '' self._final_error_msg = '' self._rollback_error_msg = '' self.setPhase('') def rollback(self): self._component_counter = 0 self._rollback_occurred = True def start(self): self.__start_info() async = self.client.async() async.call('config', 'applyStart', callback=self._start_ok, errback=self._start_err ) def __force_start(self): async = self.client.async() async.call('config', 'forceApplyStart', callback=self._start_ok, errback=self._start_err ) def __start_info(self): # Log an empty line self.mainwindow.addHTMLToInfoArea(BR) self.splash.setText(tr("Applying the new configuration")) #TODO: disable hiding on clic. self.splash.show() def __end_all(self): self.splash.hide() if self.mainwindow._standalone != STANDALONE: self.mainwindow.eas_window.setModified(False) # Log an empty line self.mainwindow.addHTMLToInfoArea(BR) if self._rollback_occurred: message = tr("The application of the new configuration failed: the previous configuration has been restored.") if self._apply_error: message = message + BR + BR + self._apply_error message = unicode(message) QMessageBox.critical( self.mainwindow, tr("Application failed"), message ) else: self.mainwindow.apply_done() def component_label(self, comp_name): return self._component_to_name.display_name(comp_name) def __handle_SessionError(self, err): """ Only acts if action is relevant. You safely can pass any error to this function. """ if isinstance(err, SessionError): QMessageBox.critical( self.mainwindow, tr("Disconnected!"), tr("You have been disconnected. " "Your changes are lost. You may log in again.") ) #FIXME: exit? def __handle_errors(self, err): debug = self.mainwindow.debug if debug: html = Html(formatException(err, debug), escape=False) + BR self.mainwindow.addHTMLToInfoArea(html, category=COLOR_WARNING) else: text = exceptionAsUnicode(err) self.addToInfoArea(text, category=COLOR_WARNING) self.__handle_SessionError(err) def logWithTimestamp(self, timestamp, html, prefix=None): html = Html(html, escape=False) + BR timestamp = formatTimestamp(timestamp, True) if prefix is not None: timestamp = timestamp + prefix self.mainwindow.addHTMLToInfoArea(html, prefix=timestamp) def __process_logs(self, logs): """ Does everything that has to be done with logs -Write them on the console. -take any relevant action returns True if there are other messages to process False if all messages have been processed """ has_more = True if len(logs) == 0: if self._interval < _MAX_INTERVAL: self._interval *= 2 for log in logs: self._interval = _BASE_INTERVAL self._last_read_index += 1 timestamp, message_type, content = log timestamp = parseDatetime(timestamp) if message_type == PHASE_CHANGE: has_more = self._change_phase(timestamp, content) elif message_type in ERRORS: self._display_err(timestamp, content) elif message_type in (GLOBAL_ERROR, GLOBAL_WARNING): colors = {GLOBAL_ERROR:COLOR_ERROR ,GLOBAL_WARNING:COLOR_WARNING} format, substitutions = content message = tr(format) % substitutions self.display_message(timestamp, message, color=colors[message_type]) elif message_type in COMPONENT_LISTS: self._process_component_list(timestamp, message_type, content) elif message_type in COMPONENT_FIRED: self._set_last_fired_component(content) message = tr("%(COMPONENT_NAME)s is being configured") % { "COMPONENT_NAME": self._last_fired_component_formatted} splash_message = self._last_fired_component self._component_counter += 1 if self._rollback_occurred: components = self._rolled_back_component_list else: components = self._applied_component_list if components: progress = " (%s/%s)" % (self._component_counter, len(components)) message += progress splash_message += progress self.logWithTimestamp(timestamp, message) self.splash.setText(splash_message) elif COMPONENT_MESSAGE == message_type: format, substitutions = content message = tr(format) % substitutions message = htmlColor(message, COLOR_VERBOSE) message = tr("%s: %s") % ( self._last_fired_component, unicode(message)) self.logWithTimestamp(timestamp, message) else: # unknow message type message = tr("%s (unknown message type: '%s')") message = message % (content, message_type) message = htmlColor(message, COLOR_INVALID) self.logWithTimestamp(timestamp, message) return has_more def _set_last_fired_component(self, component_name): translated = self.component_label(component_name) self._last_fired_component = translated self._last_fired_component_formatted = htmlBold( htmlColor(translated, COLOR_EMPHASIZED) ) def _display_err(self, timestamp, err): name = self._last_fired_component html = tr("%s triggered the following error") % name html = htmlColor(html, COLOR_ERROR) err = Html(err) # content is an error html = tr("%s: %s") % (unicode(html), unicode(err)) if not self._rollback_occurred: self._apply_error = Html(html, escape=False) self.logWithTimestamp(timestamp, html) def display_message(self, timestamp, message, color=None, prefix=None): if color is not None: html = htmlColor(message, color) else: html = Html(message) self.logWithTimestamp(timestamp, html) def _change_phase(self, timestamp, phase): """ returns True if there are other messages to process False if all messages have been processed """ self.setPhase(phase) has_more = True hexacolor = None if phase == GLOBAL_DONE: if self._rollback_occurred: return False hexacolor = COLOR_SUCCESS message = tr("The new configuration has been successfully applied") if self._final_success_msg: message = self._final_success_msg message = htmlBold(message) has_more = False elif phase == GLOBAL_APPLY_SKIPPED: hexacolor = COLOR_SUCCESS message = tr("Application skipped") message = htmlBold(message) has_more = False elif phase == GLOBAL: hexacolor = COLOR_EMPHASIZED message = tr("Applying the new configuration") message = htmlBold(message) elif phase == APPLYING: hexacolor = COLOR_EMPHASIZED message = tr("Normal application phase started") elif phase == APPLYING_DONE: hexacolor = COLOR_EMPHASIZED message = tr("Normal application phase completed") elif phase == ROLLING_BACK: self.rollback() hexacolor = COLOR_ERROR message = tr("The application of the new configuration failed: " "restoring the previous configuration") if self._rollback_error_msg: message = self._rollback_error_msg message = htmlBold(message) elif phase == ROLLING_BACK_DONE: self._rollback_occurred = True hexacolor = COLOR_ERROR message = tr("The application of the new configuration failed: " "the previous configuration has been restored") if self._final_error_msg: message = self._final_error_msg message = htmlBold(message) else: message = Html(phase) if self.mainwindow.debug \ or phase not in (APPLYING, APPLYING_DONE): prefix = formatTimestamp(timestamp, True) html = message + BR self.mainwindow.addHTMLToInfoArea(html, hexacolor, prefix) return has_more def _process_component_list(self, timestamp, message_type, component_list): displayed_list = map(self.component_label, component_list) displayed_list = u', '.join(displayed_list) if message_type == APPLIED_COMPONENT_LIST: format = tr("Applying the new configuration of components: %s") self._applied_component_list = component_list elif message_type == ROLLED_BACK_COMPONENT_LIST: format = tr("Restoring the previous configuration of components: %s") self._rolled_back_component_list = component_list else: return html = Html(format % displayed_list, escape=False) self.logWithTimestamp(timestamp, html) def _start_err(self, err): """ called when we could not start the poll dance """ if not(isinstance(err, RpcdError) and err.type == "CoreError"): self.__handle_errors(err) self.__end_all() return # old ufwi_conf backend version: falling back to old protocol async = self.client.async() async.call( 'config', 'apply', callback=self._apply_ok, errback=self._apply_err ) def __ask_if_force(self, title, message): ok = QMessageBox.question( self.mainwindow, title, message, QMessageBox.Ok | QMessageBox.Cancel ) if ok == QMessageBox.Ok: self.__force_start() return self.addToInfoArea(tr("Apply operation cancelled.")) self.__end_all() def _start_ok(self, value): """ the poll dance protocol is implemented on the server, go on! """ if value == "trigger reboot? then use force=True": self.__ask_if_force( tr("Trigger a reboot?"), tr( "Applying now (after a restoration) will trigger a reboot of the appliance. " "You can cancel now or proceed." ) ) #Anyway, return (also with cancel) return elif "then use force=True" in value: self.__ask_if_force( RESTORATION_WARNING_TITLE, RESTORATION_WARNING ) #Anyway, return (also with cancel) return if value == "reboot": self.addToInfoArea( tr("This will trigger a reboot of the appliance (after restoration)"), category=COLOR_CRITICAL ) QMessageBox.information( self.mainwindow, tr("EdenWall reboot scheduled"), tr("First application after restoration: EdenWall will reboot afterwards.") ) #reset the last read index self.reset() self._poll() # FIXME: why is this callback never called? def _apply_ok(self, arg): """ End of the apply process. """ self.addToInfoArea(tr("Application successful"), category=COLOR_FANCY) self.__end_all() def _apply_err(self, err): """ End of the apply process. """ self.addToInfoArea(tr("Application failed"), category=COLOR_WARNING) self.__end_all() self.__handle_errors(err) def _poll(self): async = self.client.async() async.call( 'config', 'applyLog', self._last_read_index, callback=self._poll_ok, errback=self._poll_err ) def _poll_ok(self, logs): has_more = self.__process_logs(logs) if has_more: #loop QTimer.singleShot(self._interval, self._poll) else: self.__end_all() def _poll_err(self, err): """ A problem in the apply log dance. After that, we'll rely on a future feature: the getState dance, to know more about the server """ self.__end_all() self.__handle_errors(err) def start_polling( self, final_success_message='', final_error_message='', rollback_error_message='', ): self.reset() self._final_success_msg = final_success_message self._final_error_message = final_error_message self._rollback_error_msg = rollback_error_message self._poll()