throw "Proxmox library not initialized";
}
-// avoid errors related to Accessible Rich Internet Applications
-// (access for people with disabilities)
-// TODO reenable after all components are upgraded
-Ext.enableAria = false;
-Ext.enableAriaButtons = false;
-Ext.enableAriaPanels = false;
-
// avoid errors when running without development tools
if (!Ext.isDefined(Ext.global.console)) {
let console = {
}
options.headers.CSRFPreventionToken = Proxmox.CSRFPreventionToken;
}
- let storedUser = Proxmox.Utils.getStoredUser();
- if (storedUser.token) {
- options.headers.Authorization = storedUser.token;
+ let storedAuth = Proxmox.Utils.getStoredAuth();
+ if (storedAuth.token) {
+ options.headers.Authorization = storedAuth.token;
}
});
noneText: gettext('none'),
NoneText: gettext('None'),
errorText: gettext('Error'),
+ warningsText: gettext('Warnings'),
unknownText: gettext('Unknown'),
defaultText: gettext('Default'),
daysText: gettext('days'),
language_map: {
ar: 'Arabic',
ca: 'Catalan',
+ zh_CN: 'Chinese (Simplified)',
+ zh_TW: 'Chinese (Traditional)',
da: 'Danish',
- de: 'German',
+ nl: 'Dutch',
en: 'English',
- es: 'Spanish',
eu: 'Euskera (Basque)',
- fa: 'Persian (Farsi)',
fr: 'French',
+ de: 'German',
he: 'Hebrew',
it: 'Italian',
ja: 'Japanese',
+ kr: 'Korean',
nb: 'Norwegian (Bokmal)',
nn: 'Norwegian (Nynorsk)',
+ fa: 'Persian (Farsi)',
pl: 'Polish',
pt_BR: 'Portuguese (Brazil)',
ru: 'Russian',
sl: 'Slovenian',
+ es: 'Spanish',
sv: 'Swedish',
tr: 'Turkish',
- zh_CN: 'Chinese (Simplified)',
- zh_TW: 'Chinese (Traditional)',
},
render_language: function(value) {
return min < width ? width : min;
},
+ // returns username + realm
+ parse_userid: function(userid) {
+ if (!Ext.isString(userid)) {
+ return [undefined, undefined];
+ }
+
+ let match = userid.match(/^(.+)@([^@]+)$/);
+ if (match !== null) {
+ return [match[1], match[2]];
+ }
+
+ return [undefined, undefined];
+ },
+
+ render_username: function(userid) {
+ let username = Proxmox.Utils.parse_userid(userid)[0] || "";
+ return Ext.htmlEncode(username);
+ },
+
+ render_realm: function(userid) {
+ let username = Proxmox.Utils.parse_userid(userid)[1] || "";
+ return Ext.htmlEncode(username);
+ },
+
getStoredAuth: function() {
let storedAuth = JSON.parse(window.localStorage.getItem('ProxmoxUser'));
return storedAuth || {};
if (Proxmox.LoggedOut) {
return undefined;
}
- let storedAuth = Proxmox.Utils.getStoredUser();
+ let storedAuth = Proxmox.Utils.getStoredAuth();
let cookie = Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name);
if ((Proxmox.UserName !== '' && cookie && !cookie.startsWith("PVE:tfa!")) || storedAuth.token) {
return cookie || storedAuth.token;
window.localStorage.removeItem("ProxmoxUser");
},
+ // The End-User gets redirected back here after login on the OpenID auth. portal, and in the
+ // redirection URL the state and auth.code are passed as URL GET params, this helper parses those
+ getOpenIDRedirectionAuthorization: function() {
+ const auth = Ext.Object.fromQueryString(window.location.search);
+ if (auth.state !== undefined && auth.code !== undefined) {
+ return auth;
+ }
+ return undefined;
+ },
+
// comp.setLoading() is buggy in ExtJS 4.0.7, so we
// use el.mask() instead
setErrorMask: function(comp, msg) {
return msg.join('<br>');
},
- monStoreErrors: function(component, store, clearMaskBeforeLoad) {
+ monStoreErrors: function(component, store, clearMaskBeforeLoad, errorCallback) {
if (clearMaskBeforeLoad) {
component.mon(store, 'beforeload', function(s, operation, eOpts) {
Proxmox.Utils.setErrorMask(component, false);
let error = request._operation.getError();
let msg = Proxmox.Utils.getResponseErrorMessage(error);
- Proxmox.Utils.setErrorMask(component, msg);
+ if (!errorCallback || !errorCallback(error, msg)) {
+ Proxmox.Utils.setErrorMask(component, msg);
+ }
});
},
Ext.Ajax.request(newopts);
},
+ // can be useful for catching displaying errors from the API, e.g.:
+ // Proxmox.Async.api2({
+ // ...
+ // }).catch(Proxmox.Utils.alertResponseFailure);
+ alertResponseFailure: (response) => {
+ Ext.Msg.alert(
+ gettext('Error'),
+ response.htmlStatus || response.result.message,
+ );
+ },
+
checked_command: function(orig_cmd) {
- Proxmox.Utils.API2Request({
- url: '/nodes/localhost/subscription',
- method: 'GET',
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, opts) {
- let data = response.result.data;
- if (data.status !== 'Active') {
- Ext.Msg.show({
- title: gettext('No valid subscription'),
- icon: Ext.Msg.WARNING,
- message: Proxmox.Utils.getNoSubKeyHtml(data.url),
- buttons: Ext.Msg.OK,
- callback: function(btn) {
- if (btn !== 'ok') {
- return;
- }
- orig_cmd();
- },
- });
- } else {
- orig_cmd();
- }
+ Proxmox.Utils.API2Request(
+ {
+ url: '/nodes/localhost/subscription',
+ method: 'GET',
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, opts) {
+ let res = response.result;
+ if (res === null || res === undefined || !res || res
+ .data.status.toLowerCase() !== 'active') {
+ Ext.Msg.show({
+ title: gettext('No valid subscription'),
+ icon: Ext.Msg.WARNING,
+ message: Proxmox.Utils.getNoSubKeyHtml(res.data.url),
+ buttons: Ext.Msg.OK,
+ callback: function(btn) {
+ if (btn !== 'ok') {
+ return;
+ }
+ orig_cmd();
+ },
+ });
+ } else {
+ orig_cmd();
+ }
+ },
},
- });
+ );
},
assemble_field_data: function(values, data) {
Proxmox.Utils.unknownText;
},
+ // NOTE: only add general, product agnostic, ones here! Else use override helper in product repos
task_desc_table: {
- acmenewcert: ['SRV', gettext('Order Certificate')],
- acmeregister: ['ACME Account', gettext('Register')],
- acmedeactivate: ['ACME Account', gettext('Deactivate')],
- acmeupdate: ['ACME Account', gettext('Update')],
- acmerefresh: ['ACME Account', gettext('Refresh')],
- acmerenew: ['SRV', gettext('Renew Certificate')],
- acmerevoke: ['SRV', gettext('Revoke Certificate')],
- 'auth-realm-sync': [gettext('Realm'), gettext('Sync')],
- 'auth-realm-sync-test': [gettext('Realm'), gettext('Sync Preview')],
- 'move_volume': ['CT', gettext('Move Volume')],
- clustercreate: ['', gettext('Create Cluster')],
- clusterjoin: ['', gettext('Join Cluster')],
+ aptupdate: ['', gettext('Update package database')],
diskinit: ['Disk', gettext('Initialize Disk with GPT')],
- vncproxy: ['VM/CT', gettext('Console')],
- spiceproxy: ['VM/CT', gettext('Console') + ' (Spice)'],
- vncshell: ['', gettext('Shell')],
spiceshell: ['', gettext('Shell') + ' (Spice)'],
- qmsnapshot: ['VM', gettext('Snapshot')],
- qmrollback: ['VM', gettext('Rollback')],
- qmdelsnapshot: ['VM', gettext('Delete Snapshot')],
- qmcreate: ['VM', gettext('Create')],
- qmrestore: ['VM', gettext('Restore')],
- qmdestroy: ['VM', gettext('Destroy')],
- qmigrate: ['VM', gettext('Migrate')],
- qmclone: ['VM', gettext('Clone')],
- qmmove: ['VM', gettext('Move disk')],
- qmtemplate: ['VM', gettext('Convert to template')],
- qmstart: ['VM', gettext('Start')],
- qmstop: ['VM', gettext('Stop')],
- qmreset: ['VM', gettext('Reset')],
- qmshutdown: ['VM', gettext('Shutdown')],
- qmreboot: ['VM', gettext('Reboot')],
- qmsuspend: ['VM', gettext('Hibernate')],
- qmpause: ['VM', gettext('Pause')],
- qmresume: ['VM', gettext('Resume')],
- qmconfig: ['VM', gettext('Configure')],
- vzsnapshot: ['CT', gettext('Snapshot')],
- vzrollback: ['CT', gettext('Rollback')],
- vzdelsnapshot: ['CT', gettext('Delete Snapshot')],
- vzcreate: ['CT', gettext('Create')],
- vzrestore: ['CT', gettext('Restore')],
- vzdestroy: ['CT', gettext('Destroy')],
- vzmigrate: ['CT', gettext('Migrate')],
- vzclone: ['CT', gettext('Clone')],
- vztemplate: ['CT', gettext('Convert to template')],
- vzstart: ['CT', gettext('Start')],
- vzstop: ['CT', gettext('Stop')],
- vzmount: ['CT', gettext('Mount')],
- vzumount: ['CT', gettext('Unmount')],
- vzshutdown: ['CT', gettext('Shutdown')],
- vzreboot: ['CT', gettext('Reboot')],
- vzsuspend: ['CT', gettext('Suspend')],
- vzresume: ['CT', gettext('Resume')],
- push_file: ['CT', gettext('Push file')],
- pull_file: ['CT', gettext('Pull file')],
- hamigrate: ['HA', gettext('Migrate')],
- hastart: ['HA', gettext('Start')],
- hastop: ['HA', gettext('Stop')],
- hashutdown: ['HA', gettext('Shutdown')],
+ srvreload: ['SRV', gettext('Reload')],
+ srvrestart: ['SRV', gettext('Restart')],
srvstart: ['SRV', gettext('Start')],
srvstop: ['SRV', gettext('Stop')],
- srvrestart: ['SRV', gettext('Restart')],
- srvreload: ['SRV', gettext('Reload')],
- cephcreatemgr: ['Ceph Manager', gettext('Create')],
- cephdestroymgr: ['Ceph Manager', gettext('Destroy')],
- cephcreatemon: ['Ceph Monitor', gettext('Create')],
- cephdestroymon: ['Ceph Monitor', gettext('Destroy')],
- cephcreateosd: ['Ceph OSD', gettext('Create')],
- cephdestroyosd: ['Ceph OSD', gettext('Destroy')],
- cephcreatepool: ['Ceph Pool', gettext('Create')],
- cephdestroypool: ['Ceph Pool', gettext('Destroy')],
- cephfscreate: ['CephFS', gettext('Create')],
- cephcreatemds: ['Ceph Metadata Server', gettext('Create')],
- cephdestroymds: ['Ceph Metadata Server', gettext('Destroy')],
- imgcopy: ['', gettext('Copy data')],
- imgdel: ['', gettext('Erase data')],
- unknownimgdel: ['', gettext('Destroy image from unknown guest')],
- download: ['', gettext('Download')],
- vzdump: (type, id) => id ? `VM/CT ${id} - ${gettext('Backup')}` : gettext('Backup Job'),
- aptupdate: ['', gettext('Update package database')],
- startall: ['', gettext('Start all VMs and Containers')],
- stopall: ['', gettext('Stop all VMs and Containers')],
- migrateall: ['', gettext('Migrate all VMs and Containers')],
- dircreate: [gettext('Directory Storage'), gettext('Create')],
- lvmcreate: [gettext('LVM Storage'), gettext('Create')],
- lvmthincreate: [gettext('LVM-Thin Storage'), gettext('Create')],
- zfscreate: [gettext('ZFS Storage'), gettext('Create')],
+ termproxy: ['', gettext('Console') + ' (xterm.js)'],
+ vncshell: ['', gettext('Shell')],
},
// to add or change existing for product specific ones
return text;
},
- format_size: function(size) {
- let units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
- let num = 0;
- while (size >= 1024 && num++ <= units.length) {
- size = size / 1024;
+ format_size: function(size, useSI) {
+ let units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
+ let order = 0;
+ const baseValue = useSI ? 1000 : 1024;
+ while (size >= baseValue && order < units.length) {
+ size = size / baseValue;
+ order++;
}
- return size.toFixed(num > 0?2:0) + " " + units[num] + "B";
+ let unit = units[order], commaDigits = 2;
+ if (order === 0) {
+ commaDigits = 0;
+ } else if (!useSI) {
+ unit += 'i';
+ }
+ return `${size.toFixed(commaDigits)} ${unit}B`;
},
render_upid: function(value, metaData, record) {
return Proxmox.Utils.format_duration_long(uptime);
},
+ systemd_unescape: function(string_value) {
+ const charcode_0 = '0'.charCodeAt(0);
+ const charcode_9 = '9'.charCodeAt(0);
+ const charcode_A = 'A'.charCodeAt(0);
+ const charcode_F = 'F'.charCodeAt(0);
+ const charcode_a = 'a'.charCodeAt(0);
+ const charcode_f = 'f'.charCodeAt(0);
+ const charcode_x = 'x'.charCodeAt(0);
+ const charcode_minus = '-'.charCodeAt(0);
+ const charcode_slash = '/'.charCodeAt(0);
+ const charcode_backslash = '\\'.charCodeAt(0);
+
+ let parse_hex_digit = function(d) {
+ if (d >= charcode_0 && d <= charcode_9) {
+ return d - charcode_0;
+ }
+ if (d >= charcode_A && d <= charcode_F) {
+ return d - charcode_A + 10;
+ }
+ if (d >= charcode_a && d <= charcode_f) {
+ return d - charcode_a + 10;
+ }
+ throw "got invalid hex digit";
+ };
+
+ let value = new TextEncoder().encode(string_value);
+ let result = new Uint8Array(value.length);
+
+ let i = 0;
+ let result_len = 0;
+
+ while (i < value.length) {
+ let c0 = value[i];
+ if (c0 === charcode_minus) {
+ result.set([charcode_slash], result_len);
+ result_len += 1;
+ i += 1;
+ continue;
+ }
+ if ((i + 4) < value.length) {
+ let c1 = value[i+1];
+ if (c0 === charcode_backslash && c1 === charcode_x) {
+ let h1 = parse_hex_digit(value[i+2]);
+ let h0 = parse_hex_digit(value[i+3]);
+ let ord = h1*16+h0;
+ result.set([ord], result_len);
+ result_len += 1;
+ i += 4;
+ continue;
+ }
+ }
+ result.set([c0], result_len);
+ result_len += 1;
+ i += 1;
+ }
+
+ return new TextDecoder().decode(result.slice(0, result.len));
+ },
+
parse_task_upid: function(upid) {
let task = {};
}
task.starttime = parseInt(res[6], 16);
task.type = res[7];
- task.id = res[8];
+ task.id = Proxmox.Utils.systemd_unescape(res[8]);
task.user = res[9];
task.desc = Proxmox.Utils.format_task_description(task.type, task.id);
return 'error';
},
+ format_task_status: function(status) {
+ let parsed = Proxmox.Utils.parse_task_status(status);
+ switch (parsed) {
+ case 'unknown': return Proxmox.Utils.unknownText;
+ case 'error': return Proxmox.Utils.errorText + ': ' + status;
+ case 'warning': return status.replace('WARNINGS', Proxmox.Utils.warningsText);
+ case 'ok': // fall-through
+ default: return status;
+ }
+ },
+
render_duration: function(value) {
if (value === undefined) {
return '-';
throw "no global OnlineHelpInfo map declared";
}
- return helpMap[section];
+ if (helpMap[section]) {
+ return helpMap[section];
+ }
+ // try to normalize - and _ separators, to support asciidoc and sphinx
+ // references at the same time.
+ let section_minus_normalized = section.replace(/_/g, '-');
+ if (helpMap[section_minus_normalized]) {
+ return helpMap[section_minus_normalized];
+ }
+ let section_underscore_normalized = section.replace(/-/g, '_');
+ return helpMap[section_underscore_normalized];
},
get_help_link: function(section) {
}
},
+ render_optional_url: function(value) {
+ if (value && value.match(/^https?:\/\//) !== null) {
+ return '<a target="_blank" href="' + value + '">' + value + '</a>';
+ }
+ return value;
+ },
+
+ render_san: function(value) {
+ var names = [];
+ if (Ext.isArray(value)) {
+ value.forEach(function(val) {
+ if (!Ext.isNumber(val)) {
+ names.push(val);
+ }
+ });
+ return names.join('<br>');
+ }
+ return value;
+ },
+
+ render_usage: val => (val * 100).toFixed(2) + '%',
+
+ render_cpu_usage: function(val, max) {
+ return Ext.String.format(
+ `${gettext('{0}% of {1}')} ${gettext('CPU(s)')}`,
+ (val*100).toFixed(2),
+ max,
+ );
+ },
+
+ render_size_usage: function(val, max, useSI) {
+ if (max === 0) {
+ return gettext('N/A');
+ }
+ let fmt = v => Proxmox.Utils.format_size(v, useSI);
+ let ratio = (val * 100 / max).toFixed(2);
+ return ratio + '% (' + Ext.String.format(gettext('{0} of {1}'), fmt(val), fmt(max)) + ')';
+ },
+
+ render_cpu: function(value, metaData, record, rowIndex, colIndex, store) {
+ if (!(record.data.uptime && Ext.isNumeric(value))) {
+ return '';
+ }
+
+ let maxcpu = record.data.maxcpu || 1;
+ if (!Ext.isNumeric(maxcpu) || maxcpu < 1) {
+ return '';
+ }
+ let cpuText = maxcpu > 1 ? 'CPUs' : 'CPU';
+ let ratio = (value * 100).toFixed(1);
+ return `${ratio}% of ${maxcpu.toString()} ${cpuText}`;
+ },
+
+ render_size: function(value, metaData, record, rowIndex, colIndex, store) {
+ if (!Ext.isNumeric(value)) {
+ return '';
+ }
+ return Proxmox.Utils.format_size(value);
+ },
+
+ render_cpu_model: function(cpu) {
+ let socketText = cpu.sockets > 1 ? gettext('Sockets') : gettext('Socket');
+ return `${cpu.cpus} x ${cpu.model} (${cpu.sockets.toString()} ${socketText})`;
+ },
+
+ /* this is different for nodes */
+ render_node_cpu_usage: function(value, record) {
+ return Proxmox.Utils.render_cpu_usage(value, record.cpus);
+ },
+
+ render_node_size_usage: function(record) {
+ return Proxmox.Utils.render_size_usage(record.used, record.total);
+ },
+
+ loadTextFromFile: function(file, callback, maxBytes) {
+ let maxSize = maxBytes || 8192;
+ if (file.size > maxSize) {
+ Ext.Msg.alert(gettext('Error'), gettext("Invalid file size: ") + file.size);
+ return;
+ }
+ let reader = new FileReader();
+ reader.onload = evt => callback(evt.target.result);
+ reader.readAsText(file);
+ },
+
+ parsePropertyString: function(value, defaultKey) {
+ var res = {},
+ error;
+
+ if (typeof value !== 'string' || value === '') {
+ return res;
+ }
+
+ Ext.Array.each(value.split(','), function(p) {
+ var kv = p.split('=', 2);
+ if (Ext.isDefined(kv[1])) {
+ res[kv[0]] = kv[1];
+ } else if (Ext.isDefined(defaultKey)) {
+ if (Ext.isDefined(res[defaultKey])) {
+ error = 'defaultKey may be only defined once in propertyString';
+ return false; // break
+ }
+ res[defaultKey] = kv[0];
+ } else {
+ error = 'invalid propertyString, not a key=value pair and no defaultKey defined';
+ return false; // break
+ }
+ return true;
+ });
+
+ if (error !== undefined) {
+ console.error(error);
+ return undefined;
+ }
+
+ return res;
+ },
+
+ printPropertyString: function(data, defaultKey) {
+ var stringparts = [],
+ gotDefaultKeyVal = false,
+ defaultKeyVal;
+
+ Ext.Object.each(data, function(key, value) {
+ if (defaultKey !== undefined && key === defaultKey) {
+ gotDefaultKeyVal = true;
+ defaultKeyVal = value;
+ } else if (Ext.isArray(value)) {
+ stringparts.push(key + '=' + value.join(';'));
+ } else if (value !== '') {
+ stringparts.push(key + '=' + value);
+ }
+ });
+
+ stringparts = stringparts.sort();
+ if (gotDefaultKeyVal) {
+ stringparts.unshift(defaultKeyVal);
+ }
+
+ return stringparts.join(',');
+ },
+
+ acmedomain_count: 5,
+
+ parseACMEPluginData: function(data) {
+ let res = {};
+ let extradata = [];
+ data.split('\n').forEach((line) => {
+ // capture everything after the first = as value
+ let [key, value] = line.split('=');
+ if (value !== undefined) {
+ res[key] = value;
+ } else {
+ extradata.push(line);
+ }
+ });
+ return [res, extradata];
+ },
+
+ delete_if_default: function(values, fieldname, default_val, create) {
+ if (values[fieldname] === '' || values[fieldname] === default_val) {
+ if (!create) {
+ if (values.delete) {
+ if (Ext.isArray(values.delete)) {
+ values.delete.push(fieldname);
+ } else {
+ values.delete += ',' + fieldname;
+ }
+ } else {
+ values.delete = fieldname;
+ }
+ }
+
+ delete values[fieldname];
+ }
+ },
+
+ printACME: function(value) {
+ if (Ext.isArray(value.domains)) {
+ value.domains = value.domains.join(';');
+ }
+ return Proxmox.Utils.printPropertyString(value);
+ },
+
+ parseACME: function(value) {
+ if (!value) {
+ return {};
+ }
+
+ var res = {};
+ var error;
+
+ Ext.Array.each(value.split(','), function(p) {
+ var kv = p.split('=', 2);
+ if (Ext.isDefined(kv[1])) {
+ res[kv[0]] = kv[1];
+ } else {
+ error = 'Failed to parse key-value pair: '+p;
+ return false;
+ }
+ return true;
+ });
+
+ if (error !== undefined) {
+ console.error(error);
+ return undefined;
+ }
+
+ if (res.domains !== undefined) {
+ res.domains = res.domains.split(/;/);
+ }
+
+ return res;
+ },
+
+ add_domain_to_acme: function(acme, domain) {
+ if (acme.domains === undefined) {
+ acme.domains = [domain];
+ } else {
+ acme.domains.push(domain);
+ acme.domains = acme.domains.filter((value, index, self) => self.indexOf(value) === index);
+ }
+ return acme;
+ },
+
+ remove_domain_from_acme: function(acme, domain) {
+ if (acme.domains !== undefined) {
+ acme.domains = acme.domains.filter(
+ (value, index, self) => self.indexOf(value) === index && value !== domain,
+ );
+ }
+ return acme;
+ },
+
+ updateColumns: function(container) {
+ let mode = Ext.state.Manager.get('summarycolumns') || 'auto';
+ let factor;
+ if (mode !== 'auto') {
+ factor = parseInt(mode, 10);
+ if (Number.isNaN(factor)) {
+ factor = 1;
+ }
+ } else {
+ factor = container.getSize().width < 1400 ? 1 : 2;
+ }
+
+ if (container.oldFactor === factor) {
+ return;
+ }
+
+ let items = container.query('>'); // direct childs
+ factor = Math.min(factor, items.length);
+ container.oldFactor = factor;
+
+ items.forEach((item) => {
+ item.columnWidth = 1 / factor;
+ });
+
+ // we have to update the layout twice, since the first layout change
+ // can trigger the scrollbar which reduces the amount of space left
+ container.updateLayout();
+ container.updateLayout();
+ },
+
+ get_health_icon: function(state, circle) {
+ if (circle === undefined) {
+ circle = false;
+ }
+
+ if (state === undefined) {
+ state = 'uknown';
+ }
+
+ var icon = 'faded fa-question';
+ switch (state) {
+ case 'good':
+ icon = 'good fa-check';
+ break;
+ case 'upgrade':
+ icon = 'warning fa-upload';
+ break;
+ case 'old':
+ icon = 'warning fa-refresh';
+ break;
+ case 'warning':
+ icon = 'warning fa-exclamation';
+ break;
+ case 'critical':
+ icon = 'critical fa-times';
+ break;
+ default: break;
+ }
+
+ if (circle) {
+ icon += '-circle';
+ }
+
+ return icon;
+ },
},
singleton: true,
me.IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$");
me.IP64_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + "/" + IPV6_CIDR_MASK + ")|(?:" + IPV4_REGEXP + "/" + IPV4_CIDR_MASK + ")$");
- let DnsName_REGEXP = "(?:(([a-zA-Z0-9]([a-zA-Z0-9\\-]*[a-zA-Z0-9])?)\\.)*([A-Za-z0-9]([A-Za-z0-9\\-]*[A-Za-z0-9])?))";
+ let DnsName_REGEXP = "(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-]*[a-zA-Z0-9])?)\\.)*(?:[A-Za-z0-9](?:[A-Za-z0-9\\-]*[A-Za-z0-9])?))";
me.DnsName_match = new RegExp("^" + DnsName_REGEXP + "$");
- me.HostPort_match = new RegExp("^(" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")(:\\d+)?$");
- me.HostPortBrackets_match = new RegExp("^\\[(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")\\](:\\d+)?$");
- me.IP6_dotnotation_match = new RegExp("^" + IPV6_REGEXP + "(\\.\\d+)?$");
- me.Vlan_match = /^vlan(\\d+)/;
- me.VlanInterface_match = /(\\w+)\\.(\\d+)/;
+ me.HostPort_match = new RegExp("^(" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")(?::(\\d+))?$");
+ me.HostPortBrackets_match = new RegExp("^\\[(" + IPV6_REGEXP + "|" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")\\](?::(\\d+))?$");
+ me.IP6_dotnotation_match = new RegExp("^(" + IPV6_REGEXP + ")(?:\\.(\\d+))?$");
+ me.Vlan_match = /^vlan(\d+)/;
+ me.VlanInterface_match = /(\w+)\.(\d+)/;
+ },
+});
+
+Ext.define('Proxmox.Async', {
+ singleton: true,
+
+ // Returns a Promise resolving to the result of an `API2Request` or rejecting to the error
+ // repsonse on failure
+ api2: function(reqOpts) {
+ return new Promise((resolve, reject) => {
+ delete reqOpts.callback; // not allowed in this api
+ reqOpts.success = response => resolve(response);
+ reqOpts.failure = response => reject(response);
+ Proxmox.Utils.API2Request(reqOpts);
+ });
+ },
+
+ // Delay for a number of milliseconds.
+ sleep: function(millis) {
+ return new Promise((resolve, _reject) => setTimeout(resolve, millis));
},
});