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