feat(storidge): add volume and snapshot details

storidge-standalone
baron_l 2019-03-01 00:19:09 +01:00
parent 6a5e0ea2a8
commit 48c79f7fce
17 changed files with 764 additions and 7 deletions

View File

@ -45,6 +45,24 @@
</div>
</div>
<div class="row" ng-if="isCioDriver">
<div class="col-sm-12">
<volume-storidge-info volume="storidgeVolume">
</volume-storidge-info>
</div>
</div>
<div class="row" ng-if="isCioDriver">
<div class="col-sm-12">
<storidge-snapshots-datatable
title-text="Snapshots" title-icon="fa-camera"
dataset="storidgeSnapshots" table-key="storidgeSnapshots"
order-by="Id"
remove-action="removeSnapshot">
</storidge-snapshots-datatable>
</div>
</div>
<!-- access-control-panel -->
<por-access-control-panel
ng-if="volume && applicationState.application.authentication"

View File

@ -1,6 +1,43 @@
angular.module('portainer.docker')
.controller('VolumeController', ['$scope', '$state', '$transition$', 'VolumeService', 'ContainerService', 'Notifications', 'HttpRequestHelper',
function ($scope, $state, $transition$, VolumeService, ContainerService, Notifications, HttpRequestHelper) {
.controller('VolumeController', ['$scope', '$state', '$transition$', '$q', 'ModalService', 'VolumeService', 'ContainerService', 'Notifications', 'HttpRequestHelper', 'StoridgeVolumeService', 'StoridgeSnapshotService',
function ($scope, $state, $transition$, $q, ModalService, VolumeService, ContainerService, Notifications, HttpRequestHelper, StoridgeVolumeService, StoridgeSnapshotService) {
$scope.storidgeSnapshots = [];
$scope.storidgeVolume = {};
$scope.removeSnapshot = function (selectedItems) {
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; }
var actionCount = selectedItems.length;
angular.forEach(selectedItems, function (item) {
StoridgeSnapshotService.remove(item.Id)
.then(function success() {
Notifications.success('Snapshot successfully removed', item.Id);
var index = $scope.containerGroups.indexOf(item);
$scope.storidgeSnapshots.splice(index, 1);
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove snapshot');
})
.finally(function final() {
--actionCount;
if (actionCount === 0) {
$state.reload();
}
});
});
}
});
};
$scope.removeVolume = function removeVolume() {
VolumeService.remove($scope.volume)
@ -21,19 +58,39 @@ function ($scope, $state, $transition$, VolumeService, ContainerService, Notific
function initView() {
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
VolumeService.volume($transition$.params().id)
.then(function success(data) {
var volume = data;
$scope.volume = volume;
var containerFilter = { volume: [volume.Id] };
return ContainerService.containers(1, containerFilter);
$scope.isCioDriver = volume.Driver.includes('cio');
if ($scope.isCioDriver) {
return $q.all({
containers: ContainerService.containers(1, containerFilter),
storidgeVolume: StoridgeVolumeService.volume($transition$.params().id)
});
} else {
return ContainerService.containers(1, containerFilter);
}
})
.then(function success(data) {
var containers = data.map(function(container) {
var dataContainers = $scope.isCioDriver ? data.containers : data;
var containers = dataContainers.map(function(container) {
container.volumeData = getVolumeDataFromContainer(container, $scope.volume.Id);
return container;
});
$scope.containersUsingVolume = containers;
if ($scope.isCioDriver) {
$scope.storidgeVolume = data.storidgeVolume;
return StoridgeSnapshotService.snapshots(data.storidgeVolume.Vdisk);
}
})
.then(function success(data) {
$scope.storidgeSnapshots = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve volume details');

View File

@ -54,6 +54,17 @@ angular.module('extension.storidge', [])
}
};
var snapshot = {
name: 'docker.volumes.volume.snapshot',
url: '/:snapshotId',
views: {
'content@': {
templateUrl: 'app/extensions/storidge/views/snapshots/inspect/snapshot.html',
controller: 'StoridgeSnapshotController'
}
}
};
var profileCreation = {
name: 'storidge.profiles.new',
url: '/new',
@ -104,6 +115,7 @@ angular.module('extension.storidge', [])
$stateRegistryProvider.register(storidge);
$stateRegistryProvider.register(drives);
$stateRegistryProvider.register(drive);
$stateRegistryProvider.register(snapshot);
$stateRegistryProvider.register(profiles);
$stateRegistryProvider.register(profile);
$stateRegistryProvider.register(profileCreation);

View File

@ -0,0 +1,95 @@
<div class="datatable">
<rd-widget>
<rd-widget-body classes="no-padding">
<div class="toolBar">
<div class="toolBarTitle">
<i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px;"></i> {{ $ctrl.titleText }}
</div>
</div>
<div class="actionBar">
<button type="button" class="btn btn-sm btn-danger"
ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeSnapshot($ctrl.state.selectedItems)">
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
</button>
</div>
<div class="searchBar">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
<input type="text" class="searchInput" ng-model="$ctrl.state.textFilter" ng-change="$ctrl.onTextFilterChange()" placeholder="Search...">
</div>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>
<span class="md-checkbox">
<input id="select_all" type="checkbox" ng-model="$ctrl.state.selectAll" ng-change="$ctrl.selectAll()" />
<label for="select_all"></label>
</span>
<a ng-click="$ctrl.changeOrderBy('Id')">
Id
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Id' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th>
<a ng-click="$ctrl.changeOrderBy('Date')">
Date
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Date' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Date' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th>
<a ng-click="$ctrl.changeOrderBy('Description')">
Description
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Description' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Description' && $ctrl.state.reverseOrder"></i>
</a>
</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)" ng-disabled="item.Status === 'normal'"/>
<label for="select_{{ $index }}"></label>
</span>
<!-- <a ui-sref="storidge.drives.drive({id: item.Id})"> {{ item.Id }}</a> -->
</td>
<td>{{ item.Date }}</td>
<td>{{ item.Description }}</td>
</tr>
<tr ng-if="!$ctrl.dataset">
<td colspan="7" class="text-center text-muted">Loading...</td>
</tr>
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
<td colspan="3" class="text-center text-muted">No snapshots available.</td>
</tr>
</tbody>
</table>
</div>
<div class="footer" ng-if="$ctrl.dataset">
<div class="infoBar" ng-if="$ctrl.state.selectedItemCount !== 0">
{{ $ctrl.state.selectedItemCount }} item(s) selected
</div>
<div class="paginationControls">
<form class="form-inline">
<span class="limitSelector">
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</span>
<dir-pagination-controls max-size="5"></dir-pagination-controls>
</form>
</div>
</div>
</rd-widget-body>
</rd-widget>
</div>

View File

@ -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: '<'
}
});

View File

@ -0,0 +1,7 @@
angular.module('portainer.docker')
.controller('StoridgeSnapshotsDatatableController', ['$scope', '$controller',
function ($scope, $controller) {
angular.extend(this, $controller('GenericDatatableController', {$scope: $scope}));
}
]);

View File

@ -0,0 +1,174 @@
<rd-widget>
<rd-widget-header icon="fa-cube" title-text="Storidge details">
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-click="$ctrl.updateVolume()" ng-if="!$ctrl.state.updateInProgress">
<span>Update the volume</span>
</button>
<button type="button" class="btn btn-primary btn-sm" ng-click="$ctrl.confirmUpdate()" ng-if="$ctrl.state.updateInProgress" button-spinner="$ctrl.state.isUpdating">
<span ng-hide="$ctrl.state.isUpdating">Confirm</span>
<span ng-show="$ctrl.state.isUpdating">Updating the volume...</span>
</button>
<button type="button" class="btn btn-danger btn-sm" ng-click="$ctrl.cancelUpdate()" ng-if="$ctrl.state.updateInProgress" ng-disabled="$ctrl.state.isUpdating">
<span>Cancel</span>
</button>
</div>
</div>
</rd-widget-header>
<rd-widget-body classes="no-padding" ng-if="!$ctrl.state.updateInProgress">
<table class="table">
<tbody>
<tr>
<td>Name</td>
<td>{{ $ctrl.volume.Name }}</td>
</tr>
<tr>
<td>Uuid</td>
<td>{{ $ctrl.volume.Uuid }}</td>
</tr>
<tr>
<td>Node</td>
<td>{{ $ctrl.volume.Node }}</td>
</tr>
<tr>
<td>Node ID</td>
<td>{{ $ctrl.volume.NodeID }}</td>
</tr>
<tr>
<td>Directory</td>
<td>{{ $ctrl.volume.Directory }}</td>
</tr>
<tr>
<td>Capacity</td>
<td>{{ $ctrl.volume.Capacity }}</td>
</tr>
<tr>
<td>Allocated</td>
<td>{{ $ctrl.volume.Allocated }}</td>
</tr>
<tr>
<td>IOPS Max</td>
<td>{{ $ctrl.volume.IOPSMax }}</td>
</tr>
<tr>
<td>IOPS Min</td>
<td>{{ $ctrl.volume.IOPSMin }}</td>
</tr>
<tr>
<td>Local Drive Only</td>
<td>{{ $ctrl.volume.LocalDriveOnly }}</td>
</tr>
<tr>
<td>Provisioning</td>
<td>{{ $ctrl.volume.Provisioning }}</td>
</tr>
<tr>
<td>Redundancy</td>
<td>{{ $ctrl.volume.Redundancy }}</td>
</tr>
<tr>
<td>Type</td>
<td>{{ $ctrl.volume.Type }}</td>
</tr>
<tr>
<td>Vdisk</td>
<td>{{ $ctrl.volume.Vdisk }}</td>
</tr>
<tr ng-if="$ctrl.volume.Labels">
<td>Labels</td>
<td>
<table class="table table-bordered table-condensed">
<tr ng-repeat="var in $ctrl.volume.Labels">
<td>{{ var|key: '=' }}</td>
<td>{{ var|value: '=' }}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
<rd-widget-body ng-if="$ctrl.state.updateInProgress">
<form class="form-horizontal" name="storidgeUpdateVolumeForm">
<!-- Node -->
<div class="form-group">
<label for="volume_node" class="col-sm-2 col-lg-1 control-label text-left">Node</label>
<div class="col-sm-10 col-lg-11">
<input type="text" class="form-control" ng-model="$ctrl.formValues.Node" name="volume_node" placeholder="2">
</div>
</div>
<!-- !Node -->
<!-- Node ID -->
<div class="form-group">
<label for="volume_nodeid" class="col-sm-2 col-lg-1 control-label text-left">Node ID</label>
<div class="col-sm-10 col-lg-11">
<input type="text" class="form-control" ng-model="$ctrl.formValues.NodeID" name="volume_nodeid" placeholder="2">
</div>
</div>
<!-- !Node ID -->
<!-- Capacity -->
<div class="form-group">
<label for="volume_capacity" class="col-sm-2 col-lg-1 control-label text-left">Capacity</label>
<div class="col-sm-10 col-lg-11">
<input type="text" class="form-control" ng-model="$ctrl.formValues.Capacity" name="volume_capacity" placeholder="2">
</div>
</div>
<!-- !Capacity -->
<!-- IOPS -->
<div class="form-group">
<label for="min_iops" class="col-sm-2 col-lg-1 control-label text-left">Min IOPS</label>
<div class="col-sm-10 col-lg-11">
<input type="number" class="form-control" ng-model="$ctrl.formValues.IOPSMin" name="min_iops" placeholder="100">
</div>
</div>
<div class="form-group">
<label for="max_iops" class="col-sm-2 col-lg-1 control-label text-left">Max IOPS</label>
<div class="col-sm-10 col-lg-11">
<input type="number" class="form-control" ng-model="$ctrl.formValues.IOPSMax" name="max_iops" placeholder="2000">
</div>
</div>
<!-- !IOPS -->
<!-- Bandwidth -->
<div class="form-group">
<label for="min_bandwidth" class="col-sm-2 col-lg-1 control-label text-left">Min Bandwidth</label>
<div class="col-sm-10 col-lg-11">
<input type="number" class="form-control" ng-model="$ctrl.formValues.BandwidthMin" name="min_bandwidth" placeholder="100">
</div>
</div>
<div class="form-group">
<label for="max_bandwidth" class="col-sm-2 col-lg-1 control-label text-left">Max Bandwidth</label>
<div class="col-sm-10 col-lg-11">
<input type="number" class="form-control" ng-model="$ctrl.formValues.BandwidthMax" name="max_bandwidth" placeholder="2000">
</div>
</div>
<!-- !Bandwidth -->
<!-- labels -->
<div class="form-group">
<div class="col-sm-12" style="margin-top: 5px;">
<label class="control-label text-left">Labels</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="$ctrl.addLabel()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> add label
</span>
</div>
<!-- labels-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="label in $ctrl.formValues.Labels" style="margin-top: 2px;">
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">name</span>
<input type="text" class="form-control" ng-model="label.name" placeholder="e.g. com.example.foo">
</div>
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">value</span>
<input type="text" class="form-control" ng-model="label.value" placeholder="e.g. bar">
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="$ctrl.removeLabel($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
</div>
<!-- !labels-input-list -->
</div>
<!-- !labels -->
</form>
</rd-widget-body>
</rd-widget>

View File

@ -0,0 +1,7 @@
angular.module('portainer.docker').component('volumeStoridgeInfo', {
templateUrl: 'app/extensions/storidge/components/volume-storidge-info/volumeStoridgeInfo.html',
controller: 'VolumeStoridgeInfoController',
bindings: {
volume: '<'
}
});

View File

@ -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() {
};
}]);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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'} }
});
}]);

View File

@ -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;
}]);

View File

@ -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;
}]);

View File

@ -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)

View File

@ -0,0 +1,53 @@
<rd-header>
<rd-header-title title-text="Snasphot details"></rd-header-title>
<rd-header-content>
<a ui-sref="docker.volumes">Volumes</a> &gt; <a ui-sref="docker.volumes.volume({id: volumeId})">{{ volumeId }}</a> &gt; Snapshots &gt; <a ui-sref="docker.volumes.volume.snapshot({id: snapshot.Id})">{{ snapshot.Id }}</a>
</rd-header-content>
</rd-header>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-hdd" title-text="Snapshot details "></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>ID</td>
<td>
{{ snapshot.Id }}
<button class="btn btn-xs btn-danger" ng-click="removeSnapshot()">
<i class="fa fa-trash space-right" aria-hidden="true"></i>Remove snapshot
</button>
</td>
</tr>
<tr>
<td>Date</td>
<td>{{ snapshot.Date }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ snapshot.Description }}</td>
</tr>
<tr>
<td>SourceID</td>
<td>{{ snapshot.SourceID }}</td>
</tr>
<tr>
<td>Type</td>
<td>{{ snapshot.Type }}</td>
</tr>
<tr>
<td>Directory</td>
<td>{{ snapshot.Directory }}</td>
</tr>
<tr>
<td>Source</td>
<td>{{ snapshot.Source }}</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>
</div>
</div>

View File

@ -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();
}]);