]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/ceph/CephInstallWizard.js
update shipped appliance info index
[pve-manager.git] / www / manager6 / ceph / CephInstallWizard.js
CommitLineData
37e75d86
TL
1Ext.define('PVE.ceph.CephInstallWizardInfo', {
2 extend: 'Ext.panel.Panel',
3 xtype: 'pveCephInstallWizardInfo',
4
5 html: `<h3>Ceph?</h3>
6 <blockquote cite="https://ceph.com/"><p>"<b>Ceph</b> is a unified,
f00da0a5 7 distributed storage system, designed for excellent performance, reliability,
37e75d86
TL
8 and scalability."</p></blockquote>
9 <p>
f00da0a5
DW
10 <b>Ceph</b> is currently <b>not installed</b> on this node. This wizard
11 will guide you through the installation. Click on the next button below
12 to begin. After the initial installation, the wizard will offer to create
13 an initial configuration. This configuration step is only
37e75d86
TL
14 needed once per cluster and will be skipped if a config is already present.
15 </p>
16 <p>
f00da0a5
DW
17 Before starting the installation, please take a look at our documentation,
18 by clicking the help button below. If you want to gain deeper knowledge about
19 Ceph, visit <a target="_blank" href="https://docs.ceph.com/en/latest/">ceph.com</a>.
37e75d86
TL
20 </p>`,
21});
22
c3035c01
TL
23Ext.define('PVE.ceph.CephVersionSelector', {
24 extend: 'Ext.form.field.ComboBox',
25 xtype: 'pveCephVersionSelector',
26
27 fieldLabel: gettext('Ceph version to install'),
28
29 displayField: 'display',
30 valueField: 'release',
31
32 queryMode: 'local',
33 editable: false,
34 forceSelection: true,
35
36 store: {
37 fields: [
38 'release',
39 'version',
40 {
41 name: 'display',
42 calculate: d => `${d.release} (${d.version})`,
43 },
44 ],
45 proxy: {
46 type: 'memory',
47 reader: {
48 type: 'json',
49 },
50 },
51 data: [
719e95c9 52 { release: "quincy", version: "17.2" },
41af7c5e 53 { release: "reef", version: "18.2" },
c3035c01
TL
54 ],
55 },
56});
57
58Ext.define('PVE.ceph.CephHighestVersionDisplay', {
59 extend: 'Ext.form.field.Display',
60 xtype: 'pveCephHighestVersionDisplay',
61
62 fieldLabel: gettext('Ceph in the cluster'),
63
64 value: 'unknown',
65
66 // called on success with (release, versionTxt, versionParts)
67 gotNewestVersion: Ext.emptyFn,
68
69 initComponent: function() {
70 let me = this;
71
72 me.callParent(arguments);
73
74 Proxmox.Utils.API2Request({
75 method: 'GET',
76 url: '/cluster/ceph/metadata',
77 params: {
78 scope: 'versions',
79 },
80 waitMsgTarget: me,
81 success: (response) => {
82 let res = response.result;
83 if (!res || !res.data || !res.data.node) {
84 me.setValue(
85 gettext('Could not detect a ceph installation in the cluster'),
86 );
87 return;
88 }
89 let nodes = res.data.node;
90 if (me.nodename) {
91 // can happen on ceph purge, we do not yet cleanup old version data
92 delete nodes[me.nodename];
93 }
94
95 let maxversion = [];
96 let maxversiontext = "";
436f638d 97 for (const [_nodename, data] of Object.entries(nodes)) {
c3035c01
TL
98 let version = data.version.parts;
99 if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) {
100 maxversion = version;
101 maxversiontext = data.version.str;
102 }
103 }
104 // FIXME: get from version selector store
105 const major2release = {
106 13: 'luminous',
107 14: 'nautilus',
108 15: 'octopus',
109 16: 'pacific',
719e95c9 110 17: 'quincy',
fa3dfb1c 111 18: 'reef',
8b4f1178 112 19: 'squid',
c3035c01
TL
113 };
114 let release = major2release[maxversion[0]] || 'unknown';
115 let newestVersionTxt = `${Ext.String.capitalize(release)} (${maxversiontext})`;
116
117 if (release === 'unknown') {
118 me.setValue(
119 gettext('Could not detect a ceph installation in the cluster'),
120 );
121 } else {
122 me.setValue(Ext.String.format(
123 gettext('Newest ceph version in cluster is {0}'),
124 newestVersionTxt,
125 ));
126 }
127 me.gotNewestVersion(release, maxversiontext, maxversion);
128 },
129 failure: function(response, opts) {
130 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
f6710aac 131 },
c3035c01
TL
132 });
133 },
134});
135
4616a55b 136Ext.define('PVE.ceph.CephInstallWizard', {
80fdf8d2
TL
137 extend: 'PVE.window.Wizard',
138 alias: 'widget.pveCephInstallWizard',
139 mixins: ['Proxmox.Mixin.CBind'],
37e75d86 140
80fdf8d2
TL
141 resizable: false,
142 nodename: undefined,
37e75d86 143
35ff09a4
TL
144 width: 760, // 4:3
145 height: 570,
146
80fdf8d2
TL
147 viewModel: {
148 data: {
149 nodename: '',
39d58bb5 150 cephRelease: 'reef',
b686fd37 151 cephRepo: 'enterprise',
80fdf8d2
TL
152 configuration: true,
153 isInstalled: false,
e6a1160f
TL
154 nodeHasSubscription: true, // avoid warning hint until fully loaded
155 allHaveSubscription: true, // avoid warning hint until fully loaded
156 },
157 formulas: {
158 repoHintHidden: get => get('allHaveSubscription') && get('cephRepo') === 'enterprise',
159 repoHint: function(get) {
160 let repo = get('cephRepo');
161 let nodeSub = get('nodeHasSubscription'), allSub = get('allHaveSubscription');
162
163 if (repo === 'enterprise') {
164 if (!nodeSub) {
165 return gettext('The enterprise repository is enabled, but there is no active subscription!');
166 } else if (!allSub) {
e6a1160f
TL
167 return gettext('Not all nodes have an active subscription, which is required for cluster-wide enterprise repo access');
168 }
169 return ''; // should be hidden
170 } else if (repo === 'no-subscription') {
171 return allSub
172 ? gettext("Cluster has active subscriptions and would be elligible for using the enterprise repository.")
173 : gettext("The no-subscription repository is not the best choice for production setups.");
174 } else {
175 return gettext('The test repository should only be used for test setups or after consulting the official Proxmox support!');
176 }
177 },
f6710aac 178 },
80fdf8d2
TL
179 },
180 cbindData: {
f6710aac 181 nodename: undefined,
80fdf8d2 182 },
37e75d86 183
80fdf8d2
TL
184 title: gettext('Setup'),
185 navigateNext: function() {
186 var tp = this.down('#wizcontent');
187 var atab = tp.getActiveTab();
8ba2d669 188
80fdf8d2
TL
189 var next = tp.items.indexOf(atab) + 1;
190 var ntab = tp.items.getAt(next);
191 if (ntab) {
192 ntab.enable();
193 tp.setActiveTab(ntab);
194 }
195 },
8058410f 196 setInitialTab: function(index) {
80fdf8d2
TL
197 var tp = this.down('#wizcontent');
198 var initialTab = tp.items.getAt(index);
199 initialTab.enable();
200 tp.setActiveTab(initialTab);
201 },
202 onShow: function() {
cd56fd55 203 this.callParent(arguments);
e6a1160f 204 let viewModel = this.getViewModel();
cd56fd55
TL
205 var isInstalled = this.getViewModel().get('isInstalled');
206 if (isInstalled) {
e6a1160f 207 viewModel.set('configuration', false);
cd56fd55
TL
208 this.setInitialTab(2);
209 }
e6a1160f
TL
210
211 PVE.Utils.getClusterSubscriptionLevel().then(subcriptionMap => {
212 viewModel.set('nodeHasSubscription', !!subcriptionMap[this.nodename]);
213
214 let allHaveSubscription = Object.values(subcriptionMap).every(level => !!level);
215 viewModel.set('allHaveSubscription', allHaveSubscription);
216 });
80fdf8d2
TL
217 },
218 items: [
219 {
220 xtype: 'panel',
221 title: gettext('Info'),
222 viewModel: {}, // needed to inherit parent viewModel data
223 border: false,
224 bodyBorder: false,
225 onlineHelp: 'chapter_pveceph',
226 layout: {
227 type: 'vbox',
228 align: 'stretch',
229 },
230 defaults: {
4616a55b
TM
231 border: false,
232 bodyBorder: false,
80fdf8d2
TL
233 },
234 items: [
235 {
236 xtype: 'pveCephInstallWizardInfo',
37e75d86 237 },
80fdf8d2
TL
238 {
239 flex: 1,
37e75d86 240 },
e6a1160f
TL
241 {
242 xtype: 'displayfield',
243 fieldLabel: gettext('Hint'),
4bbd64a2 244 labelClsExtra: 'pmx-hint',
e6a1160f
TL
245 submitValue: false,
246 labelWidth: 50,
247 bind: {
248 value: '{repoHint}',
249 hidden: '{repoHintHidden}',
250 },
251 },
80fdf8d2
TL
252 {
253 xtype: 'pveCephHighestVersionDisplay',
b686fd37 254 labelWidth: 150,
8058410f 255 cbind: {
f6710aac 256 nodename: '{nodename}',
37e75d86 257 },
80fdf8d2
TL
258 gotNewestVersion: function(release, maxversiontext, maxversion) {
259 if (release === 'unknown') {
260 return;
261 }
262 let wizard = this.up('pveCephInstallWizard');
263 wizard.getViewModel().set('cephRelease', release);
c3035c01 264 },
80fdf8d2
TL
265 },
266 {
b686fd37
TL
267 xtype: 'container',
268 layout: 'hbox',
269 defaults: {
270 border: false,
271 layout: 'anchor',
272 flex: 1,
80fdf8d2 273 },
b686fd37
TL
274 items: [{
275 xtype: 'pveCephVersionSelector',
276 labelWidth: 150,
277 padding: '0 10 0 0',
278 submitValue: false,
279 bind: {
280 value: '{cephRelease}',
281 },
282 listeners: {
283 change: function(field, release) {
284 let wizard = this.up('pveCephInstallWizard');
285 wizard.down('#next').setText(
286 Ext.String.format(gettext('Start {0} installation'), release),
287 );
288 },
c3035c01
TL
289 },
290 },
b686fd37
TL
291 {
292 xtype: 'proxmoxKVComboBox',
293 fieldLabel: gettext('Repository'),
294 padding: '0 0 0 10',
295 comboItems: [
296 ['enterprise', gettext('Enterprise (recommended)')],
297 ['no-subscription', gettext('No-Subscription')],
298 ['test', gettext('Test')],
299 ],
300 labelWidth: 150,
301 submitValue: false,
302 value: 'enterprise',
303 bind: {
304 value: '{cephRepo}',
305 },
306 }],
80fdf8d2
TL
307 },
308 ],
309 listeners: {
310 activate: function() {
311 // notify owning container that it should display a help button
312 if (this.onlineHelp) {
313 Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp);
314 }
315 let wizard = this.up('pveCephInstallWizard');
316 let release = wizard.getViewModel().get('cephRelease');
317 wizard.down('#back').hide(true);
318 wizard.down('#next').setText(
319 Ext.String.format(gettext('Start {0} installation'), release),
320 );
321 },
322 deactivate: function() {
323 if (this.onlineHelp) {
324 Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp);
325 }
326 this.up('pveCephInstallWizard').down('#next').setText(gettext('Next'));
f6710aac
TL
327 },
328 },
80fdf8d2
TL
329 },
330 {
331 title: gettext('Installation'),
332 xtype: 'panel',
333 layout: 'fit',
8058410f 334 cbind: {
f6710aac 335 nodename: '{nodename}',
80fdf8d2
TL
336 },
337 viewModel: {}, // needed to inherit parent viewModel data
338 listeners: {
339 afterrender: function() {
340 var me = this;
341 if (this.getViewModel().get('isInstalled')) {
f6710aac 342 this.mask("Ceph is already installed, click next to create your configuration.", ['pve-static-mask']);
80fdf8d2
TL
343 } else {
344 me.down('pveNoVncConsole').fireEvent('activate');
345 }
346 },
347 activate: function() {
348 let me = this;
349 const nodename = me.nodename;
350 me.updateStore = Ext.create('Proxmox.data.UpdateStore', {
351 storeid: 'ceph-status-' + nodename,
352 interval: 1000,
353 proxy: {
354 type: 'proxmox',
f6710aac 355 url: '/api2/json/nodes/' + nodename + '/ceph/status',
c3035c01
TL
356 },
357 listeners: {
80fdf8d2 358 load: function(rec, response, success, operation) {
80fdf8d2
TL
359 if (success) {
360 me.updateStore.stopUpdate();
361 me.down('textfield').setValue('success');
362 } else if (operation.error.statusText.match("not initialized", "i")) {
363 me.updateStore.stopUpdate();
f6710aac 364 me.up('pveCephInstallWizard').getViewModel().set('configuration', false);
80fdf8d2
TL
365 me.down('textfield').setValue('success');
366 } else if (operation.error.statusText.match("rados_connect failed", "i")) {
367 me.updateStore.stopUpdate();
f6710aac 368 me.up('pveCephInstallWizard').getViewModel().set('configuration', true);
80fdf8d2
TL
369 me.down('textfield').setValue('success');
370 } else if (!operation.error.statusText.match("not installed", "i")) {
371 Proxmox.Utils.setErrorMask(me, operation.error.statusText);
372 }
f6710aac
TL
373 },
374 },
80fdf8d2
TL
375 });
376 me.updateStore.startUpdate();
377 },
378 destroy: function() {
379 var me = this;
380 if (me.updateStore) {
381 me.updateStore.stopUpdate();
4616a55b 382 }
f6710aac 383 },
4616a55b 384 },
80fdf8d2
TL
385 items: [
386 {
387 xtype: 'pveNoVncConsole',
388 itemId: 'jsconsole',
389 consoleType: 'cmd',
390 xtermjs: true,
391 cbind: {
392 nodename: '{nodename}',
4616a55b 393 },
80fdf8d2 394 beforeLoad: function() {
c3035c01 395 let me = this;
80fdf8d2
TL
396 let wizard = me.up('pveCephInstallWizard');
397 let release = wizard.getViewModel().get('cephRelease');
b686fd37
TL
398 let repo = wizard.getViewModel().get('cephRepo');
399 me.cmdOpts = `--version\0${release}\0--repository\0${repo}`;
4616a55b 400 },
80fdf8d2 401 cmd: 'ceph_install',
4616a55b 402 },
80fdf8d2
TL
403 {
404 xtype: 'textfield',
405 name: 'installSuccess',
406 value: '',
407 allowBlank: false,
408 submitValue: false,
f6710aac
TL
409 hidden: true,
410 },
411 ],
80fdf8d2
TL
412 },
413 {
414 xtype: 'inputpanel',
415 title: gettext('Configuration'),
416 onlineHelp: 'chapter_pveceph',
5839e002 417 height: 300,
80fdf8d2 418 cbind: {
f6710aac 419 nodename: '{nodename}',
4616a55b 420 },
80fdf8d2
TL
421 viewModel: {
422 data: {
423 replicas: undefined,
f6710aac
TL
424 minreplicas: undefined,
425 },
80fdf8d2
TL
426 },
427 listeners: {
428 activate: function() {
429 this.up('pveCephInstallWizard').down('#submit').setText(gettext('Next'));
4616a55b 430 },
5839e002 431 afterrender: function() {
80fdf8d2 432 if (this.up('pveCephInstallWizard').getViewModel().get('configuration')) {
f6710aac 433 this.mask("Configuration already initialized", ['pve-static-mask']);
80fdf8d2
TL
434 } else {
435 this.unmask();
836d66f7
TM
436 }
437 },
80fdf8d2
TL
438 deactivate: function() {
439 this.up('pveCephInstallWizard').down('#submit').setText(gettext('Finish'));
f6710aac 440 },
80fdf8d2
TL
441 },
442 column1: [
443 {
444 xtype: 'displayfield',
f6710aac 445 value: gettext('Ceph cluster configuration') + ':',
80fdf8d2
TL
446 },
447 {
448 xtype: 'proxmoxNetworkSelector',
449 name: 'network',
450 value: '',
451 fieldLabel: 'Public Network IP/CIDR',
5f7f0095 452 autoSelect: false,
80fdf8d2 453 bind: {
f6710aac 454 allowBlank: '{configuration}',
8ba2d669 455 },
80fdf8d2 456 cbind: {
f6710aac
TL
457 nodename: '{nodename}',
458 },
4616a55b 459 },
80fdf8d2
TL
460 {
461 xtype: 'proxmoxNetworkSelector',
462 name: 'cluster-network',
463 fieldLabel: 'Cluster Network IP/CIDR',
464 allowBlank: true,
465 autoSelect: false,
466 emptyText: gettext('Same as Public Network'),
467 cbind: {
f6710aac
TL
468 nodename: '{nodename}',
469 },
470 },
80fdf8d2
TL
471 // FIXME: add hint about cluster network and/or reference user to docs??
472 ],
473 column2: [
474 {
475 xtype: 'displayfield',
f6710aac 476 value: gettext('First Ceph monitor') + ':',
80fdf8d2
TL
477 },
478 {
f25c3855 479 xtype: 'displayfield',
80fdf8d2 480 fieldLabel: gettext('Monitor node'),
f25c3855
AL
481 cbind: {
482 value: '{nodename}',
483 },
80fdf8d2
TL
484 },
485 {
486 xtype: 'displayfield',
487 value: gettext('Additional monitors are recommended. They can be created at any time in the Monitor tab.'),
f6710aac
TL
488 userCls: 'pmx-hint',
489 },
80fdf8d2
TL
490 ],
491 advancedColumn1: [
492 {
493 xtype: 'numberfield',
494 name: 'size',
495 fieldLabel: 'Number of replicas',
496 bind: {
f6710aac 497 value: '{replicas}',
4616a55b 498 },
80fdf8d2
TL
499 maxValue: 7,
500 minValue: 2,
f6710aac 501 emptyText: '3',
80fdf8d2
TL
502 },
503 {
504 xtype: 'numberfield',
505 name: 'min_size',
506 fieldLabel: 'Minimum replicas',
507 bind: {
508 maxValue: '{replicas}',
f6710aac 509 value: '{minreplicas}',
4616a55b 510 },
80fdf8d2
TL
511 minValue: 2,
512 maxValue: 3,
513 setMaxValue: function(value) {
514 this.maxValue = Ext.Number.from(value, 2);
515 // allow enough to avoid split brains with max 'size', but more makes simply no sense
516 if (this.maxValue > 4) {
517 this.maxValue = 4;
a19aa238 518 }
80fdf8d2
TL
519 this.toggleSpinners();
520 this.validate();
71798b4b 521 },
f6710aac
TL
522 emptyText: '2',
523 },
80fdf8d2
TL
524 ],
525 onGetValues: function(values) {
526 ['cluster-network', 'size', 'min_size'].forEach(function(field) {
527 if (!values[field]) {
528 delete values[field];
8ba2d669 529 }
80fdf8d2
TL
530 });
531 return values;
532 },
533 onSubmit: function() {
534 var me = this;
535 if (!this.up('pveCephInstallWizard').getViewModel().get('configuration')) {
536 var wizard = me.up('window');
537 var kv = wizard.getValues();
399ffa76 538 delete kv.delete;
80fdf8d2
TL
539 var nodename = me.nodename;
540 delete kv.nodename;
541 Proxmox.Utils.API2Request({
f8b7b9e1 542 url: `/nodes/${nodename}/ceph/init`,
80fdf8d2
TL
543 waitMsgTarget: wizard,
544 method: 'POST',
545 params: kv,
546 success: function() {
547 Proxmox.Utils.API2Request({
f25c3855 548 url: `/nodes/${nodename}/ceph/mon/${nodename}`,
80fdf8d2
TL
549 waitMsgTarget: wizard,
550 method: 'POST',
551 success: function() {
552 me.up('pveCephInstallWizard').navigateNext();
553 },
554 failure: function(response, opts) {
555 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
f6710aac 556 },
80fdf8d2 557 });
8134b6b7 558 },
80fdf8d2
TL
559 failure: function(response, opts) {
560 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
f6710aac 561 },
4616a55b 562 });
80fdf8d2
TL
563 } else {
564 me.up('pveCephInstallWizard').navigateNext();
4616a55b 565 }
f6710aac 566 },
80fdf8d2
TL
567 },
568 {
569 title: gettext('Success'),
570 xtype: 'panel',
571 border: false,
572 bodyBorder: false,
573 onlineHelp: 'pve_ceph_install',
574 html: '<h3>Installation successful!</h3>'+
f00da0a5 575 '<p>The basic installation and configuration is complete. Depending on your setup, some of the following steps are required to start using Ceph:</p>'+
80fdf8d2
TL
576 '<ol><li>Install Ceph on other nodes</li>'+
577 '<li>Create additional Ceph Monitors</li>'+
578 '<li>Create Ceph OSDs</li>'+
579 '<li>Create Ceph Pools</li></ol>'+
f00da0a5 580 '<p>To learn more, click on the help button below.</p>',
80fdf8d2
TL
581 listeners: {
582 activate: function() {
583 // notify owning container that it should display a help button
584 if (this.onlineHelp) {
585 Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp);
586 }
4616a55b 587
80fdf8d2
TL
588 var tp = this.up('#wizcontent');
589 var idx = tp.items.indexOf(this)-1;
8058410f 590 for (;idx >= 0; idx--) {
80fdf8d2
TL
591 var nc = tp.items.getAt(idx);
592 if (nc) {
593 nc.disable();
4616a55b
TM
594 }
595 }
596 },
80fdf8d2
TL
597 deactivate: function() {
598 if (this.onlineHelp) {
599 Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp);
600 }
f6710aac 601 },
80fdf8d2
TL
602 },
603 onSubmit: function() {
604 var wizard = this.up('pveCephInstallWizard');
605 wizard.close();
f6710aac
TL
606 },
607 },
608 ],
c3035c01 609});