Tagging module Mom - Mom-2.3-1
[mom.git] / pl_mom.py
1 #!/usr/bin/python
2 #
3 # Utility functions
4 #
5 # Mark Huang <mlhuang@cs.princeton.edu>
6 # Copyright (C) 2006 The Trustees of Princeton University
7 #
8 # $Id$
9 #
10
11 import os
12 import sys
13 import syslog
14 import socket
15 import xmlrpclib
16
17 # /etc/planetlab/plc_config.py is a Python fragment maintained by
18 # PlanetLabConf that contains PLC configuration variables.
19 try:
20     sys.path.append("/etc/planetlab")
21     from plc_config import *
22 except:
23     print "Warning: Configuration file /etc/planetlab/plc_config.py not found"
24     PLC_NAME = "PlanetLab"
25     PLC_SLICE_PREFIX = "pl"
26     PLC_MAIL_SUPPORT_ADDRESS = "root@" + socket.gethostname()
27     PLC_MAIL_SLICE_ADDRESS = "SLICE@" + socket.gethostname()
28
29 def format_bytes(bytes, si = True):
30     """
31     Formats bytes into a string
32     """
33     if si:
34         kilo = 1000.
35     else:
36         # Officially, a kibibyte
37         kilo = 1024.
38
39     if bytes >= (kilo * kilo * kilo):
40         return "%.1f GB" % (bytes / (kilo * kilo * kilo))
41     elif bytes >= 1000000:
42         return "%.1f MB" % (bytes / (kilo * kilo))
43     elif bytes >= 1000:
44         return "%.1f KB" % (bytes / kilo)
45     else:
46         return "%.0f bytes" % bytes
47
48 def format_period(seconds):
49     """
50     Formats a period in seconds into a string
51     """
52
53     if seconds == (24 * 60 * 60):
54         return "day"
55     elif seconds == (60 * 60):
56         return "hour"
57     elif seconds > (24 * 60 * 60):
58         return "%.1f days" % (seconds / 24. / 60. / 60.)
59     elif seconds > (60 * 60):
60         return "%.1f hours" % (seconds / 60. / 60.)
61     elif seconds > (60):
62         return "%.1f minutes" % (seconds / 60.)
63     else:
64         return "%.0f seconds" % seconds
65
66 def writepid(prog):
67     """
68     Check PID file. Exit if already running. Update PID file.
69     """
70
71     try:
72         pidfile = file("/var/run/%s.pid" % prog, "r")
73         pid = pidfile.readline().strip()
74         pidfile.close()
75         if os.path.isdir("/proc/" + pid):
76             print "Error: Another copy of %s is still running (%s)" % (prog, pid)
77             sys.exit(1)
78     except IOError:
79         pass
80
81     pidfile = file("/var/run/%s.pid" % prog, "w")
82     pidfile.write(str(os.getpid()))
83     pidfile.close()
84
85 def removepid(prog):
86     os.unlink("/var/run/%s.pid" % prog)
87
88 def slicemail(slice, subject, body):
89     sendmail = os.popen("/usr/sbin/sendmail -N never -t -f%s" % PLC_MAIL_SUPPORT_ADDRESS, "w")
90
91     to = [PLC_MAIL_MOM_LIST_ADDRESS]
92
93     if slice is not None and slice != "root":
94         to.append(PLC_MAIL_SLICE_ADDRESS.replace("SLICE", slice))
95
96     header = {'from': "%s Support <%s>" % (PLC_NAME, PLC_MAIL_SUPPORT_ADDRESS),
97               'to': ", ".join(to),
98               'version': sys.version.split(" ")[0],
99               'subject': subject}
100
101     # Write headers
102     sendmail.write(
103 """
104 Content-type: text/plain
105 From: %(from)s
106 Reply-To: %(from)s
107 To: %(to)s
108 X-Mailer: Python/%(version)s
109 Subject: %(subject)s
110
111 """.lstrip() % header)
112
113     # Write body
114     sendmail.write(body)
115
116     # Done
117     sendmail.close()
118
119 class Logger:
120     """
121     Simple file-like class for redirecting stdout and stderr to /var/log/messages
122     """
123
124     def write(self, text):
125         text = text.strip()
126         if text:
127             syslog.syslog(text)
128
129 def daemonize():
130     """
131     Daemonize self. Detach from terminal, close all open files, and fork twice.
132     """
133
134     pid = os.fork()
135     if pid == 0:
136         # Detach from terminal
137         os.setsid()
138         # Orphan myself
139         pid = os.fork()
140         if pid == 0:
141             os.chdir("/")
142         else:
143             os._exit(0)
144     else:
145         os._exit(0)
146
147     # Close all open file descriptors
148     import resource
149     maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
150     if (maxfd == resource.RLIM_INFINITY):
151         maxfd = 1024
152     for fd in range(0, maxfd):
153         try:
154             os.close(fd)
155         except OSError:
156             pass
157
158     # Redirect stdin to /dev/null
159     os.open("/dev/null", os.O_RDWR)
160
161 class NM:
162     """
163     Simple interface to local Node Manager API
164     """
165
166     def __init__(self, url = "http://localhost:812/", timeout = 30):
167         """
168         Open a new connection to the local Node Manager
169         """
170         socket.setdefaulttimeout(timeout)
171         try:
172             self.nm = xmlrpclib.ServerProxy(url)
173         except Exception, err:
174             print "Warning: Exception received while opening connection to Node Manager:", err
175             self.nm = None
176
177     def query(self, slice, attributes):
178         """
179         Get values of various slice attributes from the local Node Manager
180
181         slice - slice name
182         attributes - [(attribute_name, return_value_if_not_set), ...]
183         """
184
185         values = [attribute[1] for attribute in attributes]
186
187         if self.nm is not None:
188                 # Read rspec (the NM hash code for the slice)
189                 rcap = open("/var/run/pl_nm/%s.vm_rcap" % slice, "r")
190                 rspec = rcap.readline().strip()
191                 rcap.close()
192
193                 (rc, result) = self.nm.nm_inspect(rspec, attributes)
194                 if rc == 0 and type(result) == list and len(result) == len(values):
195                     values = result
196         return values