pve-manager/www/manager6/lxc/Network.js
Daniel Tschlatscher 4e192f9df8 fix #3719: gui: expose LXC MTU option in web UI
The option to set the mtu parameter for lxc containers already exists
in the backend. It just has to be exposed in the web UI as well.

Signed-off-by: Daniel Tschlatscher <d.tschlatscher@proxmox.com>
Reviewed-by:  Stefan Hanreich <s.hanreich@proxmox.com>
Tested-by:  Stefan Hanreich <s.hanreich@proxmox.com>
2022-11-16 20:07:22 +01:00

568 lines
12 KiB
JavaScript

Ext.define('PVE.lxc.NetworkInputPanel', {
extend: 'Proxmox.panel.InputPanel',
alias: 'widget.pveLxcNetworkInputPanel',
insideWizard: false,
onlineHelp: 'pct_container_network',
setNodename: function(nodename) {
let me = this;
if (!nodename || me.nodename === nodename) {
return;
}
me.nodename = nodename;
let bridgeSelector = me.query("[isFormField][name=bridge]")[0];
bridgeSelector.setNodename(nodename);
},
onGetValues: function(values) {
let me = this;
let id;
if (me.isCreate) {
id = values.id;
delete values.id;
} else {
id = me.ifname;
}
let newdata = {};
if (id) {
if (values.ipv6mode !== 'static') {
values.ip6 = values.ipv6mode;
}
if (values.ipv4mode !== 'static') {
values.ip = values.ipv4mode;
}
newdata[id] = PVE.Parser.printLxcNetwork(values);
}
return newdata;
},
initComponent: function() {
let me = this;
let cdata = {};
if (me.insideWizard) {
me.ifname = 'net0';
cdata.name = 'eth0';
me.dataCache = {};
}
cdata.firewall = me.insideWizard || me.isCreate;
if (!me.dataCache) {
throw "no dataCache specified";
}
if (!me.isCreate) {
if (!me.ifname) {
throw "no interface name specified";
}
if (!me.dataCache[me.ifname]) {
throw "no such interface '" + me.ifname + "'";
}
cdata = PVE.Parser.parseLxcNetwork(me.dataCache[me.ifname]);
}
for (let i = 0; i < 32; i++) {
let ifname = 'net' + i.toString();
if (me.isCreate && !me.dataCache[ifname]) {
me.ifname = ifname;
break;
}
}
me.column1 = [
{
xtype: 'hidden',
name: 'id',
value: me.ifname,
},
{
xtype: 'textfield',
name: 'name',
fieldLabel: gettext('Name'),
emptyText: '(e.g., eth0)',
allowBlank: false,
value: cdata.name,
validator: function(value) {
for (const [key, netRaw] of Object.entries(me.dataCache)) {
if (!key.match(/^net\d+/) || key === me.ifname) {
continue;
}
let net = PVE.Parser.parseLxcNetwork(netRaw);
if (net.name === value) {
return "interface name already in use";
}
}
return true;
},
},
{
xtype: 'textfield',
name: 'hwaddr',
fieldLabel: gettext('MAC address'),
vtype: 'MacAddress',
value: cdata.hwaddr,
allowBlank: true,
emptyText: 'auto',
},
{
xtype: 'PVE.form.BridgeSelector',
name: 'bridge',
nodename: me.nodename,
fieldLabel: gettext('Bridge'),
value: cdata.bridge,
allowBlank: false,
},
{
xtype: 'pveVlanField',
name: 'tag',
value: cdata.tag,
},
{
xtype: 'numberfield',
name: 'rate',
fieldLabel: gettext('Rate limit') + ' (MB/s)',
minValue: 0,
maxValue: 10*1024,
value: cdata.rate,
emptyText: 'unlimited',
allowBlank: true,
},
{
xtype: 'proxmoxcheckbox',
fieldLabel: gettext('Firewall'),
name: 'firewall',
value: cdata.firewall,
},
];
let dhcp4 = cdata.ip === 'dhcp';
if (dhcp4) {
cdata.ip = '';
cdata.gw = '';
}
let auto6 = cdata.ip6 === 'auto';
let dhcp6 = cdata.ip6 === 'dhcp';
if (auto6 || dhcp6) {
cdata.ip6 = '';
cdata.gw6 = '';
}
me.column2 = [
{
layout: {
type: 'hbox',
align: 'middle',
},
border: false,
margin: '0 0 5 0',
items: [
{
xtype: 'label',
text: 'IPv4:', // do not localize
},
{
xtype: 'radiofield',
boxLabel: gettext('Static'),
name: 'ipv4mode',
inputValue: 'static',
checked: !dhcp4,
margin: '0 0 0 10',
listeners: {
change: function(cb, value) {
me.down('field[name=ip]').setEmptyText(
value ? Proxmox.Utils.NoneText : "",
);
me.down('field[name=ip]').setDisabled(!value);
me.down('field[name=gw]').setDisabled(!value);
},
},
},
{
xtype: 'radiofield',
boxLabel: 'DHCP', // do not localize
name: 'ipv4mode',
inputValue: 'dhcp',
checked: dhcp4,
margin: '0 0 0 10',
},
],
},
{
xtype: 'textfield',
name: 'ip',
vtype: 'IPCIDRAddress',
value: cdata.ip,
emptyText: dhcp4 ? '' : Proxmox.Utils.NoneText,
disabled: dhcp4,
fieldLabel: 'IPv4/CIDR', // do not localize
},
{
xtype: 'textfield',
name: 'gw',
value: cdata.gw,
vtype: 'IPAddress',
disabled: dhcp4,
fieldLabel: gettext('Gateway') + ' (IPv4)',
margin: '0 0 3 0', // override bottom margin to account for the menuseparator
},
{
xtype: 'menuseparator',
height: '3',
margin: '0',
},
{
layout: {
type: 'hbox',
align: 'middle',
},
border: false,
margin: '0 0 5 0',
items: [
{
xtype: 'label',
text: 'IPv6:', // do not localize
},
{
xtype: 'radiofield',
boxLabel: gettext('Static'),
name: 'ipv6mode',
inputValue: 'static',
checked: !(auto6 || dhcp6),
margin: '0 0 0 10',
listeners: {
change: function(cb, value) {
me.down('field[name=ip6]').setEmptyText(
value ? Proxmox.Utils.NoneText : "",
);
me.down('field[name=ip6]').setDisabled(!value);
me.down('field[name=gw6]').setDisabled(!value);
},
},
},
{
xtype: 'radiofield',
boxLabel: 'DHCP', // do not localize
name: 'ipv6mode',
inputValue: 'dhcp',
checked: dhcp6,
margin: '0 0 0 10',
},
{
xtype: 'radiofield',
boxLabel: 'SLAAC', // do not localize
name: 'ipv6mode',
inputValue: 'auto',
checked: auto6,
margin: '0 0 0 10',
},
],
},
{
xtype: 'textfield',
name: 'ip6',
value: cdata.ip6,
emptyText: dhcp6 || auto6 ? '' : Proxmox.Utils.NoneText,
vtype: 'IP6CIDRAddress',
disabled: dhcp6 || auto6,
fieldLabel: 'IPv6/CIDR', // do not localize
},
{
xtype: 'textfield',
name: 'gw6',
vtype: 'IP6Address',
value: cdata.gw6,
disabled: dhcp6 || auto6,
fieldLabel: gettext('Gateway') + ' (IPv6)',
},
];
me.advancedColumn1 = [
{
xtype: 'proxmoxintegerfield',
fieldLabel: 'MTU',
emptyText: gettext('Same as bridge'),
name: 'mtu',
value: cdata.mtu,
minValue: 576,
maxValue: 65535,
},
];
me.callParent();
},
});
Ext.define('PVE.lxc.NetworkEdit', {
extend: 'Proxmox.window.Edit',
isAdd: true,
initComponent: function() {
let me = this;
if (!me.dataCache) {
throw "no dataCache specified";
}
if (!me.nodename) {
throw "no node name specified";
}
Ext.apply(me, {
subject: gettext('Network Device') + ' (veth)',
digest: me.dataCache.digest,
items: [
{
xtype: 'pveLxcNetworkInputPanel',
ifname: me.ifname,
nodename: me.nodename,
dataCache: me.dataCache,
isCreate: me.isCreate,
},
],
});
me.callParent();
},
});
Ext.define('PVE.lxc.NetworkView', {
extend: 'Ext.grid.GridPanel',
alias: 'widget.pveLxcNetworkView',
onlineHelp: 'pct_container_network',
dataCache: {}, // used to store result of last load
stateful: true,
stateId: 'grid-lxc-network',
load: function() {
let me = this;
Proxmox.Utils.setErrorMask(me, true);
Proxmox.Utils.API2Request({
url: me.url,
failure: function(response, opts) {
Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus);
},
success: function(response, opts) {
Proxmox.Utils.setErrorMask(me, false);
let result = Ext.decode(response.responseText);
me.dataCache = result.data || {};
let records = [];
for (const [key, value] of Object.entries(me.dataCache)) {
if (key.match(/^net\d+/)) {
let net = PVE.Parser.parseLxcNetwork(value);
net.id = key;
records.push(net);
}
}
me.store.loadData(records);
me.down('button[name=addButton]').setDisabled(records.length >= 32);
},
});
},
initComponent: function() {
let me = this;
let nodename = me.pveSelNode.data.node;
if (!nodename) {
throw "no node name specified";
}
let vmid = me.pveSelNode.data.vmid;
if (!vmid) {
throw "no VM ID specified";
}
let caps = Ext.state.Manager.get('GuiCap');
me.url = `/nodes/${nodename}/lxc/${vmid}/config`;
let store = new Ext.data.Store({
model: 'pve-lxc-network',
sorters: [
{
property: 'id',
direction: 'ASC',
},
],
});
let sm = Ext.create('Ext.selection.RowModel', {});
let run_editor = function() {
let rec = sm.getSelection()[0];
if (!rec || !caps.vms['VM.Config.Network']) {
return false; // disable default-propagation when triggered by grid dblclick
}
Ext.create('PVE.lxc.NetworkEdit', {
url: me.url,
nodename: nodename,
dataCache: me.dataCache,
ifname: rec.data.id,
listeners: {
destroy: () => me.load(),
},
autoShow: true,
});
return undefined; // make eslint happier
};
Ext.apply(me, {
store: store,
selModel: sm,
tbar: [
{
text: gettext('Add'),
name: 'addButton',
disabled: !caps.vms['VM.Config.Network'],
handler: function() {
Ext.create('PVE.lxc.NetworkEdit', {
url: me.url,
nodename: nodename,
isCreate: true,
dataCache: me.dataCache,
listeners: {
destroy: () => me.load(),
},
autoShow: true,
});
},
},
{
xtype: 'proxmoxButton',
text: gettext('Remove'),
disabled: true,
selModel: sm,
enableFn: function(rec) {
return !!caps.vms['VM.Config.Network'];
},
confirmMsg: ({ data }) =>
Ext.String.format(gettext('Are you sure you want to remove entry {0}'), `'${data.id}'`),
handler: function(btn, e, rec) {
Proxmox.Utils.API2Request({
url: me.url,
waitMsgTarget: me,
method: 'PUT',
params: {
'delete': rec.data.id,
digest: me.dataCache.digest,
},
callback: () => me.load(),
failure: (response, opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
});
},
},
{
xtype: 'proxmoxButton',
text: gettext('Edit'),
selModel: sm,
disabled: true,
enableFn: rec => !!caps.vms['VM.Config.Network'],
handler: run_editor,
},
],
columns: [
{
header: 'ID',
width: 50,
dataIndex: 'id',
},
{
header: gettext('Name'),
width: 80,
dataIndex: 'name',
},
{
header: gettext('Bridge'),
width: 80,
dataIndex: 'bridge',
},
{
header: gettext('Firewall'),
width: 80,
dataIndex: 'firewall',
renderer: Proxmox.Utils.format_boolean,
},
{
header: gettext('VLAN Tag'),
width: 80,
dataIndex: 'tag',
},
{
header: gettext('MAC address'),
width: 110,
dataIndex: 'hwaddr',
},
{
header: gettext('IP address'),
width: 150,
dataIndex: 'ip',
renderer: function(value, metaData, rec) {
if (rec.data.ip && rec.data.ip6) {
return rec.data.ip + "<br>" + rec.data.ip6;
} else if (rec.data.ip6) {
return rec.data.ip6;
} else {
return rec.data.ip;
}
},
},
{
header: gettext('Gateway'),
width: 150,
dataIndex: 'gw',
renderer: function(value, metaData, rec) {
if (rec.data.gw && rec.data.gw6) {
return rec.data.gw + "<br>" + rec.data.gw6;
} else if (rec.data.gw6) {
return rec.data.gw6;
} else {
return rec.data.gw;
}
},
},
{
header: gettext('MTU'),
width: 80,
dataIndex: 'mtu',
},
],
listeners: {
activate: me.load,
itemdblclick: run_editor,
},
});
me.callParent();
},
}, function() {
Ext.define('pve-lxc-network', {
extend: "Ext.data.Model",
proxy: { type: 'memory' },
fields: [
'id',
'name',
'hwaddr',
'bridge',
'ip',
'gw',
'ip6',
'gw6',
'tag',
'firewall',
'mtu',
],
});
});