class _Lock:
    def __init__(self):
        self._minutes = 0
        self._lock = Lock()
        self._scheduled = None
        self._stack = None
    def acquire(self,blocking=0):        
        value = self._lock.acquire(blocking)
        self._stack = traceback.extract_stack()
        self._schedule_print()
        return value
    def release(self):
        try:
            if self._scheduled:
                self._scheduled.cancel()
        finally:
            self._lock.release()
    def locked(self):
        return self._lock.locked()
    def _schedule_print(self):
        self._scheduled = scheduler.after(60,self._print,(self._stack,))
    def _print(self,stack):
        self._minutes += 1
        print 'Lock acquired: %s min' % self._minutes
        print string.join(traceback.format_list(stack))
        if self.locked():
            self._schedule_print()                 
class EnergywiseDomain(Node):
    def __init__(self):
        self._cpex_lock=RLock() # cpex switch list lock, used for setting/getting the "primary" cpex switch.
        self._cache_lock=Lock() # domain value cache lock.
        self._cpex_switch_map_lock = Lock() # lock for cpex switches cache data structure
        self._cache_value=None
        self._cache_time=0
        self._cpex_switch_map_cache=SwitchMap({})
        self._cpex_switch_map_time=0
        self.ttl=30
        self._reinit()
        return
    def _not_running(self, *args, **kw):
        raise ENotRunning("%r is not running." % self.as_node_url())
    def _empty_domain_usage(self, importance=100, skipCache=False):
        return 0.0
    def _reinit(self):
        self._cpex_switches = []
        self._snmp_switches = []
        self._all_switches = []
        self._all_domains = []
        self.domain = ''
        self.trend_node = None
        self.energywise_domain_usage = self._not_running
        return
    def configure(self, config):
         Node.configure(self, config)
         set_attribute(self, 'ttl', 30, config, int)
         return
    def configuration(self):
         config = Node.configuration(self)
         get_attribute(self, 'ttl', config, str)
         return config
    def start(self):
        self._cpex_lock.acquire()
        try:
            for child in self.children_nodes():
                if isinstance(child,EnergywiseSwitch):
                    if child.PROTOCOL_SNMP == child.protocol:
                        self._snmp_switches.append(child)
                    else:
                        self._cpex_switches.append(child)
                    self._all_switches.append(child)
                elif isinstance(child,EnergywiseDomain):
                    self._all_domains.append(child)
            # elif @fixme magic hook for reverse compatibility.
            if self._snmp_switches and self._cpex_switches:
                raise EConfiguration(
                    "All switches in a domain must be configurtion to use the"
                    " same protocol."
                    )
            self._cpex_switches.sort(_cpex_switch_cmp) 
        finally:
            self._cpex_lock.release()
        if not self.domain:
            self.domain = _find_domain(self)
        if self._snmp_switches:
            self.energywise_domain_usage = self.snmp_domain_usage
        elif self._cpex_switches:
            self.energywise_domain_usage = self.cpex_domain_usage
        else:
            self.energywise_domain_usage = self._empty_domain_usage
        Node.start(self)
        return
    def stop(self):
        self._reinit()
        Node.stop(self)
        return
    def cpex_domain_usage(self, importance=100, skipCache=False, max_attempts=3):
        self._cpex_lock.acquire()
        aggr_result = 0
        try:
            if not self._cpex_switches:
                raise ENoDomainSwitches(
                    "No switches are configured for the %r domain." % self.domain
                    )
            for switch in self._cpex_switches:
                for i in range(0, max_attempts):
                    try:
                        result = switch.cpex_domain_usage(importance)
                        if result:
                            aggr_result += result
                            break
                    except:
                        pass
        except ENoDomainSwitches:
            raise
        finally:
            self._cpex_lock.release()
        return aggr_result
    # caching for cpex switches under a domain
    # @return A dictionary of Energywise usage values keyed by switch address, possibly from cache.
    def cpex_switch_usage_map(self, importance=100, skipCache=False):
        self._cpex_switch_map_lock.acquire()
        try:
            if skipCache or self._is_cpex_switch_map_stale(time.time()):
                #fetch actual value
                usage_map = self._cpex_switch_usage_map(importance)
                self._update_cpex_switch_map(usage_map, time.time())
            return self._cpex_switch_map_cache
        finally:
            self._cpex_switch_map_lock.release()
        raise EUnreachableCode("Executed unreachable code!")
    ##
    # Call cpex_switch_usage_map on the primary cpex switch.
    # @return A dictionary of Energywise usage values keyed by switch address.
    def _cpex_switch_usage_map(self, importance=100, max_attempts=3):
        self._cpex_lock.acquire()
        aggr_result = {}
        try:
            if not self._cpex_switches:
                raise ENoDomainSwitches(
                    "No switches are configured for the %r domain." % self.domain
                    )
            for switch in self._cpex_switches:
                for i in range(0, max_attempts):
                    try:
                        result = switch.cpex_switch_usage_map(importance)
                        if result:
                            aggr_result.update(result)
                            break
                    except:
                        pass
        except ENoDomainSwitches:
            raise
        finally:
            self._cpex_lock.release()
        return aggr_result
    def snmp_domain_usage(self, importance=100, skipCache=False):
        result = 0
        for switch in self._snmp_switches:
            try:
                result += switch.snmp_switch_usage(importance, skipCache)
            except:
                msglog.exception()
                msglog.log("Energywise",msglog.types.ERR,
                           "Failed to get data from %r switch" %switch.name
                           )
        return result
    def _is_cpex_switch_map_stale(self, timestamp):
        return (self._cpex_switch_map_time + self.ttl) < timestamp
    def _update_cpex_switch_map(self, switch_map, timestamp):
        self._cpex_switch_map_cache = SwitchMap(switch_map)
        self._cpex_switch_map_time = timestamp
    def _is_domain_cache_stale(self,timestamp):
        assert self._cache_lock.locked()
        return (self._cache_time + self.ttl) < timestamp
    def _update_domain_cache(self,value, timestamp):
        self._cache_value = value
        self._cache_time = timestamp
    #caching the domain-usage
    def aggregate_domain_usage(self, importance=100, skipCache=False):
        self._cache_lock.acquire()
        try:
            if skipCache or self._is_domain_cache_stale(time.time()):
                    #fetch the actual value and update the cache
                try:
                    result = self.energywise_domain_usage(importance, skipCache)
                except:
                    msglog.exception()
                    result = 0
                for sub in self._all_domains:
                        result += sub.aggregate_domain_usage(importance, skipCache)
                self._update_domain_cache(result, time.time())
                return self._cache_value
            else:
                # use the cached value
                return self._cache_value
        finally:
            self._cache_lock.release()
        raise EUnreachableCode("Executed unreachable code!") 
    def new_trend(self,period):
        return new_trend(self,period)
    def delete_trend(self):
        return delete_trend(self)
    def get(self, skipCache=False):
        return self.aggregate_domain_usage()
    def get_result(self, skipCache=False):
        return Result(self.get(skipCache), time.time(), cached=False)
Exemple #3
0
class CBus(Node):
    __node_id__ = "50b9f586-fe01-4c1b-a92a-dc5b27b7567e"
    PROMPT = "ready>\n"

    def __init__(self):
        Node.__init__(self)
        self._dev_name = None  # Can programmatically override BEFORE start.
        self._lock = Lock()
        self._popen = None
        self._apps = []
        self._groups = []
        self._discovery_ts = None
        return

    def _get_dev_name(self):
        if self._dev_name:
            return self._dev_name
        self._dev_name = self.parent.dev
        return self._dev_name

    def configure(self, config):
        Node.configure(self, config)
        set_attribute(self, 'auto_discover', 1, config, as_boolean)
        set_attribute(self, 'debug', 0, config, as_boolean)
        return

    def configuration(self):
        config = Node.configuration(self)
        get_attribute(self, 'auto_discover', config)
        get_attribute(self, 'debug', config)
        return config

    def _restart(self):
        assert self._lock.locked()
        self._reset_popen()
        self._popen = popen2.Popen3(
            "superexec %s %s" % (CBUS_CLI, self._get_dev_name()), False)
        line = self._popen.fromchild.readline()
        if self.debug:
            print "< %r" % line
        while (line and line != CBus.PROMPT):
            line = self._popen.fromchild.readline()
            if self.debug:
                print "< %r" % line
        if self.auto_discover:
            # This is really a work around to how CBM discovers initial values.
            self._discover()
        return

    def start(self):
        self._lock.acquire()
        try:
            self._restart()
        finally:
            self._lock.release()
        Node.start(self)
        return

    def stop(self):
        self._lock.acquire()
        try:
            self._reset_popen()
            self._dev_name = None
            self._discovery_ts = None
        finally:
            self._lock.release()
        Node.stop(self)
        return

    def _reset_popen(self):
        if (self._popen is not None):
            try:
                os.kill(self._popen.pid, signal.SIGTERM)
            except OSError, e:
                if e.errno != errno.ESRCH:
                    raise
            try:
                self._popen.wait()
            except OSError, e:
                if e.errno != errno.ECHILD:
                    raise
            self._popen = None
Exemple #4
0
class CBus(Node):
    __node_id__ = "50b9f586-fe01-4c1b-a92a-dc5b27b7567e"
    PROMPT = "ready>\n"

    def __init__(self):
        Node.__init__(self)
        self._dev_name = None  # Can programmatically override BEFORE start.
        self._lock = Lock()
        self._popen = None
        self._apps = []
        self._groups = []
        self._discovery_ts = None
        return

    def _get_dev_name(self):
        if self._dev_name:
            return self._dev_name
        self._dev_name = self.parent.dev
        return self._dev_name

    def configure(self, config):
        Node.configure(self, config)
        set_attribute(self, "auto_discover", 1, config, as_boolean)
        set_attribute(self, "debug", 0, config, as_boolean)
        return

    def configuration(self):
        config = Node.configuration(self)
        get_attribute(self, "auto_discover", config)
        get_attribute(self, "debug", config)
        return config

    def _restart(self):
        assert self._lock.locked()
        self._reset_popen()
        self._popen = popen2.Popen3("superexec %s %s" % (CBUS_CLI, self._get_dev_name()), False)
        line = self._popen.fromchild.readline()
        if self.debug:
            print "< %r" % line
        while line and line != CBus.PROMPT:
            line = self._popen.fromchild.readline()
            if self.debug:
                print "< %r" % line
        if self.auto_discover:
            # This is really a work around to how CBM discovers initial values.
            self._discover()
        return

    def start(self):
        self._lock.acquire()
        try:
            self._restart()
        finally:
            self._lock.release()
        Node.start(self)
        return

    def stop(self):
        self._lock.acquire()
        try:
            self._reset_popen()
            self._dev_name = None
            self._discovery_ts = None
        finally:
            self._lock.release()
        Node.stop(self)
        return

    def _reset_popen(self):
        if self._popen is not None:
            try:
                os.kill(self._popen.pid, signal.SIGTERM)
            except OSError, e:
                if e.errno != errno.ESRCH:
                    raise
            try:
                self._popen.wait()
            except OSError, e:
                if e.errno != errno.ECHILD:
                    raise
            self._popen = None
Exemple #5
0
class PeriodicExporter(Exporter, EventConsumerMixin):
    def __init__(self):
        Exporter.__init__(self)
        EventConsumerMixin.__init__(self, self.handle_connected, self.connection_event_error)
        self.running = 0
        self._scheduled = None
        self._lock = Lock()

    def handle_connected(self, event):
        self.msglog("%s Got connection event" % self.name)
        if event.__class__ == ConnectionEvent:
            self.msglog("Connection state is %s." % str(event.state))
            if event.state == ConnectionEvent.STATE.UP:
                self.msglog("Going to start export.")
                self.go()
        else:
            msg = (
                "Unknown event recieved by %s from %s." % (self.name, str(self.connection_node))
            ) + " Event: %s" % str(event)
            msglog.log("broadway", msglog.types.WARN, msg)

    def connection_event_error(self, exc, event):
        msg = (
            "Connection Event for "
            + str(self.connection_node)
            + " had the following Error\n"
            + "Event: "
            + str(event)
            + "Error: "
            + str(exc)
        )
        msglog.log("broadway", msglog.types.WARN, msg)

    def msglog(self, msg, force=0):
        if self.debug or force:
            msglog.log("broadway.mpx.service.data.periodic_exporter", msglog.types.DB, msg)

    def configure(self, config):
        map_to_attribute(self, "period", 900, config, map_to_seconds)
        if self.period == 0:
            raise EInvalidValue("period", self.period, "Export period cannot be 0")
        set_attribute(self, "debug", 0, config, as_boolean)
        set_attribute(self, "synchronize_on", "00:00", config)
        set_attribute(self, "timeout", 60, config, int)
        set_attribute(self, "connection_node", "/services/network", config)
        set_attribute(self, "connection_attempts", 3, config, int)
        set_attribute(self, "always_export", 0, config, as_boolean)
        set_attribute(self, "breakup_on_period", 0, config, as_boolean)
        Exporter.configure(self, config)
        self._time = _TimeStore(self)

    def configuration(self):
        config = Exporter.configuration(self)
        map_from_attribute(self, "period", config, map_from_seconds)
        get_attribute(self, "connection_node", config)
        get_attribute(self, "debug", config, str)
        get_attribute(self, "connection_attempts", config)
        get_attribute(self, "timeout", config)
        get_attribute(self, "always_export", config, str)
        get_attribute(self, "synchronize_on", config)
        get_attribute(self, "breakup_on_period", config, str)
        return config

    def start(self):
        Exporter.start(self)
        if not self.running:
            node = as_node(self.connection_node)
            if hasattr(node, "event_subscribe"):
                node.event_subscribe(self, ConnectionEvent)
            else:
                if self.debug:
                    msg = "Connection node: " + str(self.connection_node) + " is not an event producer."
                    msglog.log("broadway", msglog.types.INFO, msg)
            self.connection = node
            self.running = 1
            self._init_next_time()
            self._schedule()
        else:
            raise EAlreadyRunning

    def stop(self):
        self.running = 0
        if self._scheduled is not None:
            try:
                self._scheduled.cancel()
            except:
                pass
        Exporter.stop(self)

    def go(self, end_time, start_time=None):
        if self._lock.locked():
            msglog.log("broadway", msglog.types.WARN, "Last export still active, skipping current request.")
            return
        Exporter_ThreadPool.queue_noresult(self._complete, end_time, start_time)

    def scheduled_time(self):
        return self.next_time() - self.period

    def last_time(self):
        return self._time.get_last_time()

    def _schedule(self):
        next = self.next_time()
        self._scheduled = scheduler.at(next, self.go, (next,))

    def _complete(self, end_time, start_time=None):
        self._lock.acquire()
        try:
            self._export(end_time, start_time)
        except:
            msglog.exception()
        self._lock.release()
        if self.running:
            self._schedule()

    ##
    #
    def _init_next_time(self):
        time_format = "%Y%m%d %H:%M:%S"
        sync_format = "%Y%m%d " + self.synchronize_on + ":00"
        current_time = int(time.time())
        f_sync = time.strftime(sync_format, self.time_function(current_time))
        f_now = time.strftime(time_format, self.time_function(current_time))
        sync = time.mktime(time.strptime(f_sync, time_format))
        now = time.mktime(time.strptime(f_now, time_format))
        if now > sync:
            # sync time in past, add one day to sync time.
            sync += map_to_seconds({"days": 1})
        gap = sync - now
        if self.period > gap:
            sync_time = current_time + gap
        else:
            sync_time = current_time + (gap % self.period)
        #
        #
        #
        self._next_time = sync_time
        return self._next_time

    def next_time(self):
        current_time = time.time()
        while self._next_time < current_time:
            self._next_time += self.period
        return self._next_time

    def data_since_export(self):
        start_time = self.last_time()
        end_time = self.next_time()
        return self.log.get_slice("timestamp", start_time, end_time)

    def formatted_data_since_export_as_string(self):
        length = 0
        stream = self.formatted_data_since_export()
        text = stream.read(1024)
        while len(text) > length:
            length = len(text)
            text += stream.read(1024)
        return text

    def formatted_data_since_export(self):
        return self.formatter.format(self.data_since_export())

    def export_data_since_export(self):
        return self.transporter.transport(self.formatted_data_since_export())

    def _export(self, end_time, start_time=None):
        attempts = 0
        connected = 0
        while attempts < self.connection_attempts:
            self.msglog("Acquiring connection %s." % str(self.connection_node))
            try:
                connected = self.connection.acquire()
            except:
                msglog.exception()
            if connected:
                self.msglog("Connection acquired")
                break
            attempts += 1
            self.msglog("Connection acquire failed %s times." % attempts)
        else:
            raise EConnectionError("Failed to connect %s times" % attempts)
        try:
            if start_time is None:
                start_time = self.last_time()
            if start_time == 0 and self.breakup_on_period:
                self.msglog("Start Time is 0 and set to Break on Transfer.")
                start_time = self.log.get_first_record()["timestamp"]
                self.msglog("Start Time set to timestamp of first row: %s" % time.ctime(start_time))
            retrieve = self.log.get_slice
            if self.log.name == "msglog":
                msglog.log("msglog.exporter", msglog.types.INFO, "repr(mpx.properties)\n%s\n" % (repr(properties)))
                retrieve = self.log.get_range
                end_time = time.time()
            end = end_time
            if self.breakup_on_period:
                self.msglog("Breaking on period")
                end = start_time + self.period
            self.msglog("Full export of slice from %s to %s" % (time.ctime(start_time), time.ctime(end_time)))
            while start_time != end_time:
                if end > end_time:
                    self.msglog("End greater than End Time.  Resetting to End Time")
                    end = end_time
                self.msglog("Going to export slice from %s to %s" % (time.ctime(start_time), time.ctime(end)))
                data = retrieve("timestamp", start_time, end)
                if (not data) and (not self.always_export):
                    raise ENoData("timestamp", start_time, end)
                self.msglog("Sending data to formatter.")
                try:
                    output = self.formatter.format(data)
                    if not output is None:
                        self.msglog("Sending formatted data to transporter.")
                        self.transporter.transport(output)
                    start_time = end
                except EBreakupTransfer, e:
                    entry = e.break_at
                    if entry["timestamp"] == end:
                        # prevents loop where transporter is just failing.
                        raise EIOError("EBreakupTransfer not progressing.")
                    end = entry["timestamp"]
                    msglog.log("broadway", msglog.types.WARN, "Breaking up data transfer at %s." % time.ctime(end))
                else:
                    self._time.set_last_time(start_time)
                    self.msglog("Data transported")
                    end = start_time + self.period
        finally:
            if hasattr(self.formatter, "cancel"):
                self.formatter.cancel()  # prevent mult copies of data at next successful transport
            if connected:
                self.msglog("Releasing connection.")
                self.connection.release()

    def nodebrowser_handler(self, nb, path, node, node_url):
        html = nb.get_default_view(node, node_url)
        html += "<h4>Commands</h4>\n"
        s = "%s?action=invoke&method=do_export" % self.name
        html += '<a href="%s">Force export via nodebrowser.</a>' % (s,)
        return html

    def do_export(self, end_time=None, start_time=None):
        if end_time is None:
            end_time = time.time()
        self.go(end_time, start_time)
        return "Export triggered."
class PeriodicExporter(Exporter, EventConsumerMixin):
    def __init__(self):
        Exporter.__init__(self)
        EventConsumerMixin.__init__(self, self.handle_connected,
                                    self.connection_event_error)
        self.running = 0
        self._scheduled = None
        self._lock = Lock()

    def handle_connected(self, event):
        self.msglog('%s Got connection event' % self.name)
        if event.__class__ == ConnectionEvent:
            self.msglog('Connection state is %s.' % str(event.state))
            if event.state == ConnectionEvent.STATE.UP:
                self.msglog('Going to start export.')
                self.go()
        else:
            msg = (('Unknown event recieved by %s from %s.' %
                    (self.name, str(self.connection_node))) +
                   ' Event: %s' % str(event))
            msglog.log('broadway', msglog.types.WARN, msg)

    def connection_event_error(self, exc, event):
        msg = ('Connection Event for ' + str(self.connection_node) +
               ' had the following Error\n' + 'Event: ' + str(event) +
               'Error: ' + str(exc))
        msglog.log('broadway', msglog.types.WARN, msg)

    def msglog(self, msg, force=0):
        if self.debug or force:
            msglog.log('broadway.mpx.service.data.periodic_exporter',
                       msglog.types.DB, msg)

    def configure(self, config):
        map_to_attribute(self, 'period', 900, config, map_to_seconds)
        if self.period == 0:
            raise EInvalidValue('period', self.period,
                                'Export period cannot be 0')
        set_attribute(self, 'debug', 0, config, as_boolean)
        set_attribute(self, 'synchronize_on', '00:00', config)
        set_attribute(self, 'timeout', 60, config, int)
        set_attribute(self, 'connection_node', '/services/network', config)
        set_attribute(self, 'connection_attempts', 3, config, int)
        set_attribute(self, 'always_export', 0, config, as_boolean)
        set_attribute(self, 'breakup_on_period', 0, config, as_boolean)
        Exporter.configure(self, config)
        self._time = _TimeStore(self)

    def configuration(self):
        config = Exporter.configuration(self)
        map_from_attribute(self, 'period', config, map_from_seconds)
        get_attribute(self, 'connection_node', config)
        get_attribute(self, 'debug', config, str)
        get_attribute(self, 'connection_attempts', config)
        get_attribute(self, 'timeout', config)
        get_attribute(self, 'always_export', config, str)
        get_attribute(self, 'synchronize_on', config)
        get_attribute(self, 'breakup_on_period', config, str)
        return config

    def start(self):
        Exporter.start(self)
        if not self.running:
            node = as_node(self.connection_node)
            if hasattr(node, 'event_subscribe'):
                node.event_subscribe(self, ConnectionEvent)
            else:
                if self.debug:
                    msg = ('Connection node: ' + str(self.connection_node) +
                           ' is not an event producer.')
                    msglog.log('broadway', msglog.types.INFO, msg)
            self.connection = node
            self.running = 1
            self._init_next_time()
            self._schedule()
        else:
            raise EAlreadyRunning

    def stop(self):
        self.running = 0
        if self._scheduled is not None:
            try:
                self._scheduled.cancel()
            except:
                pass
        Exporter.stop(self)

    def go(self, end_time, start_time=None):
        if self._lock.locked():
            msglog.log('broadway',msglog.types.WARN, \
                       'Last export still active, skipping current request.')
            return
        Exporter_ThreadPool.queue_noresult(self._complete, end_time,
                                           start_time)

    def scheduled_time(self):
        return self.next_time() - self.period

    def last_time(self):
        return self._time.get_last_time()

    def _schedule(self):
        next = self.next_time()
        self._scheduled = scheduler.at(next, self.go, (next, ))

    def _complete(self, end_time, start_time=None):
        self._lock.acquire()
        try:
            self._export(end_time, start_time)
        except:
            msglog.exception()
        self._lock.release()
        if self.running:
            self._schedule()

    ##
    #
    def _init_next_time(self):
        time_format = '%Y%m%d %H:%M:%S'
        sync_format = '%Y%m%d ' + self.synchronize_on + ':00'
        current_time = int(time.time())
        f_sync = time.strftime(sync_format, self.time_function(current_time))
        f_now = time.strftime(time_format, self.time_function(current_time))
        sync = time.mktime(time.strptime(f_sync, time_format))
        now = time.mktime(time.strptime(f_now, time_format))
        if now > sync:
            # sync time in past, add one day to sync time.
            sync += map_to_seconds({'days': 1})
        gap = sync - now
        if self.period > gap:
            sync_time = current_time + gap
        else:
            sync_time = current_time + (gap % self.period)
        #
        #
        #
        self._next_time = sync_time
        return self._next_time

    def next_time(self):
        current_time = time.time()
        while self._next_time < current_time:
            self._next_time += self.period
        return self._next_time

    def data_since_export(self):
        start_time = self.last_time()
        end_time = self.next_time()
        return self.log.get_slice('timestamp', start_time, end_time)

    def formatted_data_since_export_as_string(self):
        length = 0
        stream = self.formatted_data_since_export()
        text = stream.read(1024)
        while len(text) > length:
            length = len(text)
            text += stream.read(1024)
        return text

    def formatted_data_since_export(self):
        return self.formatter.format(self.data_since_export())

    def export_data_since_export(self):
        return self.transporter.transport(self.formatted_data_since_export())

    def _export(self, end_time, start_time=None):
        attempts = 0
        connected = 0
        while attempts < self.connection_attempts:
            self.msglog('Acquiring connection %s.' % str(self.connection_node))
            try:
                connected = self.connection.acquire()
            except:
                msglog.exception()
            if connected:
                self.msglog('Connection acquired')
                break
            attempts += 1
            self.msglog('Connection acquire failed %s times.' % attempts)
        else:
            raise EConnectionError('Failed to connect %s times' % attempts)
        try:
            if start_time is None:
                start_time = self.last_time()
            if start_time == 0 and self.breakup_on_period:
                self.msglog('Start Time is 0 and set to Break on Transfer.')
                start_time = self.log.get_first_record()['timestamp']
                self.msglog('Start Time set to timestamp of first row: %s' %
                            time.ctime(start_time))
            retrieve = self.log.get_slice
            if self.log.name == 'msglog':
                msglog.log('msglog.exporter', msglog.types.INFO,
                           'repr(mpx.properties)\n%s\n' % (repr(properties)))
                retrieve = self.log.get_range
                end_time = time.time()
            end = end_time
            if self.breakup_on_period:
                self.msglog('Breaking on period')
                end = start_time + self.period
            self.msglog('Full export of slice from %s to %s' %
                        (time.ctime(start_time), time.ctime(end_time)))
            while start_time != end_time:
                if end > end_time:
                    self.msglog(
                        'End greater than End Time.  Resetting to End Time')
                    end = end_time
                self.msglog('Going to export slice from %s to %s' %
                            (time.ctime(start_time), time.ctime(end)))
                data = retrieve('timestamp', start_time, end)
                if (not data) and (not self.always_export):
                    raise ENoData('timestamp', start_time, end)
                self.msglog('Sending data to formatter.')
                try:
                    output = self.formatter.format(data)
                    if not output is None:
                        self.msglog('Sending formatted data to transporter.')
                        self.transporter.transport(output)
                    start_time = end
                except EBreakupTransfer, e:
                    entry = e.break_at
                    if entry['timestamp'] == end:
                        # prevents loop where transporter is just failing.
                        raise EIOError('EBreakupTransfer not progressing.')
                    end = entry['timestamp']
                    msglog.log(
                        'broadway', msglog.types.WARN,
                        'Breaking up data transfer at %s.' % time.ctime(end))
                else:
                    self._time.set_last_time(start_time)
                    self.msglog('Data transported')
                    end = start_time + self.period
        finally:
            if hasattr(self.formatter, 'cancel'):
                self.formatter.cancel(
                )  # prevent mult copies of data at next successful transport
            if connected:
                self.msglog('Releasing connection.')
                self.connection.release()

    def nodebrowser_handler(self, nb, path, node, node_url):
        html = nb.get_default_view(node, node_url)
        html += '<h4>Commands</h4>\n'
        s = '%s?action=invoke&method=do_export' % self.name
        html += '<a href="%s">Force export via nodebrowser.</a>' % (s, )
        return html

    def do_export(self, end_time=None, start_time=None):
        if end_time is None:
            end_time = time.time()
        self.go(end_time, start_time)
        return 'Export triggered.'