From 97a3a9708869dd4d1c329413e2c888b04bd048a3 Mon Sep 17 00:00:00 2001 From: Faiyaz Ahmed Date: Thu, 8 Feb 2007 20:34:22 +0000 Subject: [PATCH] * Limits are in KBytes. --- bwmon.py | 205 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 155 insertions(+), 50 deletions(-) diff --git a/bwmon.py b/bwmon.py index 4cc6e14..cc2730c 100644 --- a/bwmon.py +++ b/bwmon.py @@ -24,11 +24,22 @@ import time import pickle import database -#import socket +import socket #import xmlrpclib import bwlimit +import logger from sets import Set +try: + sys.path.append("/etc/planetlab") + from plc_config import * +except: + logger.log("bwmon: Warning: Configuration file /etc/planetlab/plc_config.py not found") + PLC_NAME = "PlanetLab" + PLC_SLICE_PREFIX = "pl" + PLC_MAIL_SUPPORT_ADDRESS = "support@planet-lab.org" + PLC_MAIL_SLICE_ADDRESS = "SLICE@slices.planet-lab.org" + # Utility functions #from pl_mom import * @@ -125,28 +136,49 @@ class Slice: @database.synchronized def updateSliceAttributes(self, data): for sliver in data['slivers']: - if sliver['name'] == self.name: + if sliver['name'] == self.name: for attribute in sliver['attributes']: - if attribute['name'] == 'net_min_rate': + if attribute['name'] == 'net_min_rate': self.MinRate = attribute['value'] - elif attribute['name'] == 'net_max_rate': + logger.log("bwmon: Updating %s. Min Rate - %s" \ + %(self.name, self.MinRate)) + elif attribute['name'] == 'net_max_rate': self.MaxRate = attribute['value'] + logger.log("bwmon: Updating %s. Max Rate - %s" \ + %(self.name, self.MaxRate)) elif attribute['name'] == 'net_i2_min_rate': self.Mini2Rate = attribute['value'] + logger.log("bwmon: Updating %s. Min i2 Rate - %s" \ + %(self.name, self.Mini2Rate)) elif attribute['name'] == 'net_i2_max_rate': self.Maxi2Rate = attribute['value'] - elif attribute['name'] == 'net_max_kbyte': - self.MaxKbyte = attribute['value'] - elif attribute['name'] == 'net_i2_max_kbyte': + logger.log("bwmon: Updating %s. Max i2 Rate - %s" \ + %(self.name, self.Maxi2Rate)) + elif attribute['name'] == 'net_max_kbyte': + self.MaxKByte = attribute['value'] + logger.log("bwmon: Updating %s. Max KByte lim - %s" \ + %(self.name, self.MaxKByte)) + elif attribute['name'] == 'net_i2_max_kbyte': self.Maxi2KByte = attribute['value'] - elif attribute['name'] == 'net_thresh_kbyte': + logger.log("bwmon: Updating %s. Max i2 KByte - %s" \ + %(self.name, self.Maxi2KByte)) + elif attribute['name'] == 'net_thresh_kbyte': self.ThreshKByte = attribute['value'] + logger.log("bwmon: Updating %s. Thresh KByte - %s" \ + %(self.name, self.ThreshKByte)) elif attribute['name'] == 'net_i2_thresh_kbyte': self.Threshi2KByte = attribute['value'] - elif attribute['name'] == 'net_share': + logger.log("bwmon: Updating %s. i2 Thresh KByte - %s" \ + %(self.name, self.Threshi2KByte)) + elif attribute['name'] == 'net_share': self.Share = attribute['value'] - elif attribute['name'] == 'net_i2_share': + logger.log("bwmon: Updating %s. Net Share - %s" \ + %(self.name, self.Share)) + elif attribute['name'] == 'net_i2_share': self.Sharei2 = attribute['value'] + logger.log("bwmon: Updating %s. Net i2 Share - %s" \ + %(self.name, self.i2Share)) + def reset(self, runningmaxrate, runningmaxi2rate, usedbytes, usedi2bytes, data): """ @@ -169,10 +201,10 @@ class Slice: # Reset rates. if (self.MaxRate != runningmaxrate) or (self.Maxi2Rate != runningmaxi2rate): - print "%s reset to %s/%s" % \ + logger.log("bwmon: %s reset to %s/%s" % \ (self.name, bwlimit.format_tc_rate(self.MaxRate), - bwlimit.format_tc_rate(self.Maxi2Rate)) + bwlimit.format_tc_rate(self.Maxi2Rate))) bwlimit.set(xid = self.xid, minrate = self.MinRate, maxrate = self.MaxRate, @@ -197,7 +229,7 @@ class Slice: 'date': time.asctime(time.gmtime()) + " GMT", 'period': format_period(period)} - if usedi2bytes >= (self.usedbytes + self.ByteThresh): + if usedbytes >= (self.bytes + (self.ThreshKByte * 1024)): maxbyte = self.MaxKByte * 1024 bytesused = bytes - self.bytes timeused = int(time.time() - self.time) @@ -211,20 +243,20 @@ class Slice: params['class'] = "low bandwidth" params['bytes'] = format_bytes(usedbytes - self.bytes) params['maxrate'] = bwlimit.format_tc_rate(runningmaxrate) - params['limit'] = format_bytes(self.MaxKByte) + params['limit'] = format_bytes(self.MaxKByte * 1024) params['new_maxrate'] = bwlimit.format_tc_rate(new_maxrate) if verbose: - print "%(slice)s %(class)s " \ + logger.log("bwmon: %(slice)s %(class)s " \ "%(bytes)s of %(limit)s (%(new_maxrate)s/s maxrate)" % \ - params + params) # Cap low bandwidth burst rate if new_maxrate != runningmaxrate: message += template % params - print "%(slice)s %(class)s capped at %(new_maxrate)s/s " % params + logger.log("bwmon: %(slice)s %(class)s capped at %(new_maxrate)s/s " % params) - if usedi2bytes >= (self.i2bytes + self.Threshi2KBytes): + if usedi2bytes >= (self.i2bytes + (self.Threshi2KByte * 1024)): maxi2byte = self.Maxi2KByte * 1024 i2bytesused = i2bytes - self.i2bytes timeused = int(time.time() - self.time) @@ -238,17 +270,17 @@ class Slice: params['class'] = "high bandwidth" params['bytes'] = format_bytes(usedi2bytes - self.i2bytes) params['maxrate'] = bwlimit.format_tc_rate(runningmaxi2rate) - params['limit'] = format_bytes(self.Maxi2KByte) + params['limit'] = format_bytes(self.Maxi2KByte * 1024) params['new_maxexemptrate'] = bwlimit.format_tc_rate(new_maxi2rate) if verbose: - print "%(slice)s %(class)s " \ - "%(bytes)s of %(limit)s (%(new_maxrate)s/s maxrate)" % params + logger.log("bwmon: %(slice)s %(class)s " \ + "%(bytes)s of %(limit)s (%(new_maxrate)s/s maxrate)" % params) # Cap high bandwidth burst rate if new_maxi2rate != runningmaxi2rate: message += template % params - print "%(slice)s %(class)s capped at %(new_maxexemptrate)s/s" % params + logger.log("bwmon: %(slice)s %(class)s capped at %(new_maxexemptrate)s/s" % params) # Apply parameters if new_maxrate != runningmaxrate or new_maxi2rate != runningmaxi2rate: @@ -258,8 +290,8 @@ class Slice: if message and self.emailed == False: subject = "pl_mom capped bandwidth of slice %(slice)s on %(hostname)s" % params if debug: - print subject - print message + (footer % params) + logger.log("bwmon: "+ subject) + logger.log("bwmon: "+ message + (footer % params)) else: self.emailed = True slicemail(self.name, subject, message + (footer % params)) @@ -268,27 +300,29 @@ def GetSlivers(data): # Defaults global datafile, \ period, \ - default_MaxRate, \ - default_Maxi2Rate, \ - default_MinRate, \ - default_MaxKByte,\ - default_ThreshKByte,\ + default_MaxRate, \ + default_Maxi2Rate, \ + default_MinRate, \ + default_MaxKByte,\ + default_ThreshKByte,\ default_Maxi2KByte,\ default_Threshi2KByte,\ - default_Share + default_Share,\ + verbose + verbose = True # All slices names = [] try: f = open(datafile, "r+") if verbose: - print "Loading %s" % datafile + logger.log("bwmon: Loading %s" % datafile) (version, slices) = pickle.load(f) f.close() # Check version of data file if version != "$Id: bwmon.py,v 1.20 2007/01/10 16:51:04 faiyaza Exp $": - print "Not using old version '%s' data file %s" % (version, datafile) + logger.log("bwmon: Not using old version '%s' data file %s" % (version, datafile)) raise Exception except Exception: version = "$Id: bwmon.py,v 1.20 2007/01/10 16:51:04 faiyaza Exp $" @@ -298,18 +332,18 @@ def GetSlivers(data): root_xid = bwlimit.get_xid("root") default_xid = bwlimit.get_xid("default") - # {name: xid} + # {name: xid} live = {} - for sliver in data['slivers']: - live[sliver['name']] = bwlimit.get_xid(sliver['name']) + for sliver in data['slivers']: + live[sliver['name']] = bwlimit.get_xid(sliver['name']) - # Get actuall running values from tc. + # Get actual running values from tc. for params in bwlimit.get(): (xid, share, minrate, maxrate, minexemptrate, maxexemptrate, - bytes, i2bytes) = params - + usedbytes, usedi2bytes) = params + # Ignore root and default buckets if xid == root_xid or xid == default_xid: continue @@ -328,18 +362,20 @@ def GetSlivers(data): if slices.has_key(xid): slice = slices[xid] if time.time() >= (slice.time + period) or \ - bytes < slice.bytes or i2bytes < slice.i2bytes: + usedbytes < slice.bytes or usedi2bytes < slice.i2bytes: # Reset to defaults every 24 hours or if it appears # that the byte counters have overflowed (or, more # likely, the node was restarted or the HTB buckets # were re-initialized). - slice.reset(maxrate, maxexemptrate, bytes, i2bytes, data) + slice.reset(maxrate, maxexemptrate, usedbytes, usedi2bytes, data) else: # Update byte counts - slice.update(maxrate, maxexemptrate, bytes, i2bytes, data) + slice.update(maxrate, maxexemptrate, usedbytes, usedi2bytes, data) else: # New slice, initialize state - slice = slices[xid] = Slice(xid, name, maxrate, maxexemptrate, bytes, i2bytes, data) + if verbose: + logger.log("bwmon: New Slice %s" % name) + slice = slices[xid] = Slice(xid, name, maxrate, maxexemptrate, usedbytes, usedi2bytes, data) # Delete dead slices dead = Set(slices.keys()) - Set(live.values()) @@ -347,21 +383,90 @@ def GetSlivers(data): del slices[xid] bwlimit.off(xid) - print "Saving %s" % datafile + logger.log("bwmon: Saving %s" % datafile) f = open(datafile, "w") pickle.dump((version, slices), f) f.close() #def GetSlivers(data): -# for sliver in data['slivers']: -# if sliver.has_key('attributes'): -# print sliver -# for attribute in sliver['attributes']: -# if attribute['name'] == "KByteThresh": print attribute['value'] +# for sliver in data['slivers']: +# if sliver.has_key('attributes'): +# print sliver +# for attribute in sliver['attributes']: +# if attribute['name'] == "KByteThresh": print attribute['value'] def start(options, config): pass -if __name__ == '__main__': - main() +def format_bytes(bytes, si = True): + """ + Formats bytes into a string + """ + if si: + kilo = 1000. + else: + # Officially, a kibibyte + kilo = 1024. + + if bytes >= (kilo * kilo * kilo): + return "%.1f GB" % (bytes / (kilo * kilo * kilo)) + elif bytes >= 1000000: + return "%.1f MB" % (bytes / (kilo * kilo)) + elif bytes >= 1000: + return "%.1f KB" % (bytes / kilo) + else: + return "%.0f bytes" % bytes + +def format_period(seconds): + """ + Formats a period in seconds into a string + """ + + if seconds == (24 * 60 * 60): + return "day" + elif seconds == (60 * 60): + return "hour" + elif seconds > (24 * 60 * 60): + return "%.1f days" % (seconds / 24. / 60. / 60.) + elif seconds > (60 * 60): + return "%.1f hours" % (seconds / 60. / 60.) + elif seconds > (60): + return "%.1f minutes" % (seconds / 60.) + else: + return "%.0f seconds" % seconds + +def slicemail(slice, subject, body): + sendmail = os.popen("/usr/sbin/sendmail -N never -t -f%s" % PLC_MAIL_SUPPORT_ADDRESS, "w") + + # PLC has a separate list for pl_mom messages + if PLC_MAIL_SUPPORT_ADDRESS == "support@planet-lab.org": + to = ["pl-mom@planet-lab.org"] + else: + to = [PLC_MAIL_SUPPORT_ADDRESS] + + if slice is not None and slice != "root": + to.append(PLC_MAIL_SLICE_ADDRESS.replace("SLICE", slice)) + + header = {'from': "%s Support <%s>" % (PLC_NAME, PLC_MAIL_SUPPORT_ADDRESS), + 'to': ", ".join(to), + 'version': sys.version.split(" ")[0], + 'subject': subject} + + # Write headers + sendmail.write( +""" +Content-type: text/plain +From: %(from)s +Reply-To: %(from)s +To: %(to)s +X-Mailer: Python/%(version)s +Subject: %(subject)s + +""".lstrip() % header) + + # Write body + sendmail.write(body) + # Done + sendmail.close() + -- 2.43.0