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)
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
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
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.'