[pve-devel] [PATCH pve-manager 1/2] add sdn gui
Alexandre Derumier
aderumier at odiso.com
Mon Feb 10 17:27:10 CET 2020
---
www/manager6/Makefile | 17 +++
www/manager6/Makefile.rej | 9 ++
www/manager6/StateProvider.js | 4 +-
www/manager6/Utils.js | 70 +++++++++
www/manager6/Workspace.js | 1 +
www/manager6/dc/Config.js | 33 +++++
www/manager6/dc/PoolEdit.js | 13 +-
www/manager6/form/SDNControllerSelector.js | 52 +++++++
www/manager6/form/SDNZoneSelector.js | 52 +++++++
www/manager6/sdn/Browser.js | 49 +++++++
www/manager6/sdn/ControllerView.js | 146 +++++++++++++++++++
www/manager6/sdn/Status.js | 36 +++++
www/manager6/sdn/StatusView.js | 98 +++++++++++++
www/manager6/sdn/VnetEdit.js | 133 +++++++++++++++++
www/manager6/sdn/VnetView.js | 159 +++++++++++++++++++++
www/manager6/sdn/ZoneContentView.js | 105 ++++++++++++++
www/manager6/sdn/ZoneView.js | 152 ++++++++++++++++++++
www/manager6/sdn/controllers/Base.js | 73 ++++++++++
www/manager6/sdn/controllers/EvpnEdit.js | 48 +++++++
www/manager6/sdn/zones/Base.js | 73 ++++++++++
www/manager6/sdn/zones/EvpnEdit.js | 66 +++++++++
www/manager6/sdn/zones/QinQEdit.js | 63 ++++++++
www/manager6/sdn/zones/VlanEdit.js | 47 ++++++
www/manager6/sdn/zones/VxlanEdit.js | 59 ++++++++
www/manager6/tree/ResourceTree.js | 4 +
25 files changed, 1560 insertions(+), 2 deletions(-)
create mode 100644 www/manager6/Makefile.rej
create mode 100644 www/manager6/form/SDNControllerSelector.js
create mode 100644 www/manager6/form/SDNZoneSelector.js
create mode 100644 www/manager6/sdn/Browser.js
create mode 100644 www/manager6/sdn/ControllerView.js
create mode 100644 www/manager6/sdn/Status.js
create mode 100644 www/manager6/sdn/StatusView.js
create mode 100644 www/manager6/sdn/VnetEdit.js
create mode 100644 www/manager6/sdn/VnetView.js
create mode 100644 www/manager6/sdn/ZoneContentView.js
create mode 100644 www/manager6/sdn/ZoneView.js
create mode 100644 www/manager6/sdn/controllers/Base.js
create mode 100644 www/manager6/sdn/controllers/EvpnEdit.js
create mode 100644 www/manager6/sdn/zones/Base.js
create mode 100644 www/manager6/sdn/zones/EvpnEdit.js
create mode 100644 www/manager6/sdn/zones/QinQEdit.js
create mode 100644 www/manager6/sdn/zones/VlanEdit.js
create mode 100644 www/manager6/sdn/zones/VxlanEdit.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index eb7ac004..ef9b0dec 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -69,6 +69,8 @@ JSSRC= \
form/CephPoolSelector.js \
form/PermPathSelector.js \
form/SpiceEnhancementSelector.js \
+ form/SDNZoneSelector.js \
+ form/SDNControllerSelector.js \
dc/Tasks.js \
dc/Log.js \
panel/StatusPanel.js \
@@ -189,6 +191,21 @@ JSSRC= \
storage/RBDEdit.js \
storage/ZFSEdit.js \
storage/ZFSPoolEdit.js \
+ sdn/Browser.js \
+ sdn/Status.js \
+ sdn/StatusView.js \
+ sdn/ZoneView.js \
+ sdn/ZoneContentView.js \
+ sdn/VnetView.js \
+ sdn/VnetEdit.js \
+ sdn/zones/Base.js \
+ sdn/zones/VlanEdit.js \
+ sdn/zones/VxlanEdit.js \
+ sdn/zones/QinQEdit.js \
+ sdn/zones/EvpnEdit.js \
+ sdn/ControllerView.js \
+ sdn/controllers/Base.js \
+ sdn/controllers/EvpnEdit.js \
ha/StatusView.js \
ha/Status.js \
ha/GroupSelector.js \
diff --git a/www/manager6/Makefile.rej b/www/manager6/Makefile.rej
new file mode 100644
index 00000000..e8f2f84d
--- /dev/null
+++ b/www/manager6/Makefile.rej
@@ -0,0 +1,9 @@
+diff a/www/manager6/Makefile b/www/manager6/Makefile (rejected hunks)
+@@ -66,6 +66,7 @@ JSSRC= \
+ form/CalendarEvent.js \
+ form/CephPoolSelector.js \
+ form/PermPathSelector.js \
++ form/SDNTransportSelector.js \
+ dc/Tasks.js \
+ dc/Log.js \
+ panel/StatusPanel.js \
diff --git a/www/manager6/StateProvider.js b/www/manager6/StateProvider.js
index 81c76401..821c27d2 100644
--- a/www/manager6/StateProvider.js
+++ b/www/manager6/StateProvider.js
@@ -40,6 +40,7 @@ Ext.define('PVE.StateProvider', {
['ltab', 'tasks'],
['nodetab', ''],
['storagetab', ''],
+ ['sdntab', ''],
['pooltab', ''],
['kvmtab', ''],
['lxctab', ''],
@@ -49,6 +50,7 @@ Ext.define('PVE.StateProvider', {
hprefix: 'v1',
compDict: {
+ sdn: 53,
cloudinit: 52,
replication: 51,
system: 50,
@@ -221,7 +223,7 @@ Ext.define('PVE.StateProvider', {
} else {
data = me.callParent(arguments);
if (!data && name === 'GuiCap') {
- data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {} };
+ data = { vms: {}, storage: {}, access: {}, nodes: {}, dc: {}, sdn: {} };
}
}
diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index 86951ed7..3d4ac50c 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -619,6 +619,76 @@ Ext.define('PVE.Utils', { utilities: {
}
},
+ sdnvnetSchema: {
+ vnet: {
+ name: 'vnet',
+ faIcon: 'folder'
+ },
+ },
+
+ sdnzoneSchema: {
+ zone: {
+ name: 'zone',
+ hideAdd: true
+ },
+ vlan: {
+ name: 'vlan',
+ ipanel: 'VlanInputPanel',
+ faIcon: 'folder'
+ },
+ qinq: {
+ name: 'qinq',
+ ipanel: 'QinQInputPanel',
+ faIcon: 'folder'
+ },
+ vxlan: {
+ name: 'vxlan',
+ ipanel: 'VxlanInputPanel',
+ faIcon: 'folder'
+ },
+ evpn: {
+ name: 'evpn',
+ ipanel: 'EvpnInputPanel',
+ faIcon: 'folder'
+ },
+ },
+
+ sdncontrollerSchema: {
+ controller: {
+ name: 'controller',
+ hideAdd: true
+ },
+ evpn: {
+ name: 'evpn',
+ ipanel: 'EvpnInputPanel',
+ faIcon: 'folder'
+ },
+ },
+
+ format_sdnvnet_type: function(value, md, record) {
+ var schema = PVE.Utils.sdnvnetSchema[value];
+ if (schema) {
+ return schema.name;
+ }
+ return Proxmox.Utils.unknownText;
+ },
+
+ format_sdnzone_type: function(value, md, record) {
+ var schema = PVE.Utils.sdnzoneSchema[value];
+ if (schema) {
+ return schema.name;
+ }
+ return Proxmox.Utils.unknownText;
+ },
+
+ format_sdncontroller_type: function(value, md, record) {
+ var schema = PVE.Utils.sdncontrollerSchema[value];
+ if (schema) {
+ return schema.name;
+ }
+ return Proxmox.Utils.unknownText;
+ },
+
format_storage_type: function(value, md, record) {
if (value === 'rbd') {
value = (!record || record.get('monhost') ? 'rbd' : 'pveceph');
diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js
index 335e7e61..1cc86999 100644
--- a/www/manager6/Workspace.js
+++ b/www/manager6/Workspace.js
@@ -210,6 +210,7 @@ Ext.define('PVE.StdWorkspace', {
qemu: 'PVE.qemu.Config',
lxc: 'PVE.lxc.Config',
storage: 'PVE.storage.Browser',
+ sdn: 'PVE.sdn.Browser',
pool: 'pvePoolConfig'
};
var comp = {
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 52cd106f..bbc4f5c7 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -56,6 +56,39 @@ Ext.define('PVE.dc.Config', {
});
}
+ if (caps.dc['Sys.Audit']) {
+
+ me.items.push({
+ xtype: 'pveSDNStatus',
+ title: gettext('SDN'),
+ iconCls: 'fa fa-unlock',
+ itemId: 'sdn',
+ expandedOnInit: true
+ });
+
+ me.items.push({
+ xtype: 'pveSDNControllerView',
+ groups: ['sdn'],
+ title: gettext('Controllers'),
+ iconCls: 'fa fa-database',
+ itemId: 'sdncontroller'
+ });
+ me.items.push({
+ xtype: 'pveSDNZoneView',
+ groups: ['sdn'],
+ title: gettext('Zones'),
+ iconCls: 'fa fa-database',
+ itemId: 'sdnzone'
+ });
+ me.items.push({
+ xtype: 'pveSDNVnetView',
+ groups: ['sdn'],
+ title: gettext('Vnets'),
+ iconCls: 'fa fa-database',
+ itemId: 'sdnvnet'
+ });
+ }
+
if (caps.dc['Sys.Audit']) {
me.items.push({
xtype: 'pveDcBackupView',
diff --git a/www/manager6/dc/PoolEdit.js b/www/manager6/dc/PoolEdit.js
index afc24a66..b17d0b8a 100644
--- a/www/manager6/dc/PoolEdit.js
+++ b/www/manager6/dc/PoolEdit.js
@@ -43,6 +43,17 @@ Ext.define('PVE.dc.PoolEdit', {
if (!me.isCreate) {
me.load();
- }
+ } else {
+ me.type = 'vnet'
+/*
+ for (i = 0; i < 100; i++) {
+ confid = 'net' + i.toString();
+ if (!Ext.isDefined(me.vmconfig[confid])) {
+ me.confid = confid;
+ break;
+ }
+ }
+*/
+ }
}
});
diff --git a/www/manager6/form/SDNControllerSelector.js b/www/manager6/form/SDNControllerSelector.js
new file mode 100644
index 00000000..81ba0f16
--- /dev/null
+++ b/www/manager6/form/SDNControllerSelector.js
@@ -0,0 +1,52 @@
+Ext.define('PVE.form.SDNControllerSelector', {
+ extend: 'Proxmox.form.ComboGrid',
+ alias: ['widget.pveSDNControllerSelector'],
+
+ allowBlank: false,
+ valueField: 'controller',
+ displayField: 'controller',
+
+ initComponent: function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-sdn-controller',
+ sorters: {
+ property: 'controller',
+ order: 'DESC'
+ },
+ });
+
+ Ext.apply(me, {
+ store: store,
+ autoSelect: false,
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Controller'),
+ sortable: true,
+ dataIndex: 'controller',
+ flex: 1
+ },
+ ]
+ }
+ });
+
+ me.callParent();
+
+ store.load();
+ }
+
+}, function() {
+
+ Ext.define('pve-sdn-controller', {
+ extend: 'Ext.data.Model',
+ fields: [ 'controller' ],
+ proxy: {
+ type: 'proxmox',
+ url: "/api2/json/cluster/sdn/controllers"
+ },
+ idProperty: 'controller'
+ });
+
+});
diff --git a/www/manager6/form/SDNZoneSelector.js b/www/manager6/form/SDNZoneSelector.js
new file mode 100644
index 00000000..da5804ec
--- /dev/null
+++ b/www/manager6/form/SDNZoneSelector.js
@@ -0,0 +1,52 @@
+Ext.define('PVE.form.SDNZoneSelector', {
+ extend: 'Proxmox.form.ComboGrid',
+ alias: ['widget.pveSDNZoneSelector'],
+
+ allowBlank: false,
+ valueField: 'zone',
+ displayField: 'zone',
+
+ initComponent: function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-sdn-zone',
+ sorters: {
+ property: 'zone',
+ order: 'DESC'
+ },
+ });
+
+ Ext.apply(me, {
+ store: store,
+ autoSelect: false,
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Zone'),
+ sortable: true,
+ dataIndex: 'zone',
+ flex: 1
+ },
+ ]
+ }
+ });
+
+ me.callParent();
+
+ store.load();
+ }
+
+}, function() {
+
+ Ext.define('pve-sdn-zone', {
+ extend: 'Ext.data.Model',
+ fields: [ 'zone' ],
+ proxy: {
+ type: 'proxmox',
+ url: "/api2/json/cluster/sdn/zones"
+ },
+ idProperty: 'zone'
+ });
+
+});
diff --git a/www/manager6/sdn/Browser.js b/www/manager6/sdn/Browser.js
new file mode 100644
index 00000000..339fedb1
--- /dev/null
+++ b/www/manager6/sdn/Browser.js
@@ -0,0 +1,49 @@
+Ext.define('PVE.sdn.Browser', {
+ extend: 'PVE.panel.Config',
+ alias: 'widget.PVE.sdn.Browser',
+
+ initComponent: function() {
+ var me = this;
+
+ var nodename = me.pveSelNode.data.node;
+ if (!nodename) {
+ throw "no node name specified";
+ }
+
+ var sdnid = me.pveSelNode.data.sdn;
+ if (!sdnid) {
+ throw "no sdn ID specified";
+ }
+
+ me.items = [];
+
+ var caps = Ext.state.Manager.get('GuiCap');
+
+ Ext.apply(me, {
+ title: Ext.String.format(gettext("Zone {0} on node {1}"),
+ "'" + sdnid + "'", "'" + nodename + "'"),
+ hstateid: 'sdntab'
+ });
+
+// if (caps.sdn['SDN.Audit']) {
+ me.items.push({
+ xtype: 'pveSDNZoneContentView',
+ title: gettext('Content'),
+ iconCls: 'fa fa-th',
+ itemId: 'content'
+ });
+// }
+
+// if (caps.sdn['Permissions.Modify']) {
+ me.items.push({
+ xtype: 'pveACLView',
+ title: gettext('Permissions'),
+ iconCls: 'fa fa-unlock',
+ itemId: 'permissions',
+ path: '/sdn/zones/' + sdnid
+ });
+// }
+
+ me.callParent();
+ }
+});
diff --git a/www/manager6/sdn/ControllerView.js b/www/manager6/sdn/ControllerView.js
new file mode 100644
index 00000000..68a1bb5b
--- /dev/null
+++ b/www/manager6/sdn/ControllerView.js
@@ -0,0 +1,146 @@
+Ext.define('PVE.sdn.ControllerView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveSDNControllerView'],
+
+ stateful: true,
+ stateId: 'grid-sdn-controller',
+
+ createSDNControllerEditWindow: function(type, sid) {
+ var schema = PVE.Utils.sdncontrollerSchema[type];
+ if (!schema || !schema.ipanel) {
+ throw "no editor registered for controller type: " + type;
+ }
+
+ Ext.create('PVE.sdn.controllers.BaseEdit', {
+ paneltype: 'PVE.sdn.controllers.' + schema.ipanel,
+ type: type,
+ controllerid: sid,
+ autoShow: true,
+ listeners: {
+ destroy: this.reloadStore
+ }
+ });
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-sdn-controller',
+ proxy: {
+ type: 'proxmox',
+ url: "/api2/json/cluster/sdn/controllers"
+ },
+ sorters: {
+ property: 'controller',
+ order: 'DESC'
+ },
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ var type = rec.data.type,
+ controller = rec.data.controller;
+
+ me.createSDNControllerEditWindow(type, controller);
+ };
+
+ var edit_btn = new Proxmox.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', {
+ selModel: sm,
+ baseurl: '/cluster/sdn/controllers/',
+ callback: reload
+ });
+
+ // else we cannot dynamically generate the add menu handlers
+ var addHandleGenerator = function(type) {
+ return function() { me.createSDNControllerEditWindow(type); };
+ };
+ var addMenuItems = [], type;
+ /*jslint forin: true */
+
+ for (type in PVE.Utils.sdncontrollerSchema) {
+ var controller = PVE.Utils.sdncontrollerSchema[type];
+ if (controller.hideAdd) {
+ continue;
+ }
+ addMenuItems.push({
+ text: PVE.Utils.format_sdncontroller_type(type),
+ iconCls: 'fa fa-fw fa-' + controller.faIcon,
+ handler: addHandleGenerator(type)
+ });
+ }
+
+ Ext.apply(me, {
+ store: store,
+ reloadStore: reload,
+ selModel: sm,
+ viewConfig: {
+ trackOver: false
+ },
+ tbar: [
+ {
+ text: gettext('Add'),
+ menu: new Ext.menu.Menu({
+ items: addMenuItems
+ })
+ },
+ remove_btn,
+ edit_btn,
+ {
+ text: gettext('Revert'),
+ handler: function() {
+ Proxmox.Utils.API2Request({
+ url: '/cluster/sdn/controllers/',
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ },
+ ],
+ columns: [
+ {
+ header: 'ID',
+ flex: 2,
+ sortable: true,
+ dataIndex: 'controller'
+ },
+ {
+ header: gettext('Type'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'type',
+ renderer: PVE.Utils.format_sdncontroller_type
+ },
+ ],
+ listeners: {
+ activate: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+});
diff --git a/www/manager6/sdn/Status.js b/www/manager6/sdn/Status.js
new file mode 100644
index 00000000..1fe8155c
--- /dev/null
+++ b/www/manager6/sdn/Status.js
@@ -0,0 +1,36 @@
+Ext.define('PVE.sdn.Status', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pveSDNStatus',
+
+ layout: {
+ type: 'vbox',
+ align: 'stretch'
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ me.rstore = Ext.create('Proxmox.data.ObjectStore', {
+ interval: me.interval,
+ model: 'pve-sdn-status',
+ storeid: 'pve-store-' + (++Ext.idSeed),
+ groupField: 'type',
+ proxy: {
+ type: 'proxmox',
+ url: '/api2/json/cluster/resources'
+ }
+ });
+
+ me.items = [{
+ xtype: 'pveSDNStatusView',
+ title: gettext('Status'),
+ rstore: me.rstore,
+ border: 0,
+ collapsible: true,
+ padding: '0 0 20 0'
+ }];
+
+ me.callParent();
+ me.on('activate', me.rstore.startUpdate);
+ }
+});
diff --git a/www/manager6/sdn/StatusView.js b/www/manager6/sdn/StatusView.js
new file mode 100644
index 00000000..b0fc903e
--- /dev/null
+++ b/www/manager6/sdn/StatusView.js
@@ -0,0 +1,98 @@
+Ext.define('PVE.sdn.StatusView', {
+ extend: 'Ext.grid.GridPanel',
+ alias: ['widget.pveSDNStatusView'],
+
+ sortPriority: {
+ quorum: 1,
+ master: 2,
+ lrm: 3,
+ service: 4
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ if (!me.rstore) {
+ throw "no rstore given";
+ }
+
+ Proxmox.Utils.monStoreErrors(me, me.rstore);
+
+ var store = Ext.create('Proxmox.data.DiffStore', {
+ rstore: me.rstore,
+ sortAfterUpdate: true,
+ sorters: [{
+ sorterFn: function(rec1, rec2) {
+ var p1 = me.sortPriority[rec1.data.type];
+ var p2 = me.sortPriority[rec2.data.type];
+ return (p1 !== p2) ? ((p1 > p2) ? 1 : -1) : 0;
+ }
+ }],
+ filters: {
+ property: 'type',
+ value: 'sdn',
+ operator: '=='
+ }
+ });
+
+ Ext.apply(me, {
+ store: store,
+ stateful: false,
+ tbar: [
+ {
+ text: gettext('Apply'),
+ handler: function() {
+ Proxmox.Utils.API2Request({
+ url: '/cluster/sdn/',
+ method: 'PUT',
+ waitMsgTarget: me,
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ },
+ ],
+ viewConfig: {
+ trackOver: false
+ },
+ columns: [
+ {
+ header: gettext('sdn'),
+ width: 80,
+ dataIndex: 'sdn'
+ },
+ {
+ header: gettext('node'),
+ width: 80,
+ dataIndex: 'node'
+ },
+ {
+ header: gettext('Status'),
+ width: 80,
+ flex: 1,
+ dataIndex: 'status'
+ }
+ ]
+ });
+
+ me.callParent();
+
+ me.on('activate', me.rstore.startUpdate);
+ me.on('destroy', me.rstore.stopUpdate);
+
+ }
+}, function() {
+
+ Ext.define('pve-sdn-status', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'id', 'type', 'node', 'status', 'sid',
+ 'state', 'group', 'comment',
+ 'max_restart', 'max_relocate', 'type',
+ 'crm_state', 'request_state'
+ ],
+ idProperty: 'id'
+ });
+
+});
diff --git a/www/manager6/sdn/VnetEdit.js b/www/manager6/sdn/VnetEdit.js
new file mode 100644
index 00000000..afe89264
--- /dev/null
+++ b/www/manager6/sdn/VnetEdit.js
@@ -0,0 +1,133 @@
+Ext.define('PVE.sdn.VnetInputPanel', {
+ extend: 'Proxmox.panel.InputPanel',
+
+ vnet: undefined,
+
+ onGetValues: function(values) {
+ var me = this;
+
+ if (me.isCreate) {
+ values.type = 'vnet';
+ }
+
+ if (!values.ipv6) {
+ delete values.ipv6;
+ }
+
+ if (!values.ipv4) {
+ delete values.ipv4;
+ }
+
+ if (!values.mac) {
+ delete values.mac;
+ }
+
+ return values;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ me.items = [
+ {
+ xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield',
+ name: 'vnet',
+ value: me.vnet,
+ maxLength: 10,
+ allowBlank: false,
+ fieldLabel: gettext('Name')
+ },
+ {
+ xtype: 'textfield',
+ name: 'alias',
+ fieldLabel: gettext('alias'),
+ allowBlank: true
+ },
+ {
+ xtype: 'pveSDNZoneSelector',
+ fieldLabel: gettext('Zone'),
+ name: 'zone',
+ value: '',
+ allowBlank: false
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'tag',
+ minValue: 1,
+ maxValue: 16000000,
+ fieldLabel: gettext('tag'),
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'ipv4',
+ vtype: 'IPCIDRAddress',
+ fieldLabel: gettext('ipv4'),
+ fieldLabel: 'IPv4/CIDR', // do not localize
+ skipEmptyText: true,
+ allowBlank: true,
+ },
+ {
+ xtype: 'textfield',
+ name: 'ipv6',
+ vtype: 'IP6CIDRAddress',
+ fieldLabel: 'IPv6/CIDR', // do not localize
+ skipEmptyText: true,
+ allowBlank: true,
+ },
+ {
+ xtype: 'textfield',
+ name: 'mac',
+ fieldLabel: gettext('MAC address'),
+ vtype: 'MacAddress',
+ skipEmptyText: true,
+ allowBlank: true,
+ emptyText: 'auto'
+ },
+ ];
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.sdn.VnetEdit', {
+ extend: 'Proxmox.window.Edit',
+
+ vnet: undefined,
+
+ initComponent : function() {
+ var me = this;
+
+ me.isCreate = !me.vnet;
+
+ if (me.isCreate) {
+ me.url = '/api2/extjs/cluster/sdn/vnets';
+ me.method = 'POST';
+ } else {
+ me.url = '/api2/extjs/cluster/sdn/vnets/' + me.vnet;
+ me.method = 'PUT';
+ }
+
+ var ipanel = Ext.create('PVE.sdn.VnetInputPanel', {
+ isCreate: me.isCreate,
+ vnet: me.vnet
+ });
+
+ Ext.apply(me, {
+ subject: gettext('Vnet'),
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.isCreate) {
+ me.load({
+ success: function(response, options) {
+ var values = response.result.data;
+
+ ipanel.setValues(values);
+ }
+ });
+ }
+ }
+});
diff --git a/www/manager6/sdn/VnetView.js b/www/manager6/sdn/VnetView.js
new file mode 100644
index 00000000..8ef25fae
--- /dev/null
+++ b/www/manager6/sdn/VnetView.js
@@ -0,0 +1,159 @@
+Ext.define('PVE.sdn.VnetView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveSDNVnetView'],
+
+ stateful: true,
+ stateId: 'grid-sdn-vnet',
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-sdn-vnet',
+ proxy: {
+ type: 'proxmox',
+ url: "/api2/json/cluster/sdn/vnets"
+ },
+ sorters: {
+ property: 'vnet',
+ order: 'DESC'
+ }
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+
+ var win = Ext.create('PVE.sdn.VnetEdit',{
+ vnet: rec.data.vnet
+ });
+ win.on('destroy', reload);
+ win.show();
+ };
+
+ var edit_btn = new Proxmox.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', {
+ selModel: sm,
+ baseurl: '/cluster/sdn/vnets/',
+ callback: reload
+ });
+
+ Ext.apply(me, {
+ store: store,
+ reloadStore: reload,
+ selModel: sm,
+ viewConfig: {
+ trackOver: false
+ },
+ tbar: [
+ {
+ text: gettext('Create'),
+ handler: function() {
+ var win = Ext.create('PVE.sdn.VnetEdit',{
+ type: 'vnet'
+ });
+ win.on('destroy', reload);
+ win.show();
+ }
+ },
+ remove_btn,
+ edit_btn,
+ {
+ text: gettext('Revert'),
+ handler: function() {
+ Proxmox.Utils.API2Request({
+ url: '/cluster/sdn/vnets/',
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ },
+
+ ],
+ columns: [
+ {
+ header: 'ID',
+ flex: 2,
+ sortable: true,
+ dataIndex: 'vnet'
+ },
+ {
+ header: gettext('alias'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'alias',
+ },
+ {
+ header: gettext('zone'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'zone',
+ },
+ {
+ header: gettext('tag'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'tag',
+ },
+ {
+ header: gettext('ipv4'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'ipv4',
+ },
+ {
+ header: gettext('ipv6'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'ipv6',
+ },
+ {
+ header: gettext('mac'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'mac',
+ },
+ {
+ header: gettext('mtu'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'mtu',
+ },
+ ],
+ listeners: {
+ activate: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+}, function() {
+
+ Ext.define('pve-sdn-vnet', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'type'
+ ],
+ idProperty: 'vnet'
+ });
+
+});
diff --git a/www/manager6/sdn/ZoneContentView.js b/www/manager6/sdn/ZoneContentView.js
new file mode 100644
index 00000000..29f79a17
--- /dev/null
+++ b/www/manager6/sdn/ZoneContentView.js
@@ -0,0 +1,105 @@
+Ext.define('PVE.sdn.ZoneContentView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: 'widget.pveSDNZoneContentView',
+
+ stateful: true,
+ stateId: 'grid-sdnzone-content',
+ viewConfig: {
+ trackOver: false,
+ loadMask: false
+ },
+ features: [
+ {
+ ftype: 'grouping',
+ groupHeaderTpl: '{name} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})'
+ }
+ ],
+ initComponent : function() {
+ var me = this;
+
+ var nodename = me.pveSelNode.data.node;
+ if (!nodename) {
+ throw "no node name specified";
+ }
+
+ var zone = me.pveSelNode.data.sdn;
+ if (!zone) {
+ throw "no zone ID specified";
+ }
+
+ var baseurl = "/nodes/" + nodename + "/sdn/zones/" + zone + "/content";
+ var store = Ext.create('Ext.data.Store',{
+ model: 'pve-sdnzone-content',
+ groupField: 'content',
+ proxy: {
+ type: 'proxmox',
+ url: '/api2/json' + baseurl
+ },
+ sorters: {
+ property: 'vnet',
+ order: 'DESC'
+ }
+ });
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var reload = function() {
+ store.load();
+ };
+
+ Proxmox.Utils.monStoreErrors(me, store);
+
+ Ext.apply(me, {
+ store: store,
+ selModel: sm,
+ tbar: [
+ ],
+ columns: [
+ {
+ header: gettext('Vnet'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'vnet'
+ },
+ {
+ header: gettext('Status'),
+ width: 20,
+ dataIndex: 'status',
+ },
+ {
+ header: gettext('Status details'),
+ width: 20,
+ dataIndex: 'statusmsg',
+ },
+ ],
+ listeners: {
+ activate: reload
+ }
+ });
+
+ me.callParent();
+
+ }
+}, function() {
+
+ Ext.define('pve-sdnzone-content', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'vnet', 'status', 'statusmsg',
+ {
+ name: 'text',
+ convert: function(value, record) {
+ // check for volid, because if you click on a grouping header,
+ // it calls convert (but with an empty volid)
+ if (value || record.data.vnet === null) {
+ return value;
+ }
+ return PVE.Utils.format_sdnvnet_type(value, {}, record);
+ }
+ }
+ ],
+ idProperty: 'vnet'
+ });
+
+});
diff --git a/www/manager6/sdn/ZoneView.js b/www/manager6/sdn/ZoneView.js
new file mode 100644
index 00000000..951a02cb
--- /dev/null
+++ b/www/manager6/sdn/ZoneView.js
@@ -0,0 +1,152 @@
+Ext.define('PVE.sdn.ZoneView', {
+ extend: 'Ext.grid.GridPanel',
+
+ alias: ['widget.pveSDNZoneView'],
+
+ stateful: true,
+ stateId: 'grid-sdn-zone',
+
+ createSDNEditWindow: function(type, sid) {
+ var schema = PVE.Utils.sdnzoneSchema[type];
+ if (!schema || !schema.ipanel) {
+ throw "no editor registered for zone type: " + type;
+ }
+
+ Ext.create('PVE.sdn.zones.BaseEdit', {
+ paneltype: 'PVE.sdn.zones.' + schema.ipanel,
+ type: type,
+ zone: sid,
+ autoShow: true,
+ listeners: {
+ destroy: this.reloadStore
+ }
+ });
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ var store = new Ext.data.Store({
+ model: 'pve-sdn-zone',
+ proxy: {
+ type: 'proxmox',
+ url: "/api2/json/cluster/sdn/zones"
+ },
+ sorters: {
+ property: 'zone',
+ order: 'DESC'
+ },
+ });
+
+ var reload = function() {
+ store.load();
+ };
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var run_editor = function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+ var type = rec.data.type,
+ zone = rec.data.zone;
+
+ me.createSDNEditWindow(type, zone);
+ };
+
+ var edit_btn = new Proxmox.button.Button({
+ text: gettext('Edit'),
+ disabled: true,
+ selModel: sm,
+ handler: run_editor
+ });
+
+ var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', {
+ selModel: sm,
+ baseurl: '/cluster/sdn/zones/',
+ callback: reload
+ });
+
+ // else we cannot dynamically generate the add menu handlers
+ var addHandleGenerator = function(type) {
+ return function() { me.createSDNEditWindow(type); };
+ };
+ var addMenuItems = [], type;
+ /*jslint forin: true */
+
+ for (type in PVE.Utils.sdnzoneSchema) {
+ var zone = PVE.Utils.sdnzoneSchema[type];
+ if (zone.hideAdd) {
+ continue;
+ }
+ addMenuItems.push({
+ text: PVE.Utils.format_sdnzone_type(type),
+ iconCls: 'fa fa-fw fa-' + zone.faIcon,
+ handler: addHandleGenerator(type)
+ });
+ }
+
+ Ext.apply(me, {
+ store: store,
+ reloadStore: reload,
+ selModel: sm,
+ viewConfig: {
+ trackOver: false
+ },
+ tbar: [
+ {
+ text: gettext('Add'),
+ menu: new Ext.menu.Menu({
+ items: addMenuItems
+ })
+ },
+ remove_btn,
+ edit_btn,
+ {
+ text: gettext('Revert'),
+ handler: function() {
+ Proxmox.Utils.API2Request({
+ url: '/cluster/sdn/zones/',
+ method: 'DELETE',
+ waitMsgTarget: me,
+ callback: function() {
+ reload();
+ },
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ }
+ });
+ }
+ },
+ ],
+ columns: [
+ {
+ header: 'ID',
+ flex: 2,
+ sortable: true,
+ dataIndex: 'zone'
+ },
+ {
+ header: gettext('Type'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'type',
+ renderer: PVE.Utils.format_sdnzone_type
+ },
+ {
+ header: gettext('Nodes'),
+ flex: 1,
+ sortable: true,
+ dataIndex: 'nodes',
+ },
+ ],
+ listeners: {
+ activate: reload,
+ itemdblclick: run_editor
+ }
+ });
+
+ me.callParent();
+ }
+});
diff --git a/www/manager6/sdn/controllers/Base.js b/www/manager6/sdn/controllers/Base.js
new file mode 100644
index 00000000..f045acd6
--- /dev/null
+++ b/www/manager6/sdn/controllers/Base.js
@@ -0,0 +1,73 @@
+Ext.define('PVE.panel.SDNControllerBase', {
+ extend: 'Proxmox.panel.InputPanel',
+
+ type: '',
+
+ onGetValues: function(values) {
+ var me = this;
+
+ if (me.isCreate) {
+ values.type = me.type;
+ } else {
+ delete values.controller;
+ }
+
+ return values;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.sdn.controllers.BaseEdit', {
+ extend: 'Proxmox.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ me.isCreate = !me.controllerid;
+
+ if (me.isCreate) {
+ me.url = '/api2/extjs/cluster/sdn/controllers';
+ me.method = 'POST';
+ } else {
+ me.url = '/api2/extjs/cluster/sdn/controllers/' + me.controllerid;
+ me.method = 'PUT';
+ }
+
+ var ipanel = Ext.create(me.paneltype, {
+ type: me.type,
+ isCreate: me.isCreate,
+ controllerid: me.controllerid
+ });
+
+ Ext.apply(me, {
+ subject: PVE.Utils.format_sdncontroller_type(me.type),
+ isAdd: true,
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.isCreate) {
+ me.load({
+ success: function(response, options) {
+ var values = response.result.data;
+ var ctypes = values.content || '';
+
+ values.content = ctypes.split(',');
+
+ if (values.nodes) {
+ values.nodes = values.nodes.split(',');
+ }
+ values.enable = values.disable ? 0 : 1;
+
+ ipanel.setValues(values);
+ }
+ });
+ }
+ }
+});
diff --git a/www/manager6/sdn/controllers/EvpnEdit.js b/www/manager6/sdn/controllers/EvpnEdit.js
new file mode 100644
index 00000000..8264bc75
--- /dev/null
+++ b/www/manager6/sdn/controllers/EvpnEdit.js
@@ -0,0 +1,48 @@
+Ext.define('PVE.sdn.controllers.EvpnInputPanel', {
+ extend: 'PVE.panel.SDNControllerBase',
+
+ initComponent : function() {
+ var me = this;
+
+ me.items = [
+ {
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'controller',
+ maxLength: 10,
+ value: me.controllerid || '',
+ fieldLabel: 'ID',
+ allowBlank: false
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'asn',
+ minValue: 1,
+ maxValue: 4294967295,
+ value: 65000,
+ fieldLabel: gettext('asn'),
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'peers',
+ fieldLabel: gettext('peers'),
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'gateway-external-peers',
+ fieldLabel: gettext('gateway-external-peers'),
+ allowBlank: true
+ },
+ {
+ xtype: 'pveNodeSelector',
+ name: 'gateway-nodes',
+ fieldLabel: gettext('Gateway nodes'),
+ multiSelect: true,
+ autoSelect: false
+ },
+ ];
+
+ me.callParent();
+ }
+});
diff --git a/www/manager6/sdn/zones/Base.js b/www/manager6/sdn/zones/Base.js
new file mode 100644
index 00000000..c4bdc72c
--- /dev/null
+++ b/www/manager6/sdn/zones/Base.js
@@ -0,0 +1,73 @@
+Ext.define('PVE.panel.SDNZoneBase', {
+ extend: 'Proxmox.panel.InputPanel',
+
+ type: '',
+
+ onGetValues: function(values) {
+ var me = this;
+
+ if (me.isCreate) {
+ values.type = me.type;
+ } else {
+ delete values.zone;
+ }
+
+ return values;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ me.callParent();
+ }
+});
+
+Ext.define('PVE.sdn.zones.BaseEdit', {
+ extend: 'Proxmox.window.Edit',
+
+ initComponent : function() {
+ var me = this;
+
+ me.isCreate = !me.zone;
+
+ if (me.isCreate) {
+ me.url = '/api2/extjs/cluster/sdn/zones';
+ me.method = 'POST';
+ } else {
+ me.url = '/api2/extjs/cluster/sdn/zones/' + me.zone;
+ me.method = 'PUT';
+ }
+
+ var ipanel = Ext.create(me.paneltype, {
+ type: me.type,
+ isCreate: me.isCreate,
+ zone: me.zone
+ });
+
+ Ext.apply(me, {
+ subject: PVE.Utils.format_sdnzone_type(me.type),
+ isAdd: true,
+ items: [ ipanel ]
+ });
+
+ me.callParent();
+
+ if (!me.isCreate) {
+ me.load({
+ success: function(response, options) {
+ var values = response.result.data;
+ var ctypes = values.content || '';
+
+ values.content = ctypes.split(',');
+
+ if (values.nodes) {
+ values.nodes = values.nodes.split(',');
+ }
+ values.enable = values.disable ? 0 : 1;
+
+ ipanel.setValues(values);
+ }
+ });
+ }
+ }
+});
diff --git a/www/manager6/sdn/zones/EvpnEdit.js b/www/manager6/sdn/zones/EvpnEdit.js
new file mode 100644
index 00000000..6e08138d
--- /dev/null
+++ b/www/manager6/sdn/zones/EvpnEdit.js
@@ -0,0 +1,66 @@
+Ext.define('PVE.sdn.zones.EvpnInputPanel', {
+ extend: 'PVE.panel.SDNZoneBase',
+
+ onGetValues: function(values) {
+ var me = this;
+
+ if (me.isCreate) {
+ values.type = me.type;
+ } else {
+ delete values.zone;
+ }
+
+ return values;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ me.items = [
+ {
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'zone',
+ maxLength: 10,
+ value: me.zone || '',
+ fieldLabel: 'ID',
+ allowBlank: false
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'vrf-vxlan',
+ minValue: 1,
+ maxValue: 16000000,
+ fieldLabel: gettext('vrf vxlan tag'),
+ allowBlank: false
+ },
+ {
+ xtype: 'pveSDNControllerSelector',
+ fieldLabel: gettext('Controller'),
+ name: 'controller',
+ value: '',
+ allowBlank: false
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'mtu',
+ minValue: 100,
+ maxValue: 65000,
+ fieldLabel: gettext('mtu'),
+ skipEmptyText: true,
+ allowBlank: true,
+ emptyText: 'auto'
+ },
+ {
+ xtype: 'pveNodeSelector',
+ name: 'nodes',
+ fieldLabel: gettext('Nodes'),
+ emptyText: gettext('All') + ' (' + gettext('No restrictions') +')',
+ multiSelect: true,
+ autoSelect: false
+ },
+
+ ];
+
+ me.callParent();
+ }
+});
diff --git a/www/manager6/sdn/zones/QinQEdit.js b/www/manager6/sdn/zones/QinQEdit.js
new file mode 100644
index 00000000..0d05b17e
--- /dev/null
+++ b/www/manager6/sdn/zones/QinQEdit.js
@@ -0,0 +1,63 @@
+Ext.define('PVE.sdn.zones.QinQInputPanel', {
+ extend: 'PVE.panel.SDNZoneBase',
+
+ onGetValues: function(values) {
+ var me = this;
+
+ if (me.isCreate) {
+ values.type = me.type;
+ } else {
+ delete values.sdn;
+ }
+
+ return values;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ me.items = [
+ {
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'zone',
+ maxLength: 10,
+ value: me.zone || '',
+ fieldLabel: 'ID',
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'bridge',
+ fieldLabel: gettext('bridge'),
+ allowBlank: false,
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'tag',
+ fieldLabel: gettext('Service vlan'),
+ allowBlank: false
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'mtu',
+ minValue: 100,
+ maxValue: 65000,
+ fieldLabel: gettext('mtu'),
+ skipEmptyText: true,
+ allowBlank: true,
+ emptyText: 'auto'
+ },
+ {
+ xtype: 'pveNodeSelector',
+ name: 'nodes',
+ fieldLabel: gettext('Nodes'),
+ emptyText: gettext('All') + ' (' + gettext('No restrictions') +')',
+ multiSelect: true,
+ autoSelect: false
+ },
+
+ ];
+
+ me.callParent();
+ }
+});
diff --git a/www/manager6/sdn/zones/VlanEdit.js b/www/manager6/sdn/zones/VlanEdit.js
new file mode 100644
index 00000000..ca1b9d30
--- /dev/null
+++ b/www/manager6/sdn/zones/VlanEdit.js
@@ -0,0 +1,47 @@
+Ext.define('PVE.sdn.zones.VlanInputPanel', {
+ extend: 'PVE.panel.SDNZoneBase',
+
+ onGetValues: function(values) {
+ var me = this;
+
+ if (me.isCreate) {
+ values.type = me.type;
+ } else {
+ delete values.zone;
+ }
+
+ return values;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ me.items = [
+ {
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'zone',
+ maxLength: 10,
+ value: me.zone || '',
+ fieldLabel: 'ID',
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'bridge',
+ fieldLabel: gettext('bridge'),
+ allowBlank: false,
+ },
+ {
+ xtype: 'pveNodeSelector',
+ name: 'nodes',
+ fieldLabel: gettext('Nodes'),
+ emptyText: gettext('All') + ' (' + gettext('No restrictions') +')',
+ multiSelect: true,
+ autoSelect: false
+ },
+
+ ];
+
+ me.callParent();
+ }
+});
diff --git a/www/manager6/sdn/zones/VxlanEdit.js b/www/manager6/sdn/zones/VxlanEdit.js
new file mode 100644
index 00000000..72721226
--- /dev/null
+++ b/www/manager6/sdn/zones/VxlanEdit.js
@@ -0,0 +1,59 @@
+Ext.define('PVE.sdn.zones.VxlanInputPanel', {
+ extend: 'PVE.panel.SDNZoneBase',
+
+ onGetValues: function(values) {
+ var me = this;
+
+ if (me.isCreate) {
+ values.type = me.type;
+ } else {
+ delete values.zone;
+ }
+
+ delete values.mode;
+
+ return values;
+ },
+
+ initComponent : function() {
+ var me = this;
+
+ me.items = [
+ {
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ maxLength: 10,
+ name: 'zone',
+ value: me.zone || '',
+ fieldLabel: 'ID',
+ allowBlank: false
+ },
+ {
+ xtype: 'textfield',
+ name: 'peers',
+ fieldLabel: gettext('peers address list'),
+ allowBlank: false,
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'mtu',
+ minValue: 100,
+ maxValue: 65000,
+ fieldLabel: gettext('mtu'),
+ skipEmptyText: true,
+ allowBlank: true,
+ emptyText: 'auto'
+ },
+ {
+ xtype: 'pveNodeSelector',
+ name: 'nodes',
+ fieldLabel: gettext('Nodes'),
+ emptyText: gettext('All') + ' (' + gettext('No restrictions') +')',
+ multiSelect: true,
+ autoSelect: false
+ },
+
+ ];
+
+ me.callParent();
+ }
+});
diff --git a/www/manager6/tree/ResourceTree.js b/www/manager6/tree/ResourceTree.js
index 251eb542..5adb864b 100644
--- a/www/manager6/tree/ResourceTree.js
+++ b/www/manager6/tree/ResourceTree.js
@@ -19,6 +19,10 @@ Ext.define('PVE.tree.ResourceTree', {
iconCls: 'fa fa-database',
text: gettext('Storage')
},
+ sdn: {
+ iconCls: 'fa fa-database',
+ text: gettext('Sdn')
+ },
qemu: {
iconCls: 'fa fa-desktop',
text: gettext('Virtual Machine')
--
2.20.1
More information about the pve-devel
mailing list