r20365: SWAT updates, part 2
authorDerrell Lipman <derrell@samba.org>
Wed, 27 Dec 2006 21:24:27 +0000 (21:24 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:30:17 +0000 (14:30 -0500)
(This used to be commit 5b6b134a0375a527b123617d7d94fa52d7604c02)

swat/apps/swat/source/class/swat/module/statistics/Fsm.js [new file with mode: 0644]
swat/apps/swat/source/class/swat/module/statistics/Gui.js [new file with mode: 0644]
swat/apps/swat/source/class/swat/module/statistics/Statistics.js [new file with mode: 0644]

diff --git a/swat/apps/swat/source/class/swat/module/statistics/Fsm.js b/swat/apps/swat/source/class/swat/module/statistics/Fsm.js
new file mode 100644 (file)
index 0000000..486463d
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright:
+ *   (C) 2006 by Derrell Lipman
+ *       All rights reserved
+ *
+ * License:
+ *   LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat statistics class finite state machine
+ */
+qx.OO.defineClass("swat.module.statistics.Fsm", swat.module.AbstractModuleFsm,
+function()
+{
+  swat.module.AbstractModuleFsm.call(this);
+});
+
+
+qx.Class._startTimer = function(fsm)
+{
+  // Create a timer instance to expire in a few seconds
+  var timer = new qx.client.Timer(5000);
+  timer.addEventListener("interval", fsm.eventListener, fsm);
+  fsm.addObject("timer", timer);
+  timer.start();
+};
+
+
+qx.Class._stopTimer = function(fsm)
+{
+  // ... then stop the timer.  Get the timer object.
+  var timer = fsm.getObject("timer");
+            
+  // If it still exists...
+  if (timer)
+  {
+    // ... then dispose of it.
+    timer.dispose();
+    fsm.removeObject("timer");
+  }
+};
+
+
+qx.Proto.buildFsm = function(module)
+{
+  var fsm = module.fsm;
+  var thisClass = this;
+
+  /*
+   * State: Idle
+   *
+   * Actions upon entry
+   *   - if returning from RPC, display the result
+   *   - start an interval timer to request statistics again in a while
+   *
+   * Transition on:
+   *  "interval" on interval_timer
+   */
+  var state = new qx.util.fsm.State(
+    "State_Idle",
+    {
+      "onentry" :
+        function(fsm, state)
+        {
+          // Did we just return from an RPC request?
+          if (fsm.getPreviousState() == "State_AwaitRpcResult")
+          {
+            // Yup.  Display the result.  We need to get the request object
+            var request = fsm.getObject("swat.module.fsmUtils.request");
+
+            // We don't need the request object to be saved any more
+            fsm.removeObject("swat.module.fsmUtils.request");
+
+            // Display the result
+            var gui = swat.module.statistics.Gui.getInstance();
+            gui.displayData(module, request.result);
+
+            // Dispose of the request (and result)
+            request.result = null;
+            request = null;
+
+            // Restart the timer.
+            swat.module.statistics.Fsm._startTimer(fsm);
+          }
+        },
+
+      "onexit" :
+        function(fsm, state)
+        {
+          // If we're not coming right back into this state...
+          if (fsm.getNextState() != "State_Idle")
+          {
+            // ... then stop the timer.
+            swat.module.statistics.Fsm._stopTimer(fsm);
+          }
+        },
+
+      "events" :
+        {
+          // If the timer expires, send a new statistics request
+          "interval" :
+          {
+            "timer" :
+              "Transition_Idle_to_AwaitRpcResult_via_request_statistics"
+          },
+
+          // When we get an appear event, start our timer
+          "appear" :
+          {
+            "swat.module.canvas" :
+              "Transition_Idle_to_Idle_via_appear"
+          },
+
+          // When we get a disappear event, stop our timer
+          "disappear" :
+          {
+            "swat.module.canvas" :
+              "Transition_Idle_to_Idle_via_disappear"
+          }
+        }
+    });
+
+  // Replace the initial Idle state with this one
+  fsm.replaceState(state, true);
+  
+  /*
+   * Transition: Idle to AwaitRpcResult
+   *
+   * Cause: "interval" on timer
+   *
+   * Action:
+   *  Issue a Get Statistics request
+   */
+  var trans = new qx.util.fsm.Transition(
+    "Transition_Idle_to_AwaitRpcResult_via_request_statistics",
+    {
+      "nextState" :
+        "State_AwaitRpcResult",
+
+      "ontransition" :
+        function(fsm, event)
+        {
+          var rpc = fsm.getObject("swat.module.rpc");
+
+          rpc.setUrl("/services/");
+          rpc.setServiceName("samba.management");
+
+          var request =
+            rpc.callAsyncListeners(true, // coalesce failure events
+                                   "get_statistics",
+                                   true, true);
+          fsm.addObject("swat.module.fsmUtils.request", request);
+        }
+    });
+  state.addTransition(trans);
+
+  /*
+   * Transition: Idle to Idle
+   *
+   * Cause: "appear" on canvas
+   *
+   * Action:
+   *  Start our timer
+   */
+  var trans = new qx.util.fsm.Transition(
+    "Transition_Idle_to_Idle_via_appear",
+    {
+      "nextState" :
+        "State_Idle",
+
+      "ontransition" :
+        function(fsm, event)
+        {
+          swat.module.statistics.Fsm._startTimer(fsm);
+        }
+    });
+  state.addTransition(trans);
+
+  /*
+   * Transition: Idle to Idle
+   *
+   * Cause: "disappear" on canvas
+   *
+   * Action:
+   *  Stop our timer
+   */
+  var trans = new qx.util.fsm.Transition(
+    "Transition_Idle_to_Idle_via_disappear",
+    {
+      "nextState" :
+        "State_Idle",
+
+      "ontransition" :
+        function(fsm, event)
+        {
+          swat.module.statistics.Fsm._stopTimer(fsm);
+        }
+    });
+  state.addTransition(trans);
+
+  // Add the AwaitRpcResult state and all of its transitions
+  this.addAwaitRpcResultState(module);
+
+  // Allocate an RPC object
+  o = new qx.io.remote.Rpc();
+  o.setTimeout(10000);
+  o.addEventListener("completed", fsm.eventListener, fsm);
+  o.addEventListener("failed", fsm.eventListener, fsm);
+  o.addEventListener("timeout", fsm.eventListener, fsm);
+  o.addEventListener("aborted", fsm.eventListener, fsm);
+  fsm.addObject("swat.module.rpc", o);
+};
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/swat/apps/swat/source/class/swat/module/statistics/Gui.js b/swat/apps/swat/source/class/swat/module/statistics/Gui.js
new file mode 100644 (file)
index 0000000..050c488
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * Copyright:
+ *   (C) 2006 by Derrell Lipman
+ *       All rights reserved
+ *
+ * License:
+ *   LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat statistics class graphical user interface
+ */
+qx.OO.defineClass("swat.module.statistics.Gui", qx.core.Object,
+function()
+{
+  qx.core.Object.call(this);
+});
+
+
+/*
+ * The result of our request for statistics is in this form:
+ *
+ *     rpc: Object
+ *       status: INACTIVE
+ *     smb: Object
+ *       tcons: Array
+ *         0: Object
+ *           share_name: tmp
+ *           last_use_time: 1167186771
+ *           client_ip: 127.0.0.1
+ *           tid: 10928
+ *           connect_time: 1167186757
+ *       connections: 1
+ *       sessions: Array
+ *         0: Object
+ *           auth_time: 1167186757
+ *           vuid: 24588
+ *           last_use_time: 1167186771
+ *           client_ip: 127.0.0.1
+ *           connect_time: 1167186757
+ *           account_name: Administrator
+ *           domain_name: WORKGROUP
+ *       status: RUNNING
+ *     ldap: Object
+ *       status: INACTIVE
+ *     wins: Object
+ *       status: DISABLED
+ *     nbt: Object
+ *       status: RUNNING
+ *       statistics: Object
+ *         total_received: 32
+ *         total_sent: 4
+ *         query_count: 0
+ *         release_count: 0
+ *         register_count: 0
+ *     kdc: Object
+ *       status: INACTIVE
+ *     cldap: Object
+ *       status: RUNNING
+ */
+
+/**
+ * Build the raw graphical user interface.
+ */
+qx.Proto.buildGui = function(module)
+{
+  var o;
+  var fsm = module.fsm;
+  var canvas = module.canvas;
+
+  canvas.setOverflow("auto");
+
+  // Create a gui object where we'll put each widget handle that has varying
+  // data to be displayed.
+  module.gui = { };
+
+  var addCaptionedText = function(caption, dest)
+  {
+    // Add a row to the destination grid
+    dest.addRow();
+    var row = dest.getRowCount() - 1;
+    dest.setRowHeight(row, 16);
+
+    // Add the caption
+    o = new qx.ui.basic.Label(caption);
+    dest.add(o, 0, row);
+
+    // Add the text field that will contain varying data
+    o = new qx.ui.basic.Label("");
+    dest.add(o, 1, row);
+
+    // Give 'em the varying data label
+    return o;
+  };
+
+  var addGroup = function(legend, top, height, width, left, right, dest)
+  {
+    // Add a groupbox
+    var group = new qx.ui.groupbox.GroupBox(legend);
+    group.setTop(top);
+    if (left >= 0)
+    {
+      group.setLeft(left);
+    }
+    if (right >= 0)
+    {
+      group.setRight(right);
+    }
+    if (height >= 0)
+    {
+      group.setHeight(height);
+    }
+    if (typeof(width) == "string" || width >= 0)
+    {
+      group.setWidth(width);
+    }
+    group.setBackgroundColor("white");
+    group.getLegendObject().setBackgroundColor("white");
+
+    var grid = new qx.ui.layout.GridLayout();
+    grid.setLocation(0, 0);
+    grid.setDimension("100%", "100%");
+    grid.setPadding(0, 0);
+    grid.setRowCount(0);
+    grid.setColumnCount(2);
+    grid.setColumnWidth(0, 100);
+    grid.setColumnWidth(1, 200);
+
+    group.add(grid);
+    dest.add(group);
+    
+    return grid;
+  };
+
+  // Add the RPC Service group box and its status
+  var group = addGroup("RPC Service", 40, 60, "46%", 20, -1, canvas);
+  module.gui.rpc =
+  {
+    status : addCaptionedText("Status:", group)
+  };
+
+  // Add the KDC Service group box and its status
+  var group = addGroup("KDC Service", 40, 60, "46%", -1, 20, canvas);
+  module.gui.kdc =
+  {
+    status : addCaptionedText("Status:", group)
+  };
+
+  // Add the LDAP Service group box and its status
+  var group = addGroup("LDAP Service", 120, 60, "46%", 20, -1, canvas);
+  module.gui.ldap =
+  {
+    status : addCaptionedText("Status:", group)
+  };
+
+  // Add the CLDAP Service group box and its status
+  var group = addGroup("CLDAP Service", 120, 60, "46%", -1, 20, canvas);
+  module.gui.cldap =
+  {
+    status : addCaptionedText("Status:", group)
+  };
+
+  // Add the WINS Service group box and its status
+  var group = addGroup("WINS Service", 200, 60, "46%", 20, -1, canvas);
+  module.gui.wins =
+  {
+    status : addCaptionedText("Status:", group)
+  };
+
+  // Add the NBT Service group box and its status, and the statistics
+  var group = addGroup("NBT Service", 200, 140, "46%", -1, 20, canvas);
+  module.gui.nbt = 
+  {
+    status         : addCaptionedText("Status:", group),
+    total_received : addCaptionedText("Total received:", group),
+    total_sent     : addCaptionedText("Total sent:", group),
+    query_count    : addCaptionedText("Query count:", group),
+    release_count  : addCaptionedText("Release count:", group),
+    register_count : addCaptionedText("Register count:", group)
+  };
+
+  // Add the SMB Service group box (sans grid) and its status
+  var group = new qx.ui.groupbox.GroupBox("SMB Service");
+  group.set({
+                top:    360,
+                height: 400,
+                left:   20,
+                right:  20
+            });
+  group.setBackgroundColor("white");
+  group.getLegendObject().setBackgroundColor("white");
+
+  // Create the Status block
+  o = new qx.ui.basic.Label("Status:");
+  o.set({
+            top    : 0,
+            left   : 0,
+            width  : 100
+        });
+  group.add(o);
+
+  o = new qx.ui.basic.Label("");
+  o.set({
+            top    : 0,
+            left   : 100,
+            width  : 200
+        });
+  group.add(o);
+
+  // Add the status and create the table models for sessions and connections
+  module.gui.smb =
+  {
+    status   : o,
+    sessions : new qx.ui.table.SimpleTableModel(),
+    tcons    : new qx.ui.table.SimpleTableModel()
+  };
+
+  // Begin the Sessions section
+  o = new qx.ui.basic.Label("Sessions");
+  o.set({
+            top    : 20,
+            left   : 20
+        });
+  group.add(o);
+
+  // Set column labels
+  var tableModel = module.gui.smb.sessions;
+  tableModel.setColumns([
+                          "User",
+                          "Client",
+                          "Connected at",
+                          "Authenticated at",
+                          "Last used at",
+                          "VUID"
+                        ]);
+  tableModel.setData([ ]);
+
+  // Create the table for sessions
+  var table = new qx.ui.table.Table(tableModel);
+  table.set({
+                top    : 40,
+                left   : 20,
+                right  : 20,
+                height : 160
+            });
+  table.setMetaColumnCounts([1, -1]);
+  table.setStatusBarVisible(false);
+  table.setColumnVisibilityButtonVisible(false);
+  table.setColumnWidth(0, 260);
+  table.setColumnWidth(1, 80);
+  table.setColumnWidth(2, 120);
+  table.setColumnWidth(3, 120);
+  table.setColumnWidth(4, 120);
+  table.setColumnWidth(5, 60);
+
+  // Add the table to the groupbox
+  group.add(table);
+  canvas.add(group);
+
+  // Begin the Connections section
+  o = new qx.ui.basic.Label("Connections");
+  o.set({
+            top    : 220,
+            left   : 20
+        });
+  group.add(o);
+
+  // Create the table model for tcons
+  var tableModel = module.gui.smb.tcons;
+  tableModel.setColumns([
+                          "Share",
+                          "Client",
+                          "Connected at",
+                          "Last used at",
+                          "TID"
+                        ]);
+  tableModel.setData([ ]);
+
+  // Create the table for sessions
+  var table = new qx.ui.table.Table(tableModel);
+  table.set({
+                top    : 240,
+                left   : 20,
+                right  : 20,
+                bottom : 20
+            });
+  table.setMetaColumnCounts([1, -1]);
+  table.setStatusBarVisible(false);
+  table.setColumnVisibilityButtonVisible(false);
+  table.setColumnWidth(0, 260);
+  table.setColumnWidth(1, 80);
+  table.setColumnWidth(2, 120);
+  table.setColumnWidth(3, 120);
+  table.setColumnWidth(4, 60);
+
+  // Add the table to the groupbox
+  group.add(table);
+  canvas.add(group);
+
+};
+
+
+/**
+ * Populate the graphical user interface with the specified data
+ *
+ * @param module {Object}
+ *   The descriptor containing our module-specific information
+ *
+ * @result {Object}
+ *   The result returned by SAMBA to our request for statistics.  We display
+ *   the data provided by this result.
+ */
+qx.Proto.displayData = function(module, result)
+{
+  var gui = module.gui;
+
+  if (result.type == "failed")
+  {
+    // Have we already put up the FAILED message?
+    if (gui.failed)
+    {
+      // Yup.
+      gui.failed.setDisplay(true);
+      return;
+    }
+
+    // Create a semi-transparent layover o which to display a failure message
+    gui.failed = new qx.ui.layout.CanvasLayout();
+    gui.failed.set({
+                   top: 0,
+                   bottom: 0,
+                   left: 0,
+                   right: 0
+               });
+    gui.failed.setBackgroundColor("white");
+    gui.failed.setDisplay(true); // initially displayed
+    gui.failed.setOpacity(0.7);  // semi-transparent
+
+    // Add the failure message
+    var style =
+      "color: red;" +
+      "font-size: large;" +
+      "font-weight: bold;";
+    var o = new qx.ui.basic.Label("<span style='" + style + "'>" +
+                                  "Communication with SAMBA failed!",
+                                  "</span>");
+    o.set({
+              top    : 0,
+              left   : 20
+          });
+    gui.failed.add(o);
+
+    // Add the failed layover to the canvas
+    module.canvas.add(gui.failed);
+
+    return;
+  }
+
+  // Successful RPC request.
+  // If the failure message was displayed, we no longer need it.
+  if (gui.failed)
+  {
+    gui.failed.setDisplay(false);
+  }
+
+  // Create a function for formatting dates
+  var dateFormat = function(unixepoch)
+  {
+    var d = new Date(unixepoch * 1000);
+    return (d.getFullYear() + "-" +
+            ("0" + (d.getMonth() + 1)).substr(-2) + "-" +
+            ("0" + d.getDate()).substr(-2) + " " +
+            ("0" + d.getHours()).substr(-2) + ":" +
+            ("0" + d.getMinutes()).substr(-2));
+  }
+
+  // Set the status values
+  gui.rpc.status.setHtml(result.data.rpc.status);
+  gui.kdc.status.setHtml(result.data.kdc.status);
+  gui.ldap.status.setHtml(result.data.ldap.status);
+  gui.cldap.status.setHtml(result.data.cldap.status);
+  gui.wins.status.setHtml(result.data.wins.status);
+  gui.nbt.status.setHtml(result.data.nbt.status);
+  gui.smb.status.setHtml(result.data.smb.status);
+
+  // If the NBT service is running...
+  if (result.data.nbt.status == "RUNNING")
+  {
+    // ... then output the statistics
+    gui.nbt.total_received.setHtml(
+      result.data.nbt.statistics.total_received.toString());
+    gui.nbt.total_sent.setHtml(
+      result.data.nbt.statistics.total_sent.toString());
+    gui.nbt.query_count.setHtml(
+      result.data.nbt.statistics.query_count.toString());
+    gui.nbt.release_count.setHtml(
+      result.data.nbt.statistics.release_count.toString());
+    gui.nbt.register_count.setHtml(
+      result.data.nbt.statistics.register_count.toString());
+  }
+  else
+  {
+    // otherwise, clear the statistics fields
+    gui.nbt.total_received.setHtml("");
+    gui.nbt.total_sent.setHtml("");
+    gui.nbt.query_count.setHtml("");
+    gui.nbt.release_count.setHtml("");
+    gui.nbt.register_count.setHtml("");
+  }
+
+  // Initialize data for sessions list
+  var rowData = [];
+
+  // If there are any sessions...
+  if (result.data.smb.sessions instanceof Array)
+  {
+    // ... then for each session...
+    for (var i = 0; i < result.data.smb.sessions.length; i++)
+    {
+      // ... add its info to the table data
+      var sess = result.data.smb.sessions[i];
+      rowData.push([
+                     sess.account_name + "/" + sess.domain_name,
+                     sess.client_ip,
+                     dateFormat(sess.connect_time),
+                     dateFormat(sess.auth_time),
+                     dateFormat(sess.last_use_time),
+                     sess.vuid.toString()
+                   ]);
+    }
+  }
+
+  // Whether there were sessions or not, reset the session table data
+  gui.smb.sessions.setData(rowData);
+
+  // Initialize data for tcons list
+  var rowData = [];
+
+  // If there are any tcons...
+  if (result.data.smb.tcons instanceof Array)
+  {
+    // ... then for each tcon...
+    for (var i = 0; i < result.data.smb.tcons.length; i++)
+    {
+      // ... add its info to the table data
+      var conn = result.data.smb.tcons[i];
+      rowData.push([
+                     conn.share_name,
+                     conn.client_ip,
+                     dateFormat(conn.connect_time),
+                     dateFormat(conn.last_use_time),
+                     conn.tid.toString()
+                   ]);
+    }
+  }
+
+  // Whether there were tcons or not, reset the tcon table data
+  gui.smb.tcons.setData(rowData);
+};
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;
diff --git a/swat/apps/swat/source/class/swat/module/statistics/Statistics.js b/swat/apps/swat/source/class/swat/module/statistics/Statistics.js
new file mode 100644 (file)
index 0000000..00adf2f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright:
+ *   (C) 2006 by Derrell Lipman
+ *       All rights reserved
+ *
+ * License:
+ *   LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
+ */
+
+/**
+ * Swat statistics class
+ */
+qx.OO.defineClass("swat.module.statistics.Statistics",
+                  swat.module.AbstractModule,
+function()
+{
+  swat.module.AbstractModule.call(this);
+});
+
+
+/**
+ * Create the module's finite state machine and graphical user interface.
+ *
+ * This function is called the first time a module is actually selected to
+ * appear.  Creation of the module's actual FSM and GUI have been deferred
+ * until they were actually needed (now) so we need to create them.
+ *
+ * @param module {Object} @see AbstractModule
+ */
+qx.Proto.initialAppear = function(module)
+{
+  // Replace the existing (temporary) finite state machine with the real one
+  swat.module.statistics.Fsm.getInstance().buildFsm(module);
+
+  // Create the real gui
+  swat.module.statistics.Gui.getInstance().buildGui(module);
+};
+
+
+/**
+ * Singleton Instance Getter
+ */
+qx.Class.getInstance = qx.util.Return.returnInstance;