# Image Builder: Facilitate Custom Image Building for Fedora
# Copyright (C) 2012 Tim Flink Amit Saha
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# Contact: Amit Saha <amitksaha@fedoraproject.org>
# http://fedoraproject.org/wiki/User:Amitksaha
import traceback
import sys
import tempfile
import time
import logging
import os
from image_builder.worker import Worker
from image_builder.transfer import Transfer
from image_builder.notification import Notification
""" Kickstarts the Image Building process.
"""
[docs]class ImageBuilder:
# Read Image Build configuration
def __init__(self, buildconfig, kickstart = None):
self.buildconfig = buildconfig
self.kickstart = kickstart
self.iso_type = self.buildconfig['default']['type']
self.staging = self.buildconfig['default']['staging']
self.email = self.buildconfig['default']['email']
self.logger = logging.getLogger('imagebuilder')
self.logger.info('Registered a new Image Build request from {0:s}'.format(self.email))
self.logger.info('Image type:: {0:s}'.format(self.iso_type))
# if running in distributed mode, the logger
# is initiated via tasks.py
# the environment variable is set by the command line
# client
if not os.environ.has_key('LOCAL_MODE'):
self.logfile = self.logger.handlers[0].getlogfile()
# if running in local mode
else:
self.logfile = self.initlog()
self.monitor = self.checkmonitor()
self.notify_email_init()
[docs] def initlog(self):
""" Initiate the logging in local mode"""
time_now = str(time.time()).split('.')
logfile = tempfile.gettempdir() + '/imagebuild_{0:s}.log'.format(time_now[0]+time_now[1])
handler = logging.FileHandler(logfile)
formatter = logging.Formatter('%(asctime)s - %(message)s')
if not self.logger.handlers:
handler.setFormatter(formatter)
self.logger.addHandler(handler)
self.logger.propagate = 0
self.logger.setLevel(logging.DEBUG)
self.logger.info('Registered a new Image Build request from {0:s}'.format(self.email))
self.logger.info('Image type:: {0:s}'.format(self.iso_type))
return logfile
[docs] def getlogfile(self):
""" Return the log file """
return self.logfile
[docs] def checkmonitor(self):
''' Check if build monitor is running '''
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect(('localhost', 5100))
except:
return False
else:
return True
[docs] def notify_email_init(self):
''' Send a notification email upon build initiation with
the monitor access URL: <ip>:/log/tmp/imagebuild_xxx.log
'''
if os.environ.has_key('LOCAL_MODE'):
if os.environ['LOCAL_MODE'] == '1':
return
message = 'Your Image Building Request have been submitted. '
ipaddr = self.getip()
if self.monitor:
message = message + 'You may monitor the progress by going to '
message = message + 'http://' + ipaddr + ':5100/log{0:s}. '.format(self.logfile)
message = message + 'You will also recieve an email upon completion.'
else:
message = message + 'You will recieve an email upon completion.'
recipient = self.email
subject = 'Your Image Build Request'
headers = ["From: " + 'Fedora Build Service',
"Subject: " + subject,
"To: " + recipient,
"MIME-Version: 1.0",
"Content-Type: text/html"]
headers = "\r\n".join(headers)
notify = Notification()
notify.send_email(recipient, headers, message)
return
[docs] def notify_email_final(self):
''' Send a final notification email upon build completion '''
if os.environ.has_key('LOCAL_MODE'):
if os.environ['LOCAL_MODE'] == '1':
return
message = 'Your Image Building Request have been completed.'
ipaddr = self.getip()
message = '<p>The build was completed by worker:: {0:s}. Detailed log: '.format(ipaddr)
with open(self.logfile) as f:
for line in f:
message = message + '<p>' + line
recipient = self.email
subject = 'Your Image Build Request'
headers = ["From: " + 'Fedora Build Service',
"Subject: " + subject,
"To: " + recipient,
"MIME-Version: 1.0",
"Content-Type: text/html"]
headers = "\r\n".join(headers)
notify = Notification()
notify.send_email(recipient, headers, message)
return
[docs] def getip(self):
''' Get the public facing IP address'''
# Recipe: http://stackoverflow.com/a/166589/59634
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("gmail.com",80))
ipaddr = s.getsockname()[0]
s.close()
return ipaddr
[docs] def build(self):
# Worker object
worker = Worker(self.buildconfig)
# boot
if self.iso_type == 'boot':
self.logger.info('Starting the Image Build Process')
self.imgloc = worker.build_bootiso()
# DVD
if self.iso_type == 'dvd':
self.logger.info('Starting the Image Build Process')
self.imgloc = worker.build_dvd(self.kickstart)
#Live image
if self.iso_type == 'live':
self.logger.info('Starting the Image Build Process')
self.imgloc = worker.build_live(self.kickstart)
self.logger.info('Image building process complete')
#transfer image(s) and logs
if self.imgloc:
self.logger.info('Image successfully created. Transferring to staging.')
t = Transfer(self.buildconfig, self.imgloc, self.logfile)
status = t.transfer()
if status == 0:
self.logger.info('Image(s) and logs available at {0:s}'.format(self.staging))
#build completion notification email
self.notify_email_final()
return 0
if status == -1:
self.logger.info('Error in transfering image(s)/logs')
dirname, temp = os.path.split(os.path.abspath(self.imgloc[0]))
self.logger.info('Image(s) available in {0:s} on {1:s}'.format(dirname,os.uname()[1]))
#build completion notification email
self.notify_email_final()
return -1
else:
self.logger.info('Error creating image. Transferring Logs.')
t = Transfer(self.buildconfig, self.imgloc, self.logfile)
status = t.transfer()
if status == 0:
self.logger.info('Logs available at {0:s}'.format(self.staging))
#build completion notification email
self.notify_email_final()
return 0
if status == -1:
self.logger.info('Error in transfering logs to staging.')
#build completion notification email
self.notify_email_final()
return -1