2018-12-05 12:39:40 +01:00
Ext . ns ( 'PBS' ) ;
console . log ( "Starting Backup Server GUI" ) ;
Ext . define ( 'PBS.Utils' , {
singleton : true ,
2021-02-26 07:48:13 +01:00
missingText : gettext ( 'missing' ) ,
2019-01-30 15:14:20 +01:00
updateLoginData : function ( data ) {
2020-05-29 16:22:14 +02:00
Proxmox . Utils . setAuthData ( data ) ;
2019-01-30 15:14:20 +01:00
} ,
2020-05-20 12:15:38 +02:00
dataStorePrefix : 'DataStore-' ,
2020-07-08 13:32:20 +02:00
cryptmap : [
'none' ,
'mixed' ,
'sign-only' ,
'encrypt' ,
] ,
cryptText : [
Proxmox . Utils . noText ,
gettext ( 'Mixed' ) ,
gettext ( 'Signed' ) ,
gettext ( 'Encrypted' ) ,
] ,
cryptIconCls : [
'' ,
'' ,
2020-07-23 13:03:49 +02:00
'lock faded' ,
'lock good' ,
2020-07-08 13:32:20 +02:00
] ,
2020-07-09 16:50:24 +02:00
calculateCryptMode : function ( data ) {
let mixed = data . mixed ;
let encrypted = data . encrypt ;
let signed = data [ 'sign-only' ] ;
let files = data . count ;
if ( mixed > 0 ) {
return PBS . Utils . cryptmap . indexOf ( 'mixed' ) ;
2020-09-09 16:18:11 +02:00
} else if ( files === encrypted && encrypted > 0 ) {
2020-07-08 13:32:20 +02:00
return PBS . Utils . cryptmap . indexOf ( 'encrypt' ) ;
2020-09-09 16:18:11 +02:00
} else if ( files === signed && signed > 0 ) {
2020-07-08 13:32:20 +02:00
return PBS . Utils . cryptmap . indexOf ( 'sign-only' ) ;
} else if ( ( signed + encrypted ) === 0 ) {
return PBS . Utils . cryptmap . indexOf ( 'none' ) ;
} else {
return PBS . Utils . cryptmap . indexOf ( 'mixed' ) ;
}
} ,
2020-11-10 08:07:49 +01:00
noSubKeyHtml : 'You do not have a valid subscription for this server. Please visit <a target="_blank" href="https://www.proxmox.com/proxmox-backup-server/pricing">www.proxmox.com</a> to get a list of available options.' ,
2020-05-20 12:15:38 +02:00
getDataStoreFromPath : function ( path ) {
return path . slice ( PBS . Utils . dataStorePrefix . length ) ;
} ,
isDataStorePath : function ( path ) {
return path . indexOf ( PBS . Utils . dataStorePrefix ) === 0 ;
} ,
2020-11-06 17:49:42 +01:00
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 null ;
}
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 ( value !== '' && value !== undefined ) {
stringparts . push ( key + '=' + value ) ;
}
} ) ;
stringparts = stringparts . sort ( ) ;
if ( gotDefaultKeyVal ) {
stringparts . unshift ( defaultKeyVal ) ;
}
return stringparts . join ( ',' ) ;
} ,
// helper for deleting field which are set to there default values
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 ] ;
}
} ,
2020-05-26 18:16:38 +02:00
render _datetime _utc : function ( datetime ) {
let pad = ( number ) => number < 10 ? '0' + number : number ;
return datetime . getUTCFullYear ( ) +
'-' + pad ( datetime . getUTCMonth ( ) + 1 ) +
'-' + pad ( datetime . getUTCDate ( ) ) +
'T' + pad ( datetime . getUTCHours ( ) ) +
':' + pad ( datetime . getUTCMinutes ( ) ) +
':' + pad ( datetime . getUTCSeconds ( ) ) +
'Z' ;
} ,
2020-05-26 13:37:57 +02:00
render _datastore _worker _id : function ( id , what ) {
2020-11-10 11:53:09 +01:00
const res = id . match ( /^(\S+?):(\S+?)\/(\S+?)(\/(.+))?$/ ) ;
2020-05-26 18:17:01 +02:00
if ( res ) {
2020-09-25 18:40:03 +02:00
let datastore = res [ 1 ] , backupGroup = ` ${ res [ 2 ] } / ${ res [ 3 ] } ` ;
2020-06-30 13:11:22 +02:00
if ( res [ 4 ] !== undefined ) {
let datetime = Ext . Date . parse ( parseInt ( res [ 5 ] , 16 ) , 'U' ) ;
let utctime = PBS . Utils . render _datetime _utc ( datetime ) ;
2020-09-25 18:40:03 +02:00
return ` Datastore ${ datastore } ${ what } ${ backupGroup } / ${ utctime } ` ;
2020-06-30 13:11:22 +02:00
} else {
2020-09-25 18:40:03 +02:00
return ` Datastore ${ datastore } ${ what } ${ backupGroup } ` ;
2020-06-30 13:11:22 +02:00
}
2020-05-26 18:17:01 +02:00
}
2020-06-30 13:11:22 +02:00
return ` Datastore ${ what } ${ id } ` ;
2020-05-26 18:17:01 +02:00
} ,
2020-05-26 13:37:57 +02:00
2022-05-31 13:11:23 +02:00
render _prune _job _worker _id : function ( id , what ) {
const res = id . match ( /^(\S+?):(\S+)$/ ) ;
if ( ! res ) {
return ` ${ what } on Datastore ${ id } ` ;
}
let datastore = res [ 1 ] , namespace = res [ 2 ] ;
return ` ${ what } on Datastore ${ datastore } Namespace ${ namespace } ` ;
} ,
2021-02-15 07:55:13 +01:00
render _tape _backup _id : function ( id , what ) {
const res = id . match ( /^(\S+?):(\S+?):(\S+?)(:(.+))?$/ ) ;
if ( res ) {
let datastore = res [ 1 ] ;
let pool = res [ 2 ] ;
let drive = res [ 3 ] ;
return ` ${ what } ${ datastore } (pool ${ pool } , drive ${ drive } ) ` ;
}
return ` ${ what } ${ id } ` ;
} ,
2021-02-18 09:23:50 +01:00
render _drive _load _media _id : function ( id , what ) {
const res = id . match ( /^(\S+?):(\S+?)$/ ) ;
if ( res ) {
let drive = res [ 1 ] ;
let label = res [ 2 ] ;
return gettext ( 'Drive' ) + ` ${ drive } - ${ what } ' ${ label } ' ` ;
}
return ` ${ what } ${ id } ` ;
} ,
2020-11-20 17:38:43 +01:00
// mimics Display trait in backend
renderKeyID : function ( fingerprint ) {
return fingerprint . substring ( 0 , 23 ) ;
} ,
2024-04-18 12:16:58 +02:00
render _task _status : function ( value , metadata , record , rowIndex , colIndex , store ) {
2024-04-22 11:02:57 +02:00
// GC tasks use 'upid' for backwards-compat, rest use 'last-run-upid'
if (
! record . data [ 'last-run-upid' ] &&
! store . getById ( 'last-run-upid' ) ? . data . value &&
! record . data . upid &&
! store . getById ( 'upid' ) ? . data . value
) {
2021-02-19 09:08:00 +01:00
return '-' ;
}
2024-04-18 12:16:58 +02:00
if ( ! record . data [ 'last-run-endtime' ] && ! store . getById ( 'last-run-endtime' ) ? . data . value ) {
2021-02-19 09:08:00 +01:00
metadata . tdCls = 'x-grid-row-loading' ;
return '' ;
}
let parsed = Proxmox . Utils . parse _task _status ( value ) ;
let text = value ;
let icon = '' ;
switch ( parsed ) {
case 'unknown' :
icon = 'question faded' ;
text = Proxmox . Utils . unknownText ;
break ;
case 'error' :
icon = 'times critical' ;
text = Proxmox . Utils . errorText + ': ' + value ;
break ;
case 'warning' :
icon = 'exclamation warning' ;
break ;
case 'ok' :
icon = 'check good' ;
text = gettext ( "OK" ) ;
}
return ` <i class="fa fa- ${ icon } "></i> ${ text } ` ;
} ,
render _next _task _run : function ( value , metadat , record ) {
if ( ! value ) return '-' ;
let now = new Date ( ) ;
let next = new Date ( value * 1000 ) ;
if ( next < now ) {
return gettext ( 'pending' ) ;
}
return Proxmox . Utils . render _timestamp ( value ) ;
} ,
render _optional _timestamp : function ( value , metadata , record ) {
if ( ! value ) return '-' ;
return Proxmox . Utils . render _timestamp ( value ) ;
} ,
2020-11-09 16:01:24 +01:00
parse _datastore _worker _id : function ( type , id ) {
let result ;
let res ;
if ( type . startsWith ( 'verif' ) ) {
res = PBS . Utils . VERIFICATION _JOB _ID _RE . exec ( id ) ;
if ( res ) {
result = res [ 1 ] ;
}
} else if ( type . startsWith ( 'sync' ) ) {
res = PBS . Utils . SYNC _JOB _ID _RE . exec ( id ) ;
if ( res ) {
result = res [ 3 ] ;
}
} else if ( type === 'backup' ) {
res = PBS . Utils . BACKUP _JOB _ID _RE . exec ( id ) ;
if ( res ) {
result = res [ 1 ] ;
}
} else if ( type === 'garbage_collection' ) {
return id ;
} else if ( type === 'prune' ) {
return id ;
}
return result ;
} ,
2020-10-22 11:40:43 +02:00
extractTokenUser : function ( tokenid ) {
return tokenid . match ( /^(.+)!([^!]+)$/ ) [ 1 ] ;
} ,
extractTokenName : function ( tokenid ) {
return tokenid . match ( /^(.+)!([^!]+)$/ ) [ 2 ] ;
} ,
2022-11-09 15:25:24 +01:00
render _estimate : function ( value , metaData , record ) {
if ( record . data . avail === 0 ) {
return gettext ( "Full" ) ;
}
2021-03-02 14:38:49 +01:00
if ( value === undefined ) {
2020-11-09 16:01:22 +01:00
return gettext ( 'Not enough data' ) ;
}
let now = new Date ( ) ;
let estimate = new Date ( value * 1000 ) ;
let timespan = ( estimate - now ) / 1000 ;
if ( Number ( estimate ) <= Number ( now ) || isNaN ( timespan ) ) {
return gettext ( 'Never' ) ;
}
let duration = Proxmox . Utils . format _duration _human ( timespan ) ;
return Ext . String . format ( gettext ( "in {0}" ) , duration ) ;
} ,
2023-11-29 18:32:06 +01:00
// FIXME: deprecated by Proxmox.Utils.render_size_usage ?!
2020-11-09 16:01:23 +01:00
render _size _usage : function ( val , max ) {
if ( max === 0 ) {
return gettext ( 'N/A' ) ;
}
return ( val * 100 / max ) . toFixed ( 2 ) + '% (' +
Ext . String . format ( gettext ( '{0} of {1}' ) ,
Proxmox . Utils . format _size ( val ) , Proxmox . Utils . format _size ( max ) ) + ')' ;
} ,
2020-11-10 09:15:12 +01:00
get _help _tool : function ( blockid ) {
let info = Proxmox . Utils . get _help _info ( blockid ) ;
if ( info === undefined ) {
info = Proxmox . Utils . get _help _info ( 'pbs_documentation_index' ) ;
}
if ( info === undefined ) {
throw "get_help_info failed" ; // should not happen
}
let docsURI = window . location . origin + info . link ;
let title = info . title ;
if ( info . subtitle ) {
title += ' - ' + info . subtitle ;
}
return {
type : 'help' ,
tooltip : title ,
handler : function ( ) {
window . open ( docsURI ) ;
2020-11-10 09:24:35 +01:00
} ,
2023-02-09 14:31:19 +01:00
} ;
2020-11-10 09:15:12 +01:00
} ,
2020-11-10 10:18:07 +01:00
calculate _dedup _factor : function ( gcstatus ) {
let dedup = 1.0 ;
if ( gcstatus [ 'disk-bytes' ] > 0 ) {
dedup = ( gcstatus [ 'index-data-bytes' ] || 0 ) / gcstatus [ 'disk-bytes' ] ;
}
return dedup ;
} ,
2021-02-02 14:00:36 +01:00
parse _snapshot _id : function ( snapshot ) {
if ( ! snapshot ) {
return [ undefined , undefined , undefined ] ;
}
2022-05-12 16:23:26 +02:00
let nsRegex = /(?:^|\/)(ns\/([^/]+))/g ;
let namespaces = [ ] ;
let nsPaths = [ ] ;
snapshot = snapshot . replace ( nsRegex , ( _ , nsPath , ns ) => { nsPaths . push ( nsPath ) ; namespaces . push ( ns ) ; return "" ; } ) ;
let [ _match , type , group , id ] = /^\/?([^/]+)\/([^/]+)\/(.+)$/ . exec ( snapshot ) ;
2021-02-02 14:00:36 +01:00
2022-05-12 16:23:26 +02:00
return [ type , group , id , namespaces . join ( '/' ) , nsPaths . join ( '/' ) ] ;
2021-02-02 14:00:36 +01:00
} ,
2021-02-02 14:00:35 +01:00
get _type _icon _cls : function ( btype ) {
var cls = '' ;
if ( btype . startsWith ( 'vm' ) ) {
cls = 'fa-desktop' ;
} else if ( btype . startsWith ( 'ct' ) ) {
cls = 'fa-cube' ;
} else if ( btype . startsWith ( 'host' ) ) {
cls = 'fa-building' ;
}
return cls ;
} ,
2018-12-05 12:39:40 +01:00
constructor : function ( ) {
var me = this ;
2020-11-09 16:01:24 +01:00
let PROXMOX _SAFE _ID _REGEX = "([A-Za-z0-9_][A-Za-z0-9._-]*)" ;
2022-05-06 11:02:16 +02:00
me . SAFE _ID _RE = new RegExp ( ` ^ ${ PROXMOX _SAFE _ID _REGEX } $ ` ) ;
// only anchored at beginning, only parses datastore for now
2020-11-09 16:01:24 +01:00
me . VERIFICATION _JOB _ID _RE = new RegExp ( "^" + PROXMOX _SAFE _ID _REGEX + ':?' ) ;
me . SYNC _JOB _ID _RE = new RegExp ( "^" + PROXMOX _SAFE _ID _REGEX + ':' +
PROXMOX _SAFE _ID _REGEX + ':' + PROXMOX _SAFE _ID _REGEX + ':' ) ;
me . BACKUP _JOB _ID _RE = new RegExp ( "^" + PROXMOX _SAFE _ID _REGEX + ':' ) ;
2018-12-05 12:39:40 +01:00
// do whatever you want here
2020-05-25 19:06:47 +02:00
Proxmox . Utils . override _task _descriptions ( {
2021-05-11 18:08:10 +02:00
'acme-deactivate' : ( type , id ) =>
Ext . String . format ( gettext ( "Deactivate {0} Account" ) , 'ACME' ) + ` ' ${ id || 'default' } ' ` ,
'acme-register' : ( type , id ) =>
Ext . String . format ( gettext ( "Register {0} Account" ) , 'ACME' ) + ` ' ${ id || 'default' } ' ` ,
'acme-update' : ( type , id ) =>
Ext . String . format ( gettext ( "Update {0} Account" ) , 'ACME' ) + ` ' ${ id || 'default' } ' ` ,
'acme-new-cert' : [ '' , gettext ( 'Order Certificate' ) ] ,
'acme-renew-cert' : [ '' , gettext ( 'Renew Certificate' ) ] ,
'acme-revoke-cert' : [ '' , gettext ( 'Revoke Certificate' ) ] ,
2020-10-30 14:02:58 +01:00
backup : ( type , id ) => PBS . Utils . render _datastore _worker _id ( id , gettext ( 'Backup' ) ) ,
2021-03-28 16:45:23 +02:00
'barcode-label-media' : [ gettext ( 'Drive' ) , gettext ( 'Barcode-Label Media' ) ] ,
'catalog-media' : [ gettext ( 'Drive' ) , gettext ( 'Catalog Media' ) ] ,
2021-06-02 13:27:04 +02:00
'delete-datastore' : [ gettext ( 'Datastore' ) , gettext ( 'Remove Datastore' ) ] ,
2022-05-15 16:47:42 +02:00
'delete-namespace' : [ gettext ( 'Namespace' ) , gettext ( 'Remove Namespace' ) ] ,
2020-10-30 14:02:58 +01:00
dircreate : [ gettext ( 'Directory Storage' ) , gettext ( 'Create' ) ] ,
dirremove : [ gettext ( 'Directory' ) , gettext ( 'Remove' ) ] ,
2021-03-28 16:45:23 +02:00
'eject-media' : [ gettext ( 'Drive' ) , gettext ( 'Eject Media' ) ] ,
2021-03-31 09:19:19 +02:00
"format-media" : [ gettext ( 'Drive' ) , gettext ( 'Format media' ) ] ,
2021-05-17 09:03:15 +02:00
"forget-group" : [ gettext ( 'Group' ) , gettext ( 'Remove Group' ) ] ,
2021-03-28 13:50:50 +02:00
garbage _collection : [ 'Datastore' , gettext ( 'Garbage Collect' ) ] ,
2023-02-09 14:31:19 +01:00
'realm-sync' : [ 'Realm' , gettext ( 'User Sync' ) ] ,
2021-03-28 16:45:23 +02:00
'inventory-update' : [ gettext ( 'Drive' ) , gettext ( 'Inventory Update' ) ] ,
'label-media' : [ gettext ( 'Drive' ) , gettext ( 'Label Media' ) ] ,
'load-media' : ( type , id ) => PBS . Utils . render _drive _load _media _id ( id , gettext ( 'Load Media' ) ) ,
2020-11-04 14:20:44 +01:00
logrotate : [ null , gettext ( 'Log Rotation' ) ] ,
2024-11-26 11:45:49 +01:00
'mount-device' : [ gettext ( 'Datastore' ) , gettext ( 'Mount Device' ) ] ,
2020-10-30 14:02:58 +01:00
prune : ( type , id ) => PBS . Utils . render _datastore _worker _id ( id , gettext ( 'Prune' ) ) ,
2022-05-31 13:11:23 +02:00
prunejob : ( type , id ) => PBS . Utils . render _prune _job _worker _id ( id , gettext ( 'Prune Job' ) ) ,
2021-03-28 13:50:50 +02:00
reader : ( type , id ) => PBS . Utils . render _datastore _worker _id ( id , gettext ( 'Read Objects' ) ) ,
2021-03-28 16:45:23 +02:00
'rewind-media' : [ gettext ( 'Drive' ) , gettext ( 'Rewind Media' ) ] ,
2020-09-25 18:40:03 +02:00
sync : [ 'Datastore' , gettext ( 'Remote Sync' ) ] ,
2020-10-30 14:02:58 +01:00
syncjob : [ gettext ( 'Sync Job' ) , gettext ( 'Remote Sync' ) ] ,
2021-03-28 16:45:23 +02:00
'tape-backup' : ( type , id ) => PBS . Utils . render _tape _backup _id ( id , gettext ( 'Tape Backup' ) ) ,
'tape-backup-job' : ( type , id ) => PBS . Utils . render _tape _backup _id ( id , gettext ( 'Tape Backup Job' ) ) ,
'tape-restore' : [ 'Datastore' , gettext ( 'Tape Restore' ) ] ,
'unload-media' : [ gettext ( 'Drive' ) , gettext ( 'Unload Media' ) ] ,
2024-11-26 11:45:49 +01:00
'unmount-device' : [ gettext ( 'Datastore' ) , gettext ( 'Unmount Device' ) ] ,
2021-03-28 16:45:23 +02:00
verificationjob : [ gettext ( 'Verify Job' ) , gettext ( 'Scheduled Verification' ) ] ,
2020-10-20 11:10:10 +02:00
verify : [ 'Datastore' , gettext ( 'Verification' ) ] ,
verify _group : [ 'Group' , gettext ( 'Verification' ) ] ,
verify _snapshot : [ 'Snapshot' , gettext ( 'Verification' ) ] ,
2023-11-28 14:23:22 +01:00
wipedisk : [ 'Device' , gettext ( 'Wipe Disk' ) ] ,
2020-10-30 14:02:58 +01:00
zfscreate : [ gettext ( 'ZFS Storage' ) , gettext ( 'Create' ) ] ,
2020-05-25 19:06:47 +02:00
} ) ;
2021-07-09 13:44:00 +02:00
2024-04-23 13:52:18 +02:00
Proxmox . Utils . overrideNotificationFieldName ( {
'datastore' : gettext ( 'Datastore' ) ,
'job-id' : gettext ( 'Job ID' ) ,
'media-pool' : gettext ( 'Media Pool' ) ,
} ) ;
Proxmox . Utils . overrideNotificationFieldValue ( {
'acme' : gettext ( 'ACME certificate renewal' ) ,
'gc' : gettext ( 'Garbage collection' ) ,
'package-updates' : gettext ( 'Package updates are available' ) ,
'prune' : gettext ( 'Prune job' ) ,
'sync' : gettext ( 'Sync job' ) ,
'tape-backup' : gettext ( 'Tape backup notifications' ) ,
'tape-load' : gettext ( 'Tape loading request' ) ,
'verify' : gettext ( 'Verification job' ) ,
} ) ;
2021-07-09 16:52:25 +02:00
Proxmox . Schema . overrideAuthDomains ( {
2021-07-09 13:44:00 +02:00
pbs : {
name : 'Proxmox Backup authentication server' ,
add : false ,
edit : false ,
pwchange : true ,
2023-02-09 14:31:19 +01:00
sync : false ,
2021-07-09 13:44:00 +02:00
} ,
} ) ;
2024-04-23 13:52:28 +02:00
// TODO: use `overrideEndpointTypes` later - not done right now to avoid
// breakage if widget-toolkit is not updated yet.
Proxmox . Schema . notificationEndpointTypes = {
sendmail : {
name : 'Sendmail' ,
ipanel : 'pmxSendmailEditPanel' ,
iconCls : 'fa-envelope-o' ,
2024-04-25 09:37:07 +02:00
defaultMailAuthor : 'Proxmox Backup Server - $hostname' ,
2024-04-23 13:52:28 +02:00
} ,
smtp : {
name : 'SMTP' ,
ipanel : 'pmxSmtpEditPanel' ,
iconCls : 'fa-envelope-o' ,
2024-04-25 09:37:07 +02:00
defaultMailAuthor : 'Proxmox Backup Server - $hostname' ,
2024-04-23 13:52:28 +02:00
} ,
gotify : {
name : 'Gotify' ,
ipanel : 'pmxGotifyEditPanel' ,
iconCls : 'fa-bell-o' ,
} ,
2024-11-08 15:41:23 +01:00
webhook : {
name : 'Webhook' ,
ipanel : 'pmxWebhookEditPanel' ,
iconCls : 'fa-bell-o' ,
} ,
2024-04-23 13:52:28 +02:00
} ;
2020-09-25 18:40:03 +02:00
} ,
2020-11-02 14:36:10 +01:00
// Convert an ArrayBuffer to a base64url encoded string.
// A `null` value will be preserved for convenience.
bytes _to _base64url : function ( bytes ) {
if ( bytes === null ) {
return null ;
}
return btoa ( Array
. from ( new Uint8Array ( bytes ) )
. map ( val => String . fromCharCode ( val ) )
. join ( '' ) ,
)
. replace ( /\+/g , '-' )
. replace ( /\//g , '_' )
. replace ( /[=]/g , '' ) ;
} ,
// Convert an a base64url string to an ArrayBuffer.
// A `null` value will be preserved for convenience.
base64url _to _bytes : function ( b64u ) {
if ( b64u === null ) {
return null ;
}
return new Uint8Array (
atob ( b64u
. replace ( /-/g , '+' )
. replace ( /_/g , '/' ) ,
)
. split ( '' )
. map ( val => val . charCodeAt ( 0 ) ) ,
) ;
} ,
2021-02-25 11:52:42 +01:00
driveCommand : function ( driveid , command , reqOpts ) {
let params = Ext . apply ( reqOpts , {
url : ` /api2/extjs/tape/drive/ ${ driveid } / ${ command } ` ,
timeout : 5 * 60 * 1000 ,
failure : function ( response ) {
Ext . Msg . alert ( gettext ( 'Error' ) , response . htmlStatus ) ;
} ,
} ) ;
Proxmox . Utils . API2Request ( params ) ;
} ,
showMediaLabelWindow : function ( response ) {
let list = [ ] ;
for ( let [ key , val ] of Object . entries ( response . result . data ) ) {
if ( key === 'ctime' || key === 'media-set-ctime' ) {
val = Proxmox . Utils . render _timestamp ( val ) ;
}
list . push ( { key : key , value : val } ) ;
}
Ext . create ( 'Ext.window.Window' , {
title : gettext ( 'Label Information' ) ,
modal : true ,
width : 600 ,
height : 450 ,
layout : 'fit' ,
scrollable : true ,
items : [
{
xtype : 'grid' ,
store : {
data : list ,
} ,
columns : [
{
text : gettext ( 'Property' ) ,
dataIndex : 'key' ,
width : 120 ,
} ,
{
text : gettext ( 'Value' ) ,
dataIndex : 'value' ,
flex : 1 ,
} ,
] ,
} ,
] ,
} ) . show ( ) ;
} ,
showCartridgeMemoryWindow : function ( response ) {
Ext . create ( 'Ext.window.Window' , {
title : gettext ( 'Cartridge Memory' ) ,
modal : true ,
width : 600 ,
height : 450 ,
layout : 'fit' ,
scrollable : true ,
items : [
{
xtype : 'grid' ,
store : {
data : response . result . data ,
} ,
columns : [
{
text : gettext ( 'ID' ) ,
2021-03-08 12:52:06 +01:00
hidden : true ,
2021-02-25 11:52:42 +01:00
dataIndex : 'id' ,
width : 60 ,
} ,
{
text : gettext ( 'Name' ) ,
dataIndex : 'name' ,
flex : 2 ,
} ,
{
text : gettext ( 'Value' ) ,
dataIndex : 'value' ,
flex : 1 ,
} ,
] ,
} ,
] ,
} ) . show ( ) ;
} ,
showVolumeStatisticsWindow : function ( response ) {
let list = [ ] ;
for ( let [ key , val ] of Object . entries ( response . result . data ) ) {
if ( key === 'total-native-capacity' ||
key === 'total-used-native-capacity' ||
key === 'lifetime-bytes-read' ||
key === 'lifetime-bytes-written' ||
key === 'last-mount-bytes-read' ||
key === 'last-mount-bytes-written' ) {
val = Proxmox . Utils . format _size ( val ) ;
}
list . push ( { key : key , value : val } ) ;
}
Ext . create ( 'Ext.window.Window' , {
title : gettext ( 'Volume Statistics' ) ,
modal : true ,
width : 600 ,
height : 450 ,
layout : 'fit' ,
scrollable : true ,
items : [
{
xtype : 'grid' ,
store : {
data : list ,
} ,
columns : [
{
text : gettext ( 'Property' ) ,
dataIndex : 'key' ,
flex : 1 ,
} ,
{
text : gettext ( 'Value' ) ,
dataIndex : 'value' ,
flex : 1 ,
} ,
] ,
} ,
] ,
} ) . show ( ) ;
} ,
showDriveStatusWindow : function ( response ) {
let list = [ ] ;
for ( let [ key , val ] of Object . entries ( response . result . data ) ) {
if ( key === 'manufactured' ) {
val = Proxmox . Utils . render _timestamp ( val ) ;
}
if ( key === 'bytes-read' || key === 'bytes-written' ) {
val = Proxmox . Utils . format _size ( val ) ;
}
2024-05-13 12:49:24 +02:00
if ( key === 'drive-activity' ) {
val = PBS . Utils . renderDriveActivity ( val ) ;
}
2021-02-25 11:52:42 +01:00
list . push ( { key : key , value : val } ) ;
}
Ext . create ( 'Ext.window.Window' , {
title : gettext ( 'Status' ) ,
modal : true ,
width : 600 ,
height : 450 ,
layout : 'fit' ,
scrollable : true ,
items : [
{
xtype : 'grid' ,
store : {
data : list ,
} ,
columns : [
{
text : gettext ( 'Property' ) ,
dataIndex : 'key' ,
width : 120 ,
} ,
{
text : gettext ( 'Value' ) ,
dataIndex : 'value' ,
flex : 1 ,
} ,
] ,
} ,
] ,
} ) . show ( ) ;
} ,
2024-05-13 12:49:24 +02:00
tapeDriveActivities : {
'no-activity' : gettext ( 'No Activity' ) ,
'cleaning' : gettext ( 'Cleaning' ) ,
'loading' : gettext ( 'Loading' ) ,
'unloading' : gettext ( 'Unloading' ) ,
'other' : gettext ( 'Other Activity' ) ,
'reading' : gettext ( 'Reading data' ) ,
'writing' : gettext ( 'Writing data' ) ,
'locating' : gettext ( 'Locating' ) ,
'rewinding' : gettext ( 'Rewinding' ) ,
'erasing' : gettext ( 'Erasing' ) ,
'formatting' : gettext ( 'Formatting' ) ,
'calibrating' : gettext ( 'Calibrating' ) ,
'other-dt' : gettext ( 'Other DT Activity' ) ,
'microcode-update' : gettext ( 'Updating Microcode' ) ,
'reading-encrypted' : gettext ( 'Reading encrypted data' ) ,
'writing-encrypted' : gettext ( 'Writing encrypted data' ) ,
} ,
renderDriveActivity : function ( value ) {
if ( ! value ) {
return Proxmox . Utils . unknownText ;
}
return PBS . Utils . tapeDriveActivities [ value ] ? ? value ;
} ,
2024-05-13 12:49:26 +02:00
renderDriveState : function ( value , md , rec ) {
2021-03-02 12:19:37 +01:00
if ( ! value ) {
2024-05-13 12:49:26 +02:00
if ( rec ? . data ? . activity && rec ? . data ? . activity !== 'no-activity' ) {
return PBS . Utils . renderDriveActivity ( rec . data . activity ) ;
}
2021-03-02 12:19:37 +01:00
return gettext ( 'Idle' ) ;
}
let icon = '<i class="fa fa-spinner fa-pulse fa-fw"></i>' ;
if ( value . startsWith ( "UPID" ) ) {
let upid = Proxmox . Utils . parse _task _upid ( value ) ;
md . tdCls = "pointer" ;
return ` ${ icon } ${ upid . desc } ` ;
}
return ` ${ icon } ${ value } ` ;
} ,
2024-11-25 17:22:04 +01:00
/ * *
* Parses maintenance mode property string .
* Examples :
* "offline,message=foo" - > [ "offline" , "foo" ]
* "offline" - > [ "offline" , null ]
* "message=foo,offline" - > [ "offline" , "foo" ]
* null / undefined - > [ null , null ]
*
* @ param { string | null } mode - Maintenance mode string to parse .
* @ return { Array < string | null > } - Parsed maintenance mode values .
* /
2022-04-26 06:23:34 +00:00
parseMaintenanceMode : function ( mode ) {
2024-11-25 17:22:04 +01:00
if ( ! mode ) {
return [ null , null ] ;
}
return mode . split ( ',' ) . reduce ( ( [ m , msg ] , pair ) => {
const [ key , value ] = pair . split ( '=' ) ;
if ( key === 'message' ) {
return [ m , value . replace ( /^"(.*)"$/ , '$1' ) . replace ( /\\"/g , '"' ) ] ;
} else {
return [ value ? ? key , msg ] ;
}
} , [ null , null ] ) ;
2022-04-26 06:23:34 +00:00
} ,
2022-04-12 05:26:01 +00:00
renderMaintenance : function ( mode , activeTasks ) {
2022-04-12 16:11:49 +02:00
if ( ! mode ) {
return gettext ( 'None' ) ;
}
2022-04-26 06:23:34 +00:00
let [ type , message ] = PBS . Utils . parseMaintenanceMode ( mode ) ;
2022-04-12 05:26:01 +00:00
2022-04-12 16:11:49 +02:00
let extra = '' ;
2022-05-17 08:52:13 +02:00
if ( activeTasks !== undefined ) {
2024-11-25 17:22:07 +01:00
const conflictingTasks = activeTasks . write + ( type === 'offline' || type === 'unmount' ? activeTasks . read : 0 ) ;
2022-05-17 08:52:13 +02:00
if ( conflictingTasks > 0 ) {
extra += '| <i class="fa fa-spinner fa-pulse fa-fw"></i> ' ;
extra += Ext . String . format ( gettext ( '{0} conflicting tasks still active.' ) , conflictingTasks ) ;
} else {
extra += '<i class="fa fa-check"></i>' ;
}
2022-04-12 16:11:49 +02:00
}
2022-04-12 05:26:01 +00:00
2022-04-12 16:12:05 +02:00
if ( message ) {
2022-04-26 06:23:34 +00:00
extra += ` (" ${ message } ") ` ;
2022-04-12 16:12:05 +02:00
}
2022-04-12 05:26:01 +00:00
let modeText = Proxmox . Utils . unknownText ;
switch ( type ) {
2022-04-12 16:11:49 +02:00
case 'read-only' : modeText = gettext ( "Read-only" ) ;
2022-04-12 05:26:01 +00:00
break ;
2022-04-12 16:11:49 +02:00
case 'offline' : modeText = gettext ( "Offline" ) ;
2022-04-12 05:26:01 +00:00
break ;
2024-11-25 17:22:07 +01:00
case 'unmount' : modeText = gettext ( "Unmounting" ) ;
break ;
2022-04-12 05:26:01 +00:00
}
return ` ${ modeText } ${ extra } ` ;
} ,
2022-05-10 15:01:48 +02:00
render _optional _namespace : function ( value , metadata , record ) {
2022-05-14 18:39:52 +02:00
if ( ! value ) return ` - ( ${ gettext ( 'Root' ) } ) ` ;
2022-05-10 15:01:48 +02:00
return Ext . String . htmlEncode ( value ) ;
} ,
2023-11-21 15:31:55 +01:00
render _optional _remote : function ( value , metadata , record ) {
2023-11-25 17:13:47 +01:00
if ( ! value ) {
return ` - ( ${ gettext ( 'Local' ) } ) ` ;
}
2023-11-21 15:31:55 +01:00
return Ext . String . htmlEncode ( value ) ;
} ,
2022-11-28 11:13:06 +01:00
tuningOptions : {
'chunk-order' : {
2022-11-28 14:26:39 +01:00
'__default__' : Proxmox . Utils . defaultText + ` ( ${ gettext ( 'Inode' ) } ) ` ,
2022-11-28 11:13:06 +01:00
none : gettext ( 'None' ) ,
inode : gettext ( 'Inode' ) ,
} ,
'sync-level' : {
'__default__' : Proxmox . Utils . defaultText + ` ( ${ gettext ( 'Filesystem' ) } ) ` ,
none : gettext ( 'None' ) ,
file : gettext ( 'File' ) ,
filesystem : gettext ( 'Filesystem' ) ,
} ,
} ,
render _tuning _options : function ( tuning ) {
let options = [ ] ;
let order = tuning [ 'chunk-order' ] ;
delete tuning [ 'chunk-order' ] ;
order = PBS . Utils . tuningOptions [ 'chunk-order' ] [ order ? ? '__default__' ] ;
options . push ( ` ${ gettext ( 'Chunk Order' ) } : ${ order } ` ) ;
let sync = tuning [ 'sync-level' ] ;
delete tuning [ 'sync-level' ] ;
sync = PBS . Utils . tuningOptions [ 'sync-level' ] [ sync ? ? '__default__' ] ;
options . push ( ` ${ gettext ( 'Sync Level' ) } : ${ sync } ` ) ;
for ( const [ k , v ] of Object . entries ( tuning ) ) {
options . push ( ` ${ k } : ${ v } ` ) ;
}
return options . join ( ', ' ) ;
} ,
2020-11-02 14:36:10 +01:00
} ) ;