From 48c79f7fce871b82c6a30d36071fb7521ff52094 Mon Sep 17 00:00:00 2001 From: baron_l Date: Fri, 1 Mar 2019 00:19:09 +0100 Subject: [PATCH] feat(storidge): add volume and snapshot details --- app/docker/views/volumes/edit/volume.html | 18 ++ .../views/volumes/edit/volumeController.js | 65 ++++++- app/extensions/storidge/__module.js | 12 ++ .../storidgeSnapshotsDatatable.html | 95 ++++++++++ .../storidgeSnapshotsDatatable.js | 13 ++ .../storidgeSnapshotsDatatableController.js | 7 + .../volumeStoridgeInfo.html | 174 ++++++++++++++++++ .../volumeStoridgeInfo.js | 7 + .../volumeStoridgeInfoController.js | 104 +++++++++++ app/extensions/storidge/models/snapshot.js | 9 + app/extensions/storidge/models/volume.js | 43 +++++ app/extensions/storidge/rest/storidge.js | 15 +- .../storidge/services/snapshotService.js | 72 ++++++++ .../storidge/services/volumeService.js | 36 ++++ .../views/drives/inspect/driveController.js | 4 +- .../views/snapshots/inspect/snapshot.html | 53 ++++++ .../snapshots/inspect/snapshotController.js | 44 +++++ 17 files changed, 764 insertions(+), 7 deletions(-) create mode 100644 app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatable.html create mode 100644 app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatable.js create mode 100644 app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatableController.js create mode 100644 app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.html create mode 100644 app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.js create mode 100644 app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfoController.js create mode 100644 app/extensions/storidge/models/snapshot.js create mode 100644 app/extensions/storidge/models/volume.js create mode 100644 app/extensions/storidge/services/snapshotService.js create mode 100644 app/extensions/storidge/services/volumeService.js create mode 100644 app/extensions/storidge/views/snapshots/inspect/snapshot.html create mode 100644 app/extensions/storidge/views/snapshots/inspect/snapshotController.js diff --git a/app/docker/views/volumes/edit/volume.html b/app/docker/views/volumes/edit/volume.html index 9d191de5b..97b7fa697 100644 --- a/app/docker/views/volumes/edit/volume.html +++ b/app/docker/views/volumes/edit/volume.html @@ -45,6 +45,24 @@ +
+
+ + +
+
+ +
+
+ + +
+
+ + + +
+
+ {{ $ctrl.titleText }} +
+
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + Id + + + + + + Date + + + + + + Description + + + +
+ + + + + + {{ item.Date }}{{ item.Description }}
Loading...
No snapshots available.
+
+ +
+
+ diff --git a/app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatable.js b/app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatable.js new file mode 100644 index 000000000..85e7f7d8f --- /dev/null +++ b/app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatable.js @@ -0,0 +1,13 @@ +angular.module('extension.storidge').component('storidgeSnapshotsDatatable', { + templateUrl: 'app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatable.html', + controller: 'StoridgeSnapshotsDatatableController', + bindings: { + titleText: '@', + titleIcon: '@', + dataset: '<', + tableKey: '@', + orderBy: '@', + reverseOrder: '<', + removeAction: '<' + } +}); diff --git a/app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatableController.js b/app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatableController.js new file mode 100644 index 000000000..501a74343 --- /dev/null +++ b/app/extensions/storidge/components/snapshots-datatable/storidgeSnapshotsDatatableController.js @@ -0,0 +1,7 @@ +angular.module('portainer.docker') + .controller('StoridgeSnapshotsDatatableController', ['$scope', '$controller', + function ($scope, $controller) { + angular.extend(this, $controller('GenericDatatableController', {$scope: $scope})); + + } +]); \ No newline at end of file diff --git a/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.html b/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.html new file mode 100644 index 000000000..4e8ee4c2d --- /dev/null +++ b/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.html @@ -0,0 +1,174 @@ + + +
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name{{ $ctrl.volume.Name }}
Uuid{{ $ctrl.volume.Uuid }}
Node{{ $ctrl.volume.Node }}
Node ID{{ $ctrl.volume.NodeID }}
Directory{{ $ctrl.volume.Directory }}
Capacity{{ $ctrl.volume.Capacity }}
Allocated{{ $ctrl.volume.Allocated }}
IOPS Max{{ $ctrl.volume.IOPSMax }}
IOPS Min{{ $ctrl.volume.IOPSMin }}
Local Drive Only{{ $ctrl.volume.LocalDriveOnly }}
Provisioning{{ $ctrl.volume.Provisioning }}
Redundancy{{ $ctrl.volume.Redundancy }}
Type{{ $ctrl.volume.Type }}
Vdisk{{ $ctrl.volume.Vdisk }}
Labels + + + + + +
{{ var|key: '=' }}{{ var|value: '=' }}
+
+
+ +
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+ +
+ +
+
+ + +
+ +
+ +
+
+
+ +
+ +
+
+ + +
+
+ + + add label + +
+ +
+
+
+ name + +
+
+ value + +
+ +
+
+ +
+ +
+
+
\ No newline at end of file diff --git a/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.js b/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.js new file mode 100644 index 000000000..1e4dfb677 --- /dev/null +++ b/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.js @@ -0,0 +1,7 @@ +angular.module('portainer.docker').component('volumeStoridgeInfo', { + templateUrl: 'app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.html', + controller: 'VolumeStoridgeInfoController', + bindings: { + volume: '<' + } +}); diff --git a/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfoController.js b/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfoController.js new file mode 100644 index 000000000..401c1eeda --- /dev/null +++ b/app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfoController.js @@ -0,0 +1,104 @@ +angular.module('portainer.docker') +.controller('VolumeStoridgeInfoController', ['$state', 'StoridgeVolumeService', 'Notifications', +function ($state, StoridgeVolumeService, Notifications) { + var ctrl = this; + + this.state = { + updateInProgress: false, + isUpdating: false + }; + + this.addLabel = function() { + this.formValues.Labels.push({ name: '', value: ''}); + }; + + this.removeLabel = function(index) { + this.formValues.Labels.splice(index, 1); + }; + + this.initLabels = function() { + var labels = this.volume.Labels; + this.formValues.Labels = Object.keys(labels).map(function(key) { + return { name:key, value:labels[key] }; + }); + }; + + this.updateVolume = function() { + this.state.updateInProgress = true; + this.formValues = { + IOPSMin: this.volume.IOPSMin, + IOPSMax: this.volume.IOPSMax, + Node: this.volume.Node, + NodeID: this.volume.NodeID, + Capacity: this.volume.Capacity, + BandwidthMin: this.volume.BandwidthMin, + BandwidthMax: this.volume.BandwidthMax, + Labels: [] + }; + this.initLabels(); + }; + + this.cancelUpdate = function() { + this.state.updateInProgress = false; + this.formValues = {}; + }; + + this.prepareLabels = function(volume) { + var labels = {}; + this.formValues.Labels.forEach(function (label) { + if (label.name && label.value) { + labels[label.name] = label.value; + } + }); + volume.Labels = labels; + }; + + this.prepareVolume = function() { + var volume = angular.copy(this.formValues); + var data = this.volume; + + if (volume.Node === data.Node || !volume.Node) { + delete volume.Node; + } + if (volume.NodeID === data.NodeID || !volume.NodeID) { + delete volume.NodeID; + } + if (volume.Capacity === data.Capacity || !volume.Capacity) { + delete volume.Capacity; + } + if (volume.IOPSMin === data.IOPSMin || !volume.IOPSMin) { + delete volume.IOPSMin; + } + if (volume.IOPSMax === data.IOPSMax || !volume.IOPSMax) { + delete volume.IOPSMax; + } + if (volume.BandwidthMin === data.BandwidthMin || !volume.BandwidthMin) { + delete volume.BandwidthMin; + } + if (volume.BandwidthMax === data.BandwidthMax || !volume.BandwidthMax) { + delete volume.BandwidthMax; + } + return volume; + }; + + this.confirmUpdate = function() { + this.state.isUpdating = true; + + var volume = this.prepareVolume(); + this.prepareLabels(volume); + volume.Name = this.volume.Name; + StoridgeVolumeService.update(volume) + .then(function success() { + Notifications.success('Volume successfully updated'); + $state.reload(); + }) + .catch(function error(err) { + Notifications.error('Failure', err, 'Unable to update volume'); + ctrl.state.isUpdating = false; + }); + }; + + this.$onInit = function() { + }; + +}]); diff --git a/app/extensions/storidge/models/snapshot.js b/app/extensions/storidge/models/snapshot.js new file mode 100644 index 000000000..d259bbef7 --- /dev/null +++ b/app/extensions/storidge/models/snapshot.js @@ -0,0 +1,9 @@ +function StoridgeSnapshotModel(data) { + this.Id = data.identifier; + this.Date = data.date; + this.Description = data.description; + this.SourceID = data.sourceid; + this.Type = data.type; + this.Directory = data.directory; + this.Source = data.source; +} diff --git a/app/extensions/storidge/models/volume.js b/app/extensions/storidge/models/volume.js new file mode 100644 index 000000000..c2b300db5 --- /dev/null +++ b/app/extensions/storidge/models/volume.js @@ -0,0 +1,43 @@ +function StoridgeVolumeModel(data) { + this.Allocated = data['allocate%']; + this.Capacity = data.capacity; + this.Directory = data.directory; + this.IOPSMax = data.iopsmax; + this.IOPSMin = data.iopsmin; + this.LocalDriveOnly = data.localdriveonly; + this.Name = data.name; + this.Node = data.node; + this.NodeID = data.nodeid; + this.Provisioning = data.provisioning; + this.Redundancy = data.redundancy; + this.Type = data.type; + this.Uuid = data.uuid; + this.Vdisk = data.vdisk; + this.Labels = data.labels; + + // this.IP = data.ipaddr; + // this.Volume = data.volume; + // this.DriveType = data.type; + // this.Compression = data.compression; + // this.Dedup = data.dedup; + // this.Encryption = data.encryption; + // this.Replication = data.replication; + // this.Snapshot = data.snapshot; + // this.SnapshotInterval = data.snap_interval; + // this.SnapshotMax = data.snap_max; + // this.Filesystem = data.filesystem; +} + +function StoridgeVolumeUpdateModel(data) { + this.name = data.Name; + this.opts = { + node: data.Node, + nodeid: data.NodeID, + capacity: data.Capacity, + iopsmin: data.IOPSMin, + iopsmax: data.IOPSMax, + bandwidthmin: data.BandwidthMin, + bandwidthmax: data.BandwidthMax + }; + this.labels = data.Labels; +} \ No newline at end of file diff --git a/app/extensions/storidge/rest/storidge.js b/app/extensions/storidge/rest/storidge.js index d0a573417..7f090cec4 100644 --- a/app/extensions/storidge/rest/storidge.js +++ b/app/extensions/storidge/rest/storidge.js @@ -23,9 +23,22 @@ angular.module('extension.storidge') createProfile: { method: 'POST', params: { resource: 'profiles' } }, updateProfile: { method: 'PUT', params: { resource: 'profiles', id: '@name' } }, deleteProfile: { method: 'DELETE', params: { resource: 'profiles' } }, + queryDrives: { method: 'GET', params: { resource: 'drives' } }, getDrive: { method: 'GET', params: { resource: 'drives', id: '@id' } }, addDrive: { method: 'POST', params: { resource: 'drives' } }, - removeDrive: { method: 'DELETE', params: { resource: 'drives', id: '@id' } } + removeDrive: { method: 'DELETE', params: { resource: 'drives', id: '@id' } }, + + queryVolumes: { method: 'GET', params: { resource: 'volumes' } }, + createVolume: { method: 'POST', params: { resource: 'volumes' } }, + getVolume: { method: 'GET', params: { resource: 'volumes', id: '@id' } }, + updateVolume: { method: 'POST', params: { resource: 'volumes', id: '@name' } }, + removeVolume: { method: 'DELETE', params: { resource: 'volumes' , id: '@id' } }, + + querySnapshots: { method: 'GET', params: { resource: 'volumes', id: '@id', action: 'snapshots' } }, + createSnapshot: { method: 'POST', params: { resource: 'volumes', id: '@id', action: 'snapshot' } }, + getSnapshot: { method: 'GET', params: { resource: 'snapshots', id: '@id' } }, + removeSnapshot: { method: 'DELETE', params: { resource: 'snapshots', id: '@id'} } + }); }]); diff --git a/app/extensions/storidge/services/snapshotService.js b/app/extensions/storidge/services/snapshotService.js new file mode 100644 index 000000000..c260829f5 --- /dev/null +++ b/app/extensions/storidge/services/snapshotService.js @@ -0,0 +1,72 @@ +angular.module('extension.storidge') +.factory('StoridgeSnapshotService', ['$q', 'Storidge', function StoridgeSnapshotServiceFactory($q, Storidge) { + 'use strict'; + var service = {}; + + service.snapshots = snapshots; + service.snapshot = snapshot; + service.create = create; + service.remove = remove; + + function snapshots(volumeId) { + var deferred = $q.defer(); + + Storidge.querySnapshots({id: volumeId}).$promise + .then(function success(data) { + var snapshotsData = data.snapshots; + var snapshots = snapshotsData.map(function (snapshot) { + return new StoridgeSnapshotModel(snapshot); + }); + deferred.resolve(snapshots); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to retrieve Storidge snapshots', err: err }); + }); + + return deferred.promise; + } + + function snapshot(id) { + var deferred = $q.defer(); + + Storidge.getSnapshot({id:id}).$promise + .then(function success(data) { + var snapshot = new StoridgeSnapshotModel(data.snapshot); + deferred.resolve(snapshot); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to retrieve Storidge snapshot', err: err }); + }); + + return deferred.promise; + } + + function create(volumeId) { + var deferred = $q.defer(); + Storidge.createSnapshot({id: volumeId}).$promise + .then(function success(data) { + deferred.resolve(data); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to create Storidge volume snapshot', err: err }); + }); + + return deferred.promise; + } + + function remove(id) { + var deferred = $q.defer(); + + Storidge.removeSnapshot({ id: id }).$promise + .then(function success() { + deferred.resolve(); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to remove Storidge volume snapshot', err: err }); + }); + + return deferred.promise; + } + + return service; +}]); diff --git a/app/extensions/storidge/services/volumeService.js b/app/extensions/storidge/services/volumeService.js new file mode 100644 index 000000000..d534b2d04 --- /dev/null +++ b/app/extensions/storidge/services/volumeService.js @@ -0,0 +1,36 @@ +angular.module('extension.storidge') +.factory('StoridgeVolumeService', ['$q', 'Storidge', function StoridgeVolumeServiceFactory($q, Storidge) { + 'use strict'; + var service = {}; + + service.volume = function(id) { + var deferred = $q.defer(); + + Storidge.getVolume({id:id}).$promise + .then(function success(data) { + var volume = new StoridgeVolumeModel(data); + deferred.resolve(volume); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to retrieve Storidge volume', err: err }); + }); + + return deferred.promise; + }; + + service.update = function(data) { + var deferred = $q.defer(); + var volume = new StoridgeVolumeUpdateModel(data); + Storidge.updateVolume(volume).$promise + .then(function success(data) { + deferred.resolve(data); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to update Storidge volume', err: err }); + }); + + return deferred.promise; + }; + + return service; +}]); diff --git a/app/extensions/storidge/views/drives/inspect/driveController.js b/app/extensions/storidge/views/drives/inspect/driveController.js index b2995c885..3278f82bc 100644 --- a/app/extensions/storidge/views/drives/inspect/driveController.js +++ b/app/extensions/storidge/views/drives/inspect/driveController.js @@ -1,6 +1,6 @@ angular.module('extension.storidge') -.controller('StoridgeDriveController', ['$scope', '$state', '$transition$', 'Notifications', 'StoridgeDriveService', -function ($scope, $state, $transition$, Notifications, StoridgeDriveService) { +.controller('StoridgeDriveController', ['$scope', '$state', '$transition$', 'Notifications', 'ModalService', 'StoridgeDriveService', +function ($scope, $state, $transition$, Notifications, ModalService, StoridgeDriveService) { $scope.addDrive = function () { StoridgeDriveService.add($scope.drive.Device, $scope.drive.Node) diff --git a/app/extensions/storidge/views/snapshots/inspect/snapshot.html b/app/extensions/storidge/views/snapshots/inspect/snapshot.html new file mode 100644 index 000000000..fb9d1afa4 --- /dev/null +++ b/app/extensions/storidge/views/snapshots/inspect/snapshot.html @@ -0,0 +1,53 @@ + + + + Volumes > {{ volumeId }} > Snapshots > {{ snapshot.Id }} + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID + {{ snapshot.Id }} + +
Date{{ snapshot.Date }}
Description{{ snapshot.Description }}
SourceID{{ snapshot.SourceID }}
Type{{ snapshot.Type }}
Directory{{ snapshot.Directory }}
Source{{ snapshot.Source }}
+
+
+
+
\ No newline at end of file diff --git a/app/extensions/storidge/views/snapshots/inspect/snapshotController.js b/app/extensions/storidge/views/snapshots/inspect/snapshotController.js new file mode 100644 index 000000000..1534087cd --- /dev/null +++ b/app/extensions/storidge/views/snapshots/inspect/snapshotController.js @@ -0,0 +1,44 @@ +angular.module('extension.storidge') +.controller('StoridgeSnapshotController', ['$scope', '$state', '$transition$', 'Notifications', 'ModalService', 'StoridgeSnapshotService', +function ($scope, $state, $transition$, Notifications, ModalService, StoridgeSnapshotService) { + + $scope.removeSnapshot = function () { + ModalService.confirm({ + title: 'Are you sure?', + message: 'Do you want really want to remove this snapshot?', + buttons: { + confirm: { + label: 'Remove', + className: 'btn-danger' + } + }, + callback: function onConfirm(confirmed) { + if(!confirmed) { return; } + StoridgeSnapshotService.remove($scope.snapshot.Id) + .then(function () { + Notifications.success('Success', 'Snapshot removed'); + $state.go('portainer.volumes.volume', {id: $scope.volumeId}); + }) + .catch(function error(err) { + Notifications.error('Failure', err, 'Unable to remove snapshot'); + }); + } + }); + }; + + function initView() { + $scope.volumeId = $transition$.params().id; + $scope.snapshotId = $transition$.params().snapshotId; + + StoridgeSnapshotService.snapshot($scope.snapshotId) + .then(function success(data) { + $scope.snapshot = data; + }) + .catch(function error(err) { + Notifications.error('Failure', err, 'Unable to retrieve snapshot details'); + }); + } + + initView(); + +}]);