mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-08-05 10:58:09 +00:00

If a snapshot of a running VM is taken and the RAM is not included, the backend checks whether the QEMU Guest Agent is running inside the VM. If it is, it freezes the file system, otherwise the snapshot is taken without freezing, which could lead to an inconsistent file system state in the snapshot. Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
182 lines
4.0 KiB
JavaScript
182 lines
4.0 KiB
JavaScript
Ext.define('PVE.window.Snapshot', {
|
|
extend: 'Proxmox.window.Edit',
|
|
|
|
viewModel: {
|
|
data: {
|
|
type: undefined,
|
|
isCreate: undefined,
|
|
running: false,
|
|
guestAgentEnabled: false,
|
|
},
|
|
formulas: {
|
|
runningWithoutGuestAgent: (get) => get('type') === 'qemu' && get('running') && !get('guestAgentEnabled'),
|
|
shouldWarnAboutFS: (get) => get('isCreate') && get('runningWithoutGuestAgent') && get('!vmstate.checked'),
|
|
},
|
|
},
|
|
|
|
onGetValues: function(values) {
|
|
let me = this;
|
|
|
|
if (me.type === 'lxc') {
|
|
delete values.vmstate;
|
|
}
|
|
|
|
return values;
|
|
},
|
|
|
|
initComponent : function() {
|
|
var me = this;
|
|
var vm = me.getViewModel();
|
|
|
|
if (!me.nodename) {
|
|
throw "no node name specified";
|
|
}
|
|
|
|
if (!me.vmid) {
|
|
throw "no VM ID specified";
|
|
}
|
|
|
|
if (!me.type) {
|
|
throw "no type specified";
|
|
}
|
|
|
|
vm.set('type', me.type);
|
|
vm.set('running', me.running);
|
|
vm.set('isCreate', me.isCreate);
|
|
|
|
if (me.type === 'qemu' && me.isCreate) {
|
|
Proxmox.Utils.API2Request({
|
|
url: `/nodes/${me.nodename}/${me.type}/${me.vmid}/config`,
|
|
params: { 'current': '1' },
|
|
method: 'GET',
|
|
success: function(response, options) {
|
|
let res = response.result.data;
|
|
let enabled = PVE.Parser.parsePropertyString(res.agent, 'enabled');
|
|
vm.set('guestAgentEnabled', !!PVE.Parser.parseBoolean(enabled.enabled));
|
|
}
|
|
});
|
|
}
|
|
|
|
me.items = [
|
|
{
|
|
xtype: 'displayfield',
|
|
userCls: 'pmx-hint',
|
|
name: 'fswarning',
|
|
hidden: true,
|
|
value: gettext('It is recommended to either include the RAM or enable the QEMU Guest Agent when taking a snapshot of a running VM. Otherwise the file system might be in an inconsistent state when the snapshot is taken.'),
|
|
bind: {
|
|
hidden: '{!shouldWarnAboutFS}',
|
|
},
|
|
},
|
|
{
|
|
xtype: me.isCreate ? 'textfield' : 'displayfield',
|
|
name: 'snapname',
|
|
value: me.snapname,
|
|
fieldLabel: gettext('Name'),
|
|
vtype: 'ConfigId',
|
|
allowBlank: false
|
|
},
|
|
{
|
|
xtype: 'displayfield',
|
|
hidden: me.isCreate,
|
|
disabled: me.isCreate,
|
|
name: 'snaptime',
|
|
renderer: PVE.Utils.render_timestamp_human_readable,
|
|
fieldLabel: gettext('Timestamp')
|
|
},
|
|
{
|
|
xtype: 'proxmoxcheckbox',
|
|
hidden: me.type !== 'qemu' || !me.isCreate || !me.running,
|
|
disabled: me.type !== 'qemu' || !me.isCreate || !me.running,
|
|
name: 'vmstate',
|
|
reference: 'vmstate',
|
|
uncheckedValue: 0,
|
|
defaultValue: 0,
|
|
checked: 1,
|
|
fieldLabel: gettext('Include RAM')
|
|
},
|
|
{
|
|
xtype: 'textareafield',
|
|
grow: true,
|
|
editable: !me.viewonly,
|
|
name: 'description',
|
|
fieldLabel: gettext('Description')
|
|
},
|
|
{
|
|
title: gettext('Settings'),
|
|
hidden: me.isCreate,
|
|
xtype: 'grid',
|
|
itemId: 'summary',
|
|
border: true,
|
|
height: 200,
|
|
store: {
|
|
model: 'KeyValue',
|
|
sorters: [
|
|
{
|
|
property : 'key',
|
|
direction: 'ASC'
|
|
}
|
|
]
|
|
},
|
|
columns: [
|
|
{
|
|
header: gettext('Key'),
|
|
width: 150,
|
|
dataIndex: 'key',
|
|
},
|
|
{
|
|
header: gettext('Value'),
|
|
flex: 1,
|
|
dataIndex: 'value',
|
|
}
|
|
]
|
|
}
|
|
];
|
|
|
|
me.url = `/nodes/${me.nodename}/${me.type}/${me.vmid}/snapshot`;
|
|
|
|
let subject;
|
|
if (me.isCreate) {
|
|
subject = (me.type === 'qemu' ? 'VM' : 'CT') + me.vmid + ' ' + gettext('Snapshot');
|
|
me.method = 'POST';
|
|
me.showProgress = true;
|
|
} else {
|
|
subject = `${gettext('Snapshot')} ${me.snapname}`;
|
|
me.url += `/${me.snapname}/config`;
|
|
}
|
|
|
|
Ext.apply(me, {
|
|
subject: subject,
|
|
width: me.isCreate ? 450 : 620,
|
|
height: me.isCreate ? undefined : 420,
|
|
});
|
|
|
|
me.callParent();
|
|
|
|
if (!me.snapname) {
|
|
return;
|
|
}
|
|
|
|
me.load({
|
|
success: function(response) {
|
|
let kvarray = [];
|
|
Ext.Object.each(response.result.data, function(key, value) {
|
|
if (key === 'description' || key === 'snaptime') {
|
|
return;
|
|
}
|
|
kvarray.push({ key: key, value: value });
|
|
});
|
|
|
|
let summarystore = me.down('#summary').getStore();
|
|
summarystore.suspendEvents();
|
|
summarystore.add(kvarray);
|
|
summarystore.sort();
|
|
summarystore.resumeEvents();
|
|
summarystore.fireEvent('refresh', summarystore);
|
|
|
|
me.setValues(response.result.data);
|
|
}
|
|
});
|
|
}
|
|
});
|