[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