-
Notifications
You must be signed in to change notification settings - Fork 82
/
imagefactoryd
executable file
·221 lines (189 loc) · 8.77 KB
/
imagefactoryd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/usr/bin/env python3
# encoding: utf-8
# Copyright 2011 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import os
import os.path
import signal
import logging
from imgfac.PersistentImageManager import PersistentImageManager
from imgfac.ApplicationConfiguration import ApplicationConfiguration
from imgfac.BuildDispatcher import BuildDispatcher
from imgfac.Singleton import Singleton
from imgfac.rest.bottle import *
from imgfac.rest.RESTv2 import rest_api
from imgfac.PluginManager import PluginManager
from time import asctime, localtime
# Monkey patch for guestfs threading issue
# BZ 790528
# TODO: Remove at some point when the upstream fix is in our supported platforms
from imgfac.ReservationManager import ReservationManager
from guestfs import GuestFS as _GuestFS
class GuestFS(_GuestFS):
def launch(self):
res_mgr = ReservationManager()
res_mgr.get_named_lock("libguestfs_launch")
try:
_GuestFS.launch(self)
finally:
res_mgr.release_named_lock("libguestfs_launch")
import guestfs
guestfs.GuestFS = GuestFS
class Application(Singleton):
def _singleton_init(self):
super(Application, self)._singleton_init()
logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(levelname)s %(name)s thread(%(threadName)s) Message: %(message)s')
self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
self.daemon = False
self.pid_file_path = "/var/run/imagefactoryd.pid"
signal.signal(signal.SIGTERM, self.signal_handler)
self.app_config = ApplicationConfiguration().configuration
# by setting TMPDIR here we make sure that libguestfs
# (imagefactory -> oz -> libguestfs) uses the temporary directory of
# the user's choosing
os.putenv('TMPDIR', self.app_config['tmpdir'])
def __init__(self):
pass
def setup_logging(self):
if (not self.app_config['foreground']):
# We can no longer use the basicconfig convenience function to do this so do so manually
logger = logging.getLogger()
currhandler = logger.handlers[0] # stdout is the only handler initially
filehandler = logging.FileHandler('/var/log/imagefactoryd.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s thread(%(threadName)s) Message: %(message)s')
filehandler.setFormatter(formatter)
logger.addHandler(filehandler)
logger.removeHandler(currhandler)
self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
if (self.app_config['debug']):
logging.getLogger('').setLevel(logging.DEBUG)
elif (self.app_config['verbose']):
logging.getLogger('').setLevel(logging.INFO)
def signal_handler(self, signum, stack):
if (signum == signal.SIGTERM):
logging.warn('caught signal SIGTERM, stopping...')
# Run the abort() method in all running builders
# TODO: Find some way to regularly purge non-running builders from the registry
# or replace it with something else
builder_dict = BuildDispatcher().builders
for builder_id in builder_dict:
# If the build or push worker thread has already exited we do nothing
# builder classes should always cleanup before exiting the thread-starting methods
if builder_dict[builder_id].builder_thread and builder_dict[builder_id].builder_thread.is_alive():
try:
logging.debug("Executing abort method for builder id (%s)" % (builder_id))
builder_dict[builder_id].abort()
except Exception as e:
logging.warning("Got exception when attempting to abort build id (%s) during shutdown" % (builder_id))
logging.exception(e)
sys.exit(0)
def daemonize(self): #based on Python recipe 278731
UMASK = 0o77
WORKING_DIRECTORY = '/'
if self.app_config['debug']:
IO_REDIRECT = "/var/log/imagefactory.log-stderr_debug"
else:
IO_REDIRECT = os.devnull
try:
pid = os.fork()
except OSError as e:
raise Exception("%s [%d]" % (e.strerror, e.errno))
if (pid == 0):
os.setsid()
signal.signal(signal.SIGHUP, signal.SIG_IGN)
# TODO: Figure out what is generating the spurious SIGALRMs
# For now - ignore them
signal.signal(signal.SIGALRM, signal.SIG_IGN)
try:
pid = os.fork()
except OSError as e:
raise Exception("%s [%d]" % (e.strerror, e.errno))
if (pid == 0):
os.chdir(WORKING_DIRECTORY)
os.umask(UMASK)
else:
os._exit(0)
else:
os._exit(0)
for file_descriptor in range(0, 2): # close stdin, stdout, stderr
try:
os.close(file_descriptor)
except OSError:
pass # The file descriptor wasn't open to begin with, just ignore
if self.app_config['debug']:
os.open(IO_REDIRECT, os.O_RDWR | os.O_SYNC | os.O_CREAT | os.O_APPEND)
else:
os.open(IO_REDIRECT, os.O_RDWR)
os.dup2(0, 1)
os.dup2(0, 2)
return(True)
def main(self):
if (not self.app_config['foreground']):
self.daemon = self.daemonize()
self.setup_logging()
if(self.daemon):
try:
with open(self.pid_file_path, "w") as pidfile:
pidfile.write("%s\n" % (str(os.getpid()), ))
pidfile.close()
except Exception as e:
logging.warning(str(e))
logging.info("Launched as daemon...")
elif(self.app_config['foreground']):
logging.info("Launching in foreground...")
else:
logging.warning("Failed to launch as daemon...")
self.plugin_mgr = PluginManager(self.app_config['plugins'])
self.plugin_mgr.load()
# Instantiate any Singletons that do not have a thread-safe __init__ here
# And explain why
# The FilePersistentImageManager will create the storage directory if it does not exist
# Avoid the complexity of locking by doing the init here, before we go multi-thread
temp = PersistentImageManager.default_manager()
debug(self.app_config['debug'])
pem_file = self.app_config['ssl_pem'] if not self.app_config['no_ssl'] else None
if self.app_config['debug']:
# Mark the start of this run in our stderr/stdout debug redirect
sys.stderr.write("%s - starting factory process %d\n" % (asctime(localtime()), os.getpid()))
sys.stderr.flush()
# Import and activate faulthandler if it exists
try:
import faulthandler
faultfile = open("/var/log/imagefactory.log-faulthandler", "a")
faultfile.write("%s - starting factory process %d\n" % (asctime(localtime()), os.getpid()))
faultfile.flush()
faulthandler.enable(file=faultfile, all_threads = True)
logging.debug("Import of faulthandler successful")
except:
logging.debug("Unable to find python module, faulthandler... multi-thread tracebacks will not be available. See http://pypi.python.org/pypi/faulthandler/ for more information.")
pass
if self.app_config['secondary']:
try:
from imgfac.secondary.RESTSecondaryv2 import rest_api as secondary_rest_api
app = secondary_rest_api
except:
logging.error("Secondary REST API module not present - Cannot start as a secondary factory")
sys.exit(1)
else:
app = rest_api
# Don't set reloader to True or you will lose state. You have been warned.
run(server='cherrypy',
app=app,
host=self.app_config['address'],
port=self.app_config['port'],
reloader=False,
quiet=True)
if __name__ == "__main__":
sys.exit(Application().main())