]>
Commit | Line | Data |
---|---|---|
3bde324f | 1 | Ext.define('PVE.window.GuestDiskReassign', { |
a8d854af AL |
2 | extend: 'Proxmox.window.Edit', |
3 | mixins: ['Proxmox.Mixin.CBind'], | |
4 | ||
5 | resizable: false, | |
6 | modal: true, | |
7 | width: 350, | |
8 | border: false, | |
9 | layout: 'fit', | |
10 | showReset: false, | |
11 | showProgress: true, | |
12 | method: 'POST', | |
13 | ||
14 | viewModel: { | |
15 | data: { | |
16 | mpType: '', | |
17 | }, | |
18 | formulas: { | |
19 | mpMaxCount: get => get('mpType') === 'mp' | |
20 | ? PVE.Utils.mp_counts.mps - 1 | |
21 | : PVE.Utils.mp_counts.unused - 1, | |
22 | }, | |
23 | }, | |
24 | ||
25 | cbindData: function() { | |
26 | let me = this; | |
27 | return { | |
28 | vmid: me.vmid, | |
29 | disk: me.disk, | |
30 | isQemu: me.type === 'qemu', | |
31 | nodename: me.nodename, | |
32 | url: () => { | |
33 | let endpoint = me.type === 'qemu' ? 'move_disk' : 'move_volume'; | |
34 | return `/nodes/${me.nodename}/${me.type}/${me.vmid}/${endpoint}`; | |
35 | }, | |
36 | }; | |
37 | }, | |
38 | ||
39 | cbind: { | |
d72ad78b | 40 | title: get => get('isQemu') ? gettext('Reassign Disk') : gettext('Reassign Volume'), |
a8d854af AL |
41 | submitText: get => get('title'), |
42 | qemu: '{isQemu}', | |
43 | url: '{url}', | |
44 | }, | |
45 | ||
46 | getValues: function() { | |
47 | let me = this; | |
48 | let values = me.formPanel.getForm().getValues(); | |
49 | ||
50 | let params = { | |
51 | vmid: me.vmid, | |
52 | 'target-vmid': values.targetVmid, | |
53 | }; | |
54 | ||
55 | params[me.qemu ? 'disk' : 'volume'] = me.disk; | |
56 | ||
57 | if (me.qemu) { | |
58 | params['target-disk'] = `${values.controller}${values.deviceid}`; | |
59 | } else { | |
60 | params['target-volume'] = `${values.mpType}${values.mpId}`; | |
61 | } | |
62 | return params; | |
63 | }, | |
64 | ||
65 | controller: { | |
66 | xclass: 'Ext.app.ViewController', | |
67 | ||
68 | initViewModel: function(model) { | |
69 | let view = this.getView(); | |
70 | let mpTypeValue = view.disk.match(/^unused\d+/) ? 'unused' : 'mp'; | |
71 | model.set('mpType', mpTypeValue); | |
72 | }, | |
73 | ||
74 | onMpTypeChange: function(value) { | |
6c067839 TL |
75 | let view = this.getView(); |
76 | view.getViewModel().set('mpType', value.getValue()); | |
77 | view.lookup('mpIdSelector').validate(); | |
a8d854af AL |
78 | }, |
79 | ||
80 | onTargetVMChange: function(f, vmid) { | |
81 | let me = this; | |
82 | let view = me.getView(); | |
83 | let diskSelector = view.lookup('diskSelector'); | |
84 | if (!vmid) { | |
85 | diskSelector.setVMConfig(null); | |
86 | me.VMConfig = null; | |
87 | return; | |
88 | } | |
89 | ||
6c067839 | 90 | let url = `/nodes/${view.nodename}/${view.type}/${vmid}/config`; |
a8d854af AL |
91 | Proxmox.Utils.API2Request({ |
92 | url: url, | |
93 | method: 'GET', | |
94 | failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus), | |
6c067839 | 95 | success: function({ result }, options) { |
a8d854af | 96 | if (view.qemu) { |
6c067839 | 97 | diskSelector.setVMConfig(result.data); |
a8d854af AL |
98 | diskSelector.setDisabled(false); |
99 | } else { | |
100 | let mpIdSelector = view.lookup('mpIdSelector'); | |
101 | let mpType = view.lookup('mpType'); | |
102 | ||
6c067839 | 103 | view.VMConfig = result.data; |
a8d854af AL |
104 | |
105 | mpIdSelector.setValue( | |
106 | PVE.Utils.nextFreeMP( | |
107 | view.getViewModel().get('mpType'), | |
108 | view.VMConfig, | |
109 | ).id, | |
110 | ); | |
111 | ||
112 | mpType.setDisabled(false); | |
113 | mpIdSelector.setDisabled(false); | |
114 | mpIdSelector.validate(); | |
115 | } | |
116 | }, | |
117 | }); | |
118 | }, | |
119 | }, | |
120 | ||
2281ca32 | 121 | defaultFocus: 'sourceDisk', |
a8d854af AL |
122 | items: [ |
123 | { | |
14ea602f TL |
124 | xtype: 'displayfield', |
125 | name: 'sourceDisk', | |
126 | fieldLabel: gettext('Source'), | |
127 | cbind: { | |
128 | name: get => get('isQemu') ? 'disk' : 'volume', | |
129 | value: '{disk}', | |
130 | }, | |
131 | allowBlank: false, | |
132 | }, | |
133 | { | |
134 | xtype: 'vmComboSelector', | |
14ea602f TL |
135 | name: 'targetVmid', |
136 | allowBlank: false, | |
d72ad78b | 137 | fieldLabel: gettext('Target Guest'), |
14ea602f TL |
138 | store: { |
139 | model: 'PVEResources', | |
140 | autoLoad: true, | |
141 | sorters: 'vmid', | |
142 | cbind: {}, // for nested cbinds | |
143 | filters: [ | |
144 | { | |
145 | property: 'type', | |
6c067839 | 146 | cbind: { value: '{type}' }, |
14ea602f TL |
147 | }, |
148 | { | |
149 | property: 'node', | |
6c067839 | 150 | cbind: { value: '{nodename}' }, |
14ea602f | 151 | }, |
6c067839 | 152 | // FIXME: remove, artificial restriction that doesn't gains us anything.. |
14ea602f TL |
153 | { |
154 | property: 'vmid', | |
155 | operator: '!=', | |
6c067839 | 156 | cbind: { value: '{vmid}' }, |
14ea602f TL |
157 | }, |
158 | { | |
159 | property: 'template', | |
160 | value: 0, | |
161 | }, | |
162 | ], | |
163 | }, | |
164 | listeners: { change: 'onTargetVMChange' }, | |
165 | }, | |
166 | { | |
167 | xtype: 'pveControllerSelector', | |
168 | reference: 'diskSelector', | |
169 | withUnused: true, | |
170 | disabled: true, | |
171 | cbind: { | |
172 | hidden: '{!isQemu}', | |
173 | }, | |
174 | }, | |
175 | { | |
176 | xtype: 'container', | |
177 | layout: 'hbox', | |
178 | cbind: { | |
179 | hidden: '{isQemu}', | |
180 | disabled: '{isQemu}', | |
a8d854af AL |
181 | }, |
182 | items: [ | |
183 | { | |
14ea602f | 184 | xtype: 'pmxDisplayEditField', |
a8d854af | 185 | cbind: { |
14ea602f TL |
186 | editable: get => !get('disk').match(/^unused\d+/), |
187 | value: get => get('disk').match(/^unused\d+/) ? 'unused' : 'mp', | |
a8d854af | 188 | }, |
14ea602f TL |
189 | disabled: true, |
190 | name: 'mpType', | |
191 | reference: 'mpType', | |
192 | fieldLabel: gettext('Add as'), | |
193 | submitValue: true, | |
194 | flex: 4, | |
195 | editConfig: { | |
196 | xtype: 'proxmoxKVComboBox', | |
197 | name: 'mpTypeCombo', | |
14ea602f TL |
198 | deleteEmpty: false, |
199 | cbind: { | |
200 | hidden: '{isQemu}', | |
201 | }, | |
202 | comboItems: [ | |
203 | ['mp', gettext('Mount Point')], | |
204 | ['unused', gettext('Unused')], | |
a8d854af | 205 | ], |
14ea602f | 206 | listeners: { change: 'onMpTypeChange' }, |
a8d854af | 207 | }, |
a8d854af AL |
208 | }, |
209 | { | |
14ea602f TL |
210 | xtype: 'proxmoxintegerfield', |
211 | name: 'mpId', | |
212 | reference: 'mpIdSelector', | |
213 | minValue: 0, | |
214 | flex: 1, | |
215 | allowBlank: false, | |
216 | validateOnChange: true, | |
a8d854af | 217 | disabled: true, |
14ea602f TL |
218 | bind: { |
219 | maxValue: '{mpMaxCount}', | |
a8d854af | 220 | }, |
14ea602f TL |
221 | validator: function(value) { |
222 | let view = this.up('window'); | |
223 | let type = view.getViewModel().get('mpType'); | |
224 | if (Ext.isDefined(view.VMConfig[`${type}${value}`])) { | |
225 | return "Mount point is already in use."; | |
226 | } | |
227 | return true; | |
a8d854af | 228 | }, |
a8d854af AL |
229 | }, |
230 | ], | |
231 | }, | |
232 | ], | |
233 | ||
234 | initComponent: function() { | |
235 | let me = this; | |
236 | ||
237 | if (!me.nodename) { | |
238 | throw "no node name specified"; | |
239 | } | |
240 | ||
241 | if (!me.vmid) { | |
242 | throw "no VM ID specified"; | |
243 | } | |
244 | ||
245 | if (!me.type) { | |
246 | throw "no type specified"; | |
247 | } | |
248 | ||
249 | me.callParent(); | |
250 | }, | |
251 | }); |