[gnome-disk-utility/udisks2-port] Preliminary iSCSI support



commit cae1204e431b77c48da684839db777839fea8328
Author: David Zeuthen <davidz redhat com>
Date:   Fri Mar 25 18:18:58 2011 -0400

    Preliminary iSCSI support
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 data/ui/palimpsest.ui               |  275 ++++++++++++++--
 src/palimpsest/Makefile.am          |    1 +
 src/palimpsest/gdudevicetreemodel.c |  282 +++++++++++++++-
 src/palimpsest/gduiscsipathmodel.c  |  332 ++++++++++++++++++
 src/palimpsest/gduiscsipathmodel.h  |   55 +++
 src/palimpsest/gdutypes.h           |    3 +
 src/palimpsest/gduwindow.c          |  645 +++++++++++++++++++++++++++++------
 7 files changed, 1462 insertions(+), 131 deletions(-)
---
diff --git a/data/ui/palimpsest.ui b/data/ui/palimpsest.ui
index 1a1d5fe..e139cb3 100644
--- a/data/ui/palimpsest.ui
+++ b/data/ui/palimpsest.ui
@@ -863,12 +863,6 @@
                               </packing>
                             </child>
                             <child>
-                              <placeholder/>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                            <child>
                               <object class="GtkLabel" id="devtab-compat-media-label">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
@@ -905,6 +899,12 @@
                                 <property name="y_padding">4</property>
                               </packing>
                             </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
                           </object>
                           <packing>
                             <property name="expand">True</property>
@@ -947,13 +947,241 @@
               </packing>
             </child>
             <child>
-              <placeholder/>
+              <object class="GtkVBox" id="iscsitab_vbox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkTable" id="iscsitab-table">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="n_rows">3</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="iscsitab-alias-label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">Alias</property>
+                        <attributes>
+                          <attribute name="foreground" value="#555555555555"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="iscsitab-name-label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">Name</property>
+                        <attributes>
+                          <attribute name="foreground" value="#555555555555"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="iscsitab-alias-value-label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                        <property name="xalign">0</property>
+                        <property name="selectable">True</property>
+                        <property name="ellipsize">end</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="iscsitab-name-value-label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                        <property name="xalign">0</property>
+                        <property name="selectable">True</property>
+                        <property name="ellipsize">end</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="iscsitab-connection-label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">_Connection</property>
+                        <property name="use_underline">True</property>
+                        <attributes>
+                          <attribute name="foreground" value="#555555555555"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="iscsitab-connection-hbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="iscsitab-main-label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">&lt;b&gt;C_onnections&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">iscsi-connections-treeview</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkScrolledWindow" id="iscsitab-scrolledwindow">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="hscrollbar_policy">never</property>
+                            <property name="shadow_type">in</property>
+                            <child>
+                              <object class="GtkTreeView" id="iscsi-connections-treeview">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="show_expanders">False</property>
+                                <property name="level_indentation">12</property>
+                                <child internal-child="selection">
+                                  <object class="GtkTreeSelection" id="treeview-selection"/>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkToolbar" id="iscsitab-toolbar">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="icon_size">1</property>
+                            <child>
+                              <object class="GtkToolButton" id="iscsitab-add-toolbutton">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="icon_name">list-add-symbolic</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="homogeneous">True</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkToolButton" id="iscsitab-remove-toolbutton">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="icon_name">list-remove-symbolic</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="homogeneous">True</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
             </child>
             <child type="tab">
-              <object class="GtkLabel" id="newtab_label">
+              <object class="GtkLabel" id="iscsitab_label">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="label" translatable="yes">new</property>
+                <property name="label" translatable="yes">iscsi</property>
               </object>
               <packing>
                 <property name="position">2</property>
@@ -961,29 +1189,28 @@
               </packing>
             </child>
             <child>
-              <placeholder/>
+              <object class="GtkVBox" id="newtab_vbox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="position">3</property>
+              </packing>
             </child>
             <child type="tab">
-              <placeholder/>
-            </child>
-            <child>
-              <object class="GtkVBox" id="newtab_vbox">
+              <object class="GtkLabel" id="newtab_label">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <child>
-                  <placeholder/>
-                </child>
-                <child>
-                  <placeholder/>
-                </child>
-                <child>
-                  <placeholder/>
-                </child>
+                <property name="label" translatable="yes">new</property>
               </object>
               <packing>
-                <property name="position">2</property>
+                <property name="position">3</property>
+                <property name="tab_fill">False</property>
               </packing>
             </child>
+            <child>
+              <placeholder/>
+            </child>
             <child type="tab">
               <placeholder/>
             </child>
diff --git a/src/palimpsest/Makefile.am b/src/palimpsest/Makefile.am
index 9019a68..6cca8ca 100644
--- a/src/palimpsest/Makefile.am
+++ b/src/palimpsest/Makefile.am
@@ -27,6 +27,7 @@ palimpsest_SOURCES = 					\
 	gdu.h						\
 	gduapplication.h	gduapplication.c	\
 	gdudevicetreemodel.h	gdudevicetreemodel.c	\
+	gduiscsipathmodel.h	gduiscsipathmodel.c	\
 	gdutypes.h					\
 	gduutils.h		gduutils.c		\
 	gduvolumegrid.h		gduvolumegrid.c		\
diff --git a/src/palimpsest/gdudevicetreemodel.c b/src/palimpsest/gdudevicetreemodel.c
index 8d18119..a7eb9a6 100644
--- a/src/palimpsest/gdudevicetreemodel.c
+++ b/src/palimpsest/gdudevicetreemodel.c
@@ -39,6 +39,10 @@ struct _GduDeviceTreeModel
   GList *current_blocks;
   GtkTreeIter block_iter;
   gboolean block_iter_valid;
+
+  GList *current_iscsi_targets_and_luns;
+  GtkTreeIter iscsi_targets_iter;
+  gboolean iscsi_targets_iter_valid;
 };
 
 typedef struct
@@ -156,9 +160,12 @@ gdu_device_tree_model_set_property (GObject      *object,
     }
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
 typedef struct
 {
   GDBusObject *object;
+  const gchar *object_path;
   GtkTreeIter iter;
   gboolean found;
 } FindIterData;
@@ -188,6 +195,14 @@ find_iter_for_object_cb (GtkTreeModel  *model,
       goto out;
     }
 
+  if (g_strcmp0 (g_dbus_object_get_object_path (iter_object), data->object_path) == 0)
+    {
+      data->iter = *iter;
+      data->found = TRUE;
+      goto out;
+    }
+
+
  out:
   if (iter_object != NULL)
     g_object_unref (iter_object);
@@ -216,6 +231,28 @@ find_iter_for_object (GduDeviceTreeModel *model,
   return data.found;
 }
 
+static gboolean
+find_iter_for_object_path (GduDeviceTreeModel *model,
+                           const gchar        *object_path,
+                           GtkTreeIter        *out_iter)
+{
+  FindIterData data;
+
+  memset (&data, 0, sizeof (data));
+  data.object_path = object_path;
+  data.found = FALSE;
+  gtk_tree_model_foreach (GTK_TREE_MODEL (model),
+                          find_iter_for_object_cb,
+                          &data);
+  if (data.found)
+    {
+      if (out_iter != NULL)
+        *out_iter = data.iter;
+    }
+
+  return data.found;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
@@ -488,6 +525,27 @@ remove_lun (GduDeviceTreeModel *model,
   ;
 }
 
+static gboolean
+should_include_lun (GDBusObject *object,
+                    gboolean     allow_iscsi)
+{
+  UDisksLun *lun;
+  gboolean ret;
+
+  ret = FALSE;
+
+  lun = UDISKS_PEEK_LUN (object);
+
+  /* unless specificlly allowed, don't show LUNs paired with an iSCSI target */
+  if (!allow_iscsi && g_strcmp0 (udisks_lun_get_iscsi_target (lun), "/") != 0)
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
 static void
 update_luns (GduDeviceTreeModel *model)
 {
@@ -511,7 +569,8 @@ update_luns (GduDeviceTreeModel *model)
       if (lun == NULL)
         continue;
 
-      luns = g_list_prepend (luns, g_object_ref (object));
+      if (should_include_lun (object, FALSE))
+        luns = g_list_prepend (luns, g_object_ref (object));
     }
 
   luns = g_list_sort (luns, (GCompareFunc) _g_dbus_object_compare);
@@ -789,12 +848,233 @@ update_blocks (GduDeviceTreeModel *model)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static GtkTreeIter *
+get_iscsi_header_iter (GduDeviceTreeModel *model)
+{
+  gchar *s;
+
+  if (model->iscsi_targets_iter_valid)
+    goto out;
+
+  s = g_strdup_printf ("<small><span foreground=\"#555555\">%s</span></small>",
+                       _("iSCSI Targets"));
+  gtk_tree_store_insert_with_values (GTK_TREE_STORE (model),
+                                     &model->iscsi_targets_iter,
+                                     NULL, /* GtkTreeIter *parent */
+                                     0,
+                                     GDU_DEVICE_TREE_MODEL_COLUMN_IS_HEADING, TRUE,
+                                     GDU_DEVICE_TREE_MODEL_COLUMN_HEADING_TEXT, s,
+                                     GDU_DEVICE_TREE_MODEL_COLUMN_SORT_KEY, "02_iscsi",
+                                     -1);
+  g_free (s);
+
+  model->iscsi_targets_iter_valid = TRUE;
+
+ out:
+  return &model->iscsi_targets_iter;
+}
+
+static void
+nuke_iscsi_targets_header (GduDeviceTreeModel *model)
+{
+  if (model->iscsi_targets_iter_valid)
+    {
+      gtk_tree_store_remove (GTK_TREE_STORE (model), &model->iscsi_targets_iter);
+      model->iscsi_targets_iter_valid = FALSE;
+    }
+}
+
+static void
+add_iscsi_target (GduDeviceTreeModel  *model,
+                  GDBusObject         *object,
+                  GtkTreeIter         *parent)
+{
+  UDisksIScsiTarget *target;
+  GIcon *icon;
+  gchar *s;
+  gchar *sort_key;
+  GtkTreeIter iter;
+
+  target = UDISKS_PEEK_ISCSI_TARGET (object);
+
+#if 0
+  GIcon *base_icon;
+  GIcon *emblem_icon;
+  GEmblem *emblem;
+  emblem_icon = g_themed_icon_new_with_default_fallbacks ("emblem-web");
+  emblem = g_emblem_new (emblem_icon);
+  base_icon = g_themed_icon_new_with_default_fallbacks ("drive-harddisk");
+  icon = g_emblemed_icon_new (base_icon, emblem);
+  g_object_unref (emblem);
+  g_object_unref (base_icon);
+  g_object_unref (emblem_icon);
+#endif
+  icon = g_themed_icon_new_with_default_fallbacks ("network-server");
+
+  s = g_strdup_printf ("%s\n"
+                       "<small><span foreground=\"#555555\">%s</span></small>",
+                       "Remote iSCSI Target", /* TODO: alias */
+                       udisks_iscsi_target_get_name (target));
+
+  sort_key = g_strdup (g_dbus_object_get_object_path (object)); /* for now */
+  gtk_tree_store_insert_with_values (GTK_TREE_STORE (model),
+                                     &iter,
+                                     parent,
+                                     0,
+                                     GDU_DEVICE_TREE_MODEL_COLUMN_ICON, icon,
+                                     GDU_DEVICE_TREE_MODEL_COLUMN_NAME, s,
+                                     GDU_DEVICE_TREE_MODEL_COLUMN_SORT_KEY, sort_key,
+                                     GDU_DEVICE_TREE_MODEL_COLUMN_OBJECT, object,
+                                     -1);
+  g_object_unref (icon);
+  g_free (sort_key);
+  g_free (s);
+}
+
+static void
+remove_iscsi_target (GduDeviceTreeModel  *model,
+                     GDBusObject         *object)
+{
+  GtkTreeIter iter;
+
+  if (!find_iter_for_object (model,
+                             object,
+                             &iter))
+    {
+      g_warning ("Error finding iter for object at %s",
+                 g_dbus_object_get_object_path (object));
+      goto out;
+    }
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
+
+ out:
+  ;
+}
+
+static gboolean
+should_include_iscsi_target (GDBusObject *object)
+{
+  /* for now, just include all of them */
+  return TRUE;
+}
+
+static void
+update_iscsi_targets (GduDeviceTreeModel *model)
+{
+  GDBusObjectManager *object_manager;
+  GList *objects;
+  GList *iscsi_targets_and_luns;
+  GList *added;
+  GList *removed;
+  GList *l;
+
+  object_manager = udisks_client_get_object_manager (model->client);
+  objects = g_dbus_object_manager_get_objects (object_manager);
+
+  iscsi_targets_and_luns = NULL;
+  for (l = objects; l != NULL; l = l->next)
+    {
+      GDBusObject *object = G_DBUS_OBJECT (l->data);
+      UDisksIScsiTarget *target;
+
+      target = UDISKS_PEEK_ISCSI_TARGET (object);
+      if (target == NULL)
+        continue;
+
+      if (should_include_iscsi_target (object))
+        {
+          GList *ll;
+          const gchar *target_object_path;
+
+          iscsi_targets_and_luns = g_list_prepend (iscsi_targets_and_luns, g_object_ref (object));
+
+          /* also include the LUNs that are associated with this target */
+          target_object_path = g_dbus_object_get_object_path (object);
+          for (ll = objects; ll != NULL; ll = ll->next)
+            {
+              GDBusObject *lun_object = G_DBUS_OBJECT (ll->data);
+              UDisksLun *lun;
+              lun = UDISKS_PEEK_LUN (lun_object);
+              if (lun != NULL)
+                {
+                  if (g_strcmp0 (udisks_lun_get_iscsi_target (lun), target_object_path) == 0)
+                    {
+                      if (should_include_lun (lun_object, TRUE))
+                        iscsi_targets_and_luns = g_list_prepend (iscsi_targets_and_luns, g_object_ref (lun_object));
+                    }
+                }
+            }
+        }
+    }
+
+  iscsi_targets_and_luns = g_list_sort (iscsi_targets_and_luns, (GCompareFunc) _g_dbus_object_compare);
+  model->current_iscsi_targets_and_luns = g_list_sort (model->current_iscsi_targets_and_luns, (GCompareFunc) _g_dbus_object_compare);
+  diff_sorted_lists (model->current_iscsi_targets_and_luns,
+                     iscsi_targets_and_luns,
+                     (GCompareFunc) _g_dbus_object_compare,
+                     &added,
+                     &removed);
+
+  for (l = removed; l != NULL; l = l->next)
+    {
+      GDBusObject *object = G_DBUS_OBJECT (l->data);
+
+      g_assert (g_list_find (model->current_iscsi_targets_and_luns, object) != NULL);
+
+      model->current_iscsi_targets_and_luns = g_list_remove (model->current_iscsi_targets_and_luns, object);
+      remove_iscsi_target (model, object);
+      g_object_unref (object);
+    }
+
+  /* Two passes: first add the iSCSI targets ... */
+  for (l = added; l != NULL; l = l->next)
+    {
+      GDBusObject *object = G_DBUS_OBJECT (l->data);
+      if (UDISKS_PEEK_ISCSI_TARGET (object) != NULL)
+        {
+          model->current_iscsi_targets_and_luns = g_list_prepend (model->current_iscsi_targets_and_luns,
+                                                              g_object_ref (object));
+          add_iscsi_target (model, object, get_iscsi_header_iter (model));
+        }
+    }
+  /* ... and then the LUNs */
+  for (l = added; l != NULL; l = l->next)
+    {
+      GDBusObject *object = G_DBUS_OBJECT (l->data);
+      if (UDISKS_PEEK_LUN (object) != NULL)
+        {
+          GtkTreeIter iter;
+          model->current_iscsi_targets_and_luns = g_list_prepend (model->current_iscsi_targets_and_luns,
+                                                                  g_object_ref (object));
+          g_warn_if_fail (find_iter_for_object_path (model,
+                                                     udisks_lun_get_iscsi_target (UDISKS_PEEK_LUN (object)),
+                                                     &iter));
+          add_lun (model, object, &iter);
+        }
+    }
+
+  if (g_list_length (model->current_iscsi_targets_and_luns) == 0)
+    nuke_iscsi_targets_header (model);
+
+  g_list_free (added);
+  g_list_free (removed);
+  g_list_foreach (iscsi_targets_and_luns, (GFunc) g_object_unref, NULL);
+  g_list_free (iscsi_targets_and_luns);
+
+  g_list_foreach (objects, (GFunc) g_object_unref, NULL);
+  g_list_free (objects);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 update_all (GduDeviceTreeModel *model)
 {
   /* TODO: if this is CPU intensive we could coalesce all updates / schedule timeouts */
   update_luns (model);
   update_blocks (model);
+  update_iscsi_targets (model);
 }
 
 static void
diff --git a/src/palimpsest/gduiscsipathmodel.c b/src/palimpsest/gduiscsipathmodel.c
new file mode 100644
index 0000000..9b554e6
--- /dev/null
+++ b/src/palimpsest/gduiscsipathmodel.c
@@ -0,0 +1,332 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+#include <glib/gi18n.h>
+
+#include "gduiscsipathmodel.h"
+#include "gduutils.h"
+
+struct _GduIScsiPathModel
+{
+  GtkListStore parent_instance;
+
+  UDisksClient *client;
+
+  UDisksIScsiTarget *iscsi_target;
+  GDBusObject *object;
+};
+
+typedef struct
+{
+  GtkListStoreClass parent_class;
+} GduIScsiPathModelClass;
+
+enum
+{
+  PROP_0,
+  PROP_CLIENT,
+  PROP_OBJECT
+};
+
+static void update_all (GduIScsiPathModel *model);
+static void on_notify_for_iscsi_target (GObject    *object,
+                                        GParamSpec *pspec,
+                                        gpointer    user_data);
+
+G_DEFINE_TYPE (GduIScsiPathModel, gdu_iscsi_path_model, GTK_TYPE_LIST_STORE);
+
+static void
+gdu_iscsi_path_model_finalize (GObject *object)
+{
+  GduIScsiPathModel *model = GDU_ISCSI_PATH_MODEL (object);
+
+  g_signal_handlers_disconnect_by_func (model->iscsi_target,
+                                        G_CALLBACK (on_notify_for_iscsi_target),
+                                        model);
+  g_object_unref (model->iscsi_target);
+  g_object_unref (model->client);
+
+  G_OBJECT_CLASS (gdu_iscsi_path_model_parent_class)->finalize (object);
+}
+
+static void
+gdu_iscsi_path_model_init (GduIScsiPathModel *model)
+{
+}
+
+static void
+gdu_iscsi_path_model_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GduIScsiPathModel *model = GDU_ISCSI_PATH_MODEL (object);
+
+  switch (prop_id)
+    {
+    case PROP_CLIENT:
+      g_value_set_object (value, gdu_iscsi_path_model_get_client (model));
+      break;
+
+    case PROP_OBJECT:
+      g_value_set_object (value, gdu_iscsi_path_model_get_object (model));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdu_iscsi_path_model_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GduIScsiPathModel *model = GDU_ISCSI_PATH_MODEL (object);
+
+  switch (prop_id)
+    {
+    case PROP_CLIENT:
+      model->client = g_value_dup_object (value);
+      break;
+
+    case PROP_OBJECT:
+      model->object = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+gdu_iscsi_path_model_constructed (GObject *object)
+{
+  GduIScsiPathModel *model = GDU_ISCSI_PATH_MODEL (object);
+  GType types[GDU_ISCSI_PATH_MODEL_N_COLUMNS];
+  /* GDBusObjectManager *object_manager; */
+
+  model->iscsi_target = UDISKS_GET_ISCSI_TARGET (model->object);
+  g_assert (model->iscsi_target != NULL);
+
+  types[0] = G_TYPE_BOOLEAN;
+  types[1] = G_TYPE_STRING;
+  types[2] = G_TYPE_INT;
+  types[3] = G_TYPE_INT;
+  types[4] = G_TYPE_STRING;
+  types[5] = G_TYPE_STRING;
+  G_STATIC_ASSERT (6 == GDU_ISCSI_PATH_MODEL_N_COLUMNS);
+  gtk_list_store_set_column_types (GTK_LIST_STORE (model),
+                                   GDU_ISCSI_PATH_MODEL_N_COLUMNS,
+                                   types);
+
+  update_all (model);
+  g_signal_connect (model->iscsi_target,
+                    "notify",
+                    G_CALLBACK (on_notify_for_iscsi_target),
+                    model);
+
+  if (G_OBJECT_CLASS (gdu_iscsi_path_model_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (gdu_iscsi_path_model_parent_class)->constructed (object);
+}
+
+static void
+gdu_iscsi_path_model_class_init (GduIScsiPathModelClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize     = gdu_iscsi_path_model_finalize;
+  gobject_class->constructed  = gdu_iscsi_path_model_constructed;
+  gobject_class->get_property = gdu_iscsi_path_model_get_property;
+  gobject_class->set_property = gdu_iscsi_path_model_set_property;
+
+  /**
+   * GduIScsiPathModel:client:
+   *
+   * The #UDisksClient used by the #GduIScsiPathModel instance.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_CLIENT,
+                                   g_param_spec_object ("client",
+                                                        "Client",
+                                                        "The client used by the tree model",
+                                                        UDISKS_TYPE_CLIENT,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GduIScsiPathModel:object:
+   *
+   * The #GDBusObject that is a iSCSI target.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_OBJECT,
+                                   g_param_spec_object ("object",
+                                                        "Object",
+                                                        "The iSCSI target",
+                                                        G_TYPE_DBUS_OBJECT,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * gdu_iscsi_path_model_new:
+ * @client: A #UDisksClient.
+ * @object: A #GDBusObject.
+ *
+ * Creates a new #GduIScsiPathModel for viewing the paths on the iSCSI
+ * target on @object.
+ *
+ * Returns: A #GduIScsiPathModel. Free with g_object_unref().
+ */
+GduIScsiPathModel *
+gdu_iscsi_path_model_new (UDisksClient  *client,
+                          GDBusObject   *object)
+{
+  return GDU_ISCSI_PATH_MODEL (g_object_new (GDU_TYPE_ISCSI_PATH_MODEL,
+                                             "client", client,
+                                             "object", object,
+                                             NULL));
+}
+
+/**
+ * gdu_iscsi_path_model_get_client:
+ * @model: A #GduIScsiPathModel.
+ *
+ * Gets the #UDisksClient used by @model.
+ *
+ * Returns: (transfer none): A #UDisksClient. Do not free, the object
+ * belongs to @model.
+ */
+UDisksClient *
+gdu_iscsi_path_model_get_client (GduIScsiPathModel *model)
+{
+  g_return_val_if_fail (GDU_IS_ISCSI_PATH_MODEL (model), NULL);
+  return model->client;
+}
+
+/**
+ * gdu_iscsi_path_model_get_object:
+ * @model: A #GduIScsiPathModel.
+ *
+ * Gets the #GDBusObject used by @model.
+ *
+ * Returns: (transfer none): A #GDBusObject. Do not free, the object
+ * belongs to @model.
+ */
+GDBusObject *
+gdu_iscsi_path_model_get_object (GduIScsiPathModel *model)
+{
+  g_return_val_if_fail (GDU_IS_ISCSI_PATH_MODEL (model), NULL);
+  return model->object;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_all (GduIScsiPathModel *model)
+{
+  GVariant *portals_and_interfaces;
+  GVariantIter portal_iter;
+  const gchar *portal_name;
+  gint port;
+  gint group;
+  GVariantIter *iface_iter;
+
+  gtk_list_store_clear (GTK_LIST_STORE (model));
+
+  portals_and_interfaces = udisks_iscsi_target_get_portals_and_interfaces (model->iscsi_target);
+  g_variant_iter_init (&portal_iter, portals_and_interfaces);
+
+  while (g_variant_iter_next (&portal_iter,
+                              "(^&ayiia(ays))",
+                              &portal_name,
+                              &port,
+                              &group,
+                              &iface_iter))
+    {
+      const gchar *iface_name;
+      const gchar *state;
+
+      while (g_variant_iter_next (iface_iter,
+                                  "(^&ays)",
+                                  &iface_name,
+                                  &state))
+        {
+          gchar *status;
+          gboolean active;
+          if (g_strcmp0 (state, "LOGGED_IN") == 0)
+            {
+              active = TRUE;
+              status = g_strdup (_("Logged In"));
+            }
+          else if (g_strcmp0 (state, "FAILED") == 0)
+            {
+              active = TRUE;
+              status = g_strdup_printf ("<span foreground=\"#ff0000\"><b>%s</b></span>", _("FAILED"));
+            }
+          else if (g_strcmp0 (state, "") == 0)
+            {
+              active = FALSE;
+              status = g_strdup (_("Not Logged In"));
+            }
+          else
+            {
+              active = TRUE;
+              status = g_strdup (state);
+            }
+
+          gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
+                                             NULL, /* GtkTreeIter *out */
+                                             G_MAXINT, /* position */
+                                             GDU_ISCSI_PATH_MODEL_COLUMN_ACTIVE, active,
+                                             GDU_ISCSI_PATH_MODEL_COLUMN_PORTAL_ADDRESS, portal_name,
+                                             GDU_ISCSI_PATH_MODEL_COLUMN_PORTAL_PORT, port,
+                                             GDU_ISCSI_PATH_MODEL_COLUMN_TPGT, group,
+                                             GDU_ISCSI_PATH_MODEL_COLUMN_INTERFACE, iface_name,
+                                             GDU_ISCSI_PATH_MODEL_COLUMN_STATUS, status,
+                                             -1);
+          g_free (status);
+        }
+      g_variant_iter_free (iface_iter);
+    }
+}
+
+static void
+on_notify_for_iscsi_target (GObject    *object,
+                            GParamSpec *pspec,
+                            gpointer    user_data)
+{
+  GduIScsiPathModel *model = GDU_ISCSI_PATH_MODEL (user_data);
+  update_all (model);
+}
diff --git a/src/palimpsest/gduiscsipathmodel.h b/src/palimpsest/gduiscsipathmodel.h
new file mode 100644
index 0000000..a1eca25
--- /dev/null
+++ b/src/palimpsest/gduiscsipathmodel.h
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __GDU_ISCSI_PATH_MODEL_H__
+#define __GDU_ISCSI_PATH_MODEL_H__
+
+#include <gtk/gtk.h>
+#include "gdutypes.h"
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_ISCSI_PATH_MODEL         (gdu_iscsi_path_model_get_type ())
+#define GDU_ISCSI_PATH_MODEL(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_ISCSI_PATH_MODEL, GduIScsiPathModel))
+#define GDU_IS_ISCSI_PATH_MODEL(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_ISCSI_PATH_MODEL))
+
+enum
+{
+  GDU_ISCSI_PATH_MODEL_COLUMN_ACTIVE,
+  GDU_ISCSI_PATH_MODEL_COLUMN_PORTAL_ADDRESS,
+  GDU_ISCSI_PATH_MODEL_COLUMN_PORTAL_PORT,
+  GDU_ISCSI_PATH_MODEL_COLUMN_TPGT,
+  GDU_ISCSI_PATH_MODEL_COLUMN_INTERFACE,
+  GDU_ISCSI_PATH_MODEL_COLUMN_STATUS,
+  GDU_ISCSI_PATH_MODEL_N_COLUMNS
+};
+
+GType               gdu_iscsi_path_model_get_type   (void) G_GNUC_CONST;
+GduIScsiPathModel  *gdu_iscsi_path_model_new        (UDisksClient      *client,
+                                                     GDBusObject       *object);
+UDisksClient       *gdu_iscsi_path_model_get_client (GduIScsiPathModel *model);
+GDBusObject        *gdu_iscsi_path_model_get_object (GduIScsiPathModel *model);
+
+
+G_END_DECLS
+
+#endif /* __GDU_ISCSI_PATH_MODEL_H__ */
diff --git a/src/palimpsest/gdutypes.h b/src/palimpsest/gdutypes.h
index dd70775..4cdae4e 100644
--- a/src/palimpsest/gdutypes.h
+++ b/src/palimpsest/gdutypes.h
@@ -43,6 +43,9 @@ typedef struct _GduWindow GduWindow;
 struct _GduVolumeGrid;
 typedef struct _GduVolumeGrid GduVolumeGrid;
 
+struct _GduIScsiPathModel;
+typedef struct _GduIScsiPathModel GduIScsiPathModel;
+
 G_END_DECLS
 
 #endif /* __GDU_TYPES_H__ */
diff --git a/src/palimpsest/gduwindow.c b/src/palimpsest/gduwindow.c
index f0f8149..e8668a8 100644
--- a/src/palimpsest/gduwindow.c
+++ b/src/palimpsest/gduwindow.c
@@ -31,12 +31,14 @@
 #include "gdudevicetreemodel.h"
 #include "gduutils.h"
 #include "gduvolumegrid.h"
+#include "gduiscsipathmodel.h"
 
 /* Keep in sync with tabs in palimpsest.ui file */
 typedef enum
 {
   DETAILS_PAGE_NOT_SELECTED,
   DETAILS_PAGE_DEVICE,
+  DETAILS_PAGE_ISCSI_TARGET,
 } DetailsPage;
 
 struct _GduWindow
@@ -54,6 +56,7 @@ struct _GduWindow
 
   GtkWidget *volume_grid;
   GtkWidget *write_cache_switch;
+  GtkWidget *iscsi_connection_switch;
 
   GHashTable *label_connections;
 };
@@ -70,9 +73,25 @@ enum
   PROP_CLIENT
 };
 
+static void gdu_window_show_error (GduWindow   *window,
+                                   const gchar *message,
+                                   GError      *orig_error);
+
+static void setup_device_page (GduWindow *window, GDBusObject *object);
+static void update_device_page (GduWindow *window);
+static void teardown_device_page (GduWindow *window);
+
+static void setup_iscsi_target_page (GduWindow *window, GDBusObject *object);
+static void update_iscsi_target_page (GduWindow *window);
+static void teardown_iscsi_target_page (GduWindow *window);
+
 static void on_volume_grid_changed (GduVolumeGrid  *grid,
                                     gpointer        user_data);
 
+static void iscsi_connection_switch_on_notify_active (GObject     *object,
+                                                      GParamSpec  *pspec,
+                                                      gpointer     user_data);
+
 G_DEFINE_TYPE (GduWindow, gdu_window, GTK_TYPE_WINDOW);
 
 static void
@@ -190,6 +209,10 @@ set_selected_object (GduWindow   *window,
         {
           select_details_page (window, object, DETAILS_PAGE_DEVICE);
         }
+      else if (UDISKS_PEEK_ISCSI_TARGET (object) != NULL)
+        {
+          select_details_page (window, object, DETAILS_PAGE_ISCSI_TARGET);
+        }
       else
         {
           g_assert_not_reached ();
@@ -274,6 +297,13 @@ gdu_window_constructed (GObject *object)
   context = gtk_widget_get_style_context (gdu_window_get_widget (window, "device-tree-scrolledwindow"));
   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
   context = gtk_widget_get_style_context (gdu_window_get_widget (window, "device-tree-add-remove-toolbar"));
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
+  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
+
+  context = gtk_widget_get_style_context (gdu_window_get_widget (window, "iscsitab-scrolledwindow"));
+  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
+  context = gtk_widget_get_style_context (gdu_window_get_widget (window, "iscsitab-toolbar"));
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
 
   window->model = gdu_device_tree_model_new (window->client);
@@ -321,7 +351,7 @@ gdu_window_constructed (GObject *object)
                                        NULL);
   renderer = gtk_cell_renderer_text_new ();
   g_object_set (G_OBJECT (renderer),
-                "ellipsize", PANGO_ELLIPSIZE_END,
+                "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
                 NULL);
   gtk_tree_view_column_pack_start (column, renderer, FALSE);
   gtk_tree_view_column_set_attributes (column,
@@ -371,12 +401,25 @@ gdu_window_constructed (GObject *object)
                     G_CALLBACK (on_volume_grid_changed),
                     window);
 
+  /* devtab's Write Cache switch */
   window->write_cache_switch = gtk_switch_new ();
   gtk_box_pack_start (GTK_BOX (gdu_window_get_widget (window, "devtab-write-cache-hbox")),
                       window->write_cache_switch,
                       FALSE, TRUE, 0);
   gtk_label_set_mnemonic_widget (GTK_LABEL (gdu_window_get_widget (window, "devtab-write-cache-label")),
                                  window->write_cache_switch);
+
+  /* iSCSI tab's Connection switch */
+  window->iscsi_connection_switch = gtk_switch_new ();
+  g_signal_connect (window->iscsi_connection_switch,
+                    "notify::active",
+                    G_CALLBACK (iscsi_connection_switch_on_notify_active),
+                    window);
+  gtk_box_pack_start (GTK_BOX (gdu_window_get_widget (window, "iscsitab-connection-hbox")),
+                      window->iscsi_connection_switch,
+                      FALSE, TRUE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (gdu_window_get_widget (window, "iscsitab-connection-label")),
+                                 window->iscsi_connection_switch);
 }
 
 static void
@@ -507,7 +550,7 @@ gdu_window_get_widget (GduWindow    *window,
 static void
 teardown_details_page (GduWindow   *window,
                        GDBusObject *object,
-                       gint         page)
+                       DetailsPage  page)
 {
   //g_debug ("teardown for %s, page %d",
   //       object != NULL ? g_dbus_object_get_object_path (object) : "<none>",
@@ -516,8 +559,13 @@ teardown_details_page (GduWindow   *window,
     {
     case DETAILS_PAGE_NOT_SELECTED:
       break;
+
     case DETAILS_PAGE_DEVICE:
-      gdu_volume_grid_set_block_device (GDU_VOLUME_GRID (window->volume_grid), NULL);
+      teardown_device_page (window);
+      break;
+
+    case DETAILS_PAGE_ISCSI_TARGET:
+      teardown_iscsi_target_page (window);
       break;
     }
 }
@@ -642,84 +690,9 @@ block_device_compare_on_preferred (GDBusObject *a,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
-setup_device_page (GduWindow   *window,
-                   GDBusObject *object)
-{
-  UDisksLun *lun;
-  UDisksBlockDevice *block;
-  GList *children;
-  GList *l;
-
-  children = gtk_container_get_children (GTK_CONTAINER (gdu_window_get_widget (window, "devtab-table")));
-  for (l = children; l != NULL; l = l->next)
-    {
-      GtkWidget *child = GTK_WIDGET (l->data);
-      gtk_widget_hide (child);
-    }
-  g_list_free (children);
-
-  lun = UDISKS_PEEK_LUN (object);
-  block = UDISKS_PEEK_BLOCK_DEVICE (object);
-
-  if (lun != NULL)
-    {
-      GList *block_devices;
-      gchar *lun_name;
-      gchar *lun_desc;
-      GIcon *lun_icon;
-      GIcon *lun_media_icon;
-
-      /* TODO: for multipath, ensure e.g. mpathk is before sda, sdb */
-      block_devices = get_top_level_block_devices_for_lun (window, g_dbus_object_get_object_path (object));
-      block_devices = g_list_sort (block_devices, (GCompareFunc) block_device_compare_on_preferred);
-
-      udisks_util_get_lun_info (lun,
-                                &lun_name,
-                                &lun_desc,
-                                &lun_icon,
-                                &lun_media_icon);
-      gdu_volume_grid_set_container_icon (GDU_VOLUME_GRID (window->volume_grid),
-                                          lun_icon);
-
-      gdu_volume_grid_set_container_visible (GDU_VOLUME_GRID (window->volume_grid), TRUE);
-      if (block_devices != NULL)
-        gdu_volume_grid_set_block_device (GDU_VOLUME_GRID (window->volume_grid), block_devices->data);
-      else
-        gdu_volume_grid_set_block_device (GDU_VOLUME_GRID (window->volume_grid), NULL);
-
-      g_free (lun_name);
-      g_free (lun_desc);
-      g_object_unref (lun_icon);
-      g_object_unref (lun_media_icon);
-
-      g_list_foreach (block_devices, (GFunc) g_object_unref, NULL);
-      g_list_free (block_devices);
-    }
-  else if (block != NULL)
-    {
-      gdu_volume_grid_set_container_visible (GDU_VOLUME_GRID (window->volume_grid), FALSE);
-      gdu_volume_grid_set_block_device (GDU_VOLUME_GRID (window->volume_grid), object);
-    }
-  else
-    {
-      g_assert_not_reached ();
-    }
-}
-
-static void update_devtab (GduWindow *window);
-
-static void
-update_device_page (GduWindow *window)
-{
-  update_devtab (window);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
 setup_details_page (GduWindow    *window,
                     GDBusObject  *object,
-                    gint          page)
+                    DetailsPage   page)
 {
   //g_debug ("setup for %s, page %d",
   //         object != NULL ? g_dbus_object_get_object_path (object) : "<none>",
@@ -733,12 +706,16 @@ setup_details_page (GduWindow    *window,
     case DETAILS_PAGE_DEVICE:
       setup_device_page (window, object);
       break;
+
+    case DETAILS_PAGE_ISCSI_TARGET:
+      setup_iscsi_target_page (window, object);
+      break;
     }
 }
 
 static void
-update_details_page (GduWindow  *window,
-                     gint        page)
+update_details_page (GduWindow   *window,
+                     DetailsPage  page)
 {
   //g_debug ("update for %s, page %d",
   //         object != NULL ? g_dbus_object_get_object_path (object) : "<none>",
@@ -752,6 +729,10 @@ update_details_page (GduWindow  *window,
     case DETAILS_PAGE_DEVICE:
       update_device_page (window);
       break;
+
+    case DETAILS_PAGE_ISCSI_TARGET:
+      update_iscsi_target_page (window);
+      break;
     }
 }
 
@@ -799,6 +780,14 @@ update_all (GduWindow    *window,
           update_details_page (window, window->current_page);
         }
       break;
+
+    case DETAILS_PAGE_ISCSI_TARGET:
+      if (object == window->current_object)
+        {
+          update_details_page (window, window->current_page);
+        }
+      /* Nothing to update */
+      break;
     }
 }
 
@@ -852,10 +841,77 @@ on_interface_proxy_properties_changed (GDBusObjectManagerClient   *manager,
   update_all (window, G_DBUS_OBJECT (object_proxy));
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
-update_devtab_for_lun (GduWindow    *window,
-                       GDBusObject  *object,
-                       UDisksLun    *lun)
+setup_device_page (GduWindow   *window,
+                   GDBusObject *object)
+{
+  UDisksLun *lun;
+  UDisksBlockDevice *block;
+  GList *children;
+  GList *l;
+
+  children = gtk_container_get_children (GTK_CONTAINER (gdu_window_get_widget (window, "devtab-table")));
+  for (l = children; l != NULL; l = l->next)
+    {
+      GtkWidget *child = GTK_WIDGET (l->data);
+      gtk_widget_hide (child);
+    }
+  g_list_free (children);
+
+  lun = UDISKS_PEEK_LUN (object);
+  block = UDISKS_PEEK_BLOCK_DEVICE (object);
+
+  if (lun != NULL)
+    {
+      GList *block_devices;
+      gchar *lun_name;
+      gchar *lun_desc;
+      GIcon *lun_icon;
+      GIcon *lun_media_icon;
+
+      /* TODO: for multipath, ensure e.g. mpathk is before sda, sdb */
+      block_devices = get_top_level_block_devices_for_lun (window, g_dbus_object_get_object_path (object));
+      block_devices = g_list_sort (block_devices, (GCompareFunc) block_device_compare_on_preferred);
+
+      udisks_util_get_lun_info (lun,
+                                &lun_name,
+                                &lun_desc,
+                                &lun_icon,
+                                &lun_media_icon);
+      gdu_volume_grid_set_container_icon (GDU_VOLUME_GRID (window->volume_grid),
+                                          lun_icon);
+
+      gdu_volume_grid_set_container_visible (GDU_VOLUME_GRID (window->volume_grid), TRUE);
+      if (block_devices != NULL)
+        gdu_volume_grid_set_block_device (GDU_VOLUME_GRID (window->volume_grid), block_devices->data);
+      else
+        gdu_volume_grid_set_block_device (GDU_VOLUME_GRID (window->volume_grid), NULL);
+
+      g_free (lun_name);
+      g_free (lun_desc);
+      g_object_unref (lun_icon);
+      g_object_unref (lun_media_icon);
+
+      g_list_foreach (block_devices, (GFunc) g_object_unref, NULL);
+      g_list_free (block_devices);
+    }
+  else if (block != NULL)
+    {
+      gdu_volume_grid_set_container_visible (GDU_VOLUME_GRID (window->volume_grid), FALSE);
+      gdu_volume_grid_set_block_device (GDU_VOLUME_GRID (window->volume_grid), object);
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
+}
+
+static void
+update_device_page_for_lun (GduWindow    *window,
+                            GDBusObject  *object,
+                            UDisksLun    *lun)
 {
   gchar *s;
   GList *block_devices;
@@ -866,7 +922,7 @@ update_devtab_for_lun (GduWindow    *window,
   const gchar* const *lun_media_compat;
   gchar *media_compat_for_display;
 
-  //g_debug ("In update_devtab_for_lun() - selected=%s",
+  //g_debug ("In update_device_page_for_lun() - selected=%s",
   //         object != NULL ? g_dbus_object_get_object_path (object) : "<nothing>");
 
   /* TODO: for multipath, ensure e.g. mpathk is before sda, sdb */
@@ -936,10 +992,10 @@ update_devtab_for_lun (GduWindow    *window,
 }
 
 static void
-update_devtab_for_block (GduWindow         *window,
-                         GDBusObject       *object,
-                         UDisksBlockDevice *block,
-                         guint64            size)
+update_device_page_for_block (GduWindow         *window,
+                              GDBusObject       *object,
+                              UDisksBlockDevice *block,
+                              guint64            size)
 {
   const gchar *backing_file;
   const gchar *usage;
@@ -948,7 +1004,7 @@ update_devtab_for_block (GduWindow         *window,
   gint partition_type;
   gchar *type_for_display;
 
-  //g_debug ("In update_devtab_for_block() - size=%" G_GUINT64_FORMAT " selected=%s",
+  //g_debug ("In update_device_page_for_block() - size=%" G_GUINT64_FORMAT " selected=%s",
   //         size,
   //         object != NULL ? g_dbus_object_get_object_path (object) : "<nothing>");
 
@@ -1026,21 +1082,21 @@ update_devtab_for_block (GduWindow         *window,
 }
 
 static void
-update_devtab_for_no_media (GduWindow         *window,
-                            GDBusObject       *object,
-                            UDisksBlockDevice *block)
+update_device_page_for_no_media (GduWindow         *window,
+                                 GDBusObject       *object,
+                                 UDisksBlockDevice *block)
 {
-  //g_debug ("In update_devtab_for_no_media() - selected=%s",
+  //g_debug ("In update_device_page_for_no_media() - selected=%s",
   //         object != NULL ? g_dbus_object_get_object_path (object) : "<nothing>");
 }
 
 static void
-update_devtab_for_free_space (GduWindow         *window,
-                              GDBusObject       *object,
-                              UDisksBlockDevice *block,
-                              guint64            size)
+update_device_page_for_free_space (GduWindow         *window,
+                                   GDBusObject       *object,
+                                   UDisksBlockDevice *block,
+                                   guint64            size)
 {
-  //g_debug ("In update_devtab_for_free_space() - size=%" G_GUINT64_FORMAT " selected=%s",
+  //g_debug ("In update_device_page_for_free_space() - size=%" G_GUINT64_FORMAT " selected=%s",
   //         size,
   //         object != NULL ? g_dbus_object_get_object_path (object) : "<nothing>");
 
@@ -1060,7 +1116,7 @@ update_devtab_for_free_space (GduWindow         *window,
 }
 
 static void
-update_devtab (GduWindow *window)
+update_device_page (GduWindow *window)
 {
   GDBusObject *object;
   GList *children;
@@ -1088,9 +1144,9 @@ update_devtab (GduWindow *window)
   if (type == GDU_VOLUME_GRID_ELEMENT_TYPE_CONTAINER)
     {
       if (lun != NULL)
-        update_devtab_for_lun (window, object, lun);
+        update_device_page_for_lun (window, object, lun);
       else if (block != NULL)
-        update_devtab_for_block (window, object, block, size);
+        update_device_page_for_block (window, object, block, size);
     }
   else
     {
@@ -1107,15 +1163,15 @@ update_devtab (GduWindow *window)
               break;
 
             case GDU_VOLUME_GRID_ELEMENT_TYPE_DEVICE:
-              update_devtab_for_block (window, object, block, size);
+              update_device_page_for_block (window, object, block, size);
               break;
 
             case GDU_VOLUME_GRID_ELEMENT_TYPE_NO_MEDIA:
-              update_devtab_for_no_media (window, object, block);
+              update_device_page_for_no_media (window, object, block);
               break;
 
             case GDU_VOLUME_GRID_ELEMENT_TYPE_FREE_SPACE:
-              update_devtab_for_free_space (window, object, block, size);
+              update_device_page_for_free_space (window, object, block, size);
               break;
             }
         }
@@ -1123,11 +1179,388 @@ update_devtab (GduWindow *window)
 }
 
 static void
+teardown_device_page (GduWindow *window)
+{
+  gdu_volume_grid_set_block_device (GDU_VOLUME_GRID (window->volume_grid), NULL);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+iscsi_target_format_portal_address (GtkCellLayout   *cell_layout,
+                                    GtkCellRenderer *renderer,
+                                    GtkTreeModel    *tree_model,
+                                    GtkTreeIter     *iter,
+                                    gpointer         user_data)
+{
+  /* GduWindow *window = GDU_WINDOW (user_data); */
+  gchar *portal_address;
+  gint portal_port;
+  gchar *markup;
+
+  gtk_tree_model_get (tree_model,
+                      iter,
+                      GDU_ISCSI_PATH_MODEL_COLUMN_PORTAL_ADDRESS, &portal_address,
+                      GDU_ISCSI_PATH_MODEL_COLUMN_PORTAL_PORT, &portal_port,
+                      -1);
+
+  /* only show port if it is non-standard */
+  if (portal_port != 3260)
+    markup = g_strdup_printf ("%s:%d", portal_address, portal_port);
+  else
+    markup = g_strdup (portal_address);
+
+  g_object_set (renderer,
+                "markup", markup,
+                NULL);
+
+  g_free (markup);
+  g_free (portal_address);
+}
+
+static void
+iscsi_target_login_cb (UDisksIScsiTarget *target,
+                       GAsyncResult      *res,
+                       gpointer           user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  GError *error;
+  error = NULL;
+  if (!udisks_iscsi_target_call_login_finish (target, res, &error))
+    {
+      gdu_window_show_error (window,
+                             _("Error logging in to iSCSI target"),
+                             error);
+      g_error_free (error);
+    }
+  g_object_unref (window);
+}
+
+static void
+iscsi_target_logout_cb (UDisksIScsiTarget *target,
+                        GAsyncResult      *res,
+                        gpointer           user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  GError *error;
+  error = NULL;
+  if (!udisks_iscsi_target_call_logout_finish (target, res, &error))
+    {
+      gdu_window_show_error (window,
+                             _("Error logging out of iSCSI target"),
+                             error);
+      g_error_free (error);
+    }
+  g_object_unref (window);
+}
+
+static void
+on_iscsi_active_toggled (GtkCellRendererToggle *renderer,
+                         gchar                 *path,
+                         gpointer               user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  gboolean is_active;
+  GtkTreeView *tree_view;
+  GtkTreeModel *tree_model;
+  GtkTreeIter iter;
+  gchar *portal_address;
+  gchar *iface_name;
+  gint portal_port;
+  UDisksIScsiTarget *target;
+
+  portal_address = NULL;
+  iface_name = NULL;
+
+  tree_view = GTK_TREE_VIEW (gdu_window_get_widget (window, "iscsi-connections-treeview"));
+  tree_model = gtk_tree_view_get_model (tree_view);
+
+  target = UDISKS_PEEK_ISCSI_TARGET (window->current_object);
+  if (target == NULL)
+    {
+      g_warning ("Expected selected object to be an iSCSI target");
+      goto out;
+    }
+
+  if (!gtk_tree_model_get_iter_from_string (tree_model,
+                                            &iter,
+                                            path))
+    {
+      g_warning ("Unable to get tree iter");
+      goto out;
+    }
+
+  gtk_tree_model_get (tree_model,
+                      &iter,
+                      GDU_ISCSI_PATH_MODEL_COLUMN_PORTAL_ADDRESS, &portal_address,
+                      GDU_ISCSI_PATH_MODEL_COLUMN_PORTAL_PORT, &portal_port,
+                      GDU_ISCSI_PATH_MODEL_COLUMN_INTERFACE, &iface_name,
+                      -1);
+
+  is_active = gtk_cell_renderer_toggle_get_active (renderer);
+  if (is_active)
+    {
+          const gchar *options[] = {NULL};
+          udisks_iscsi_target_call_logout (target,
+                                           options,
+                                           portal_address,
+                                           portal_port,
+                                           iface_name,
+                                           NULL,  /* GCancellable* */
+                                           (GAsyncReadyCallback) iscsi_target_login_cb,
+                                           g_object_ref (window));
+    }
+  else
+    {
+          const gchar *options[] = {NULL};
+          udisks_iscsi_target_call_login (target,
+                                          options,
+                                          portal_address,
+                                          portal_port,
+                                          iface_name,
+                                          NULL,  /* GCancellable* */
+                                          (GAsyncReadyCallback) iscsi_target_login_cb,
+                                          g_object_ref (window));
+    }
+
+ out:
+  g_free (portal_address);
+  g_free (iface_name);
+}
+
+static void
+init_iscsi_target_page (GduWindow   *window)
+{
+  static volatile gsize init_val = 0;
+  GtkTreeView *tree_view;
+  /* GtkTreeSelection *selection; */
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *renderer;
+
+  if (!g_once_init_enter (&init_val))
+    goto out;
+
+  tree_view = GTK_TREE_VIEW (gdu_window_get_widget (window, "iscsi-connections-treeview"));
+  gtk_tree_view_set_rules_hint (tree_view, TRUE);
+#if 0
+  selection = gtk_tree_view_get_selection (tree_view);
+  gtk_tree_selection_set_select_function (selection, dont_select_headings, NULL, NULL);
+  g_signal_connect (selection,
+                    "changed",
+                    G_CALLBACK (on_tree_selection_changed),
+                    window);
+#endif
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_append_column (tree_view, column);
+  renderer = gtk_cell_renderer_toggle_new ();
+  gtk_tree_view_column_pack_end (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer,
+                                       "active", GDU_ISCSI_PATH_MODEL_COLUMN_ACTIVE, NULL);
+  g_signal_connect (renderer,
+                    "toggled",
+                    G_CALLBACK (on_iscsi_active_toggled),
+                    window);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Portal"));
+  gtk_tree_view_append_column (tree_view, column);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, TRUE);
+  gtk_tree_view_column_set_alignment (column, 0.0);
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                      renderer,
+                                      iscsi_target_format_portal_address,
+                                      window,
+                                      NULL);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Network Interface"));
+  gtk_tree_view_append_column (tree_view, column);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer,
+                                       "markup", GDU_ISCSI_PATH_MODEL_COLUMN_INTERFACE, NULL);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("TPGT"));
+  gtk_tree_view_append_column (tree_view, column);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer,
+                                       "markup", GDU_ISCSI_PATH_MODEL_COLUMN_TPGT, NULL);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Status"));
+  gtk_tree_view_append_column (tree_view, column);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_attributes (column, renderer,
+                                       "markup", GDU_ISCSI_PATH_MODEL_COLUMN_STATUS, NULL);
+
+  g_once_init_leave (&init_val, 1);
+ out:
+  ;
+}
+
+static gboolean
+iscsi_target_has_active_connections (UDisksIScsiTarget *target)
+{
+  GVariant *portals_and_interfaces;
+  GVariantIter portal_iter;
+  GVariantIter *iface_iter;
+  gboolean ret;
+
+  ret = FALSE;
+  portals_and_interfaces = udisks_iscsi_target_get_portals_and_interfaces (target);
+  g_variant_iter_init (&portal_iter, portals_and_interfaces);
+  while (g_variant_iter_next (&portal_iter,
+                              "(^&ayiia(ays))",
+                              NULL, /* &portal_adress */
+                              NULL, /* &port */
+                              NULL, /* &tpgt */
+                              &iface_iter))
+    {
+      const gchar *state;
+      while (g_variant_iter_next (iface_iter,
+                                  "(^&ays)",
+                                  NULL, /* &iface_name */
+                                  &state))
+        {
+          if (g_strcmp0 (state, "LOGGED_IN") == 0)
+            {
+              ret = TRUE;
+              goto out;
+            }
+        }
+    }
+ out:
+  return ret;
+}
+
+
+static void
+update_iscsi_target_page (GduWindow   *window)
+{
+  GList *children;
+  GList *l;
+  UDisksIScsiTarget *target;
+
+  /* first hide everything */
+  children = gtk_container_get_children (GTK_CONTAINER (gdu_window_get_widget (window, "iscsitab-table")));
+  for (l = children; l != NULL; l = l->next)
+    {
+      GtkWidget *child = GTK_WIDGET (l->data);
+      gtk_widget_hide (child);
+    }
+  g_list_free (children);
+
+  target = UDISKS_PEEK_ISCSI_TARGET (window->current_object);
+  /* TODO: get Alias from somewhere */
+  set_markup (window,
+              "iscsitab-alias-label",
+              "iscsitab-alias-value-label",
+              "", SET_MARKUP_FLAGS_HYPHEN_IF_EMPTY);
+  set_markup (window,
+              "iscsitab-name-label",
+              "iscsitab-name-value-label",
+              udisks_iscsi_target_get_name (target), SET_MARKUP_FLAGS_NONE);
+
+  gtk_switch_set_active (GTK_SWITCH (window->iscsi_connection_switch),
+                         iscsi_target_has_active_connections (target));
+  gtk_widget_show (gdu_window_get_widget (window, "iscsitab-connection-label"));
+  gtk_widget_show_all (gdu_window_get_widget (window, "iscsitab-connection-hbox"));
+
+}
+
+static void
+iscsi_connection_switch_on_notify_active (GObject     *object,
+                                          GParamSpec  *pspec,
+                                          gpointer     user_data)
+{
+  GduWindow *window = GDU_WINDOW (user_data);
+  gboolean active;
+  gboolean has_connections;
+  UDisksIScsiTarget *target;
+
+  target = UDISKS_PEEK_ISCSI_TARGET (window->current_object);
+  if (target == NULL)
+    {
+      g_warning ("Expected selected object to be an iSCSI target");
+      goto out;
+    }
+
+  active = !! gtk_switch_get_active (GTK_SWITCH (window->iscsi_connection_switch));
+  has_connections = !! iscsi_target_has_active_connections (target);
+  if (active != has_connections)
+    {
+      if (!has_connections)
+        {
+          const gchar *options[] = {NULL};
+          udisks_iscsi_target_call_login (target,
+                                          options,
+                                          "", /* portal_address */
+                                          0, /* portal_port */
+                                          "", /* interface_name */
+                                          NULL,  /* GCancellable* */
+                                          (GAsyncReadyCallback) iscsi_target_login_cb,
+                                          g_object_ref (window));
+        }
+      else
+        {
+          const gchar *options[] = {NULL};
+          udisks_iscsi_target_call_logout (target,
+                                           options,
+                                           "", /* portal_address */
+                                           0, /* portal_port */
+                                           "", /* interface_name */
+                                           NULL,  /* GCancellable* */
+                                           (GAsyncReadyCallback) iscsi_target_logout_cb,
+                                           g_object_ref (window));
+        }
+    }
+  gtk_switch_set_active (GTK_SWITCH (window->iscsi_connection_switch), has_connections);
+
+ out:
+  ;
+}
+
+static void
+setup_iscsi_target_page (GduWindow   *window,
+                         GDBusObject *object)
+{
+  GtkTreeView *tree_view;
+  GduIScsiPathModel *model;
+  GtkTreeIter first_iter;
+
+  init_iscsi_target_page (window);
+
+  tree_view = GTK_TREE_VIEW (gdu_window_get_widget (window, "iscsi-connections-treeview"));
+  model = gdu_iscsi_path_model_new (window->client, object);
+  gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (model));
+  /* select the first row */
+  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &first_iter))
+    gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tree_view), &first_iter);
+  g_object_unref (model);
+}
+
+static void
+teardown_iscsi_target_page (GduWindow *window)
+{
+  GtkTreeView *tree_view;
+
+  tree_view = GTK_TREE_VIEW (gdu_window_get_widget (window, "iscsi-connections-treeview"));
+  gtk_tree_view_set_model (tree_view, NULL);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 on_volume_grid_changed (GduVolumeGrid  *grid,
                         gpointer        user_data)
 {
   GduWindow *window = GDU_WINDOW (user_data);
-  update_devtab (window);
+  update_device_page (window);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]