#!/usr/bin/python
import gtk
import gtk.glade
import gobject
import os
import sys
import string
import socket
import fcntl
import struct
from popen2 import Popen3
# starts network tool from gnome-system-tools and uses icons from there, needs to depend on it
class LTSPManager:
def __init__(self):
self.wTree=gtk.glade.XML ("./ltsp-manager.glade")
self.win = self.wTree.get_widget("window1")
self.buildwin = self.wTree.get_widget("build_window")
self.selectwin = self.wTree.get_widget("select_window")
self.win.connect("destroy", lambda w: gtk.main_quit())
close_button = self.wTree.get_widget("close_button")
close_button.connect("clicked", lambda w: gtk.main_quit())
help_button = self.wTree.get_widget("help_button")
help_button.connect("clicked", lambda w: os.system('firefox http://people.ubuntu.com/~ogra/LTSPManager/'))
self.lines=0
self.fulfill_prereq()
def initialize(self):
self.get_devices()
self.set_toggles()
self.parse_dhcpd_conf()
self.get_keymaps()
self.get_kbmodels()
self.get_serial_protocols()
self.get_videodrivers()
self.wTree.get_widget("range_from_entry").connect("focus-out-event", self.compare_range)
self.wTree.get_widget("range_to_entry").connect("focus-out-event", self.compare_range)
def show_mainwin(self, path):
self.root=path
self.buildwin.hide()
self.selectwin.hide()
self.initialize()
self.win.show_all()
def fulfill_prereq(self):
self.win.hide()
self.selectwin.hide()
self.buildwin.hide()
pathlist = self.get_root_path()
arch = os.popen('dpkg --print-architecture').read().strip()
i=0
if len(pathlist) > 1:
self.selectwin.show_all()
combo = self.wTree.get_widget("chroot_combo")
select_cancel = self.wTree.get_widget("chroot_cancel_button")
select_cancel.connect("clicked", lambda w: gtk.main_quit())
select_ok = self.wTree.get_widget("chroot_ok_button")
select_ok.connect("clicked", lambda w: self.show_mainwin('/opt/ltsp/'+combo.get_active_text()))
for path in pathlist:
if path == 'i386' or path == 'powerpc' or path == 'amd64':
combo.append_text(path)
if path == arch:
combo.set_active(i)
i=i+1
elif len(pathlist) == 1 and (pathlist[0] == 'i386' or
pathlist[0] == 'amd64' or pathlist[0] == 'powerpc'):
self.show_mainwin('/opt/ltsp/'+pathlist[0])
else:
combo = self.wTree.get_widget("build_arch_selector")
build_button = self.wTree.get_widget("build_start_button")
build_button.connect("clicked", lambda w: self.build_client(combo.get_active_text()))
build_cancel = self.wTree.get_widget("build_cancel_button")
build_cancel.connect("clicked", lambda w: self.cleanup(combo.get_active_text()))
if arch == 'amd64':
archlist = ['amd64', 'i386']
else:
archlist = [arch]
for item in archlist:
combo.append_text(item)
combo.set_active(0)
self.buildwin.show_all()
self.wTree.get_widget("build_help_button").hide()
def get_root_path(self):
pathlist = os.popen('ls /opt/ltsp/ 2>/dev/null')
if pathlist:
return pathlist.read().split('\n')[:-1]
else:
return 0
def cleanup(self, root):
dirs = ['proc','sys','var/run','var/lock']
os.system('pkill ltsp-build-client')
os.system('pkill debootstrap')
for directory in dirs:
os.system('umount /opt/ltsp/'+root+'/'+directory+' 2>/dev/null')
os.system('rm -rf /opt/ltsp/'+root)
sys.exit(0)
def build_client(self, arch):
progbar = self.wTree.get_widget("build_progressbar")
self.wTree.get_widget("build_start_button").set_sensitive(0)
self.wTree.get_widget("build_arch_selector").set_sensitive(0)
fraction=0
pp=Popen3('/usr/sbin/ltsp-build-client --arch '+arch+' 2>/dev/null')
for nn in xrange(2000):
while gtk.events_pending():
gtk.main_iteration(True)
line = pp.fromchild.readline().strip()
if line.startswith('I: '):
line = line.lstrip('I: ')
out = self.line_filter(line)
if out:
progbar.set_text(out)
step = fraction+0.000666667 #0.000731529 for http, 0.000877193 for cdroms
if step > 1.0:
step = 1.0
progbar.set_fraction(step)
fraction = step
progbar.set_fraction(1.0)
progbar.set_text('LTSP Setup Done...')
gobject.timeout_add(3000, self.show_mainwin('/opt/ltsp/'+arch))
def line_filter(self, line):
if line.startswith('Retrieving') or line.startswith('Validating') or line.startswith('Extracting') or line.startswith('Installing') or line.startswith('Unpacking') or line.startswith('Configuring') or line.startswith('Need to get') or line.startswith('Setting up') or line.startswith('Cleaning'):
line = line.split('(')[0]
if not line.endswith('...'):
line=line+'...'
self.lines=self.lines+1
return line
elif line.startswith('Get:'):
line = 'Retrieving '+line.split()[3]+'...'
self.lines=self.lines+1
return line
else:
return
def set_toggles(self):
# Server
nbd_checkbutton = self.wTree.get_widget("nbd_checkbutton")
nbd_checkbutton.connect("clicked", lambda w: \
self.toggle([self.wTree.get_widget("swap_hbox")]))
# Client input
kbd_checkbutton = self.wTree.get_widget("kbd_checkbutton")
kbd_checkbutton.connect("clicked", lambda w: \
self.toggle([self.wTree.get_widget("kbd_layout_hbox"), \
self.wTree.get_widget("kbd_model_hbox")]))
mouse_checkbutton = self.wTree.get_widget("mouse_checkbutton")
mouse_checkbutton.connect("clicked", lambda w: \
self.toggle([self.wTree.get_widget("mousedev_hbox"), \
self.wTree.get_widget("mouseproto_hbox"), \
self.wTree.get_widget("emu_hbox")]))
# Client screen
noauto_checkbutton = self.wTree.get_widget("noauto_checkbutton")
noauto_checkbutton.connect("clicked", lambda w: \
self.toggle([self.wTree.get_widget("hsync_hbox"), \
self.wTree.get_widget("vref_hbox"), \
self.wTree.get_widget("driver_hbox")]))
xfs_checkbutton = self.wTree.get_widget("xfs_checkbutton")
xfs_checkbutton.connect("clicked", lambda w: \
self.toggle([self.wTree.get_widget("xfs_hbox")]))
# LDM
ldm_command_checkbutton = self.wTree.get_widget("ldm_command_checkbutton")
ldm_command_checkbutton.connect("clicked", lambda w: \
self.toggle([self.wTree.get_widget("ldm_command_hbox")]))
env_checkbutton = self.wTree.get_widget("env_checkbutton")
env_checkbutton.connect("clicked", lambda w: \
self.toggle([self.wTree.get_widget("env_hbox")]))
return True
def toggle(self, widgetlist):
status = widgetlist[0].get_property('sensitive')
if status == True:
status = False
else:
status = True
for widget in widgetlist:
widget.set_sensitive(status)
def get_devices(self):
list = []
self.devlist = []
devlist = os.popen("cat /proc/net/dev|grep :|\
awk '{split($0, dev, \":\"); printf \"%s\\n\", \
substr(dev[1], 3)}'|grep -v lo|grep -v sit")
list.append(devlist.read().split('\n'))
for device in list[0][:-1]:
if not self.get_ip(device) == False:
self.devlist.append(device)
else:
# make that a info dialog
self.win.set_sensitive(False)
dialog = gtk.MessageDialog(self.win,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_INFO, gtk.BUTTONS_OK, None)
dialog.set_markup('Not all available interfaces activated!\n\nThe interface '+device+' is present but offline. Please bring it up before running LTSP Manager if you want to use it. For LTSP.')
dialog.connect("destroy", lambda w: self.win.set_sensitive(True))
resp = dialog.run()
if resp:
dialog.destroy()
return True
def get_keymaps(self):
list = []
keyb_list = os.popen("grep xkb_keymap "+self.root+"/etc/X11/xkb/keymap/* | \
awk '{split($0, kb);printf \"%s\\n\", kb[2]}'|\
grep -v xkb_keymap|sort|uniq")
list.append(keyb_list.read().split('\n'))
for item in list[0][:-1]:
self.wTree.get_widget("kbd_layout_combobox").append_text(str(item).strip('\"'))
def get_kbmodels(self):
list = []
newlist = []
keyb_list = os.popen("grep xkb_geometry "+self.root+"/etc/X11/xkb/geometry/*| \
awk '{split($0, kb);printf \"%s%s\\n\", kb[2],kb[3]}'")
list.append(keyb_list.read().split('\n'))
for item in list[0][:-1]:
newlist.append(item.lstrip('xkb_geometry').rstrip('{').strip('\"'))
newlist = self.unique(newlist)
newlist.sort(lambda x, y: cmp(string.lower(x), string.lower(y)))
for model in newlist:
self.wTree.get_widget("kbd_model_combobox").append_text(model)
def get_serial_protocols(self):
modelist = os.popen("chroot "+self.root+" inputattach --help")
for line in modelist.read().split('\n'):
if line.lstrip().startswith('--'):
self.wTree.get_widget("mouseproto_combobox").append_text(string.join(line.split()[2:]).rstrip())
def get_videodrivers(self):
list = []
cardlist = os.popen("ls "+self.root+"/usr/lib/xorg/modules/drivers/*.so")
list.append(cardlist.read().split('\n'))
for item in list[0][:-1]:
newitem = item.split('/')[-1].rstrip('_drv.so')
self.wTree.get_widget("driver_combobox").append_text(newitem)
def get_ip(self, ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
retval = socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
except:
return False
return retval
def parse_dhcpd_conf(self):
try:
file = open('/etc/ltsp/dhcpd.conf', 'r')
except:
# make that an error dialog
self.win.set_sensitive(False)
dialog = gtk.MessageDialog(self.win,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, None)
dialog.set_markup('Can\'t open /etc/ltsp/dhcpd.conf !\n\nThe file /etc/ltsp/dhcpd.conf could not be opened, make sure the ltsp-server-standalone package is installed correctly. LTSP Manager will quit now.')
dialog.connect("destroy", lambda w: self.win.set_sensitive(True))
resp = dialog.run()
if resp:
sys.exit(1)
# read all the data
for line in file.read().split('\n'):
line = line.strip().lstrip('option ')
if line.startswith('range'):
self.range_from = line.strip('range').strip(';').split()[0]
self.wTree.get_widget("range_from_entry").set_text(self.range_from)
self.range_to = line.strip('range').strip(';').split()[1]
self.wTree.get_widget("range_to_entry").set_text(self.range_to)
elif line.startswith('domain-name '):
self.domain_name = line.strip('domain-name ').strip(';').split()[0].strip('\"')
self.wTree.get_widget("domain_entry").set_text(self.domain_name)
elif line.startswith('domain-name-servers '):
self.dns_server = line.strip('domain-name-servers ').strip(';').split()[0]
self.wTree.get_widget("dns_entry").set_text(self.dns_server)
elif line.startswith('broadcast-address '):
self.broadcast = line.strip('broadcast-address ').strip(';').split()[0]
self.wTree.get_widget("broadcast_entry").set_text(self.broadcast)
elif line.startswith('routers '):
self.routers = line.strip('routers ').strip(';').split()[0]
self.wTree.get_widget("gateway_entry").set_text(self.routers)
elif line.startswith('subnet-mask '):
self.netmask = line.strip('subnet-mask ').strip(';').split()[0]
self.wTree.get_widget("netmask_entry").set_text(self.netmask)
net = self.range_from.rsplit(".", 1)[0]+'.'
self.netlist = [net]
match = False
# determine the right interface and pre-select it
iface = self.wTree.get_widget("iface_combobox")
iface.append_text('none (old config)')
for dev in self.devlist:
iface.append_text(dev)
self.netlist.append(self.get_ip(dev).rsplit(".", 1)[0]+'.')
if self.get_ip(dev).startswith(net):
iface.set_active(self.devlist.index(dev)+1)
match = True
if match == False:
iface.set_active(0)
# make that an error dialog
self.win.set_sensitive(False)
dialog = gtk.MessageDialog(self.win,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, None)
dialog.set_markup('No Matching Interfaces found !\n\nThere are no Network interfaces running that match the network configuration ('+net+'0) of the current LTSP DHCP configuration. Please adjust the DHCP settings to match a currently running interface with static IP address.')
dialog.connect("destroy", lambda w: self.win.set_sensitive(True))
resp = dialog.run()
if resp:
self.wTree.get_widget("notebook1").set_current_page(3)
dialog.destroy()
else:
iface.remove_text(0)
self.netlist.reverse()
self.netlist.pop()
self.netlist.reverse()
iface.connect("changed", lambda w: self.update_dhcp(iface))
def update_dhcp(self, iface):
net = self.netlist[iface.get_active()]
orig_net = [self.wTree.get_widget("range_from_entry").get_text().rsplit(".", 1)[1], \
self.wTree.get_widget("range_to_entry").get_text().rsplit(".", 1)[1], \
self.wTree.get_widget("broadcast_entry").get_text().rsplit(".", 1)[1], \
self.wTree.get_widget("gateway_entry").get_text().rsplit(".", 1)[1] ]
self.wTree.get_widget("range_from_entry").set_text(net+orig_net[0])
self.wTree.get_widget("range_to_entry").set_text(net+orig_net[1])
self.wTree.get_widget("broadcast_entry").set_text(net+orig_net[2])
self.wTree.get_widget("gateway_entry").set_text(net+orig_net[3])
def compare_range(self, widget, event):
base,range0 = self.wTree.get_widget("range_from_entry").get_text().rsplit(".", 1)
range1 = self.wTree.get_widget("range_to_entry").get_text().rsplit(".", 1)[1]
if int(range0) >= 255 or int(range1) >= 255:
self.wTree.get_widget("range_from_entry").set_text(str(base)+'.253')
self.wTree.get_widget("range_to_entry").set_text(str(base)+'.254')
return
if int(range0) >= int(range1):
self.wTree.get_widget("range_to_entry").set_text(str(base)+'.'+str(int(range0)+1))
def unique(self,s):
n = len(s)
if n == 0:
return []
u = {}
try:
for x in s:
u[x] = 1
except TypeError:
del u # move on to the next method
else:
return u.keys()
try:
t = list(s)
t.sort()
except TypeError:
del t # move on to the next method
else:
assert n > 0
last = t[0]
lasti = i = 1
while i < n:
if t[i] != last:
t[lasti] = last = t[i]
lasti += 1
i += 1
return t[:lasti]
# Brute force is all that's left.
u = []
for x in s:
if x not in u:
u.append(x)
return u
def main(self):
gtk.main()
if __name__ == "__main__":
base = LTSPManager()
base.main()