Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
laelaps_init.d.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 
3 ###############################################################################
4 #
5 # Package: RoadNarrows Robotics Laelaps System V Init.d Console
6 #
7 # Link: https://github.com/roadnarrows-robotics/laelaps
8 #
9 # File: laelaps_init.d
10 #
11 ## \file
12 ##
13 ## $LastChangedDate: 2016-03-18 09:57:27 -0600 (Fri, 18 Mar 2016) $
14 ## $Rev: 4354 $
15 ##
16 ## \brief Graphical user interface console to control the Laelaps init.d
17 ## daemons.
18 ##
19 ## \author Robin Knight (robin.knight@roadnarrows.com)
20 ##
21 ## \par Copyright
22 ## \h_copy 2016-2017. RoadNarrows LLC.\n
23 ## http://www.roadnarrows.com\n
24 ## All Rights Reserved
25 ##
26 # @EulaBegin@
27 # @EulaEnd@
28 #
29 ###############################################################################
30 
31 import sys
32 import os
33 import platform
34 import time
35 import math
36 import subprocess
37 import re
38 import threading
39 import getopt
40 
41 from Tkinter import *
42 from Tkconstants import *
43 from tkFileDialog import *
44 import tkFont
45 
46 # running as su does not necessary have all paths setup - so fix up here
47 sys.path.insert(0, "/usr/local/lib/python2.7/site-packages")
48 sys.path.insert(0, "/prj/lib/python2.7/site-packages")
49 
50 from Laelaps.Utils import *
51 
52 ## \brief Application version. Update as needed.
53 appVersion = '1.0.0'
54 
55 ## \brief Additional image search paths.
56 imagePath = [
57  "/prj/share/Laelaps/images",
58  "/prj/share/Laelaps/images/icons",
59  "/prj/share/appkit/images",
60  "/prj/share/appkit/images/icons",
61  "/usr/local/share/Laelaps/images",
62  "/usr/local/share/Laelaps/images/icons"
63  "/usr/local/share/appkit/images",
64  "/usr/local/share/appkit/images/icons"
65 ]
66 
67 ## \brief Common foreground colors.
68 fgColors = {
69  'normal': 'black',
70  'ok': '#008800',
71  'focus': '#0000aa',
72  'warning': '#aa6600',
73  'error': '#cc0000'
74 }
75 
76 ## \brief Status text foreground colors.
77 statusText = {
78  'unknown': fgColors['normal'],
79  'running': fgColors['ok'],
80  'stopped': fgColors['error']
81 }
82 
83 # pre-compiled regular expressions
84 reDone = re.compile(r"done", re.IGNORECASE)
85 reFail = re.compile(r"fail", re.IGNORECASE)
86 reDoneDone = re.compile(r"done.*done", re.DOTALL | re.IGNORECASE)
87 reFailDone = re.compile(r"fail.*done", re.DOTALL | re.IGNORECASE)
88 reRunning = re.compile(r"\s*is\s*running", re.IGNORECASE)
89 reNotRunning = re.compile(r"\s*is\s*not\s*running", re.IGNORECASE)
90 reActive = re.compile(r"\s*active:\s*active.*", re.IGNORECASE)
91 reInactive = re.compile(r"\s*active:\s*inactive.*", re.IGNORECASE)
92 
93 #
94 ## \brief Determine the OS specifics.
95 ##
96 ## \return Return a 3-tuple (osname, version, id)
97 #
98 def os_distribution():
99  try:
100  return platform.linux_distribution()
101  except:
102  return ('n/a', 'n/a', 'n/a')
103 
104 #
105 ## \brief Determine if OS version 1 is greater or equal to version 2.
106 ##
107 ## The version is expected to be in major[.minor] format.
108 ##
109 ## \param v1 Version 1 as a string, float, or int.
110 ## \param v2 Version 2 as a string, float, or int.
111 ##
112 ## \return Returns True or False.
113 #
114 def os_ver_ge(v1, v2):
115  if type(v1) is 'str':
116  try:
117  v1 = float(v1)
118  except:
119  v1 = 0;
120  if type(v2) is 'str':
121  try:
122  v2 = float(v2)
123  except:
124  v2 = 0;
125  return v1 >= v2
126 
127 
128 # ------------------------------------------------------------------------------
129 # Class window
130 # ------------------------------------------------------------------------------
131 
132 ##
133 ## \brief Window class supporting application.
134 ##
135 class window(Frame):
136  #
137  ## \brief Constructor.
138  ##
139  ## \param master Window parent master widget.
140  ## \param cnf Configuration dictionary.
141  ## \param kw Keyword options.
142  #
143  def __init__(self, master=None, cnf={}, **kw):
144  # intialize window data
145  kw = self.initData(kw)
146 
147  self.m_imageLoader = ImageLoader(py_pkg='Laelaps.images',
148  image_paths=imagePath)
149 
150  Frame.__init__(self, master=master, cnf=cnf, **kw)
151  self.master.title("Laelaps Init.d Console")
152 
153  self.m_icons['app_icon'] = \
154  self.m_imageLoader.loadImage("icons/LaelapsInitIcon.png")
155  if self.m_icons['app_icon'] is not None:
156  self.master.tk.call('wm', 'iconphoto', self.master._w,
157  self.m_icons['app_icon'])
158 
159  # craete and show widgets
160  self.createWidgets()
161 
162  self.grid(row=0, column=0, padx=5, pady=5)
163 
164  self.after(100, self.autoRefresh)
165 
166  #
167  ## \brief Initialize class state data.
168  ##
169  ## Any keywords for this application specific window that are not supported
170  ## by the Frame Tkinter class must be removed.
171  ##
172  ## \param kw Keyword options.
173  ##
174  ## \return Modified keywords sans this specific class.
175  #
176  def initData(self, kw):
177  self.m_osdist = os_distribution()
178  self.m_debug = False # default debug level
179  self.m_icons = {} # must keep loaded icons referenced
180  self.m_wBttn = {} # button widgets
181  self.m_svcKeys = [
182  'laelaps_bsproxy', 'laelaps_roscore', 'laelaps_control',
183  'laelaps_xbox', 'laelaps_teleop']
184  self.m_svcDesc = {
185  'laelaps_bsproxy': 'BotSense Proxy Server',
186  'laelaps_roscore': 'ROS Master, Parameter Server, rosout logging node',
187  'laelaps_control': 'Laelaps Control ROS node',
188  'laelaps_xbox': 'HID Xbox360 daemon / ROS node',
189  'laelaps_teleop': 'Laelaps Teleoperation ROS node'}
190  self.m_lock = threading.Lock()
191 
192  if kw.has_key('debug'):
193  self.m_debug = kw['debug']
194  del kw['debug']
195 
196  # variables only used for debugging
197  if self.m_debug:
198  pass
199 
200  return kw
201 
202  #
203  ## \brief Create gui widgets with supporting data and show.
204  #
205  def createWidgets(self):
206  self.createHeading()
207  self.createLeftButtons()
208  self.createCenterPanel()
209  self.createRightButtons()
210  self.update_idletasks()
211  self.createStatusBar()
212 
213  #
214  ## \brief Create top gui heading.
215  #
216  def createHeading(self):
217  # rn logo
218  w = Label(self)
219  self.m_icons['rn_logo'] = self.m_imageLoader.loadImage("RNLogo48.png");
220  if self.m_icons['rn_logo']:
221  w['image'] = self.m_icons['rn_logo']
222  else:
223  w['text'] = 'rn'
224  w['anchor'] = W
225  w['width'] = 5
226  w.grid(row=0, column=0, sticky=W)
227 
228  # top heading
229  w = Label(self)
230  w['font'] = ('Helvetica', 16)
231  w['text'] = 'Laelaps Init.d Console'
232  w['anchor'] = CENTER
233  w.grid(row=0, column=1, sticky=E+W)
234 
235  # laelaps logo
236  w = Label(self)
237  self.m_icons['laelaps_logo'] = \
238  self.m_imageLoader.loadImage("icon_laelaps_logo.png");
239  if self.m_icons['laelaps_logo']:
240  w['image'] = self.m_icons['laelaps_logo']
241  w['anchor'] = E
242  else:
243  w['text'] = 'laelaps'
244  w['anchor'] = E
245  w['width'] = 5
246  w.grid(row=0, column=2, sticky=E)
247 
248  #
249  ## \brief Create gui left hand side buttons.
250  #
251  def createLeftButtons(self):
252  wframe = Frame(self)
253  wframe['borderwidth'] = 2
254  wframe['relief'] = 'ridge'
255  wframe.grid(row=1, column=0, padx=1, pady=3, sticky=N+W+E)
256 
257  row = 0
258 
259  # start
260  w = self.createButton(wframe, "Start", "icon_play.png",
261  self.cbStartServices)
262  w['state'] = 'disabled'
263  w.grid(row=row, column=0, sticky=N+E+W)
264 
265  row += 1
266 
267  # stop
268  w = self.createButton(wframe, "Stop", "icon_stop.png",
269  self.cbStopServices)
270  w['state'] = 'disabled'
271  w.grid(row=row, column=0, sticky=N+E+W)
272 
273  row += 1
274 
275  # restart
276  w = self.createButton(wframe, "Restart", "icon_resume.png",
277  self.cbRestartServices)
278  w['state'] = 'disabled'
279  w.grid(row=row, column=0, sticky=N+E+W)
280 
281  row += 1
282 
283  #
284  ## \brief Create gui center panel.
285  #
286  def createCenterPanel(self):
287  wframe = Frame(self)
288  wframe['borderwidth'] = 2
289  wframe['relief'] = 'ridge'
290  wframe.grid(row=1, column=1, padx=1, pady=3, sticky=N+W+E)
291 
292  row = 0
293  col = 0
294 
295  for text in ['Sel', 'Status', 'Service', 'Description']:
296 
297  w = Label(wframe, text=text, foreground=fgColors['focus'])
298  w['font'] = ('Helvetica', 10, "bold")
299  w.grid(row=row, column=col, padx=3, pady=3, stick=W)
300 
301  col += 1
302 
303  row += 1
304  colspan = len(self.m_svcKeys)
305  bg0 = '#ffffcc'
306  bg1 = wframe['bg']
307  bg = bg0
308 
309  self.m_service = { }
310 
311  for key in self.m_svcKeys:
312  col = 0
313  service = { }
314 
315  service['sel'] = IntVar(0)
316 
317  w = Checkbutton(wframe, bg=bg, variable=service['sel'],
318  command=self.cbSelect)
319  w.grid(row=row, column=col, padx=1, pady=3, sticky=W)
320 
321  col += 1
322 
323  service['status'] = StringVar()
324  service['status'].set("unknown")
325 
326  w = Label(wframe, bg=bg, textvariable=service['status'])
327  w['fg'] = statusText[service['status'].get()]
328  w['width'] = 8
329  w.grid(row=row, column=col, padx=1, pady=3, stick=W)
330  service['wstatus'] = w
331 
332  col += 1
333 
334  w = Label(wframe, bg=bg, anchor=W, justify=LEFT, text=key)
335  w.grid(row=row, column=col, padx=1, pady=3, stick=W+E)
336 
337  col += 1
338 
339  w = Label(wframe, bg=bg, anchor=W, justify=LEFT, text=self.m_svcDesc[key])
340  w.grid(row=row, column=col, padx=1, pady=3, stick=W+E)
341 
342  col += 1
343 
344  self.m_service[key] = service
345 
346  if bg == bg0:
347  bg = bg1
348  else:
349  bg = bg0
350 
351  row += 1
352 
353  #
354  ## \brief Create gui right hand side buttons.
355  #
357  wframe = Frame(self)
358  wframe['borderwidth'] = 2
359  wframe['relief'] = 'ridge'
360  wframe.grid(row=1, column=2, padx=1, pady=3, sticky=N+W+E)
361 
362  row = 0
363 
364  # refresh
365  w = self.createButton(wframe, "Refresh", "icon_refresh.png",
366  self.cbRefreshStatus)
367  w.grid(row=row, column=0, sticky=N+E+W)
368 
369  row += 1
370  # save
371  w = self.createButton(wframe, "Save", "icon_floppy.png",
372  self.cbSave)
373  w['state'] = 'disabled'
374  w.grid(row=row, column=0, sticky=N+E+W)
375 
376  row += 1
377 
378  # save
379  w = self.createButton(wframe, "Quit", "icon_exit.png",
380  self.destroy)
381  w.grid(row=row, column=0, sticky=N+E+W)
382 
383  #
384  ## \brief Create gui multi-line status bar at bottom of gui window.
385  #
386  def createStatusBar(self):
387  wframe = Frame(self)
388  wframe['borderwidth'] = 2
389  wframe['relief'] = 'ridge'
390  wframe.grid(row=2, column=0, columnspan=3, padx=1, pady=3, sticky=N+E+W+S)
391 
392  w = Scrollbar(wframe)
393  w.grid(row=0, column=1, sticky=N+S)
394  self.m_wScrollBar = w;
395 
396  self.m_wStatusBar = Text(wframe)
397  self.m_wStatusBar['width'] = 105
398  self.m_wStatusBar['height'] = 10
399  self.m_wStatusBar['wrap'] = WORD
400  self.m_wStatusBar['relief'] = 'flat'
401  self.m_wStatusBar['fg'] = fgColors['normal']
402  self.m_wStatusBar['state'] = 'disabled'
403  self.m_wStatusBar.grid(row=0, column=0, padx=3, pady=3, sticky=N+E+W+S)
404 
405  # attach
406  self.m_wStatusBar['yscrollcommand'] = self.m_wScrollBar.set
407  self.m_wScrollBar['command'] = self.m_wStatusBar.yview
408 
409  #
410  ## \brief Create button.
411  ##
412  ## \param parent Parent widget.
413  ## \param text Button text.
414  ## \param imagefile Image file name. None for no image.
415  ## \param command Callback for button push.
416  ## \param fg Foreground text color.
417  ##
418  ## \return Button widget.
419  #
420  def createButton(self, parent, text, imagefile, command, fg='black'):
421  key = str.lower(text.replace("\n", "_"))
422  self.m_icons[key] = self.m_imageLoader.loadImage(imagefile)
423  w = Button(parent)
424  w['text'] = text
425  if self.m_icons[key]:
426  w['image'] = self.m_icons[key]
427  w['compound'] = LEFT
428  w['padx'] = 0
429  w['pady'] = 0
430  w['anchor'] = W
431  w['width'] = 105
432  else:
433  w['anchor'] = CENTER
434  w['width'] = 10
435  w['fg'] = fg
436  w['command'] = command
437  self.m_wBttn[key] = w
438  return self.m_wBttn[key]
439 
440  #
441  ## \brief Clear all select checkboxes.
442  #
443  def clearSelect(self):
444  for key in self.m_svcKeys:
445  self.m_service[key]['sel'].set(0)
446  self.cbSelect()
447 
448  #
449  ## \brief Checkbox change state callback.
450  #
451  def cbSelect(self):
452  bttns = ['start', 'stop', 'restart']
453  nselected = 0
454  for key in self.m_svcKeys:
455  if self.m_service[key]['sel'].get():
456  nselected += 1
457  if nselected > 0:
458  state = 'normal'
459  else:
460  state = 'disabled'
461  for key in bttns:
462  self.m_wBttn[key]['state'] = state
463 
464  #
465  ## \brief Start selected services callback.
466  #
467  def cbStartServices(self):
468  self.showSbInfo("Starting selected services...\n")
469  for key in self.m_svcKeys:
470  if self.m_service[key]['sel'].get():
471  text = " Starting {0} service".format(key)
472  self.showSbInfo(text)
473  self.update_idletasks()
474  pf = self.execStart(key)
475  self.showSbResult(text, pf)
476  #self.clearSelect()
477  self.refresh()
478 
479  #
480  ## \brief Stop selected services callback.
481  #
482  def cbStopServices(self):
483  self.showSbInfo("Stopping selected services...\n")
484  for key in self.m_svcKeys:
485  if self.m_service[key]['sel'].get():
486  text = " Stopping {0} service".format(key)
487  self.showSbInfo(text)
488  self.update_idletasks()
489  pf = self.execStop(key)
490  self.showSbResult(text, pf)
491  #self.clearSelect()
492  self.refresh()
493 
494  #
495  ## \brief Restart selected services callback.
496  #
497  def cbRestartServices(self):
498  self.showSbInfo("Restarting selected services...\n")
499  for key in self.m_svcKeys:
500  if self.m_service[key]['sel'].get():
501  text = " Restarting {0} service".format(key)
502  self.showSbInfo(text)
503  self.update_idletasks()
504  pf = self.execRestart(key)
505  self.showSbResult(text, pf)
506  #self.clearSelect()
507  self.refresh()
508 
509  #
510  ## \brief Refresh services status callback.
511  #
512  def cbRefreshStatus(self):
513  self.showSbInfo("Refreshing status of all services...\n")
514  for key in self.m_svcKeys:
515  text = " Checking {0} status".format(key)
516  self.showSbInfo(text)
517  self.update_idletasks()
518  status,output = self.execStatus(key)
519  self.showSbStatus(text, status)
520  self.setStatus(key, status)
521 
522  #
523  ## \brief Save new settings callback.
524  #
525  def cbSave(self):
526  self.showSbInfo("Saving new settings...\n")
527 
528  #
529  ## \brief Destroy window callback.
530  #
531  def destroy(self):
532  self.quit()
533 
534  #
535  ## \brief Show information text on status bar.
536  ##
537  ## \param text Info text string.
538  #
539  def showSbInfo(self, text):
540  self.m_wStatusBar["state"] = "normal"
541  idx0 = self.m_wStatusBar.index(INSERT)
542  self.m_wStatusBar.insert(END, text)
543  idx1 = self.m_wStatusBar.index(INSERT)
544  self.m_wStatusBar.tag_add("norm", idx0, idx1)
545  self.m_wStatusBar.tag_config("norm", foreground=fgColors['normal'])
546  self.m_wStatusBar.see(END)
547  self.m_wStatusBar["state"] = "disabled"
548 
549  #
550  ## \brief Show error text on status bar.
551  ##
552  ## \param text Error text string.
553  #
554  def showSbError(self, text):
555  self.m_wStatusBar["state"] = "normal"
556  idx0 = self.m_wStatusBar.index(INSERT)
557  self.m_wStatusBar.insert(END, text)
558  idx1 = self.m_wStatusBar.index(INSERT)
559  self.m_wStatusBar.tag_add("err", idx0, idx1)
560  self.m_wStatusBar.tag_config("err", foreground=fgColors['error'])
561  self.m_wStatusBar.see(END)
562  self.m_wStatusBar["state"] = "disabled"
563 
564  #
565  ## \brief Show ok text on status bar.
566  ##
567  ## \param text Ok text string.
568  #
569  def showSbOk(self, text):
570  self.m_wStatusBar["state"] = "normal"
571  idx0 = self.m_wStatusBar.index(INSERT)
572  self.m_wStatusBar.insert(END, text)
573  idx1 = self.m_wStatusBar.index(INSERT)
574  self.m_wStatusBar.tag_add("ok", idx0, idx1)
575  self.m_wStatusBar.tag_config("ok", foreground=fgColors['ok'])
576  self.m_wStatusBar.see(END)
577  self.m_wStatusBar["state"] = "disabled"
578 
579  #
580  ## \brief Show operation result on status bar.
581  ##
582  ## \param text Prefix text already displayed on current status bar line.
583  ## \param success Operation was [not] a success.
584  #
585  def showSbResult(self, text, success):
586  n = self.m_wStatusBar['width'] - len(text)
587  if success:
588  rc = '[ok]'
589  self.showSbOk("%*s\n" % (n, rc))
590  else:
591  rc = '[failed]'
592  self.showSbError("%*s\n" % (n, rc))
593 
594  #
595  ## \brief Show service status result on status bar.
596  ##
597  ## \param text Prefix text already displayed on current status bar line.
598  ## \param status Service status. One of: 'running' 'stopped' 'unknown'
599  #
600  def showSbStatus(self, text, status):
601  n = self.m_wStatusBar['width'] - len(text)
602  if status == 'running':
603  rc = '[running]'
604  self.showSbOk("%*s\n" % (n, rc))
605  elif status == 'stopped':
606  rc = '[stopped]'
607  self.showSbError("%*s\n" % (n, rc))
608  else:
609  rc = '[unknown]'
610  self.showSbInfo("%*s\n" % (n, rc))
611 
612  #
613  ## \brief Set service status field.
614  ##
615  ## \param service Service (key).
616  ## \param status Service status. One of: 'running' 'stopped' 'unknown'
617  #
618  def setStatus(self, service, status):
619  self.m_service[service]['status'].set(status)
620  self.m_service[service]['wstatus']['fg'] = statusText[status]
621 
622  def autoRefresh(self):
623  self.refresh()
624  self.after(2000, self.autoRefresh)
625 
626  #
627  ## \brief Refresh status of all services.
628  #
629  def refresh(self):
630  for key in self.m_svcKeys:
631  status,output = self.execStatus(key)
632  self.setStatus(key, status)
633 
634  #
635  ## \brief Execute 'service <service> start' subprocess.
636  ##
637  ## \param service Service (key).
638  ##
639  ## \return Returns True on success, False on failure.
640  #
641  def execStart(self, service):
642  rsp = ''
643  hasLock = self.m_lock.acquire()
644  try:
645  rsp = subprocess.check_output(["service", service, "start"],
646  stderr=subprocess.STDOUT)
647  except subprocess.CalledProcessError, inst:
648  self.m_lock.release()
649  return False
650  self.m_lock.release()
651  if os_ver_ge(self.m_osdist[1], 15.04):
652  return True # no output
653  else:
654  if reFail.search(rsp):
655  return False
656  else:
657  return True
658 
659  #
660  ## \brief Execute 'service <service> stop' subprocess.
661  ##
662  ## \param service Service (key).
663  ##
664  ## \return Returns True on success, False on failure.
665  #
666  def execStop(self, service):
667  rsp = ''
668  hasLock = self.m_lock.acquire()
669  try:
670  rsp = subprocess.check_output(["service", service, "stop"],
671  stderr=subprocess.STDOUT)
672  except subprocess.CalledProcessError, inst:
673  self.m_lock.release()
674  return False
675  self.m_lock.release()
676  if os_ver_ge(self.m_osdist[1], 15.04):
677  return True # no output
678  else:
679  if reFail.search(rsp):
680  return False
681  else:
682  return True
683 
684  #
685  ## \brief Execute 'service <service> restart' subprocess.
686  ##
687  ## \param service Service (key).
688  ##
689  ## \return Returns True on success, False on failure.
690  #
691  def execRestart(self, service):
692  rsp = ''
693  hasLock = self.m_lock.acquire()
694  try:
695  rsp = subprocess.check_output(["service", service, "restart"],
696  stderr=subprocess.STDOUT)
697  except subprocess.CalledProcessError, inst:
698  self.m_lock.release()
699  return False
700  self.m_lock.release()
701  if os_ver_ge(self.m_osdist[1], 15.04):
702  return True # no output
703  else:
704  if reDoneDone.search(rsp):
705  return True
706  elif reFailDone.search(rsp):
707  return True
708  else:
709  return False
710 
711  #
712  ## \brief Execute 'service <service> status' subprocess.
713  ##
714  ## On Ubuntu 16.04+ the output to 'service <service> status' changed. It does
715  ## call the init.d script, but rather uses the systemd calls. These output
716  ## muliple lines of info. So this script now supports the new interace but
717  ## is also backwards capatible with Ubuntu 14.04- releases (hopefully).
718  ##
719  ## \param service Service (key).
720  ##
721  ## \return Service status. One of: 'running' 'stopped' 'unknown'
722  #
723  def execStatus(self, service):
724  rsp = ''
725  hasLock = self.m_lock.acquire()
726  try:
727  rsp = subprocess.check_output(["service", service, "status"],
728  stderr=subprocess.STDOUT)
729  except subprocess.CalledProcessError, inst:
730  rsp = inst.output
731  self.m_lock.release()
732 
733  if os_ver_ge(self.m_osdist[1], 15.04):
734  rsp = rsp.split("\n")
735  for line in rsp:
736  if reActive.search(line):
737  return ('running', line)
738  elif reInactive.search(line):
739  return ('stopped', line)
740  return ('unknown', "")
741  else:
742  if reRunning.search(rsp):
743  return ('running', rsp)
744  elif reNotRunning.search(rsp):
745  return ('stopped', rsp)
746  else:
747  return ('unknown', rsp)
748 
749 
750 # ------------------------------------------------------------------------------
751 # Exception Class usage
752 # ------------------------------------------------------------------------------
753 
754 ##
755 ## \brief Unit test command-line exception class.
756 ##
757 ## Raise usage excpetion.
758 ##
759 class usage(Exception):
760 
761  ##
762  ## \brief Constructor.
763  ##
764  ## \param msg Error message string.
765  ##
766  def __init__(self, msg):
767  ## error message attribute
768  self.msg = msg
769 
770 
771 # ------------------------------------------------------------------------------
772 # Class application
773 # ------------------------------------------------------------------------------
774 
775 ##
776 ## \brief Laelaps control panel.
777 ##
778 class application():
779 
780  #
781  ## \brief Constructor.
782  #
783  def __init__(self):
784  self._Argv0 = __file__
785  self.m_win = None
786 
787  #
788  ## \brief Print usage error.
789  ##
790  ## \param emsg Error message string.
791  #
792  def printUsageErr(self, emsg):
793  if emsg:
794  print "%s: %s" % (self._Argv0, emsg)
795  else:
796  print "%s: error" % (self._Argv0)
797  print "Try '%s --help' for more information." % (self._Argv0)
798 
799  ## \brief Print Command-Line Usage Message.
800  def printUsage(self):
801  print \
802 """
803 usage: %s [OPTIONS]
804  %s --help
805 
806 Options and arguments:
807 
808 -h, --help : Display this help and exit.
809 """ % (self._Argv0, self._Argv0)
810 
811  #
812  ## \brief Get command-line options
813  ##
814  ## \param argv Argument list. If not None, then overrides
815  ## command-line arguments.
816  ## \param [out] kwargs Keyword argument list.
817  ##
818  ## \return Parsed keyword arguments.
819  #
820  def getOptions(self, argv=None, **kwargs):
821  if argv is None:
822  argv = sys.argv
823 
824  self._Argv0 = kwargs.get('argv0', __file__)
825 
826  # defaults
827  kwargs['debug'] = False # Set to False when finished debugging.
828 
829  # parse command-line options
830  try:
831  opts, args = getopt.getopt(argv[1:], "?h",
832  ['help', ''])
833  except getopt.error, msg:
834  raise usage(msg)
835  for opt, optarg in opts:
836  if opt in ('-h', '--help', '-?'):
837  self.printUsage()
838  sys.exit(0)
839 
840  #if len(args) < 1:
841  # self.printUsageErr("No input xml file specified")
842  # sys.exit(2)
843  #else:
844  # kwargs['filename'] = args[0]
845 
846  return kwargs
847 
848  #
849  ## \brief Run application.
850  ##
851  ## \param argv Optional argument list to override command-line arguments.
852  ## \param kwargs Optional keyword argument list.
853  ##
854  ## \return Exit code.
855  #
856  def run(self, argv=None, **kwargs):
857 
858  # parse command-line options and arguments
859  try:
860  kwargs = self.getOptions(argv, **kwargs)
861  except usage, e:
862  print e.msg
863  return 2
864 
865  # create root
866  root = Tk()
867 
868  # create application window
869  self.m_win = window(master=root, **kwargs)
870 
871  root.protocol('WM_DELETE_WINDOW', root.destroy)
872 
873  # loop
874  self.m_win.mainloop()
875 
876  return 0
877 
878 
879 # ------------------------------------------------------------------------------
880 # main
881 # ------------------------------------------------------------------------------
882 if __name__ == '__main__':
883  app = application();
884  sys.exit( app.run() );
def showSbError(self, text)
Show error text on status bar.
Unit test command-line exception class.
def showSbResult(self, text, success)
Show operation result on status bar.
def printUsage(self)
Print Command-Line Usage Message.
def cbStopServices(self)
Stop selected services callback.
def createRightButtons(self)
Create gui right hand side buttons.
def execStatus(self, service)
Execute &#39;service <service> status&#39; subprocess.
def execStop(self, service)
Execute &#39;service <service> stop&#39; subprocess.
Laelaps control panel.
def createHeading(self)
Create top gui heading.
def initData(self, kw)
Initialize class state data.
def execStart(self, service)
Execute &#39;service <service> start&#39; subprocess.
Class to handle image loading.
Definition: Utils.py:76
def printUsageErr(self, emsg)
Print usage error.
def refresh(self)
Refresh status of all services.
def cbSave(self)
Save new settings callback.
def setStatus(self, service, status)
Set service status field.
def createCenterPanel(self)
Create gui center panel.
def cbStartServices(self)
Start selected services callback.
msg
error message attribute
def createStatusBar(self)
Create gui multi-line status bar at bottom of gui window.
def createLeftButtons(self)
Create gui left hand side buttons.
def execRestart(self, service)
Execute &#39;service <service> restart&#39; subprocess.
def cbRestartServices(self)
Restart selected services callback.
def cbSelect(self)
Checkbox change state callback.
def getOptions(self, argv=None, kwargs)
Get command-line options.
def showSbOk(self, text)
Show ok text on status bar.
def run(self, argv=None, kwargs)
Run application.
def clearSelect(self)
Clear all select checkboxes.
def __init__(self)
Constructor.
def createWidgets(self)
Create gui widgets with supporting data and show.
def showSbStatus(self, text, status)
Show service status result on status bar.
def createButton(self, parent, text, imagefile, command, fg='black')
Create button.
Window class supporting application.
def destroy(self)
Destroy window callback.
def __init__(self, msg)
Constructor.
def cbRefreshStatus(self)
Refresh services status callback.
def showSbInfo(self, text)
Show information text on status bar.
def __init__(self, master=None, cnf={}, kw)
Constructor.