]>
Commit | Line | Data |
---|---|---|
24313a9d FE |
1 | Ext.define('apt-repolist', { |
2 | extend: 'Ext.data.Model', | |
3 | fields: [ | |
4 | 'Path', | |
5 | 'Index', | |
d91987a5 | 6 | 'Origin', |
24313a9d FE |
7 | 'FileType', |
8 | 'Enabled', | |
9 | 'Comment', | |
10 | 'Types', | |
11 | 'URIs', | |
12 | 'Suites', | |
13 | 'Components', | |
14 | 'Options', | |
15 | ], | |
16 | }); | |
17 | ||
21860ea4 FE |
18 | Ext.define('Proxmox.window.APTRepositoryAdd', { |
19 | extend: 'Proxmox.window.Edit', | |
20 | alias: 'widget.pmxAPTRepositoryAdd', | |
21 | ||
22 | isCreate: true, | |
23 | isAdd: true, | |
24 | ||
25 | subject: gettext('Repository'), | |
5e9eb245 | 26 | width: 600, |
21860ea4 FE |
27 | |
28 | initComponent: function() { | |
29 | let me = this; | |
30 | ||
31 | if (!me.repoInfo || me.repoInfo.length === 0) { | |
32 | throw "repository information not initialized"; | |
33 | } | |
34 | ||
35 | let description = Ext.create('Ext.form.field.Display', { | |
36 | fieldLabel: gettext('Description'), | |
37 | name: 'description', | |
38 | }); | |
39 | ||
40 | let status = Ext.create('Ext.form.field.Display', { | |
41 | fieldLabel: gettext('Status'), | |
42 | name: 'status', | |
43 | renderer: function(value) { | |
44 | let statusText = gettext('Not yet configured'); | |
45 | if (value !== '') { | |
46 | statusText = Ext.String.format( | |
47 | '{0}: {1}', | |
48 | gettext('Configured'), | |
49 | value ? gettext('enabled') : gettext('disabled'), | |
50 | ); | |
51 | } | |
52 | ||
53 | return statusText; | |
54 | }, | |
55 | }); | |
56 | ||
57 | let repoSelector = Ext.create('Proxmox.form.KVComboBox', { | |
58 | fieldLabel: gettext('Repository'), | |
59 | xtype: 'proxmoxKVComboBox', | |
60 | name: 'handle', | |
61 | allowBlank: false, | |
62 | comboItems: me.repoInfo.map(info => [info.handle, info.name]), | |
824f9977 TL |
63 | validator: function(renderedValue) { |
64 | let handle = this.value; | |
65 | // we cannot use this.callParent in instantiations | |
66 | let valid = Proxmox.form.KVComboBox.prototype.validator.call(this, renderedValue); | |
21860ea4 | 67 | |
824f9977 | 68 | if (!valid || !handle) { |
21860ea4 FE |
69 | return false; |
70 | } | |
71 | ||
72 | const info = me.repoInfo.find(elem => elem.handle === handle); | |
21860ea4 FE |
73 | if (!info) { |
74 | return false; | |
75 | } | |
76 | ||
58b71860 | 77 | if (info.status) { |
824f9977 TL |
78 | return Ext.String.format(gettext('{0} is already configured'), renderedValue); |
79 | } | |
80 | return valid; | |
21860ea4 FE |
81 | }, |
82 | listeners: { | |
83 | change: function(f, value) { | |
84 | const info = me.repoInfo.find(elem => elem.handle === value); | |
85 | description.setValue(info.description); | |
86 | status.setValue(info.status); | |
87 | }, | |
88 | }, | |
89 | }); | |
90 | ||
91 | repoSelector.setValue(me.repoInfo[0].handle); | |
92 | ||
21860ea4 | 93 | Ext.apply(me, { |
5e9eb245 TL |
94 | items: [ |
95 | repoSelector, | |
96 | description, | |
97 | status, | |
98 | ], | |
21860ea4 FE |
99 | repoSelector: repoSelector, |
100 | }); | |
101 | ||
102 | me.callParent(); | |
103 | }, | |
104 | }); | |
105 | ||
24313a9d FE |
106 | Ext.define('Proxmox.node.APTRepositoriesErrors', { |
107 | extend: 'Ext.grid.GridPanel', | |
108 | ||
109 | xtype: 'proxmoxNodeAPTRepositoriesErrors', | |
110 | ||
24313a9d FE |
111 | store: {}, |
112 | ||
f59d1076 | 113 | scrollable: true, |
d8b5cd80 | 114 | |
24313a9d FE |
115 | viewConfig: { |
116 | stripeRows: false, | |
5e0cecb7 DC |
117 | getRowClass: (record) => { |
118 | switch (record.data.status) { | |
119 | case 'warning': return 'proxmox-warning-row'; | |
120 | case 'critical': return 'proxmox-invalid-row'; | |
121 | default: return ''; | |
122 | } | |
123 | }, | |
24313a9d FE |
124 | }, |
125 | ||
5e0cecb7 DC |
126 | hideHeaders: true, |
127 | ||
24313a9d FE |
128 | columns: [ |
129 | { | |
5e0cecb7 DC |
130 | dataIndex: 'status', |
131 | renderer: (value) => `<i class="fa fa-fw ${Proxmox.Utils.get_health_icon(value, true)}"></i>`, | |
132 | width: 50, | |
24313a9d FE |
133 | }, |
134 | { | |
5e0cecb7 | 135 | dataIndex: 'message', |
24313a9d FE |
136 | flex: 1, |
137 | }, | |
138 | ], | |
139 | }); | |
140 | ||
141 | Ext.define('Proxmox.node.APTRepositoriesGrid', { | |
142 | extend: 'Ext.grid.GridPanel', | |
24313a9d | 143 | xtype: 'proxmoxNodeAPTRepositoriesGrid', |
5e76f730 | 144 | mixins: ['Proxmox.Mixin.CBind'], |
24313a9d FE |
145 | |
146 | title: gettext('APT Repositories'), | |
147 | ||
994fe897 TL |
148 | cls: 'proxmox-apt-repos', // to allow applying styling to general components with local effect |
149 | ||
d8b5cd80 DC |
150 | border: false, |
151 | ||
24313a9d FE |
152 | tbar: [ |
153 | { | |
154 | text: gettext('Reload'), | |
155 | iconCls: 'fa fa-refresh', | |
156 | handler: function() { | |
157 | let me = this; | |
158 | me.up('proxmoxNodeAPTRepositories').reload(); | |
159 | }, | |
160 | }, | |
d76eedb4 FE |
161 | { |
162 | text: gettext('Add'), | |
faacb77f | 163 | name: 'addRepo', |
21860ea4 FE |
164 | disabled: true, |
165 | repoInfo: undefined, | |
5e76f730 FE |
166 | cbind: { |
167 | onlineHelp: '{onlineHelp}', | |
168 | }, | |
21860ea4 FE |
169 | handler: function(button, event, record) { |
170 | Proxmox.Utils.checked_command(() => { | |
171 | let me = this; | |
172 | let panel = me.up('proxmoxNodeAPTRepositories'); | |
173 | ||
174 | let extraParams = {}; | |
175 | if (panel.digest !== undefined) { | |
176 | extraParams.digest = panel.digest; | |
177 | } | |
178 | ||
179 | Ext.create('Proxmox.window.APTRepositoryAdd', { | |
180 | repoInfo: me.repoInfo, | |
d7aeb02f | 181 | url: `/api2/extjs/nodes/${panel.nodename}/apt/repositories`, |
21860ea4 FE |
182 | method: 'PUT', |
183 | extraRequestParams: extraParams, | |
5e76f730 | 184 | onlineHelp: me.onlineHelp, |
21860ea4 FE |
185 | listeners: { |
186 | destroy: function() { | |
187 | panel.reload(); | |
188 | }, | |
189 | }, | |
190 | }).show(); | |
191 | }); | |
d76eedb4 FE |
192 | }, |
193 | }, | |
af48de6b | 194 | '-', |
d76eedb4 | 195 | { |
8832b590 | 196 | xtype: 'proxmoxAltTextButton', |
bb64cd03 TL |
197 | defaultText: gettext('Enable'), |
198 | altText: gettext('Disable'), | |
faacb77f | 199 | name: 'repoEnable', |
d76eedb4 | 200 | disabled: true, |
003c4982 DC |
201 | bind: { |
202 | text: '{enableButtonText}', | |
203 | }, | |
d76eedb4 FE |
204 | handler: function(button, event, record) { |
205 | let me = this; | |
206 | let panel = me.up('proxmoxNodeAPTRepositories'); | |
207 | ||
208 | let params = { | |
209 | path: record.data.Path, | |
210 | index: record.data.Index, | |
211 | enabled: record.data.Enabled ? 0 : 1, // invert | |
212 | }; | |
213 | ||
214 | if (panel.digest !== undefined) { | |
215 | params.digest = panel.digest; | |
216 | } | |
217 | ||
218 | Proxmox.Utils.API2Request({ | |
219 | url: `/nodes/${panel.nodename}/apt/repositories`, | |
220 | method: 'POST', | |
221 | params: params, | |
222 | failure: function(response, opts) { | |
223 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
224 | panel.reload(); | |
225 | }, | |
226 | success: function(response, opts) { | |
227 | panel.reload(); | |
228 | }, | |
229 | }); | |
230 | }, | |
231 | }, | |
24313a9d FE |
232 | ], |
233 | ||
234 | sortableColumns: false, | |
205d2751 TL |
235 | viewConfig: { |
236 | stripeRows: false, | |
237 | getRowClass: (record, index) => record.get('Enabled') ? '' : 'proxmox-disabled-row', | |
238 | }, | |
24313a9d FE |
239 | |
240 | columns: [ | |
241 | { | |
24313a9d FE |
242 | header: gettext('Enabled'), |
243 | dataIndex: 'Enabled', | |
64f65c02 LW |
244 | align: 'center', |
245 | renderer: Proxmox.Utils.renderEnabledIcon, | |
24313a9d FE |
246 | width: 90, |
247 | }, | |
248 | { | |
249 | header: gettext('Types'), | |
250 | dataIndex: 'Types', | |
251 | renderer: function(types, cell, record) { | |
252 | return types.join(' '); | |
253 | }, | |
254 | width: 100, | |
255 | }, | |
256 | { | |
257 | header: gettext('URIs'), | |
258 | dataIndex: 'URIs', | |
259 | renderer: function(uris, cell, record) { | |
260 | return uris.join(' '); | |
261 | }, | |
262 | width: 350, | |
263 | }, | |
264 | { | |
265 | header: gettext('Suites'), | |
266 | dataIndex: 'Suites', | |
e71fc6e4 DC |
267 | renderer: function(suites, metaData, record) { |
268 | let err = ''; | |
269 | if (record.data.warnings && record.data.warnings.length > 0) { | |
270 | let txt = [gettext('Warning')]; | |
271 | record.data.warnings.forEach((warning) => { | |
272 | if (warning.property === 'Suites') { | |
273 | txt.push(warning.message); | |
274 | } | |
275 | }); | |
276 | metaData.tdAttr = `data-qtip="${Ext.htmlEncode(txt.join('<br>'))}"`; | |
5a1fddb6 TL |
277 | if (record.data.Enabled) { |
278 | metaData.tdCls = 'proxmox-invalid-row'; | |
279 | err = '<i class="fa fa-fw critical fa-exclamation-circle"></i> '; | |
280 | } else { | |
281 | metaData.tdCls = 'proxmox-warning-row'; | |
282 | err = '<i class="fa fa-fw warning fa-exclamation-circle"></i> '; | |
283 | } | |
e71fc6e4 DC |
284 | } |
285 | return suites.join(' ') + err; | |
24313a9d FE |
286 | }, |
287 | width: 130, | |
288 | }, | |
289 | { | |
290 | header: gettext('Components'), | |
291 | dataIndex: 'Components', | |
06689819 | 292 | renderer: function(components, metaData, record) { |
65f4704b TL |
293 | if (components === undefined) { |
294 | return ''; | |
295 | } | |
06689819 TL |
296 | let err = ''; |
297 | if (components.length === 1) { | |
298 | // FIXME: this should be a flag set to the actual repsotiories, i.e., a tristate | |
4fc57df4 | 299 | // like production-ready = <yes|no|other> (Option<bool>) |
06689819 TL |
300 | if (components[0].match(/\w+(-no-subscription|test)\s*$/i)) { |
301 | metaData.tdCls = 'proxmox-warning-row'; | |
302 | err = '<i class="fa fa-fw warning fa-exclamation-circle"></i> '; | |
303 | ||
304 | let qtip = components[0].match(/no-subscription/) | |
305 | ? gettext('The no-subscription repository is NOT production-ready') | |
306 | : gettext('The test repository may contain unstable updates') | |
307 | ; | |
308 | metaData.tdAttr = `data-qtip="${Ext.htmlEncode(qtip)}"`; | |
309 | } | |
310 | } | |
311 | return components.join(' ') + err; | |
24313a9d FE |
312 | }, |
313 | width: 170, | |
314 | }, | |
315 | { | |
316 | header: gettext('Options'), | |
317 | dataIndex: 'Options', | |
318 | renderer: function(options, cell, record) { | |
319 | if (!options) { | |
320 | return ''; | |
321 | } | |
322 | ||
323 | let filetype = record.data.FileType; | |
324 | let text = ''; | |
325 | ||
326 | options.forEach(function(option) { | |
327 | let key = option.Key; | |
328 | if (filetype === 'list') { | |
329 | let values = option.Values.join(','); | |
330 | text += `${key}=${values} `; | |
331 | } else if (filetype === 'sources') { | |
332 | let values = option.Values.join(' '); | |
333 | text += `${key}: ${values}<br>`; | |
334 | } else { | |
017a6376 | 335 | throw "unknown file type"; |
24313a9d FE |
336 | } |
337 | }); | |
338 | return text; | |
339 | }, | |
340 | flex: 1, | |
341 | }, | |
03c4c65b | 342 | { |
d91987a5 FE |
343 | header: gettext('Origin'), |
344 | dataIndex: 'Origin', | |
036f48c1 | 345 | width: 120, |
3d6b76ee | 346 | renderer: function(value, meta, rec) { |
f0966f29 TL |
347 | if (typeof value !== 'string' || value.length === 0) { |
348 | value = gettext('Other'); | |
349 | } | |
036f48c1 | 350 | let cls = 'fa fa-fw fa-question-circle-o'; |
3d6b76ee FE |
351 | let originType = this.up('proxmoxNodeAPTRepositories').classifyOrigin(value); |
352 | if (originType === 'Proxmox') { | |
036f48c1 | 353 | cls = 'pmx-itype-icon pmx-itype-icon-proxmox-x'; |
3d6b76ee | 354 | } else if (originType === 'Debian') { |
036f48c1 TL |
355 | cls = 'pmx-itype-icon pmx-itype-icon-debian-swirl'; |
356 | } | |
357 | return `<i class='${cls}'></i> ${value}`; | |
358 | }, | |
03c4c65b | 359 | }, |
24313a9d FE |
360 | { |
361 | header: gettext('Comment'), | |
362 | dataIndex: 'Comment', | |
363 | flex: 2, | |
e61ffdd6 | 364 | renderer: Ext.String.htmlEncode, |
24313a9d FE |
365 | }, |
366 | ], | |
367 | ||
7705801f TL |
368 | features: [ |
369 | { | |
370 | ftype: 'grouping', | |
371 | groupHeaderTpl: '{[ "File: " + values.name ]} ({rows.length} repositor{[values.rows.length > 1 ? "ies" : "y"]})', | |
372 | enableGroupingMenu: false, | |
373 | }, | |
374 | ], | |
375 | ||
eb1fff82 TL |
376 | store: { |
377 | model: 'apt-repolist', | |
378 | groupField: 'Path', | |
379 | sorters: [ | |
380 | { | |
381 | property: 'Index', | |
382 | direction: 'ASC', | |
383 | }, | |
384 | ], | |
385 | }, | |
386 | ||
24313a9d FE |
387 | initComponent: function() { |
388 | let me = this; | |
389 | ||
390 | if (!me.nodename) { | |
391 | throw "no node name specified"; | |
392 | } | |
393 | ||
24313a9d FE |
394 | me.callParent(); |
395 | }, | |
396 | }); | |
397 | ||
398 | Ext.define('Proxmox.node.APTRepositories', { | |
399 | extend: 'Ext.panel.Panel', | |
24313a9d FE |
400 | xtype: 'proxmoxNodeAPTRepositories', |
401 | mixins: ['Proxmox.Mixin.CBind'], | |
402 | ||
403 | digest: undefined, | |
404 | ||
5e76f730 FE |
405 | onlineHelp: undefined, |
406 | ||
3fc020f4 TL |
407 | product: 'Proxmox VE', // default |
408 | ||
3d6b76ee | 409 | classifyOrigin: function(origin) { |
a14bafec | 410 | origin ||= ''; |
3d6b76ee FE |
411 | if (origin.match(/^\s*Proxmox\s*$/i)) { |
412 | return 'Proxmox'; | |
413 | } else if (origin.match(/^\s*Debian\s*(:?Backports)?$/i)) { | |
414 | return 'Debian'; | |
415 | } | |
416 | return 'Other'; | |
417 | }, | |
418 | ||
003c4982 DC |
419 | controller: { |
420 | xclass: 'Ext.app.ViewController', | |
421 | ||
422 | selectionChange: function(grid, selection) { | |
423 | let me = this; | |
424 | if (!selection || selection.length < 1) { | |
425 | return; | |
426 | } | |
427 | let rec = selection[0]; | |
428 | let vm = me.getViewModel(); | |
429 | vm.set('selectionenabled', rec.get('Enabled')); | |
f59d1076 DC |
430 | vm.notify(); |
431 | }, | |
432 | ||
433 | updateState: function() { | |
434 | let me = this; | |
435 | let vm = me.getViewModel(); | |
436 | ||
5e0cecb7 DC |
437 | let store = vm.get('errorstore'); |
438 | store.removeAll(); | |
439 | ||
df7def01 TL |
440 | let status = 'good'; // start with best, the helper below will downgrade if needed |
441 | let text = gettext('All OK, you have production-ready repositories configured!'); | |
442 | ||
df7def01 | 443 | let addGood = message => store.add({ status: 'good', message }); |
46231d0d | 444 | let addWarn = (message, important) => { |
fe787c8c | 445 | if (status !== 'critical') { |
df7def01 | 446 | status = 'warning'; |
46231d0d | 447 | text = important ? message : gettext('Warning'); |
df7def01 TL |
448 | } |
449 | store.add({ status: 'warning', message }); | |
450 | }; | |
6c9af178 TL |
451 | let addCritical = (message, important) => { |
452 | status = 'critical'; | |
453 | text = important ? message : gettext('Error'); | |
454 | store.add({ status: 'critical', message }); | |
455 | }; | |
456 | ||
457 | let errors = vm.get('errors'); | |
458 | errors.forEach(error => addCritical(`${error.path} - ${error.error}`)); | |
f59d1076 DC |
459 | |
460 | let activeSubscription = vm.get('subscriptionActive'); | |
461 | let enterprise = vm.get('enterpriseRepo'); | |
462 | let nosubscription = vm.get('noSubscriptionRepo'); | |
463 | let test = vm.get('testRepo'); | |
7d5201a3 FE |
464 | let cephRepos = { |
465 | enterprise: vm.get('cephEnterpriseRepo'), | |
466 | nosubscription: vm.get('cephNoSubscriptionRepo'), | |
467 | test: vm.get('cephTestRepo'), | |
468 | }; | |
f59d1076 | 469 | let wrongSuites = vm.get('suitesWarning'); |
e6ed4498 | 470 | let mixedSuites = vm.get('mixedSuites'); |
f59d1076 | 471 | |
5e0cecb7 | 472 | if (!enterprise && !nosubscription && !test) { |
6c9af178 TL |
473 | addCritical( |
474 | Ext.String.format(gettext('No {0} repository is enabled, you do not get any updates!'), vm.get('product')), | |
475 | ); | |
6bac17e3 TL |
476 | } else if (errors.length > 0) { |
477 | // nothing extra, just avoid that we show "get updates" | |
5e0cecb7 DC |
478 | } else if (enterprise && !nosubscription && !test && activeSubscription) { |
479 | addGood(Ext.String.format(gettext('You get supported updates for {0}'), vm.get('product'))); | |
480 | } else if (nosubscription || test) { | |
481 | addGood(Ext.String.format(gettext('You get updates for {0}'), vm.get('product'))); | |
482 | } | |
483 | ||
484 | if (wrongSuites) { | |
96ecd62a | 485 | addWarn(gettext('Some suites are misconfigured')); |
5e0cecb7 DC |
486 | } |
487 | ||
e6ed4498 FE |
488 | if (mixedSuites) { |
489 | addWarn(gettext('Detected mixed suites before upgrade')); | |
490 | } | |
491 | ||
7d5201a3 FE |
492 | let productionReadyCheck = (repos, type, noSubAlternateName) => { |
493 | if (!activeSubscription && repos.enterprise) { | |
494 | addWarn(Ext.String.format( | |
495 | gettext('The {0}enterprise repository is enabled, but there is no active subscription!'), | |
496 | type, | |
497 | )); | |
498 | } | |
5e0cecb7 | 499 | |
7d5201a3 FE |
500 | if (repos.nosubscription) { |
501 | addWarn(Ext.String.format( | |
502 | gettext('The {0}no-subscription{1} repository is not recommended for production use!'), | |
503 | type, | |
504 | noSubAlternateName, | |
505 | )); | |
506 | } | |
5e0cecb7 | 507 | |
7d5201a3 FE |
508 | if (repos.test) { |
509 | addWarn(Ext.String.format( | |
510 | gettext('The {0}test repository may pull in unstable updates and is not recommended for production use!'), | |
511 | type, | |
512 | )); | |
513 | } | |
514 | }; | |
515 | ||
516 | productionReadyCheck({ enterprise, nosubscription, test }, '', ''); | |
517 | // TODO drop alternate 'main' name when no longer relevant | |
518 | productionReadyCheck(cephRepos, 'Ceph ', '/main'); | |
5e0cecb7 DC |
519 | |
520 | if (errors.length > 0) { | |
4227a557 | 521 | text = gettext('Fatal parsing error for at least one repository'); |
f59d1076 DC |
522 | } |
523 | ||
524 | let iconCls = Proxmox.Utils.get_health_icon(status, true); | |
525 | ||
526 | vm.set('state', { | |
527 | iconCls, | |
528 | text, | |
529 | }); | |
003c4982 DC |
530 | }, |
531 | }, | |
532 | ||
24313a9d FE |
533 | viewModel: { |
534 | data: { | |
3fc020f4 | 535 | product: 'Proxmox VE', // default |
5e0cecb7 | 536 | errors: [], |
f59d1076 | 537 | suitesWarning: false, |
e6ed4498 | 538 | mixedSuites: false, // used before major upgrade |
24313a9d FE |
539 | subscriptionActive: '', |
540 | noSubscriptionRepo: '', | |
541 | enterpriseRepo: '', | |
f59d1076 | 542 | testRepo: '', |
7d5201a3 FE |
543 | cephEnterpriseRepo: '', |
544 | cephNoSubscriptionRepo: '', | |
545 | cephTestRepo: '', | |
003c4982 | 546 | selectionenabled: false, |
f59d1076 | 547 | state: {}, |
24313a9d FE |
548 | }, |
549 | formulas: { | |
003c4982 DC |
550 | enableButtonText: (get) => get('selectionenabled') |
551 | ? gettext('Disable') : gettext('Enable'), | |
24313a9d | 552 | }, |
5e0cecb7 DC |
553 | stores: { |
554 | errorstore: { | |
555 | fields: ['status', 'message'], | |
556 | }, | |
557 | }, | |
24313a9d FE |
558 | }, |
559 | ||
82071150 DC |
560 | scrollable: true, |
561 | layout: { | |
562 | type: 'vbox', | |
563 | align: 'stretch', | |
564 | }, | |
565 | ||
24313a9d FE |
566 | items: [ |
567 | { | |
f59d1076 DC |
568 | xtype: 'panel', |
569 | border: false, | |
570 | layout: { | |
571 | type: 'hbox', | |
572 | align: 'stretch', | |
24313a9d | 573 | }, |
5e0cecb7 | 574 | height: 200, |
f59d1076 DC |
575 | title: gettext('Status'), |
576 | items: [ | |
577 | { | |
578 | xtype: 'box', | |
f411afb4 | 579 | flex: 2, |
f59d1076 DC |
580 | margin: 10, |
581 | data: { | |
582 | iconCls: Proxmox.Utils.get_health_icon(undefined, true), | |
583 | text: '', | |
584 | }, | |
585 | bind: { | |
586 | data: '{state}', | |
587 | }, | |
588 | tpl: [ | |
f411afb4 | 589 | '<center class="centered-flex-column" style="font-size:15px;line-height: 25px;">', |
f59d1076 | 590 | '<i class="fa fa-4x {iconCls}"></i>', |
f59d1076 DC |
591 | '{text}', |
592 | '</center>', | |
593 | ], | |
594 | }, | |
595 | { | |
596 | xtype: 'proxmoxNodeAPTRepositoriesErrors', | |
597 | name: 'repositoriesErrors', | |
f411afb4 | 598 | flex: 7, |
f59d1076 DC |
599 | margin: 10, |
600 | bind: { | |
5e0cecb7 | 601 | store: '{errorstore}', |
f59d1076 DC |
602 | }, |
603 | }, | |
604 | ], | |
24313a9d FE |
605 | }, |
606 | { | |
607 | xtype: 'proxmoxNodeAPTRepositoriesGrid', | |
608 | name: 'repositoriesGrid', | |
f59d1076 | 609 | flex: 1, |
24313a9d FE |
610 | cbind: { |
611 | nodename: '{nodename}', | |
5e76f730 | 612 | onlineHelp: '{onlineHelp}', |
24313a9d FE |
613 | }, |
614 | majorUpgradeAllowed: false, // TODO get release information from an API call? | |
003c4982 DC |
615 | listeners: { |
616 | selectionchange: 'selectionChange', | |
bb64cd03 | 617 | }, |
24313a9d FE |
618 | }, |
619 | ], | |
620 | ||
621 | check_subscription: function() { | |
622 | let me = this; | |
623 | let vm = me.getViewModel(); | |
624 | ||
625 | Proxmox.Utils.API2Request({ | |
626 | url: `/nodes/${me.nodename}/subscription`, | |
627 | method: 'GET', | |
af48de6b | 628 | failure: (response, opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus), |
24313a9d FE |
629 | success: function(response, opts) { |
630 | const res = response.result; | |
af48de6b | 631 | const subscription = !(!res || !res.data || res.data.status.toLowerCase() !== 'active'); |
24313a9d | 632 | vm.set('subscriptionActive', subscription); |
f59d1076 | 633 | me.getController().updateState(); |
24313a9d FE |
634 | }, |
635 | }); | |
636 | }, | |
637 | ||
638 | updateStandardRepos: function(standardRepos) { | |
639 | let me = this; | |
640 | let vm = me.getViewModel(); | |
641 | ||
faacb77f | 642 | let addButton = me.down('button[name=addRepo]'); |
d76eedb4 | 643 | |
faacb77f | 644 | addButton.repoInfo = []; |
24313a9d FE |
645 | for (const standardRepo of standardRepos) { |
646 | const handle = standardRepo.handle; | |
647 | const status = standardRepo.status; | |
648 | ||
649 | if (handle === "enterprise") { | |
650 | vm.set('enterpriseRepo', status); | |
651 | } else if (handle === "no-subscription") { | |
652 | vm.set('noSubscriptionRepo', status); | |
f59d1076 DC |
653 | } else if (handle === 'test') { |
654 | vm.set('testRepo', status); | |
7d5201a3 FE |
655 | } else if (handle.match(/^ceph-[a-zA-Z]+-enterprise$/)) { |
656 | vm.set('cephEnterpriseRepo', status); | |
657 | } else if (handle.match(/^ceph-[a-zA-Z]+-no-subscription$/)) { | |
658 | vm.set('cephNoSubscriptionRepo', status); | |
659 | } else if (handle.match(/^ceph-[a-zA-Z]+-test$/)) { | |
660 | vm.set('cephTestRepo', status); | |
24313a9d | 661 | } |
f59d1076 | 662 | me.getController().updateState(); |
d76eedb4 | 663 | |
21860ea4 FE |
664 | addButton.repoInfo.push(standardRepo); |
665 | addButton.digest = me.digest; | |
24313a9d | 666 | } |
21860ea4 FE |
667 | |
668 | addButton.setDisabled(false); | |
24313a9d FE |
669 | }, |
670 | ||
671 | reload: function() { | |
672 | let me = this; | |
673 | let vm = me.getViewModel(); | |
674 | let repoGrid = me.down('proxmoxNodeAPTRepositoriesGrid'); | |
24313a9d FE |
675 | |
676 | me.store.load(function(records, operation, success) { | |
677 | let gridData = []; | |
678 | let errors = []; | |
679 | let digest; | |
f59d1076 | 680 | let suitesWarning = false; |
24313a9d | 681 | |
e6ed4498 FE |
682 | // Usually different suites will give errors anyways, but before a major upgrade the |
683 | // current and the next suite are allowed, so it makes sense to check for mixed suites. | |
684 | let checkMixedSuites = false; | |
685 | let mixedSuites = false; | |
686 | ||
24313a9d FE |
687 | if (success && records.length > 0) { |
688 | let data = records[0].data; | |
689 | let files = data.files; | |
690 | errors = data.errors; | |
691 | digest = data.digest; | |
692 | ||
e71fc6e4 DC |
693 | let infos = {}; |
694 | for (const info of data.infos) { | |
695 | let path = info.path; | |
696 | let idx = info.index; | |
697 | ||
698 | if (!infos[path]) { | |
699 | infos[path] = {}; | |
700 | } | |
701 | if (!infos[path][idx]) { | |
702 | infos[path][idx] = { | |
703 | origin: '', | |
704 | warnings: [], | |
e6ed4498 FE |
705 | // Used as a heuristic to detect mixed repositories pre-upgrade. The |
706 | // warning is set on all repositories that do configure the next suite. | |
707 | gotIgnorePreUpgradeWarning: false, | |
e71fc6e4 DC |
708 | }; |
709 | } | |
710 | ||
711 | if (info.kind === 'origin') { | |
712 | infos[path][idx].origin = info.message; | |
fd468868 | 713 | } else if (info.kind === 'warning') { |
e71fc6e4 | 714 | infos[path][idx].warnings.push(info); |
fd468868 | 715 | } else if (info.kind === 'ignore-pre-upgrade-warning') { |
e6ed4498 | 716 | infos[path][idx].gotIgnorePreUpgradeWarning = true; |
fd468868 FE |
717 | if (!repoGrid.majorUpgradeAllowed) { |
718 | infos[path][idx].warnings.push(info); | |
e6ed4498 FE |
719 | } else { |
720 | checkMixedSuites = true; | |
fd468868 | 721 | } |
e71fc6e4 DC |
722 | } |
723 | } | |
724 | ||
725 | ||
24313a9d FE |
726 | files.forEach(function(file) { |
727 | for (let n = 0; n < file.repositories.length; n++) { | |
728 | let repo = file.repositories[n]; | |
729 | repo.Path = file.path; | |
730 | repo.Index = n; | |
e71fc6e4 | 731 | if (infos[file.path] && infos[file.path][n]) { |
78be60a0 | 732 | repo.Origin = infos[file.path][n].origin || Proxmox.Utils.unknownText; |
e71fc6e4 | 733 | repo.warnings = infos[file.path][n].warnings || []; |
e455399a | 734 | |
e6ed4498 FE |
735 | if (repo.Enabled) { |
736 | if (repo.warnings.some(w => w.property === 'Suites')) { | |
737 | suitesWarning = true; | |
738 | } | |
739 | ||
740 | let originType = me.classifyOrigin(repo.Origin); | |
741 | // Only Proxmox and Debian repositories checked here, because the | |
742 | // warning can be missing for others for a different reason (e.g. | |
743 | // using 'stable' or non-Debian code names). | |
744 | if (checkMixedSuites && repo.Types.includes('deb') && | |
745 | (originType === 'Proxmox' || originType === 'Debian') && | |
746 | !infos[file.path][n].gotIgnorePreUpgradeWarning | |
747 | ) { | |
748 | mixedSuites = true; | |
749 | } | |
e455399a | 750 | } |
e71fc6e4 | 751 | } |
24313a9d FE |
752 | gridData.push(repo); |
753 | } | |
754 | }); | |
755 | ||
24313a9d FE |
756 | repoGrid.store.loadData(gridData); |
757 | ||
758 | me.updateStandardRepos(data['standard-repos']); | |
759 | } | |
760 | ||
761 | me.digest = digest; | |
762 | ||
5e0cecb7 | 763 | vm.set('errors', errors); |
f59d1076 | 764 | vm.set('suitesWarning', suitesWarning); |
e6ed4498 | 765 | vm.set('mixedSuites', mixedSuites); |
f59d1076 | 766 | me.getController().updateState(); |
24313a9d FE |
767 | }); |
768 | ||
769 | me.check_subscription(); | |
770 | }, | |
771 | ||
772 | listeners: { | |
773 | activate: function() { | |
774 | let me = this; | |
775 | me.reload(); | |
776 | }, | |
777 | }, | |
778 | ||
779 | initComponent: function() { | |
780 | let me = this; | |
781 | ||
782 | if (!me.nodename) { | |
783 | throw "no node name specified"; | |
784 | } | |
785 | ||
786 | let store = Ext.create('Ext.data.Store', { | |
787 | proxy: { | |
788 | type: 'proxmox', | |
789 | url: `/api2/json/nodes/${me.nodename}/apt/repositories`, | |
790 | }, | |
791 | }); | |
792 | ||
793 | Ext.apply(me, { store: store }); | |
794 | ||
795 | Proxmox.Utils.monStoreErrors(me, me.store, true); | |
796 | ||
797 | me.callParent(); | |
3fc020f4 TL |
798 | |
799 | me.getViewModel().set('product', me.product); | |
24313a9d FE |
800 | }, |
801 | }); |