[pve-devel] r4895 - in pve-manager/pve2: bin lib/PVE lib/PVE/API2 www www/ext www/manager www/manager/data www/manager/grid

svn-commits at proxmox.com svn-commits at proxmox.com
Wed Jul 14 12:19:25 CEST 2010


Author: dietmar
Date: 2010-07-14 10:19:25 +0000 (Wed, 14 Jul 2010)
New Revision: 4895

Added:
   pve-manager/pve2/www/manager/StorageBrowser.js
   pve-manager/pve2/www/manager/StorageConfig.js
   pve-manager/pve2/www/manager/grid/
   pve-manager/pve2/www/manager/grid/ObjectView.js
Removed:
   pve-manager/pve2/www/manager/BufferView.js
Modified:
   pve-manager/pve2/bin/pvesh
   pve-manager/pve2/lib/PVE/API2.pm
   pve-manager/pve2/lib/PVE/API2/AccessControl.pm
   pve-manager/pve2/lib/PVE/API2/Cluster.pm
   pve-manager/pve2/lib/PVE/API2/Storage.pm
   pve-manager/pve2/lib/PVE/API2/User.pm
   pve-manager/pve2/lib/PVE/API2/VM.pm
   pve-manager/pve2/lib/PVE/APIDaemon.pm
   pve-manager/pve2/lib/PVE/JSONSchema.pm
   pve-manager/pve2/lib/PVE/REST.pm
   pve-manager/pve2/www/Makefile.am
   pve-manager/pve2/www/ext/Makefile.am
   pve-manager/pve2/www/manager/ClusterConfig.js
   pve-manager/pve2/www/manager/Makefile.am
   pve-manager/pve2/www/manager/PVECache.js
   pve-manager/pve2/www/manager/PVEUtils.js
   pve-manager/pve2/www/manager/ResourceTree.js
   pve-manager/pve2/www/manager/data/ObjectReader.js
   pve-manager/pve2/www/manager/data/ObjectStore.js
   pve-manager/pve2/www/manager/index.pl
Log:
add more experimental code


Modified: pve-manager/pve2/bin/pvesh
===================================================================
--- pve-manager/pve2/bin/pvesh	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/bin/pvesh	2010-07-14 10:19:25 UTC (rev 4895)
@@ -222,11 +222,7 @@
     my ($handler, $info) = PVE::API2->find_handler('GET', $stack);
     return undef if !$handler || !$info;
 
-    my $found =  PVE::JSONSchema::method_get_index_link($info);
-
-    return wantarray ? ($handler, $info) : 1 if $found;
-
-    return undef;
+    return wantarray ? ($handler, $info) : 1;
 }
 
 sub list_dir {
@@ -234,9 +230,13 @@
 
     my ($handler, $info) = test_dir($dir);
     if (!$handler || !$info) {
-	die "no such directory\n";
+	die "no such resource\n";
     }
 
+    if (!PVE::JSONSchema::method_get_child_link($info)) {
+	die "resource does not define child links\n";
+    }
+
     my $params = get_options($info, $args);
 
     my $res = call_handler($handler, $info, $dir, $params);
@@ -247,7 +247,7 @@
 
     my $data = $res->{data};
 
-    my $lnk = PVE::JSONSchema::method_get_index_link($info);
+    my $lnk = PVE::JSONSchema::method_get_child_link($info);
     if ($lnk && $data) {
 	my $href = $lnk->{href};
 	if ($href =~ m/^\{(\S+)\}$/) {
@@ -258,12 +258,11 @@
 
 		if (defined(my $value = $elem->{$prop})) {
 		    if ($value ne '') {
-			my $tt = $prop eq 'subdir' ? 'D' : '-';
 			if (scalar(keys %$elem) > 1) {
 			    my $tv = to_json($elem, {allow_nonref => 1, canonical => 1});
-			    print "$tt $value $tv\n";
+			    print "$value $tv\n";
 			} else {
-			    print "$tt $value\n";
+			    print "$value\n";
 			}
 		    }
 		}
@@ -293,7 +292,7 @@
 	    return;
 	} else {
 	    my $new_dir = abs_path($cdir, $path);
-	    die "no such directory\n" if !test_dir($new_dir);
+	    die "no such resource\n" if !test_dir($new_dir);
 	    $cdir = $new_dir;
 	}
 

Modified: pve-manager/pve2/lib/PVE/API2/AccessControl.pm
===================================================================
--- pve-manager/pve2/lib/PVE/API2/AccessControl.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/API2/AccessControl.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -32,7 +32,7 @@
 		subdir => { type => 'string' },
 	    },
 	},
-	links => [ { rel => 'index', href => "{subdir}" } ],
+	links => [ { rel => 'child', href => "{subdir}" } ],
     },
 }); 
 sub index {

Modified: pve-manager/pve2/lib/PVE/API2/Cluster.pm
===================================================================
--- pve-manager/pve2/lib/PVE/API2/Cluster.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/API2/Cluster.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -27,7 +27,7 @@
 	    properties => {},
 	},
 	links => [
-	    { rel => 'index', href => "{name}" }, 
+	    { rel => 'child', href => "{name}" }, 
 	    ],
     },
 });

Modified: pve-manager/pve2/lib/PVE/API2/Storage.pm
===================================================================
--- pve-manager/pve2/lib/PVE/API2/Storage.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/API2/Storage.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -1,5 +1,25 @@
 package PVE::API2::Storage;
 
+# fixme: split objects
+
+# /storage/config GET whole config, CREATE storage
+# /storage/config/{storeid}/ GET/SET storage config
+
+# /storage/status/{node}/{storeid}/state GET/SET state (activate/disable)
+
+# /storage/scan/lvm list volume groups
+# /storage/scan/nfs list nfs exports
+# /storage/scan/iscsi list iscsi exports
+
+# /storage/content/{storeid}/{node}  list/upload content
+# /storage/content/{storeid}/{node}/{volname} DELETE content
+
+# iso/*.iso
+# vztmpl/*.tgz
+# backup/*.tar
+# images/{vmid}/*.qcow2
+# images/vm-100-test1
+
 use strict;
 use warnings;
 
@@ -14,9 +34,11 @@
 
 use base qw(PVE::RESTHandler);
 
+my @ctypes = qw(images vztmpl iso backup);
+
 __PACKAGE__->register_method ({
     name => 'index', 
-    match_re => [], 
+    match_re => [], # /storage/
     method => 'GET',
     description => "Storage index.",
     parameters => {
@@ -27,17 +49,194 @@
 	type => 'array',
 	items => {
 	    type => "object",
-	    properties => {},
+	    properties => { subdir => { type => 'string'} },
 	},
+	links => [ { rel => 'child', href => "{subdir}" } ],
+    },
+});
+sub index {
+    my ($conn, $resp, $param) = @_;
+
+    my $res = [
+	{ subdir => 'config' },
+	{ subdir => 'status' },
+	{ subdir => 'content' },
+	{ subdir => 'index' },
+	{ subdir => 'scan' },
+	];
+
+    return $res;
+
+}
+
+__PACKAGE__->register_method ({
+    name => 'read_config', 
+    match_re => [ 'config', '\S+' ], 
+    method => 'GET',
+    description => "Read storage configuration.",
+    parameters => {
+    	additionalProperties => 0,
+	properties => {},
+    },
+    returns => {},
+});
+sub read_config {
+    my ($conn, $resp, $param) = @_;
+
+    my ($storeid) = $conn->{rel_uri} =~ m!/([^/]+)$!;
+
+    my $cfg = PVE::Config::read_file ("storagecfg");
+
+    my $scfg = PVE::Storage::storage_config ($cfg, $storeid);
+
+    $scfg->{digest} = $cfg->{digest};
+    $scfg->{time} = time(); # fixme: remove
+
+    return $scfg;
+}
+
+__PACKAGE__->register_method ({
+    name => 'list_storage_config', 
+    match_re => [ 'config' ], # /storage/config
+    method => 'GET',
+    description => "Storage index.",
+    parameters => {
+    	additionalProperties => 0,
+	properties => {},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => "object",
+	    properties => { storage => { type => 'string'} },
+	},
+	links => [ { rel => 'child', href => "{storage}" } ],
+    },
+});
+sub list_storage_config {
+    my ($conn, $resp, $param) = @_;
+
+    my $cfg = PVE::Config::read_file ("storagecfg");
+
+    my @sids =  PVE::Storage::storage_ids ($cfg);
+
+    my $res = [];
+    foreach my $storeid (@sids) {
+	my $scfg = PVE::Storage::storage_config ($cfg, $storeid);
+	$scfg->{storage} = $storeid;
+	push @$res, $scfg;
+    }
+
+    return $res;
+}
+
+__PACKAGE__->register_method ({
+    name => 'scan_index', 
+    match_re => [ 'scan' ], 
+    method => 'GET',
+    description => "Index of available svan methods",
+    parameters => {
+    	additionalProperties => 0,
+	properties => {},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => "object",
+	    properties => { type => { type => 'string'} },
+	},
+	links => [ { rel => 'child', href => "{type}" } ],
+    },
+});
+sub scan_index {
+    my ($conn, $resp, $param) = @_;
+
+    my $res = [ 
+	{ type => 'lvm' },
+	{ type => 'iscsi' },
+	{ type => 'nfs' },
+	];
+
+    return $res;
+};
+
+__PACKAGE__->register_method ({
+    name => 'scan_server', 
+    match_re => [ 'scan', '(lvm|nfs|iscsi)' ], 
+    method => 'GET',
+    description => "Scan remote storage server.",
+    parameters => {
+    	additionalProperties => 0,
+	properties => {},
+    },
+    returns => {},
+});
+sub scan_server {
+    my ($conn, $resp, $param) = @_;
+
+    return [];
+};
+
+__PACKAGE__->register_method ({
+    name => 'list_nodes', 
+    match_re => [ '(status|content)' ],  # /storage/(status|content)
+    method => 'GET',
+    description => "List storage nodes.",
+    parameters => {
+    	additionalProperties => 0,
+	properties => {},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => "object",
+	    properties => { node => { type => 'string' } },
+	},
 	links => [
-	    { rel => 'index', href => "{name}" }, 
+	    { rel => 'child', href => "{node}" }, 
 	    ],
     },
 });
-sub index {
+sub list_nodes {
     my ($conn, $resp, $param) = @_;
 
+    my ($storeid) = $conn->{rel_uri} =~ m|/([^/]+)$|;
 
+    # fixme: check if storeid exists on node 
+
+    my $nodes = [ # fixme: use the real list
+	{ node => 'node-0'},
+	{ node => 'node-1'},
+	{ node => 'node-2'},
+	{ node => 'node-3'},
+	];
+
+    return $nodes;
+    
+}
+
+# fixme: move to somewhere else
+__PACKAGE__->register_method ({
+    name => 'cluster_index', 
+    match_re => [ 'index'], 
+    method => 'GET',
+    description => "Cluster wide storage status.",
+    parameters => {
+    	additionalProperties => 0,
+	properties => {},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => "object",
+	    properties => {},
+	},
+#	links => [ { rel => 'child', href => "{storage}" } ],
+    },
+});
+sub cluster_index {
+    my ($conn, $resp, $param) = @_;
+
     my $nodes = [ 'node-0', 'node-1', 'node-2', 'node-3' ]; # fixme: use the real list
 
     my $cfg = PVE::Config::read_file ("storagecfg");
@@ -65,4 +264,97 @@
     return $res;
 }
 
+__PACKAGE__->register_method ({
+    name => 'list_content', 
+    protected => 1,
+    match_re => [ 'content', '\S+', '\S+' ], # /storage/content/{storeid}/{nodeid}
+    method => 'GET',
+    description => "List storage content.",
+    parameters => {
+    	additionalProperties => 0,
+	properties => { 
+	    content => {
+		type => 'string',
+		enum => [ @ctypes  ],
+		optional => 1,
+	    }
+	},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => "object",
+	    properties => { volname => { type => 'string' } },
+	},
+	links => [
+	    { rel => 'child', href => "{volname}" }, 
+	    ],
+    },
+});
+sub list_content {
+    my ($conn, $resp, $param) = @_;
+
+    my $cts = $param->{content} ? [ $param->{content} ] : [ @ctypes ];
+
+    my ($storeid, $nodeid) = $conn->{rel_uri} =~ m!/([^/]+)/([^/]+)$!;
+
+    # fixme: verify $node
+
+    my $cfg = PVE::Config::read_file ("storagecfg");
+
+    my $scfg = PVE::Storage::storage_config ($cfg, $storeid);
+
+    my $res = [];
+    foreach my $ct (@$cts) {
+	my $data;
+	if ($ct eq 'images') {
+	    $data = PVE::Storage::vdisk_list ($cfg, $storeid);
+	} elsif ($ct eq 'iso') {
+	    $data = PVE::Storage::template_list ($cfg, $storeid, 'iso');
+	} elsif ($ct eq 'vztmpl') {
+	    $data = PVE::Storage::template_list ($cfg, $storeid, 'vztmpl');
+	} elsif ($ct eq 'backup') {
+	    $data = PVE::Storage::template_list ($cfg, $storeid, 'backup');
+	}
+
+	next if !$data || !$data->{$storeid};
+
+	foreach my $item (@{$data->{$storeid}}) {
+	    push @$res, $item;
+	}
+    }
+
+    return $res;    
+}
+
+__PACKAGE__->register_method ({
+    name => 'list_status', 
+    protected => 1,
+    match_re => [ 'status', '\S+' ], # /storage/status/{nodeid}
+    method => 'GET',
+    description => "Get storage status.",
+    parameters => {
+    	additionalProperties => 0,
+	properties => {},
+    },
+    returns => {},
+});
+sub list_status {
+    my ($conn, $resp, $param) = @_;
+
+    my $cts = $param->{content} ? [ $param->{content} ] : [ @ctypes ];
+
+    my ($nodeid) = $conn->{rel_uri} =~ m!/([^/]+)$!;
+
+    # fixme: verify $node
+
+    my $cfg = PVE::Config::read_file ("storagecfg");
+
+    my $info = PVE::Storage::storage_info ($cfg);
+
+    return $info;
+}
+
+
+
 1;

Modified: pve-manager/pve2/lib/PVE/API2/User.pm
===================================================================
--- pve-manager/pve2/lib/PVE/API2/User.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/API2/User.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -30,7 +30,7 @@
 		id => { type => 'string' },
 	    },
 	},
-	links => [ { rel => 'index', href => "{id}" } ],
+	links => [ { rel => 'child', href => "{id}" } ],
     },
 }); 
 sub index {

Modified: pve-manager/pve2/lib/PVE/API2/VM.pm
===================================================================
--- pve-manager/pve2/lib/PVE/API2/VM.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/API2/VM.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -27,7 +27,7 @@
 	    properties => {},
 	},
 	links => [
-	    { rel => 'index', href => "{id}" }, 
+	    { rel => 'child', href => "{id}" }, 
 	    ],
     },
 });

Modified: pve-manager/pve2/lib/PVE/API2.pm
===================================================================
--- pve-manager/pve2/lib/PVE/API2.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/API2.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -45,7 +45,7 @@
 		subdir => { type => 'string' },
 	    },
 	},
-	links => [ { rel => 'index', href => "{subdir}" } ],
+	links => [ { rel => 'child', href => "{subdir}" } ],
     },
 }); 
 sub index {

Modified: pve-manager/pve2/lib/PVE/APIDaemon.pm
===================================================================
--- pve-manager/pve2/lib/PVE/APIDaemon.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/APIDaemon.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -15,7 +15,7 @@
 use Data::Dumper; # fixme: remove
 use PVE::REST;
 use JSON;
- 
+
 # This is a quite simple pre-fork server - only listens to local port
 
 @ISA = qw(HTTP::Daemon);

Modified: pve-manager/pve2/lib/PVE/JSONSchema.pm
===================================================================
--- pve-manager/pve2/lib/PVE/JSONSchema.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/JSONSchema.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -568,7 +568,7 @@
 validate_schema($method_schema);
 
 # and now some utility methods (used by pve api)
-sub method_get_index_link {
+sub method_get_child_link {
     my ($info) = @_;
 
     return undef if !$info;
@@ -581,7 +581,7 @@
 
     my $found;
     foreach my $lnk (@$links) {
-	if ($lnk->{href} && $lnk->{rel} && ($lnk->{rel} eq 'index')) {
+	if ($lnk->{href} && $lnk->{rel} && ($lnk->{rel} eq 'child')) {
 	    $found = $lnk;
 	    last;
 	}

Modified: pve-manager/pve2/lib/PVE/REST.pm
===================================================================
--- pve-manager/pve2/lib/PVE/REST.pm	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/lib/PVE/REST.pm	2010-07-14 10:19:25 UTC (rev 4895)
@@ -84,7 +84,7 @@
 	    my $msg = $res->{message} || '';
 	    $raw .= "<h1>ERROR $res->{status} $msg</h1>";
 	}
-	my $lnk = PVE::JSONSchema::method_get_index_link($info);
+	my $lnk = PVE::JSONSchema::method_get_child_link($info);
 	if ($lnk && $data && $data->{data} && is_success($res->{status})) {
 
 	    my $href = $lnk->{href};
@@ -114,7 +114,7 @@
 	$raw .= "</body></html>";
 
     } elsif ($format eq 'extjs') {
-	$ct = 'text/plain';
+	$ct = 'application/json';
 	$raw = to_json($data, {utf8 => 1, allow_nonref => 1});
     } else {
 	$ct = 'text/plain';

Modified: pve-manager/pve2/www/Makefile.am
===================================================================
--- pve-manager/pve2/www/Makefile.am	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/Makefile.am	2010-07-14 10:19:25 UTC (rev 4895)
@@ -1,6 +1,6 @@
 include $(top_builddir)/common.mk
 
-SUBDIRS = templates images manager css ext
+SUBDIRS = templates images ext css manager
 
 bin_SCRIPTS = 			\
 	startup.pl

Modified: pve-manager/pve2/www/ext/Makefile.am
===================================================================
--- pve-manager/pve2/www/ext/Makefile.am	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/ext/Makefile.am	2010-07-14 10:19:25 UTC (rev 4895)
@@ -11,6 +11,7 @@
 ext_DATA = 				\
 	extjs/ext-all.js 		\
 	extjs/adapter/ext/ext-base.js 	\
+	extjs/adapter/ext/ext-base-debug.js 	\
 	extjs/ext-all-debug.js		\
 	extjs/resources/css/ext-all.css	\
 	extjs/resources/css/xtheme-blue.css \

Deleted: pve-manager/pve2/www/manager/BufferView.js
===================================================================
--- pve-manager/pve2/www/manager/BufferView.js	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/BufferView.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -1,244 +0,0 @@
-/*!
- * Ext JS Library 3.2.1
- * Copyright(c) 2006-2010 Ext JS, Inc.
- * licensing at extjs.com
- * http://www.extjs.com/license
- */
-Ext.ns('Ext.ux.grid');
-
-/**
- * @class Ext.ux.grid.BufferView
- * @extends Ext.grid.GridView
- * A custom GridView which renders rows on an as-needed basis.
- */
-Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, {
-	/**
-	 * @cfg {Number} rowHeight
-	 * The height of a row in the grid.
-	 */
-	rowHeight: 19,
-
-	/**
-	 * @cfg {Number} borderHeight
-	 * The combined height of border-top and border-bottom of a row.
-	 */
-	borderHeight: 2,
-
-	/**
-	 * @cfg {Boolean/Number} scrollDelay
-	 * The number of milliseconds before rendering rows out of the visible
-	 * viewing area. Defaults to 100. Rows will render immediately with a config
-	 * of false.
-	 */
-	scrollDelay: 100,
-
-	/**
-	 * @cfg {Number} cacheSize
-	 * The number of rows to look forward and backwards from the currently viewable
-	 * area.  The cache applies only to rows that have been rendered already.
-	 */
-	cacheSize: 20,
-
-	/**
-	 * @cfg {Number} cleanDelay
-	 * The number of milliseconds to buffer cleaning of extra rows not in the
-	 * cache.
-	 */
-	cleanDelay: 500,
-
-	initTemplates : function(){
-		Ext.ux.grid.BufferView.superclass.initTemplates.call(this);
-		var ts = this.templates;
-		// empty div to act as a place holder for a row
-	        ts.rowHolder = new Ext.Template(
-		        '<div class="x-grid3-row {alt}" style="{tstyle}"></div>'
-		);
-		ts.rowHolder.disableFormats = true;
-		ts.rowHolder.compile();
-
-		ts.rowBody = new Ext.Template(
-		        '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
-			'<tbody><tr>{cells}</tr>',
-			(this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
-			'</tbody></table>'
-		);
-		ts.rowBody.disableFormats = true;
-		ts.rowBody.compile();
-	},
-
-	getStyleRowHeight : function(){
-		return Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight;
-	},
-
-	getCalculatedRowHeight : function(){
-		return this.rowHeight + this.borderHeight;
-	},
-
-	getVisibleRowCount : function(){
-		var rh = this.getCalculatedRowHeight(),
-		    visibleHeight = this.scroller.dom.clientHeight;
-		return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh);
-	},
-
-	getVisibleRows: function(){
-		var count = this.getVisibleRowCount(),
-		    sc = this.scroller.dom.scrollTop,
-		    start = (sc === 0 ? 0 : Math.floor(sc/this.getCalculatedRowHeight())-1);
-		return {
-			first: Math.max(start, 0),
-			last: Math.min(start + count + 2, this.ds.getCount()-1)
-		};
-	},
-
-	doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){
-		var ts = this.templates, 
-            ct = ts.cell, 
-            rt = ts.row, 
-            rb = ts.rowBody, 
-            last = colCount-1,
-		    rh = this.getStyleRowHeight(),
-		    vr = this.getVisibleRows(),
-		    tstyle = 'width:'+this.getTotalWidth()+';height:'+rh+'px;',
-		    // buffers
-		    buf = [], 
-            cb, 
-            c, 
-            p = {}, 
-            rp = {tstyle: tstyle}, 
-            r;
-		for (var j = 0, len = rs.length; j < len; j++) {
-			r = rs[j]; cb = [];
-			var rowIndex = (j+startRow),
-			    visible = rowIndex >= vr.first && rowIndex <= vr.last;
-			if (visible) {
-				for (var i = 0; i < colCount; i++) {
-					c = cs[i];
-					p.id = c.id;
-					p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
-					p.attr = p.cellAttr = "";
-					p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
-					p.style = c.style;
-					if (p.value === undefined || p.value === "") {
-						p.value = " ";
-					}
-					if (r.dirty && typeof r.modified[c.name] !== 'undefined') {
-						p.css += ' x-grid3-dirty-cell';
-					}
-					cb[cb.length] = ct.apply(p);
-				}
-			}
-			var alt = [];
-			if(stripe && ((rowIndex+1) % 2 === 0)){
-			    alt[0] = "x-grid3-row-alt";
-			}
-			if(r.dirty){
-			    alt[1] = " x-grid3-dirty-row";
-			}
-			rp.cols = colCount;
-			if(this.getRowClass){
-			    alt[2] = this.getRowClass(r, rowIndex, rp, ds);
-			}
-			rp.alt = alt.join(" ");
-			rp.cells = cb.join("");
-			buf[buf.length] =  !visible ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp));
-		}
-		return buf.join("");
-	},
-
-	isRowRendered: function(index){
-		var row = this.getRow(index);
-		return row && row.childNodes.length > 0;
-	},
-
-	syncScroll: function(){
-		Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments);
-		this.update();
-	},
-
-	// a (optionally) buffered method to update contents of gridview
-	update: function(){
-		if (this.scrollDelay) {
-			if (!this.renderTask) {
-				this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this);
-			}
-			this.renderTask.delay(this.scrollDelay);
-		}else{
-			this.doUpdate();
-		}
-	},
-    
-    onRemove : function(ds, record, index, isUpdate){
-        Ext.ux.grid.BufferView.superclass.onRemove.apply(this, arguments);
-        if(isUpdate !== true){
-            this.update();
-        }
-    },
-
-	doUpdate: function(){
-		if (this.getVisibleRowCount() > 0) {
-			var g = this.grid, 
-                cm = g.colModel, 
-                ds = g.store,
-    	        cs = this.getColumnData(),
-		        vr = this.getVisibleRows(),
-                row;
-			for (var i = vr.first; i <= vr.last; i++) {
-				// if row is NOT rendered and is visible, render it
-				if(!this.isRowRendered(i) && (row = this.getRow(i))){
-					var html = this.doRender(cs, [ds.getAt(i)], ds, i, cm.getColumnCount(), g.stripeRows, true);
-					row.innerHTML = html;
-				}
-			}
-			this.clean();
-		}
-	},
-
-	// a buffered method to clean rows
-	clean : function(){
-		if(!this.cleanTask){
-			this.cleanTask = new Ext.util.DelayedTask(this.doClean, this);
-		}
-		this.cleanTask.delay(this.cleanDelay);
-	},
-
-	doClean: function(){
-		if (this.getVisibleRowCount() > 0) {
-			var vr = this.getVisibleRows();
-			vr.first -= this.cacheSize;
-			vr.last += this.cacheSize;
-
-			var i = 0, rows = this.getRows();
-			// if first is less than 0, all rows have been rendered
-			// so lets clean the end...
-			if(vr.first <= 0){
-				i = vr.last + 1;
-			}
-			for(var len = this.ds.getCount(); i < len; i++){
-				// if current row is outside of first and last and
-				// has content, update the innerHTML to nothing
-				if ((i < vr.first || i > vr.last) && rows[i].innerHTML) {
-					rows[i].innerHTML = '';
-				}
-			}
-		}
-	},
-    
-    removeTask: function(name){
-        var task = this[name];
-        if(task && task.cancel){
-            task.cancel();
-            this[name] = null;
-        }
-    },
-    
-    destroy : function(){
-        this.removeTask('cleanTask');
-        this.removeTask('renderTask');  
-        Ext.ux.grid.BufferView.superclass.destroy.call(this);
-    },
-
-	layout: function(){
-		Ext.ux.grid.BufferView.superclass.layout.call(this);
-		this.update();
-	}
-});
\ No newline at end of file

Modified: pve-manager/pve2/www/manager/ClusterConfig.js
===================================================================
--- pve-manager/pve2/www/manager/ClusterConfig.js	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/ClusterConfig.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -7,9 +7,8 @@
 
 	var clusterid = self.clusterid;
 
-	if (!clusterid) { 
-	    clusterid = 'default';
-	}
+	if (!clusterid)
+	    throw "no cluster ID specified";
 
 	Ext.apply(self, { 
 	    title: "Cluster '" + clusterid + "'",
@@ -23,9 +22,9 @@
 		    html: 'status ' + clusterid
 		},
 		{
+		    xtype: 'pveStorageConfig',
 		    title: 'Storage',
-		    id: 'storage',
-		    html: 'storage ' + clusterid
+		    id: 'storage'
 		},
 		{
 		    title: 'Users',

Modified: pve-manager/pve2/www/manager/Makefile.am
===================================================================
--- pve-manager/pve2/www/manager/Makefile.am	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/Makefile.am	2010-07-14 10:19:25 UTC (rev 4895)
@@ -1,18 +1,24 @@
 include $(top_builddir)/common.mk
 
-JSSRC= 				\
-	PVEUtils.js 		\
-	PVECache.js 		\
-	Workspace.js 		\
-	LoginWindow.js 		\
-	PVEFilter.js 		\
-	ResourceTree.js 	\
-	ConfigPanel.js		\
-	PVEConsole.js		\
-	KVMConfig.js		\
-	NodeConfig.js 		\
-	ClusterConfig.js 	\
-	BufferView.js 		\
+JSSRC= 				                 	\
+	../ext/extjs/examples/ux/MultiSelect.js	 	\
+	../ext/extjs/examples/ux/BufferView.js 		\
+	data/ObjectReader.js				\
+	data/ObjectStore.js				\
+	grid/ObjectView.js				\
+	PVEUtils.js 					\
+	PVECache.js 					\
+	Workspace.js 					\
+	LoginWindow.js 					\
+	PVEFilter.js 					\
+	ResourceTree.js 				\
+	ConfigPanel.js					\
+	PVEConsole.js					\
+	KVMConfig.js					\
+	NodeConfig.js 					\
+	StorageConfig.js				\
+	StorageBrowser.js				\
+	ClusterConfig.js 				\
 	PVESearch.js
 
 pvemanagerlib.js: ${JSSRC}
@@ -35,6 +41,6 @@
 	chown -R www-data:www-data ${DESTDIR}/${pvelibdir}
 
 clean-local:
-	-rm -rf *~ pvemanagerlib.js
+	-rm -rf *~ store/*~ pvemanagerlib.js
 
 

Modified: pve-manager/pve2/www/manager/PVECache.js
===================================================================
--- pve-manager/pve2/www/manager/PVECache.js	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/PVECache.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -367,13 +367,13 @@
 
 	ststore: function(){
 
-	    var fields = PVE.Utils.get_field_defaults(['name', 'node', 'shared', 'disk', 'maxdisk']);
+	    var fields = PVE.Utils.get_field_defaults(['name', 'storage', 'node', 'shared', 'disk', 'maxdisk']);
 
 	    var store = new  PVE.UpdateStore({
 		itype: 'storage',
 		idProperty: 'name',
 		autoDestroy: false,
-		url: '/api2/json/storage',
+		url: '/api2/json/storage/index',
 		fields: fields
 	    });
 

Modified: pve-manager/pve2/www/manager/PVEUtils.js
===================================================================
--- pve-manager/pve2/www/manager/PVEUtils.js	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/PVEUtils.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -102,6 +102,39 @@
 	    return days.toFixed(0) + 'd';	
         },
 
+	format_storage_type: function(value) {
+	    var desc = {
+		dir: 'Directory',
+		nfs: 'NFS',
+		lvm: 'LVM',
+		iscsi: 'iSCSI'
+	    };
+	    return desc[value] || 'unknown';
+	},
+
+	format_boolean: function(value) {
+	    return value ? 'Yes' : 'No';
+	},
+
+	format_neg_boolean: function(value) {
+	    return !value ? 'Yes' : 'No';
+	},
+
+	format_content_types: function(value) {
+	    var cta = [];
+
+	    if (value.images)
+		cta.push('Images');
+	    if (value.backup)
+		cta.push('Backups');
+	    if (value.vztmpl)
+		cta.push('Templates');
+	    if (value.iso)
+		cta.push('ISO');
+
+	    return cta.join(', ');
+	},
+
 	render_cpu: function(value, metaData, record, rowIndex, colIndex, store) {
 
 	    var cpu = value;

Modified: pve-manager/pve2/www/manager/ResourceTree.js
===================================================================
--- pve-manager/pve2/www/manager/ResourceTree.js	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/ResourceTree.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -42,8 +42,8 @@
 	    Ext.apply(info, {
 		cls: 'x-tree-node-harddisk',
 		target: { 
-		    title: "Storage " + info.itemid, 
-		    border: false 
+		    xtype: 'pveStorageBrowser',
+		    confdata: info.data
 		} 
 	    });
 	} else if (itype === 'vm') {
@@ -324,7 +324,8 @@
 	    text: "Datacenter 1",
 	    cls: 'x-tree-node-collapsed',
 	    target: { 
-		xtype: 'pveClusterConfig'
+		xtype: 'pveClusterConfig',
+		clusterid: 'default'
 	    } 
 	});
 

Added: pve-manager/pve2/www/manager/StorageBrowser.js
===================================================================
--- pve-manager/pve2/www/manager/StorageBrowser.js	                        (rev 0)
+++ pve-manager/pve2/www/manager/StorageBrowser.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -0,0 +1,229 @@
+Ext.ns("PVE");
+
+PVE.StorageStatus = Ext.extend(Ext.Panel, {
+
+    initComponent : function() {
+	var self = this;
+
+	var storeid = self.confdata.storage;
+
+	var update_config = function() {
+	    self.store.load();
+	};
+
+	Ext.apply(self, {
+	    layout: 'hbox',
+	    autoScroll: true,
+
+	    layoutConfig: {
+		defaultMargins: "10 10 10 0",
+		align: 'stretchmax'
+	    },
+
+	    items: [
+		{
+		    xtype: 'pveObjectView',
+		    store: self.store,
+		    margins: "10 10 10 10",
+		    width: 300,
+		    tbar: [
+			"<b>Configuration", "->", 
+			{ 
+			    text: "Edit",
+			    handler: function() {
+
+				var form =  new PVE.StorageConfigPanel();
+				var win = new Ext.Window({
+				    title: "Modify Directory Storage",
+				    modal: true,
+				    border:false,
+				    layout: 'fit',
+				    maximizable: true,
+				    items: form,
+				    width: 800,
+				    height: 400,
+
+				    buttons: [
+					{
+					    text: 'OK',
+					    handler: function(){
+						form.submitHandler({
+						    success: function() { 
+							win.close();
+							update_config();
+						    }
+						});
+					    }
+					},{
+					    text: 'Cancel',
+					    handler: function(){
+						win.close();
+						update_config();
+					    }
+					}
+				    ]
+				});
+
+				win.show();
+			    }
+			},
+			{ 
+			    text: "Refresh",
+			    handler: update_config
+			}
+		    ]
+		    //flex: 1,
+		},
+		{
+		    width: 300,
+		    tbar: [ 
+			"<b>Status", '->',
+			{ 
+			    text: "Refresh"
+			}
+		    ],
+		    html: "data2"
+		}
+	    ]
+	});
+
+	PVE.StorageStatus.superclass.initComponent.call(self);
+    }
+});
+
+Ext.reg('pveStorageStatus', PVE.StorageStatus);
+
+PVE.StorageBrowser = Ext.extend(PVE.ConfigPanel, {
+
+    initComponent : function() {
+	var self = this;
+
+	var node = self.confdata.node;
+	var storeid = self.confdata.storage;
+	var shared =  self.confdata.shared;
+
+	if (!storeid) 
+	    throw "no storage ID specified";
+
+	if (!shared && !node) 
+	    throw "no node specified";
+
+	var title = "Storage '" + storeid + "'";
+	
+	if (!shared)
+	    title = title + " on node '" + node + "'";
+
+	var cond_view_comp = function(id, enable) {
+
+	    var tabs = self.get(0);
+	    var comp = self.findById(id);
+	    if (!comp)
+		return;
+
+	    if (enable) {
+		tabs.unhideTabStripItem(id);
+	    } else {
+		var active = tabs.getActiveTab();
+		tabs.hideTabStripItem(id);
+		if (active) {
+		    active = Ext.isObject(active) ? active.getId() : active;
+		    if (active === id) {
+			tabs.setActiveTab(0);
+		    }
+		}
+	    }
+	};
+
+	var smid = "storage.config." + storeid;  
+	var store = Ext.StoreMgr.lookup(smid);
+
+	var set_visible_tabs = function() {
+	    var rec = store.getById('content');
+	    if (!rec)
+		return;
+
+	    var ct = rec.data.value || {};
+
+	    cond_view_comp('images', ct.images);
+	    cond_view_comp('iso', ct.iso);
+	    cond_view_comp('vztmpl', ct.vztmpl);
+	    cond_view_comp('backup', ct.backup);
+	};
+	
+	if (!store) {
+	    store = new PVE.data.ObjectStore({
+		url: "/api2/json/storage/config/" + storeid,
+		method: 'GET',
+		id: smid,
+		rows: {
+		    type: { header: 'Storage Type', renderer: PVE.Utils.format_storage_type },
+		    path: { header: 'Path' },
+		    shared: { header: 'Shared', renderer: PVE.Utils.format_boolean },
+		    disabled: { header: 'Enabled',  renderer: PVE.Utils.format_neg_boolean },
+		    time: { header: 'TIME' },
+		    content: { header: 'Content', renderer: PVE.Utils.format_content_types }
+		}
+	    });
+	} 
+
+	store.on('load',  set_visible_tabs);
+
+	store.load();
+
+	Ext.apply(self, {
+	    title: title, 
+	    layout: 'fit',
+  	    border: false,
+
+	    defaults: { 
+		border: false
+	    },
+
+	    items: [
+		{
+		    title: 'Summary',
+		    xtype: 'pveStorageStatus',
+		    confdata: self.confdata,
+		    store: store,
+		    id: 'status'
+		},
+		{
+		    title: 'Images',
+		    id: 'images',
+		    html: 'images'
+		},
+		{
+		    title: 'ISO',
+		    id: 'iso',
+		    html: 'iso'
+		},
+		{
+		    title: 'Templates',
+		    id: 'vztmpl',
+		    html: 'vztmpl'
+		},
+		{
+		    title: 'Backups',
+		    id: 'backup',
+		    html: 'backup'
+		},
+		{
+		    title: 'Permissions',
+		    id: 'permissions',
+		    html: 'services '
+		}
+	    ]
+	});
+
+	PVE.StorageBrowser.superclass.initComponent.call(self);
+
+	self.on('destroy', function () {
+	    store.un('load', set_visible_tabs);
+	});
+
+
+    }
+});
+
+Ext.reg('pveStorageBrowser', PVE.StorageBrowser);
+

Added: pve-manager/pve2/www/manager/StorageConfig.js
===================================================================
--- pve-manager/pve2/www/manager/StorageConfig.js	                        (rev 0)
+++ pve-manager/pve2/www/manager/StorageConfig.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -0,0 +1,264 @@
+Ext.ns("PVE");
+
+PVE.StorageConfigPanel = Ext.extend(Ext.FormPanel, {
+
+    initComponent : function() {
+	var self = this;
+
+	var items_col1 = [
+	    {
+		xtype: 'textfield',
+		fieldLabel: 'Storage name',
+		name: 'storage'
+		//disabled: true,
+		//allowBlank: false
+	    },
+	    {
+		xtype: 'textfield',
+		fieldLabel: 'Directory',
+		name: 'path',
+		allowBlank: false
+	    }
+	];
+
+	var items_col2 = [
+	    {
+		xtype: 'checkbox',
+		fieldLabel: 'Disabled',
+		name: 'disabled',
+		value: 'test1'
+	    },
+	    {
+		xtype: 'checkbox',
+		fieldLabel: 'Shared',
+		name: 'shared'
+	    },
+	    {
+		xtype: 'multiselect',
+		//xtype: 'combo',
+		forceSelection: true,
+		triggerAction: 'all',
+		fieldLabel: 'Content',
+		name: 'content',
+		style : 'margin-bottom:10px',// avoid scrolbars with Firefox
+		width: 150,
+		height: 'auto',
+		store: [['images', 'Images'], ['backup', 'Backup'], 
+			['vztmpl', 'OpenVZ Templates'], ['iso', 'ISO']]
+	    }
+	];
+
+	var items_col3 = [
+	    {
+		xtype: 'multiselect',
+		fieldLabel: 'Nodes',
+		name: 'nodelist',
+		style : 'margin-bottom:10px',// avoid scrolbars with Firefox
+		width: 150,
+		height: 'auto',
+		store: ['Node0', 'Node1', 'Node2', 'Node3', 'Node4', 'Node5']
+	    }
+	];
+
+	// NOTE: If subclassing FormPanel, any configuration options for 
+	// the BasicForm must be applied to initialConfig
+
+	Ext.apply(self, Ext.apply(self.initialConfig, {
+	    url: "/api2/extjs/test",
+	    bodyStyle: 'padding:5px',
+	    autoScroll: true,
+
+ 	    submitHandler: function(options) {
+
+		var form = self.getForm();
+
+		if(form.isValid()){
+		    self.el.mask('Please wait...', 'x-mask-loading');
+
+		    form.submit({
+			failure: function(f, resp){
+			    self.el.unmask();
+			    Ext.MessageBox.alert('Failure', "Please try again");
+			    if (Ext.isFunction(options.failure)) {
+				options.failure();
+			    }
+			},
+			success: function(f, resp){
+			    self.el.unmask();
+			    Ext.MessageBox.alert('Success', "Submit successful");
+			    if (Ext.isFunction(options.success)) {
+				options.success();
+			    }
+			}
+		    });
+		} else {
+		    Ext.MessageBox.alert('Failure', "Verify failed");
+		    if (Ext.isFunction(options.failure)) {
+			options.failure();
+		    }
+		}
+	    },
+
+	    items: [
+		{
+		    xtype:'fieldset',
+		    title: 'Settings',
+		    defaults: { border: false },
+		    items: [
+			{
+			    layout:'column',
+			    defaults: {
+				columnWidth: '.5',
+				border: false
+			    },            
+			    items: [
+				{
+				    layout: 'form',
+ 				    defaults: { anchor: '-20' },
+ 				    items: items_col1
+				},
+				{
+				    layout: 'form',
+				    defaults: { anchor: '100%' },
+ 				    items: items_col2
+				}
+			    ]
+			}
+		    ]
+		},
+		{ 
+		    xtype:'fieldset',
+		    checkboxToggle:true,
+		    collapsed: true,
+ 		    title: 'Restrict access to specific nodes',
+		    items: {
+			layout: 'form',
+ 			border:false,
+			defaults: { border:false, anchor: '-10' },
+ 			items: items_col3
+		    }
+		}
+	    ]
+	}));
+
+	PVE.StorageConfigPanel.superclass.initComponent.call(self);
+    }
+    
+});
+
+Ext.reg('pveStorageConfigPanel', PVE.StorageConfigPanel);
+
+PVE.StorageConfig = Ext.extend(Ext.grid.GridPanel, {
+
+    initComponent : function() {
+	var self = this;
+
+	var store = new Ext.data.JsonStore({
+	    autoLoad: true,
+	    autoDestroy: true,
+	    url: '/api2/json/storage/config',
+	    root: 'data',
+	    idProperty: 'storage',
+	    fields: ['storage', 'type', 'path', 
+		     { name: 'shared', type: 'boolean' }, 
+		     { name: 'disable', type: 'boolean' }, 
+		     'content']
+	});
+
+	Ext.apply(self, {
+	    store: store,
+
+	    tbar: [
+		{ 
+		    text: 'Add',
+		    handler: function()  {
+			alert("ADD new storage");
+		    }
+		},
+		{ 
+		    text: 'Delete',
+		    handler: function()  {
+			var sm = self.getSelectionModel();
+			var sel = sm.getSelected();
+			if (!sel) {
+			    alert("no selection");
+			    return;
+			} 
+			alert("Delete " + sel.data.storage);
+		    }
+		}
+	    ],
+
+	    colModel: new Ext.grid.ColumnModel({
+		defaults: {
+		    width: 200,
+		    sortable: true
+		},
+		columns: [
+		    { header: 'Storage ID', dataIndex: 'storage' },
+		    { header: 'Type', dataIndex: 'type', width: 60, renderer: PVE.Utils.format_storage_type },
+		    { header: 'Shared', dataIndex: 'shared', width: 60, renderer: PVE.Utils.format_boolean },
+		    { header: 'Enabled', dataIndex: 'disable', width: 60, renderer: PVE.Utils.format_neg_boolean },
+		    { header: 'Content', dataIndex: 'content', renderer: PVE.Utils.format_content_types,
+		      sortable: false }
+
+		]
+	    }),
+	    
+	    sm: new Ext.grid.RowSelectionModel({
+		singleSelect: true
+	    }),
+
+	    listeners: {
+		rowdblclick: function()  {
+		    var sm = self.getSelectionModel();
+		    var sel = sm.getSelected();
+		    if (!sel) {
+			alert("no selection");
+			return;
+		    } 
+
+		    var form =  new PVE.StorageConfigPanel();
+		    var win = new Ext.Window({
+			title: "Add/Modify Directory Storage",
+			modal: true,
+			border:false,
+			layout: 'fit',
+			maximizable: true,
+			items: form,
+			width: 800,
+			height: 400,
+
+			buttons: [
+			    {
+				text: 'OK',
+				handler: function(){
+				    form.submitHandler({
+					success: function() { 
+					    win.close();
+					}
+				    });
+				}
+			    },{
+				text: 'Cancel',
+				handler: function(){
+				    win.close();
+				}
+			    }
+			]
+		    });
+
+		    win.show();
+		}
+	    },
+
+	    stateful: false,
+	    border: false
+	});
+
+	PVE.StorageConfig.superclass.initComponent.call(self);
+    }
+    
+});
+
+Ext.reg('pveStorageConfig', PVE.StorageConfig);

Modified: pve-manager/pve2/www/manager/data/ObjectReader.js
===================================================================
--- pve-manager/pve2/www/manager/data/ObjectReader.js	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/data/ObjectReader.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -1,4 +1,4 @@
-Ext.ns("PVE");
+Ext.ns("PVE.data");
 
 // a reader to store a single JSON Object (hash) into a storage
 

Modified: pve-manager/pve2/www/manager/data/ObjectStore.js
===================================================================
--- pve-manager/pve2/www/manager/data/ObjectStore.js	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/data/ObjectStore.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -1,4 +1,4 @@
-Ext.ns("PVE");
+Ext.ns("PVE.data");
 
 // a store for simple JSON Object (hash)
 

Added: pve-manager/pve2/www/manager/grid/ObjectView.js
===================================================================
--- pve-manager/pve2/www/manager/grid/ObjectView.js	                        (rev 0)
+++ pve-manager/pve2/www/manager/grid/ObjectView.js	2010-07-14 10:19:25 UTC (rev 4895)
@@ -0,0 +1,64 @@
+Ext.ns("PVE.grid");
+
+// a special grid to display PVE.data.ObjectStore
+
+PVE.grid.ObjectView = Ext.extend(Ext.grid.GridPanel, {
+
+    initComponent : function() {
+	var self = this;
+
+	if (!self.store)
+	    throw "no store specified";
+
+	var rows = self.store.rows || {};
+
+	var render_key = function(key) {
+	    var rowdef = rows[key] || {};
+	    return rowdef.header|| key;
+	};
+
+	var render_value = function(value, metaData, record, rowIndex, colIndex, store) {
+	    var key = record.data.name;
+	    var rowdef = rows[key] || {};
+
+	    var renderer = rowdef.renderer;
+	    if (renderer)
+		return renderer(value, metaData, record, rowIndex, colIndex, store);
+
+	    return value;
+	};
+
+	Ext.apply(self, {
+	    stateful: false,
+	    enableHdMenu: false,
+	    height: 200,
+	    colModel: new Ext.grid.ColumnModel({
+		defaults: {
+		    sortable: false
+		},
+		columns: [
+		    {
+			header: 'Name',
+			width: 0.3,
+			dataIndex: 'name',
+			renderer: render_key
+		    },{
+			header: 'Value',
+			width: 0.7,
+			dataIndex: 'value',
+			renderer: render_value
+		    }
+		]
+	    }),
+	    viewConfig: { forceFit: true },	    
+
+	    sm: new Ext.grid.RowSelectionModel({singleSelect:true})
+
+	});
+
+	PVE.grid.ObjectView.superclass.initComponent.call(self);
+    }
+});
+
+Ext.reg('pveObjectView', PVE.grid.ObjectView);
+

Modified: pve-manager/pve2/www/manager/index.pl
===================================================================
--- pve-manager/pve2/www/manager/index.pl	2010-07-14 10:08:19 UTC (rev 4894)
+++ pve-manager/pve2/www/manager/index.pl	2010-07-14 10:19:25 UTC (rev 4895)
@@ -48,7 +48,7 @@
     <link rel="stylesheet" type="text/css" href="/ext/ext-all.css" />
     <link rel="stylesheet" type="text/css" href="/css/ext-pve.css" />
  
-    <script type="text/javascript" src="/ext/ext-base.js"></script>
+    <script type="text/javascript" src="/ext/ext-base-debug.js"></script>
     <script type="text/javascript" src="/ext/ext-all-debug.js"></script>
     <script type="text/javascript" src="/ext/pvemanagerlib.js"></script>
     



More information about the pve-devel mailing list