Left: | ||
Right: |
OLD | NEW |
---|---|
1 from twisted.internet.defer import inlineCallbacks | 1 from twisted.internet.defer import inlineCallbacks |
2 from twisted.internet.threads import deferToThread | 2 from twisted.internet.threads import deferToThread |
3 | 3 |
4 from juju.errors import ServiceError | 4 from juju.errors import ServiceError |
5 | 5 |
6 import os | 6 import os |
7 import subprocess | 7 import subprocess |
8 | 8 |
9 | 9 |
10 def _check_call(args, env=None, output_path=None): | 10 def _check_call(args, env=None, output_path=None): |
11 if not output_path: | 11 if not output_path or not os.access(output_path, os.W_OK): |
12 output_path = os.devnull | 12 f = open(os.devnull, "a") |
13 else: | |
14 f = open(output_path, "a") | |
13 | 15 |
14 with open(output_path, "a") as f: | 16 try: |
15 return subprocess.check_call( | 17 return subprocess.check_call( |
16 args, | 18 args, |
17 stdout=f.fileno(), stderr=f.fileno(), | 19 stdout=f.fileno(), stderr=f.fileno(), |
18 env=env) | 20 env=env) |
21 finally: | |
22 f.close() | |
19 | 23 |
20 | 24 |
21 def _cat(filename, use_sudo=False): | 25 def _cat(filename, use_sudo=False): |
22 args = ("cat", filename) | 26 args = ("cat", filename) |
23 if use_sudo and not os.access(filename, os.R_OK): | 27 if use_sudo and not os.access(filename, os.R_OK): |
24 args = ("sudo",) + args | 28 args = ("sudo",) + args |
25 | 29 |
26 p = subprocess.Popen( | 30 p = subprocess.Popen( |
27 args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | 31 args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
28 stdout_data, _ = p.communicate() | 32 stdout_data, _ = p.communicate() |
29 r = p.returncode | 33 r = p.returncode |
30 return (r, stdout_data) | 34 return (r, stdout_data) |
31 | 35 |
32 | 36 |
33 class TwistedDaemonService(object): | 37 class TwistedDaemonService(object): |
34 """Manage the starting and stopping of an Agent. | 38 """Manage the starting and stopping of an Agent. |
35 | 39 |
36 This manager tracks the agent via its --pidfile. The pidfile argument | 40 This manager tracks the agent via its --pidfile. The pidfile argument |
37 specifies the location of the pid file that is used to track this service. | 41 specifies the location of the pid file that is used to track this service. |
38 | 42 |
39 """ | 43 """ |
40 def __init__(self, name, pidfile, use_sudo=False): | 44 def __init__(self, name, pidfile, use_sudo=False): |
41 self._name = name | 45 self._name = name |
42 self._use_sudo = use_sudo | 46 self._use_sudo = use_sudo |
43 self._description = None | 47 self._description = None |
44 self._environ = None | 48 self._environ = None |
45 self._command = None | 49 self._command = None |
46 self._output_path = None | 50 self.output_path = None |
47 | 51 |
48 self._pid_path = pidfile | 52 self._pid_path = pidfile |
49 self._pid = None | 53 self._pid = None |
50 | 54 |
51 @property | |
52 def output_path(self): | |
53 if self._output_path is not None: | |
54 return self._output_path | |
55 return "/tmp/%s.output" % self._name | |
56 | |
57 @output_path.setter | |
58 def output_path(self, path): | |
59 self._output_path = path | |
60 | 55 |
61 def set_description(self, description): | 56 def set_description(self, description): |
62 self._description = description | 57 self._description = description |
63 | 58 |
64 def set_environ(self, environ): | 59 def set_environ(self, environ): |
65 for k, v in environ.items(): | 60 for k, v in environ.items(): |
66 environ[k] = str(v) | 61 environ[k] = str(v) |
67 self._environ = environ | 62 self._environ = environ |
68 | 63 |
69 def set_command(self, command): | 64 def set_command(self, command): |
70 if "--pidfile" not in command: | 65 if "--pidfile" not in command: |
71 command += ["--pidfile", self._pid_path] | 66 command += ["--pidfile", self._pid_path] |
72 else: | 67 else: |
73 # pid file is in command (consume it for get_pid) | 68 # pid file is in command (consume it for get_pid) |
74 idx = command.index("--pidfile") | 69 idx = command.index("--pidfile") |
75 self._pid_path = command[idx+1] | 70 self._pid_path = command[idx+1] |
76 | 71 |
77 self._command = command | 72 self._command = command |
78 | 73 |
79 @inlineCallbacks | 74 @inlineCallbacks |
80 def _trash_output(self): | 75 def _trash_output(self): |
81 if os.path.exists(self.output_path): | 76 if self.output_path and os.path.exists(self.output_path): |
82 # Just using os.unlink will fail when we're running TEST_SUDO | 77 # Just using os.unlink will fail when we're running TEST_SUDO |
83 # tests which hit this code path (because root will own | 78 # tests which hit this code path (because root will own |
84 # self.output_path) | 79 # self.output_path) |
85 yield self._call("rm", "-f", self.output_path) | 80 yield self._call("rm", "-f", self.output_path) |
86 | 81 |
87 if os.path.exists(self._pid_path): | 82 if os.path.exists(self._pid_path): |
88 yield self._call("rm", "-f", self._pid_path) | 83 yield self._call("rm", "-f", self._pid_path) |
89 | 84 |
90 def _call(self, *args, **kwargs): | 85 def _call(self, *args, **kwargs): |
91 if self._use_sudo: | 86 if self._use_sudo: |
(...skipping 15 matching lines...) Expand all Loading... | |
107 | 102 |
108 if not self.is_installed(): | 103 if not self.is_installed(): |
109 yield self.install() | 104 yield self.install() |
110 | 105 |
111 yield self._trash_output() | 106 yield self._trash_output() |
112 yield self._call(*self._command, output_path=self.output_path) | 107 yield self._call(*self._command, output_path=self.output_path) |
113 | 108 |
114 @inlineCallbacks | 109 @inlineCallbacks |
115 def destroy(self): | 110 def destroy(self): |
116 if (yield self.is_running()): | 111 if (yield self.is_running()): |
117 yield self._call("kill", self.get_pid()) | 112 yield self._call("kill", self.get_pid()) |
hazmat
2012/08/28 14:08:22
having seen wrap-around pid behavior where this re
| |
118 yield self._trash_output() | 113 yield self._trash_output() |
119 | 114 |
120 def get_pid(self): | 115 def get_pid(self): |
121 if self._pid != None: | 116 if self._pid != None: |
122 return self._pid | 117 return self._pid |
123 | 118 |
hazmat
2012/08/28 14:08:22
what happens to the stop/destroy case if the pidfi
| |
124 if not os.path.exists(self._pid_path): | |
125 return None | |
126 r, data = _cat(self._pid_path, use_sudo=self._use_sudo) | 119 r, data = _cat(self._pid_path, use_sudo=self._use_sudo) |
127 if r != 0: | 120 if r != 0: |
128 return None | 121 return None |
129 | 122 |
130 # verify that pid is a number but leave | 123 # verify that pid is a number but leave |
131 # it as a string suitable for passing to kill | 124 # it as a string suitable for passing to kill |
132 if not data.strip().isdigit(): | 125 if not data.strip().isdigit(): |
133 return None | 126 return None |
134 pid = data.strip() | 127 pid = data.strip() |
135 self._pid = pid | 128 self._pid = pid |
136 return self._pid | 129 return self._pid |
137 | 130 |
138 def is_running(self): | 131 def is_running(self): |
139 pid = self.get_pid() | 132 pid = self.get_pid() |
140 if not pid: | 133 if not pid: |
141 return False | 134 return False |
142 proc_file = "/proc/%s" % pid | 135 proc_file = "/proc/%s" % pid |
143 if not os.path.exists(proc_file): | 136 if not os.path.exists(proc_file): |
144 return False | 137 return False |
145 return True | 138 return True |
146 | 139 |
147 def is_installed(self): | 140 def is_installed(self): |
148 return False | 141 return False |
OLD | NEW |