1 """A few things that didn't seem to fit anywhere else."""
15 PID_FILE = '/var/run/nodemanager.pid'
19 interface = get_if_from_hwaddr(get_hwaddr_from_plnode())
20 if not interface: interface = "eth0"
23 def get_hwaddr_from_plnode():
25 for line in open("/usr/boot/plnode.txt", 'r').readlines():
26 if line.startswith("NET_DEVICE"):
27 return line.split("=")[1].strip().strip('"')
32 def get_if_from_hwaddr(hwaddr):
36 dev_hwaddr = sioc.gifhwaddr(dev)
37 if dev_hwaddr == hwaddr: return dev
42 def as_daemon_thread(run):
43 """Call function <run> with no arguments in its own thread."""
44 thr = threading.Thread(target=run)
48 def close_nonstandard_fds():
49 """Close all open file descriptors other than 0, 1, and 2."""
51 for fd in range(3, os.sysconf(_SC_OPEN_MAX)):
53 except OSError: pass # most likely an fd that isn't open
55 # after http://www.erlenstar.demon.co.uk/unix/faq_2.html
57 """Daemonize the current process."""
58 if os.fork() != 0: os._exit(0)
60 if os.fork() != 0: os._exit(0)
63 devnull = os.open(os.devnull, os.O_RDWR)
65 # xxx fixme - this is just to make sure that nothing gets stupidly lost - should use devnull
66 crashlog = os.open('/var/log/nodemanager.daemon', os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644)
70 def fork_as(su, function, *args):
71 """fork(), cd / to avoid keeping unused directories open, close all nonstandard file descriptors (to avoid capturing open sockets), fork() again (to avoid zombies) and call <function> with arguments <args> in the grandchild process. If <su> is not None, set our group and user ids appropriately in the child process."""
76 close_nonstandard_fds()
78 pw_ent = pwd.getpwnam(su)
82 if child_pid == 0: function(*args)
84 os.seteuid(os.getuid()) # undo su so we can write the log file
85 os.setegid(os.getgid())
86 logger.log_exc("tools: fork_as")
88 else: os.waitpid(child_pid, 0)
93 """We use a pid file to ensure that only one copy of NM is running at a given time.
94 If successful, this function will write a pid file containing the pid of the current process.
95 The return value is the pid of the other running process, or None otherwise."""
97 if os.access(PID_FILE, os.F_OK): # check for a pid file
98 handle = open(PID_FILE) # pid file exists, read it
99 other_pid = int(handle.read())
101 # check for a process with that pid by sending signal 0
102 try: os.kill(other_pid, 0)
104 if e.errno == errno.ESRCH: other_pid = None # doesn't exist
105 else: raise # who knows
106 if other_pid == None:
107 # write a new pid file
108 write_file(PID_FILE, lambda f: f.write(str(os.getpid())))
111 def write_file(filename, do_write, **kw_args):
112 """Write file <filename> atomically by opening a temporary file, using <do_write> to write that file, and then renaming the temporary file."""
113 shutil.move(write_temp_file(do_write, **kw_args), filename)
115 def write_temp_file(do_write, mode=None, uidgid=None):
116 fd, temporary_filename = tempfile.mkstemp()
117 if mode: os.chmod(temporary_filename, mode)
118 if uidgid: os.chown(temporary_filename, *uidgid)
119 f = os.fdopen(fd, 'w')
122 return temporary_filename
124 # replace a target file with a new contents - checks for changes
125 # can handle chmod if requested
126 # can also remove resulting file if contents are void, if requested
127 # performs atomically:
128 # writes in a tmp file, which is then renamed (from sliverauth originally)
129 # returns True if a change occurred, or the file is deleted
130 def replace_file_with_string (target, new_contents, chmod=None, remove_if_empty=False):
132 current=file(target).read()
135 if current==new_contents:
136 # if turns out to be an empty string, and remove_if_empty is set,
137 # then make sure to trash the file if it exists
138 if remove_if_empty and not new_contents and os.path.isfile(target):
139 logger.verbose("tools.replace_file_with_string: removing file %s"%target)
140 try: os.unlink(target)
143 # overwrite target file: create a temp in the same directory
144 path=os.path.dirname(target) or '.'
145 fd, name = tempfile.mkstemp('','repl',path)
146 os.write(fd,new_contents)
148 if os.path.exists(target):
150 shutil.move(name,target)
151 if chmod: os.chmod(target,chmod)
156 # utilities functions to get (cached) information from the node
158 # get node_id from /etc/planetlab/node_id and cache it
164 _node_id=int(file("/etc/planetlab/node_id").read())
169 _root_context_arch=None
170 def root_context_arch():
171 global _root_context_arch
172 if not _root_context_arch:
173 sp=subprocess.Popen(["uname","-i"],stdout=subprocess.PIPE)
174 (_root_context_arch,_)=sp.communicate()
175 _root_context_arch=_root_context_arch.strip()
176 return _root_context_arch
181 def __init__(self, file):
182 logger.log("tools: Lock %s initialized." % file, 2)
183 self.fd = os.open(file, os.O_RDWR|os.O_CREAT, 0600)
184 flags = fcntl.fcntl(self.fd, fcntl.F_GETFD)
185 flags |= fcntl.FD_CLOEXEC
186 fcntl.fcntl(self.fd, fcntl.F_SETFD, flags)
190 logger.log("tools: Lock acquired.", 2)
191 fcntl.lockf(self.fd, fcntl.LOCK_SH)
193 logger.log("tools: Lock released.", 2)
194 fcntl.lockf(self.fd, fcntl.LOCK_UN)
197 # Utilities for getting the IP address of a LXC/Openvswitch slice. Do this by
198 # running ifconfig inside of the slice's context.
200 def get_sliver_process(slice_name, process_cmdline):
201 """ Utility function to find a process inside of an LXC sliver. Returns
202 (cgroup_fn, pid). cgroup_fn is the filename of the cgroup file for
203 the process, for example /proc/2592/cgroup. Pid is the process id of
204 the process. If the process is not found then (None, None) is returned.
207 cmd = 'grep %s /proc/*/cgroup | grep freezer'%slice_name
208 output = os.popen(cmd).readlines()
210 # the slice couldn't be found
211 logger.log("get_sliver_process: couldn't find slice %s" % slice_name)
219 path = l.split(':')[0]
220 comp = l.rsplit(':')[-1]
221 slice_name_check = comp.rsplit('/')[-1]
223 if (slice_name_check == slice_name):
225 pid = slice_path.split('/')[2]
226 cmdline = open('/proc/%s/cmdline'%pid).read().rstrip('\n\x00')
227 if (cmdline == process_cmdline):
228 cgroup_fn = slice_path
233 if (not cgroup_fn) or (not pid):
234 logger.log("get_sliver_process: process %s not running in slice %s" % (process_cmdline, slice_name))
237 return (cgroup_fn, pid)
239 def get_sliver_ifconfig(slice_name, device="eth0"):
240 """ return the output of "ifconfig" run from inside the sliver.
242 side effects: adds "/usr/sbin" to sys.path
245 # setns is part of lxcsu and is installed to /usr/sbin
246 if not "/usr/sbin" in sys.path:
247 sys.path.append("/usr/sbin")
250 (cgroup_fn, pid) = get_sliver_process(slice_name, "/sbin/init")
251 if (not cgroup_fn) or (not pid):
254 path = '/proc/%s/ns/net'%pid
258 setns.chcontext(path)
260 args = ["/sbin/ifconfig", device]
261 sub = subprocess.Popen(args, stderr = subprocess.PIPE, stdout = subprocess.PIPE)
264 if (sub.returncode != 0):
265 logger.log("get_slice_ifconfig: error in ifconfig: %s" % sub.stderr.read())
267 result = sub.stdout.read()
269 setns.chcontext("/proc/1/ns/net")
273 def get_sliver_ip(slice_name):
274 ifconfig = get_sliver_ifconfig(slice_name)
278 for line in ifconfig.split("\n"):
279 if "inet addr:" in line:
280 # example: ' inet addr:192.168.122.189 Bcast:192.168.122.255 Mask:255.255.255.0'
281 parts = line.strip().split()
282 if len(parts)>=2 and parts[1].startswith("addr:"):
283 return parts[1].split(":")[1]