foreman_snapshot_management 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +9 -0
- data/Rakefile +0 -0
- data/app/controllers/api/v2/snapshots_controller.rb +107 -0
- data/app/controllers/concerns/foreman/controller/parameters/snapshot.rb +15 -0
- data/app/controllers/foreman_snapshot_management/snapshots_controller.rb +72 -54
- data/app/models/concerns/fog_extensions/vsphere/snapshots/mock.rb +33 -0
- data/app/models/concerns/fog_extensions/vsphere/snapshots/real.rb +43 -0
- data/app/models/foreman_snapshot_management/snapshot.rb +80 -22
- data/app/models/foreman_snapshot_management/vmware_extensions.rb +38 -6
- data/app/overrides/hosts/add_tab_to_host_overview.rb +2 -12
- data/app/views/api/v2/snapshots/base.json.rabl +3 -0
- data/app/views/api/v2/snapshots/create.json.rabl +3 -0
- data/app/views/api/v2/snapshots/destroy.json.rabl +3 -0
- data/app/views/api/v2/snapshots/index.json.rabl +3 -0
- data/app/views/api/v2/snapshots/main.json.rabl +9 -0
- data/app/views/api/v2/snapshots/revert.json.rabl +3 -0
- data/app/views/api/v2/snapshots/show.json.rabl +3 -0
- data/app/views/api/v2/snapshots/update.json.rabl +3 -0
- data/app/views/foreman_snapshot_management/hosts/_snapshots_tab.html.erb +3 -0
- data/app/views/foreman_snapshot_management/hosts/_snapshots_tab_content.html.erb +7 -0
- data/app/views/foreman_snapshot_management/snapshots/_index.html.erb +27 -16
- data/config/routes.rb +17 -0
- data/lib/foreman_snapshot_management/engine.rb +45 -11
- data/lib/foreman_snapshot_management/version.rb +1 -1
- data/test/controllers/api/v2/snapshots_test.rb +57 -0
- data/test/controllers/foreman_snapshot_management/snapshots_controller_test.rb +85 -0
- metadata +21 -6
- data/app/helpers/foreman_snapshot_management/snapshotadministration_helper.rb +0 -72
- data/test/unit/foreman_snapshot_management_test.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '048b5bbc495a00c7caf44380c5ee03ccd319e592'
|
4
|
+
data.tar.gz: 7d331abbbb267a00c70485e8a74bb1e76d30f684
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2f5363bbd5646c3cd7383968fa0529ba483c9e42a0adcf1d483c6668cb5c5e43c8fdf19f46449658ceb07864022ce108644f0697724e4499fe1a493852e0018
|
7
|
+
data.tar.gz: 473771e206b8768650d3085c7494f5b7b4a07c3859894bb7573a2acfff2e4501c44915eb0c545bcadc4938a3efa862d1b1e070aff1ce6525bea147d34971ed74
|
data/README.md
CHANGED
@@ -13,3 +13,12 @@ As Hypervisor VMware vSphere is supported.
|
|
13
13
|
## Installation
|
14
14
|
|
15
15
|
See [How_to_Install_a_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Plugin) for how to install Foreman plugins
|
16
|
+
|
17
|
+
## Copyright
|
18
|
+
Copyright (c) 2017 ATIX AG - http://www.atix.de
|
19
|
+
|
20
|
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
21
|
+
|
22
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
23
|
+
|
24
|
+
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
|
data/Rakefile
CHANGED
File without changes
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Api
|
2
|
+
module V2
|
3
|
+
class SnapshotsController < V2::BaseController
|
4
|
+
include Api::Version2
|
5
|
+
include Foreman::Controller::Parameters::Snapshot
|
6
|
+
|
7
|
+
before_action :find_required_nested_object
|
8
|
+
before_action :check_snapshot_capability
|
9
|
+
before_action :find_resource, :only => %w[show update destroy revert]
|
10
|
+
|
11
|
+
api :GET, 'hosts/:host_id/snapshots/', N_('List all snapshots')
|
12
|
+
param :host_id, :identifier_dottable, :required => true
|
13
|
+
def index
|
14
|
+
@snapshots = resource_scope_for_index
|
15
|
+
end
|
16
|
+
|
17
|
+
api :GET, '/hosts/:host_id/snapshots/:id', 'Show a snapshot'
|
18
|
+
param :host_id, :identifier_dottable, :required => true
|
19
|
+
param :id, :identifier_dottable, :required => true
|
20
|
+
|
21
|
+
def show; end
|
22
|
+
|
23
|
+
def_param_group :snapshot do
|
24
|
+
param :snapshot, Hash, :required => true, :action_aware => true do
|
25
|
+
param :name, String, :required => true, :desc => N_('Name of this snapshot')
|
26
|
+
param :description, String, :desc => N_('Description of this snapshot')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
api :POST, '/hosts/:host_id/snapshots/', N_('Create a snapshot')
|
31
|
+
param :host_id, :identifier_dottable, :required => true
|
32
|
+
param_group :snapshot, :as => :create
|
33
|
+
|
34
|
+
def create
|
35
|
+
@snapshot = resource_class.new(snapshot_params.to_h.merge(host: @nested_obj))
|
36
|
+
process_response @snapshot.save
|
37
|
+
end
|
38
|
+
|
39
|
+
api :PUT, '/hosts/:host_id/snapshots/:id/', N_('Update a snapshot')
|
40
|
+
param :host_id, :identifier_dottable, :required => true
|
41
|
+
param :id, :identifier_dottable, :required => true
|
42
|
+
param_group :snapshot
|
43
|
+
|
44
|
+
def update
|
45
|
+
process_response @snapshot.update_attributes(snapshot_params)
|
46
|
+
end
|
47
|
+
|
48
|
+
api :DELETE, '/hosts/:host_id/snapshots/:id', N_('Delete a snapshot')
|
49
|
+
param :host_id, :identifier_dottable, :required => true
|
50
|
+
param :id, :identifier_dottable, :required => true
|
51
|
+
|
52
|
+
def destroy
|
53
|
+
process_response @snapshot.destroy
|
54
|
+
end
|
55
|
+
|
56
|
+
api :PUT, '/hosts/:host_id/snapshots/:id/revert', N_('Revert Host to a snapshot')
|
57
|
+
param :host_id, :identifier_dottable, :required => true
|
58
|
+
param :id, :identifier_dottable, :required => true
|
59
|
+
|
60
|
+
def revert
|
61
|
+
process_response @snapshot.revert
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def resource_scope(_options = {})
|
67
|
+
# TODO: Ask host for snapshots
|
68
|
+
@resource_scope ||= resource_class.all_for_host(@nested_obj)
|
69
|
+
end
|
70
|
+
|
71
|
+
def resource_scope_for_index
|
72
|
+
resource_scope.paginate(paginate_options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def resource_class
|
76
|
+
::ForemanSnapshotManagement::Snapshot
|
77
|
+
end
|
78
|
+
|
79
|
+
def find_resource
|
80
|
+
@snapshot = resource_class.find_for_host(@nested_obj, params[:id])
|
81
|
+
not_found unless @snapshot
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_snapshot_capability
|
85
|
+
not_found unless @nested_obj.compute_resource && @nested_obj.compute_resource.capabilities.include?(:snapshots)
|
86
|
+
end
|
87
|
+
|
88
|
+
def action_permission
|
89
|
+
case params[:action]
|
90
|
+
when 'revert'
|
91
|
+
:revert
|
92
|
+
else
|
93
|
+
super
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def parent_permission(child_permission)
|
98
|
+
case child_permission.to_s
|
99
|
+
when 'revert'
|
100
|
+
:edit
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Foreman::Controller::Parameters::Snapshot
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class_methods do
|
5
|
+
def snapshot_params_filter
|
6
|
+
Foreman::ParameterFilter.new(::ForemanSnapshotManagement::Snapshot).tap do |filter|
|
7
|
+
filter.permit :name, :description
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def snapshot_params
|
13
|
+
self.class.snapshot_params_filter.filter_params(params, parameter_filter_context)
|
14
|
+
end
|
15
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module ForemanSnapshotManagement
|
2
2
|
class SnapshotsController < ApplicationController
|
3
|
+
include ::Foreman::Controller::Parameters::Snapshot
|
4
|
+
|
3
5
|
before_action :find_host
|
6
|
+
before_action :check_snapshot_capability
|
4
7
|
before_action :enumerate_snapshots, only: [:index]
|
5
8
|
before_action :find_snapshot, only: %i[destroy revert update]
|
6
9
|
helper_method :xeditable?
|
@@ -18,26 +21,13 @@ module ForemanSnapshotManagement
|
|
18
21
|
#
|
19
22
|
# This method creates a Snapshot with a given name and optional description.
|
20
23
|
def create
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# Check state of Snapshot creation
|
29
|
-
if task['task_state'] == 'success'
|
30
|
-
status = _('Snapshot successfully created')
|
31
|
-
else
|
32
|
-
status = _('Error occurred while creating snapshot: %s') % task['task_state']
|
33
|
-
end
|
34
|
-
|
35
|
-
# Redirect to specific Host page
|
36
|
-
redirect_to host_path(@host, anchor: 'snapshots'), flash: { notice: status }
|
37
|
-
rescue
|
38
|
-
logger.error 'Failed to take snapshot.'
|
39
|
-
status = _('Error occurred while creating snapshot.')
|
40
|
-
redirect_to host_path(@host, anchor: 'snapshots'), flash: { error: status }
|
24
|
+
@snapshot = Snapshot.new(snapshot_params.merge(host: @host))
|
25
|
+
|
26
|
+
if @snapshot.create
|
27
|
+
process_success
|
28
|
+
else
|
29
|
+
msg = _('Error occurred while creating Snapshot: %s') % @snapshot.errors.full_messages.to_sentence
|
30
|
+
process_error :error_msg => msg
|
41
31
|
end
|
42
32
|
end
|
43
33
|
|
@@ -45,51 +35,35 @@ module ForemanSnapshotManagement
|
|
45
35
|
#
|
46
36
|
# This method removes a Snapshot from a given host.
|
47
37
|
def destroy
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
# Check state of Snapshot creation
|
52
|
-
if task['task_state'] == 'success'
|
53
|
-
status = 'Snapshot successfully deleted'
|
38
|
+
if @snapshot.destroy
|
39
|
+
process_success
|
54
40
|
else
|
55
|
-
|
41
|
+
msg = _('Error occurred while removing Snapshot: %s') % @snapshot.errors.full_messages.to_sentence
|
42
|
+
process_error :error_msg => msg
|
56
43
|
end
|
57
|
-
|
58
|
-
# Redirect to specific Host page
|
59
|
-
redirect_to host_path(@host, anchor: 'snapshots'), flash: { notice: status }
|
60
44
|
end
|
61
45
|
|
62
46
|
# Revert Snapshot
|
63
47
|
#
|
64
48
|
# This method reverts a host to a given Snapshot.
|
65
49
|
def revert
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
# Check state of Snapshot creation
|
70
|
-
if task['state'] == 'success'
|
71
|
-
status = _('VM successfully rolled back')
|
50
|
+
if @snapshot.revert
|
51
|
+
process_success :success_msg => _('VM successfully rolled back.')
|
72
52
|
else
|
73
|
-
|
53
|
+
msg = _('Error occurred while rolling back VM: %s') % @snapshot.errors.full_messages.to_sentence
|
54
|
+
process_error :error_msg => msg
|
74
55
|
end
|
75
|
-
|
76
|
-
# Redirect to specific Host page
|
77
|
-
redirect_to host_path(@host, anchor: 'snapshots'), flash: { notice: status }
|
78
56
|
end
|
79
57
|
|
80
58
|
# Update Snapshot
|
81
59
|
#
|
82
60
|
# This method renames a Snapshot from a given host.
|
83
61
|
def update
|
84
|
-
|
85
|
-
@snapshot.name = params['snapshot']['name'] if params['snapshot']['name']
|
86
|
-
@snapshot.description = params['snapshot']['description'] if params['snapshot']['description']
|
87
|
-
begin
|
88
|
-
task = @snapshot.save
|
62
|
+
if @snapshot.update_attributes(snapshot_params)
|
89
63
|
render json: { name: @snapshot.name, description: @snapshot.description }
|
90
|
-
|
91
|
-
|
92
|
-
render json: { errors:
|
64
|
+
else
|
65
|
+
msg = _('Failed to update Snapshot: %s') % @snapshot.errors.full_messages.to_sentence
|
66
|
+
render json: { errors: msg }, status: :unprocessable_entity
|
93
67
|
end
|
94
68
|
end
|
95
69
|
|
@@ -100,18 +74,62 @@ module ForemanSnapshotManagement
|
|
100
74
|
private
|
101
75
|
|
102
76
|
def find_host
|
103
|
-
|
104
|
-
|
105
|
-
|
77
|
+
host_id = params[:host_id]
|
78
|
+
if host_id.blank?
|
79
|
+
not_found
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
@host = Host.authorized(host_permission).friendly.find(host_id)
|
83
|
+
unless @host
|
84
|
+
not_found
|
85
|
+
return(false)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def host_permission
|
90
|
+
case action_permission.to_s
|
91
|
+
when 'create', 'destroy'
|
92
|
+
'edit'
|
93
|
+
when 'edit', 'view'
|
94
|
+
'view'
|
95
|
+
when 'revert'
|
96
|
+
'revert'
|
97
|
+
else
|
98
|
+
raise ::Foreman::Exception.new(N_('unknown host permission for %s'), "#{params[:controller]}##{action_permission}")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def action_permission
|
103
|
+
case params[:action]
|
104
|
+
when 'revert'
|
105
|
+
:revert
|
106
|
+
else
|
107
|
+
super
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_snapshot_capability
|
112
|
+
not_found unless @host.compute_resource && @host.compute_resource.capabilities.include?(:snapshots)
|
106
113
|
end
|
107
114
|
|
108
115
|
def enumerate_snapshots
|
109
|
-
#
|
110
|
-
@snapshots = Snapshot.all_for_host
|
116
|
+
# Array of Snapshot Objects
|
117
|
+
@snapshots = Snapshot.all_for_host(@host)
|
111
118
|
end
|
112
119
|
|
113
120
|
def find_snapshot
|
114
|
-
@snapshot = Snapshot.find_for_host
|
121
|
+
@snapshot = Snapshot.find_for_host(@host, params['id'])
|
122
|
+
not_found unless @snapshot
|
123
|
+
end
|
124
|
+
|
125
|
+
def process_success(hash = {})
|
126
|
+
hash[:success_redirect] ||= host_path(@host, anchor: 'snapshots')
|
127
|
+
super(hash)
|
128
|
+
end
|
129
|
+
|
130
|
+
def process_error(hash = {})
|
131
|
+
hash[:redirect] ||= host_path(@host, anchor: 'snapshots')
|
132
|
+
super(hash)
|
115
133
|
end
|
116
134
|
end
|
117
135
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module FogExtensions
|
2
|
+
module Vsphere
|
3
|
+
module Snapshots
|
4
|
+
module Mock
|
5
|
+
# Overwrite this to stop infinite recursion
|
6
|
+
# TODO: Add proper test data
|
7
|
+
def list_child_snapshots(_snapshot, _opts = {})
|
8
|
+
[
|
9
|
+
]
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_snapshot(options = {})
|
13
|
+
raise ArgumentError, 'snapshot is a required parameter' unless options.key? 'snapshot'
|
14
|
+
raise ArgumentError, 'removeChildren is a required parameter' unless options.key? 'removeChildren'
|
15
|
+
|
16
|
+
{
|
17
|
+
'task_state' => 'success'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def rename_snapshot(options = {})
|
22
|
+
raise ArgumentError, 'snapshot is a required parameter' unless options.key? 'snapshot'
|
23
|
+
raise ArgumentError, 'name is a required parameter' unless options.key? 'name'
|
24
|
+
raise ArgumentError, 'description is a required parameter' unless options.key? 'description'
|
25
|
+
|
26
|
+
{
|
27
|
+
'task_state' => 'success'
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module FogExtensions
|
2
|
+
module Vsphere
|
3
|
+
module Snapshots
|
4
|
+
module Real
|
5
|
+
# Extends fog-vsphere gem for a remove Snapshot method.
|
6
|
+
def remove_snapshot(options = {})
|
7
|
+
raise ArgumentError, 'snapshot is a required parameter' unless options.key? 'snapshot'
|
8
|
+
raise ArgumentError, 'removeChildren is a required parameter' unless options.key? 'removeChildren'
|
9
|
+
|
10
|
+
unless ::Fog::Compute::Vsphere::Snapshot === options['snapshot']
|
11
|
+
raise ArgumentError, 'snapshot is a required parameter'
|
12
|
+
end
|
13
|
+
|
14
|
+
task = options['snapshot'].mo_ref.RemoveSnapshot_Task(
|
15
|
+
removeChildren: options['removeChildren']
|
16
|
+
)
|
17
|
+
task.wait_for_completion
|
18
|
+
|
19
|
+
{
|
20
|
+
'task_state' => task.info.state
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
# Extends fog-vsphere gem for a rename Snapshot method.
|
25
|
+
# Does not have a return value, VMWare API throws a fault if there are errors
|
26
|
+
def rename_snapshot(options = {})
|
27
|
+
raise ArgumentError, 'snapshot is a required parameter' unless options.key? 'snapshot'
|
28
|
+
raise ArgumentError, 'name is a required parameter' unless options.key? 'name'
|
29
|
+
raise ArgumentError, 'description is a required parameter' unless options.key? 'description'
|
30
|
+
|
31
|
+
unless ::Fog::Compute::Vsphere::Snapshot === options['snapshot']
|
32
|
+
raise ArgumentError, 'snapshot is a required parameter'
|
33
|
+
end
|
34
|
+
|
35
|
+
options['snapshot'].mo_ref.RenameSnapshot(
|
36
|
+
name: options['name'],
|
37
|
+
description: options['description']
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,73 +1,131 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
1
3
|
module ForemanSnapshotManagement
|
2
4
|
class Snapshot
|
3
5
|
extend ActiveModel::Callbacks
|
4
6
|
include ActiveModel::Conversion
|
5
7
|
include ActiveModel::Model
|
6
8
|
include ActiveModel::Dirty
|
9
|
+
include ActiveModel::ForbiddenAttributesProtection
|
7
10
|
|
8
11
|
define_model_callbacks :create, :save, :destroy, :revert
|
9
|
-
attr_accessor :id, :
|
12
|
+
attr_accessor :id, :raw_snapshot, :name, :description, :host_id, :parent, :create_time
|
10
13
|
|
11
|
-
def self.
|
12
|
-
snapshots
|
13
|
-
|
14
|
-
add_snapshot_with_children(snapshots, host, snap)
|
14
|
+
def self.all_for_host(host)
|
15
|
+
snapshots = host.compute_resource.get_snapshots(host.uuid).map do |raw_snapshot|
|
16
|
+
new_from_vmware(host, raw_snapshot)
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
|
-
def self.
|
19
|
-
|
20
|
-
|
21
|
-
add_snapshot_with_children(snapshots, host, root_snapshot) if root_snapshot
|
22
|
-
snapshots
|
20
|
+
def self.find_for_host(host, id)
|
21
|
+
raw_snapshot = host.compute_resource.get_snapshot(host.uuid, id)
|
22
|
+
new_from_vmware(host, raw_snapshot)
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.
|
26
|
-
|
25
|
+
def self.new_from_vmware(host, raw_snapshot, opts = {})
|
26
|
+
new(
|
27
|
+
host: host,
|
28
|
+
id: raw_snapshot.ref,
|
29
|
+
raw_snapshot: raw_snapshot,
|
30
|
+
name: raw_snapshot.name,
|
31
|
+
description: raw_snapshot.description,
|
32
|
+
parent: opts[:parent],
|
33
|
+
create_time: raw_snapshot.create_time
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def children
|
38
|
+
return [] unless raw_snapshot
|
39
|
+
child_snapshots = raw_snapshot.child_snapshots.flat_map do |child_snapshot|
|
40
|
+
self.class.new_from_vmware(host, child_snapshot, parent: self)
|
41
|
+
end
|
42
|
+
child_snapshots + child_snapshots.flat_map(&:children)
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
"#<#{self.class}:0x#{self.__id__.to_s(16)} name=#{name} id=#{id} description=#{description} host_id=#{host_id} parent=#{parent.try(:id)} children=#{children.map(&:id).inspect}>"
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
_('Snapshot')
|
27
51
|
end
|
28
52
|
|
29
|
-
def
|
30
|
-
|
53
|
+
def formatted_create_time()
|
54
|
+
create_time.strftime("%F %H:%M")
|
31
55
|
end
|
32
56
|
|
33
57
|
def persisted?
|
34
|
-
|
58
|
+
self.id.present?
|
35
59
|
end
|
36
60
|
|
37
61
|
# host accessors
|
38
62
|
def host
|
39
|
-
Host.find(
|
63
|
+
Host.find(self.host_id)
|
40
64
|
end
|
41
65
|
|
42
66
|
def host=(host)
|
43
|
-
|
67
|
+
self.host_id = host.id
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_time
|
71
|
+
raw_snapshot.try(:create_time)
|
72
|
+
end
|
73
|
+
|
74
|
+
def assign_attributes(new_attributes)
|
75
|
+
attributes = new_attributes.stringify_keys
|
76
|
+
attributes = sanitize_for_mass_assignment(attributes)
|
77
|
+
attributes.each do |k, v|
|
78
|
+
public_send("#{k}=", v)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_attributes(new_attributes)
|
83
|
+
assign_attributes(new_attributes)
|
84
|
+
save
|
44
85
|
end
|
45
86
|
|
46
87
|
# crud
|
47
88
|
def create
|
48
89
|
run_callbacks(:create) do
|
49
|
-
|
90
|
+
handle_snapshot_errors do
|
91
|
+
host.compute_resource.create_snapshot(host.uuid, name, description)
|
92
|
+
end
|
50
93
|
end
|
51
94
|
end
|
52
95
|
|
53
96
|
def save
|
54
97
|
run_callbacks(:save) do
|
55
|
-
|
98
|
+
handle_snapshot_errors do
|
99
|
+
host.compute_resource.update_snapshot(raw_snapshot, name, description)
|
100
|
+
end
|
56
101
|
end
|
57
102
|
end
|
58
103
|
|
59
104
|
def destroy
|
60
105
|
run_callbacks(:destroy) do
|
61
|
-
result =
|
62
|
-
|
106
|
+
result = handle_snapshot_errors do
|
107
|
+
result = host.compute_resource.remove_snapshot(raw_snapshot, false)
|
108
|
+
end
|
109
|
+
self.id = nil
|
63
110
|
result
|
64
111
|
end
|
65
112
|
end
|
66
113
|
|
67
114
|
def revert
|
68
115
|
run_callbacks(:revert) do
|
69
|
-
|
116
|
+
handle_snapshot_errors do
|
117
|
+
host.compute_resource.revert_snapshot(raw_snapshot)
|
118
|
+
end
|
70
119
|
end
|
71
120
|
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def handle_snapshot_errors
|
125
|
+
yield
|
126
|
+
rescue Foreman::WrappedException => e
|
127
|
+
errors.add(:base, e.wrapped_exception.message)
|
128
|
+
false
|
129
|
+
end
|
72
130
|
end
|
73
131
|
end
|
@@ -1,26 +1,41 @@
|
|
1
1
|
module ForemanSnapshotManagement
|
2
2
|
module VmwareExtensions
|
3
|
-
|
3
|
+
# Extend VMWare's capabilities with snapshots.
|
4
|
+
def capabilities
|
5
|
+
super + [:snapshots]
|
6
|
+
end
|
4
7
|
|
5
8
|
# Create a Snapshot.
|
6
9
|
#
|
7
10
|
# This method creates a Snapshot with a given name and optional description.
|
8
11
|
def create_snapshot(uuid, name, description)
|
9
|
-
client.vm_take_snapshot('instance_uuid' => uuid, 'name' => name, 'description' => description)
|
12
|
+
task = client.vm_take_snapshot('instance_uuid' => uuid, 'name' => name, 'description' => description)
|
13
|
+
task_successful?(task)
|
14
|
+
rescue RbVmomi::Fault => e
|
15
|
+
Foreman::Logging.exception('Error creating VMWare Snapshot', e)
|
16
|
+
raise ::Foreman::WrappedException.new(e, N_('Unable to create VMWare Snapshot'))
|
10
17
|
end
|
11
18
|
|
12
19
|
# Remove Snapshot
|
13
20
|
#
|
14
21
|
# This method removes a Snapshot from a given host.
|
15
|
-
def remove_snapshot(snapshot,
|
16
|
-
client.remove_snapshot('snapshot' => snapshot, 'removeChildren' =>
|
22
|
+
def remove_snapshot(snapshot, remove_children)
|
23
|
+
task = client.remove_snapshot('snapshot' => snapshot, 'removeChildren' => remove_children)
|
24
|
+
task_successful?(task)
|
25
|
+
rescue RbVmomi::Fault => e
|
26
|
+
Foreman::Logging.exception('Error removing VMWare Snapshot', e)
|
27
|
+
raise ::Foreman::WrappedException.new(e, N_('Unable to remove VMWare Snapshot'))
|
17
28
|
end
|
18
29
|
|
19
30
|
# Revert Snapshot
|
20
31
|
#
|
21
32
|
# This method revert a host to a given Snapshot.
|
22
33
|
def revert_snapshot(snapshot)
|
23
|
-
client.revert_to_snapshot(snapshot)
|
34
|
+
task = client.revert_to_snapshot(snapshot)
|
35
|
+
task_successful?(task)
|
36
|
+
rescue RbVmomi::Fault => e
|
37
|
+
Foreman::Logging.exception('Error reverting VMWare Snapshot', e)
|
38
|
+
raise ::Foreman::WrappedException.new(e, N_('Unable to revert VMWare Snapshot'))
|
24
39
|
end
|
25
40
|
|
26
41
|
# Update Snapshot
|
@@ -28,13 +43,30 @@ module ForemanSnapshotManagement
|
|
28
43
|
# This method renames a Snapshot from a given host.
|
29
44
|
def update_snapshot(snapshot, name, description)
|
30
45
|
client.rename_snapshot('snapshot' => snapshot, 'name' => name, 'description' => description)
|
46
|
+
true
|
47
|
+
rescue RbVmomi::Fault => e
|
48
|
+
Foreman::Logging.exception('Error updating VMWare Snapshot', e)
|
49
|
+
raise ::Foreman::WrappedException.new(e, N_('Unable to update VMWare Snapshot'))
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get Snapshot
|
53
|
+
#
|
54
|
+
# This methods returns a specific Snapshot for a given host.
|
55
|
+
def get_snapshot(server_id, snapshot_id)
|
56
|
+
client.snapshots(server_id: server_id).get(snapshot_id)
|
31
57
|
end
|
32
58
|
|
33
59
|
# Get Snapshots
|
34
60
|
#
|
35
61
|
# This methods returns Snapshots from a given host.
|
36
62
|
def get_snapshots(server_id)
|
37
|
-
client.snapshots(server_id: server_id)
|
63
|
+
client.snapshots(server_id: server_id).all(recursive: true)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def task_successful?(task)
|
69
|
+
task['task_state'] == 'success' || task['state'] == 'success'
|
38
70
|
end
|
39
71
|
end
|
40
72
|
end
|
@@ -1,21 +1,11 @@
|
|
1
|
-
tab = "<% if @host.provider == 'VMware' %>
|
2
|
-
<li><a href='#snapshots' data-toggle='tab'><%= _('Snapshots') %></a></li>
|
3
|
-
<% end %>"
|
4
|
-
|
5
|
-
tab_content = "<div id='snapshots' class='tab-pane'
|
6
|
-
data-ajax-url='<%= host_snapshots_path(host_id: @host)%>'
|
7
|
-
data-on-complete='onContentLoad'>
|
8
|
-
<%= spinner(_('Loading Parameters information ...')) %>
|
9
|
-
</div>"
|
10
|
-
|
11
1
|
# Add a Snapshots tab in the view of a host
|
12
2
|
Deface::Override.new(virtual_path: 'hosts/show',
|
13
3
|
name: 'add_host_snapshot_tab',
|
14
4
|
insert_bottom: 'ul',
|
15
|
-
|
5
|
+
partial: 'foreman_snapshot_management/hosts/snapshots_tab')
|
16
6
|
|
17
7
|
# Load content of Snapshots tab
|
18
8
|
Deface::Override.new(virtual_path: 'hosts/show',
|
19
9
|
name: 'add_host_snapshots_tab_content',
|
20
10
|
insert_bottom: 'div#myTabContent',
|
21
|
-
|
11
|
+
partial: 'foreman_snapshot_management/hosts/snapshots_tab_content')
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<% if @host.compute_resource && @host.compute_resource.capabilities.include?(:snapshots) && authorized_for(:controller => 'foreman_snapshot_management/snapshots', :action => :index) %>
|
2
|
+
<div id='snapshots' class='tab-pane'
|
3
|
+
data-ajax-url='<%= host_snapshots_path(host_id: @host)%>'
|
4
|
+
data-on-complete='onContentLoad'>
|
5
|
+
<%= spinner(_('Loading Snapshots information ...')) %>
|
6
|
+
</div>
|
7
|
+
<% end %>
|
@@ -8,29 +8,40 @@
|
|
8
8
|
</tr>
|
9
9
|
</thead>
|
10
10
|
<tbody>
|
11
|
-
|
12
|
-
<td>
|
13
|
-
<%= f.text_field :name, class: 'form-control' %>
|
14
|
-
</td>
|
15
|
-
<td>
|
16
|
-
<%= f.text_field :description, class: 'form-control' %>
|
17
|
-
</td>
|
18
|
-
<td>
|
19
|
-
<%= f.submit _('Create'), class: 'btn btn-success' %>
|
20
|
-
</td>
|
21
|
-
</tr>
|
22
|
-
<% @snapshots.each do |ref, snap| %>
|
11
|
+
<% if authorized_for(:controller => 'foreman_snapshot_management/snapshots', :action => :create) %>
|
23
12
|
<tr>
|
24
13
|
<td>
|
25
|
-
<%=
|
14
|
+
<%= f.text_field :name, class: 'form-control' %>
|
26
15
|
</td>
|
27
16
|
<td>
|
28
|
-
<%=
|
17
|
+
<%= f.text_field :description, class: 'form-control' %>
|
18
|
+
</td>
|
19
|
+
<td>
|
20
|
+
<%= f.submit _('Create'), class: 'btn btn-success' %>
|
21
|
+
</td>
|
22
|
+
</tr>
|
23
|
+
<% end %>
|
24
|
+
<% @snapshots.each do |snapshot| %>
|
25
|
+
<tr>
|
26
|
+
<td>
|
27
|
+
<% if authorized_for(:controller => 'foreman_snapshot_management/snapshots', :action => :edit) %>
|
28
|
+
<%= edit_textfield snapshot, :name %>
|
29
|
+
<% else %>
|
30
|
+
<%= snapshot.name %>
|
31
|
+
<% end %>
|
32
|
+
<br /><%= snapshot.formatted_create_time() %>
|
33
|
+
</td>
|
34
|
+
<td>
|
35
|
+
<% if authorized_for(:controller => 'foreman_snapshot_management/snapshots', :action => :edit) %>
|
36
|
+
<%= edit_textarea snapshot, :description %>
|
37
|
+
<% else %>
|
38
|
+
<%= snapshot.description %>
|
39
|
+
<% end %>
|
29
40
|
</td>
|
30
41
|
<td>
|
31
42
|
<%= action_buttons(
|
32
|
-
display_link_if_authorized(_('Rollback'), hash_for_revert_host_snapshot_path(host_id: @host, id:
|
33
|
-
display_delete_if_authorized(hash_for_host_snapshot_path(host_id: @host, id:
|
43
|
+
display_link_if_authorized(_('Rollback'), hash_for_revert_host_snapshot_path(host_id: @host, id: snapshot.id), method: :put, class: 'btn btn-primary', data: {confirm: _("Are you sure to revert this Snapshot?")}),
|
44
|
+
display_delete_if_authorized(hash_for_host_snapshot_path(host_id: @host, id: snapshot.id), data: {confirm: _("Are you sure to delete this Snapshot?")}),
|
34
45
|
) %>
|
35
46
|
</td>
|
36
47
|
</tr>
|
data/config/routes.rb
CHANGED
@@ -1,4 +1,21 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
|
+
namespace :api, :defaults => { :format => 'json' } do
|
3
|
+
scope '(:apiv)', :module => :v2,
|
4
|
+
:defaults => { :apiv => 'v2' },
|
5
|
+
:apiv => /v1|v2/,
|
6
|
+
:constraints => ApiConstraints.new(:version => 2, :default => true) do
|
7
|
+
constraints(:host_id => /[^\/]+/) do
|
8
|
+
resources :hosts, :only => [] do
|
9
|
+
constraints(:id => /[^\/]+/) do
|
10
|
+
resources :snapshots, except: [:new, :edit] do
|
11
|
+
put :revert, :on => :collection
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
2
19
|
constraints(host_id: %r{[^\/]+}) do
|
3
20
|
resources :hosts, only: [] do
|
4
21
|
resources :snapshots, module: 'foreman_snapshot_management' do
|
@@ -4,25 +4,52 @@ module ForemanSnapshotManagement
|
|
4
4
|
class Engine < ::Rails::Engine
|
5
5
|
engine_name 'foreman_snapshot_management'
|
6
6
|
|
7
|
-
config.autoload_paths += Dir["#{config.root}/app/
|
8
|
-
config.autoload_paths += Dir["#{config.root}/app/
|
9
|
-
config.autoload_paths += Dir["#{config.root}/app/models/foreman_snapshot_management"]
|
10
|
-
config.autoload_paths += Dir["#{config.root}/app/overrides"]
|
7
|
+
config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
|
8
|
+
config.autoload_paths += Dir["#{config.root}/app/controllers/concerns"]
|
11
9
|
|
12
10
|
initializer 'foreman_snapshot_management.register_plugin', before: :finisher_hook do |_app|
|
13
11
|
Foreman::Plugin.register :foreman_snapshot_management do
|
14
12
|
requires_foreman '>= 1.14'
|
15
13
|
|
14
|
+
apipie_documented_controllers ["#{ForemanSnapshotManagement::Engine.root}/app/controllers/api/v2/*.rb"]
|
15
|
+
|
16
16
|
# Add permissions
|
17
17
|
security_block :foreman_snapshot_management do
|
18
|
-
permission :
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
permission :view_snapshots, {
|
19
|
+
:'foreman_snapshot_management/snapshots' => [:index],
|
20
|
+
:'api/v2/snapshots' => [:index, :show]
|
21
|
+
}, :resource_type => 'Host'
|
22
|
+
|
23
|
+
permission :create_snapshots, {
|
24
|
+
:'foreman_snapshot_management/snapshots' => [:create],
|
25
|
+
:'api/v2/snapshots' => [:create]
|
26
|
+
}, :resource_type => 'Host'
|
27
|
+
|
28
|
+
permission :edit_snapshots, {
|
29
|
+
:'foreman_snapshot_management/snapshots' => [:update],
|
30
|
+
:'api/v2/snapshots' => [:update]
|
31
|
+
}, :resource_type => 'Host'
|
32
|
+
|
33
|
+
permission :destroy_snapshots, {
|
34
|
+
:'foreman_snapshot_management/snapshots' => [:destroy],
|
35
|
+
:'api/v2/snapshots' => [:destroy]
|
36
|
+
}, :resource_type => 'Host'
|
37
|
+
|
38
|
+
permission :revert_snapshots, {
|
39
|
+
:'foreman_snapshot_management/snapshots' => [:revert],
|
40
|
+
:'api/v2/snapshots' => [:revert]
|
41
|
+
}, :resource_type => 'Host'
|
22
42
|
end
|
23
43
|
|
24
|
-
#
|
25
|
-
role '
|
44
|
+
# Adds roles if they do not exist
|
45
|
+
role 'Snapshot Viewer', [:view_snapshots]
|
46
|
+
role 'Snapshot Manager', [
|
47
|
+
:view_snapshots,
|
48
|
+
:create_snapshots,
|
49
|
+
:edit_snapshots,
|
50
|
+
:destroy_snapshots,
|
51
|
+
:revert_snapshots
|
52
|
+
]
|
26
53
|
end
|
27
54
|
end
|
28
55
|
|
@@ -45,7 +72,14 @@ module ForemanSnapshotManagement
|
|
45
72
|
# Include concerns in this config.to_prepare block
|
46
73
|
config.to_prepare do
|
47
74
|
begin
|
48
|
-
|
75
|
+
# Load Foreman extensions
|
76
|
+
::Foreman::Model::Vmware.send(:prepend, ForemanSnapshotManagement::VmwareExtensions)
|
77
|
+
|
78
|
+
# Load Fog extensions
|
79
|
+
if Foreman::Model::Vmware.available?
|
80
|
+
Fog::Compute::Vsphere::Real.send(:prepend, FogExtensions::Vsphere::Snapshots::Real)
|
81
|
+
Fog::Compute::Vsphere::Mock.send(:prepend, FogExtensions::Vsphere::Snapshots::Mock)
|
82
|
+
end
|
49
83
|
rescue => e
|
50
84
|
Rails.logger.warn "ForemanSnapshotManagement: skipping engine hook (#{e})"
|
51
85
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Api::V2::SnapshotsControllerTest < ActionController::TestCase
|
4
|
+
let(:compute_resource) do
|
5
|
+
cr = FactoryGirl.create(:compute_resource, :vmware, :uuid => 'Solutions')
|
6
|
+
ComputeResource.find_by_id(cr.id)
|
7
|
+
end
|
8
|
+
let(:uuid) { '5032c8a5-9c5e-ba7a-3804-832a03e16381' }
|
9
|
+
let(:host) { FactoryGirl.create(:host, :managed, :compute_resource => compute_resource, :uuid => uuid) }
|
10
|
+
let(:snapshot_id) { 'snapshot-0101' }
|
11
|
+
setup { ::Fog.mock! }
|
12
|
+
teardown { ::Fog.unmock! }
|
13
|
+
|
14
|
+
test 'should get index' do
|
15
|
+
get :index, { :host_id => host.to_param }
|
16
|
+
assert_response :success
|
17
|
+
assert_not_nil assigns(:snapshots)
|
18
|
+
body = ActiveSupport::JSON.decode(@response.body)
|
19
|
+
refute_empty body
|
20
|
+
refute_empty body['results']
|
21
|
+
end
|
22
|
+
|
23
|
+
test 'should show snapshot' do
|
24
|
+
get :show, { :host_id => host.to_param, :id => snapshot_id }
|
25
|
+
assert_not_nil assigns(:snapshot)
|
26
|
+
assert_response :success
|
27
|
+
body = ActiveSupport::JSON.decode(@response.body)
|
28
|
+
refute_empty body
|
29
|
+
end
|
30
|
+
|
31
|
+
test 'should 404 for unknown snapshot' do
|
32
|
+
get :show, { :host_id => host.to_param, :id => 'does-not-exist' }
|
33
|
+
assert_response :not_found
|
34
|
+
end
|
35
|
+
|
36
|
+
test 'should create snapshot' do
|
37
|
+
post :create, { :host_id => host.to_param, :name => 'test' }
|
38
|
+
assert_response :created
|
39
|
+
assert_not_nil assigns(:snapshot)
|
40
|
+
end
|
41
|
+
|
42
|
+
test 'should update snapshot' do
|
43
|
+
name = 'test'
|
44
|
+
put :update, { :host_id => host.to_param, :id => snapshot_id.to_param, :name => name.to_param }
|
45
|
+
assert_response :success
|
46
|
+
end
|
47
|
+
|
48
|
+
test 'should destroy snapshot' do
|
49
|
+
delete :destroy, { :host_id => host.to_param, :id => snapshot_id.to_param }
|
50
|
+
assert_response :success
|
51
|
+
end
|
52
|
+
|
53
|
+
test 'should revert snapshot' do
|
54
|
+
put :revert, { :host_id => host.to_param, :id => snapshot_id.to_param }
|
55
|
+
assert_response :success
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module ForemanSnapshotManagement
|
4
|
+
class SnapshotsControllerTest < ActionController::TestCase
|
5
|
+
let(:compute_resource) do
|
6
|
+
cr = FactoryGirl.create(:compute_resource, :vmware, :uuid => 'Solutions')
|
7
|
+
ComputeResource.find_by_id(cr.id)
|
8
|
+
end
|
9
|
+
let(:uuid) { '5032c8a5-9c5e-ba7a-3804-832a03e16381' }
|
10
|
+
let(:host) { FactoryGirl.create(:host, :managed, :compute_resource => compute_resource, :uuid => uuid) }
|
11
|
+
let(:snapshot_id) { 'snapshot-0101' }
|
12
|
+
setup { ::Fog.mock! }
|
13
|
+
teardown { ::Fog.unmock! }
|
14
|
+
|
15
|
+
context 'GET #index' do
|
16
|
+
test 'should get index' do
|
17
|
+
get :index, { :host_id => host.to_param }, set_session_user
|
18
|
+
assert_response :success
|
19
|
+
assert_not_nil assigns(:snapshots)
|
20
|
+
assert_template 'foreman_snapshot_management/snapshots/_index'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'POST #create' do
|
25
|
+
test 'create valid' do
|
26
|
+
post :create, { :host_id => host.to_param, :snapshot => { :name => 'test' } }, set_session_user
|
27
|
+
assert_redirected_to host_url(host, :anchor => 'snapshots')
|
28
|
+
assert_includes flash[:notice], 'Successfully created Snapshot.'
|
29
|
+
end
|
30
|
+
|
31
|
+
test 'create invalid' do
|
32
|
+
ForemanSnapshotManagement::Snapshot.any_instance.stubs(:create).returns(false)
|
33
|
+
post :create, { :host_id => host.to_param, :snapshot => { :name => nil } }, set_session_user
|
34
|
+
assert_redirected_to host_url(host, :anchor => 'snapshots')
|
35
|
+
assert_includes flash[:error], 'Error occurred while creating Snapshot'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'DELETE #destroy' do
|
40
|
+
test 'destroy successful' do
|
41
|
+
delete :destroy, { :host_id => host.to_param, :id => snapshot_id }, set_session_user
|
42
|
+
assert_redirected_to host_url(host, :anchor => 'snapshots')
|
43
|
+
assert_includes flash[:notice], 'Successfully deleted Snapshot.'
|
44
|
+
end
|
45
|
+
|
46
|
+
test 'destroy with error' do
|
47
|
+
ForemanSnapshotManagement::Snapshot.any_instance.stubs(:destroy).returns(false)
|
48
|
+
delete :destroy, { :host_id => host.to_param, :id => snapshot_id }, set_session_user
|
49
|
+
assert_redirected_to host_url(host, :anchor => 'snapshots')
|
50
|
+
assert_includes flash[:error], 'Error occurred while removing Snapshot'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'PUT #revert' do
|
55
|
+
test 'revert successful' do
|
56
|
+
put :revert, { :host_id => host.to_param, :id => snapshot_id }, set_session_user
|
57
|
+
assert_redirected_to host_url(host, :anchor => 'snapshots')
|
58
|
+
assert_includes flash[:notice], 'VM successfully rolled back.'
|
59
|
+
end
|
60
|
+
|
61
|
+
test 'revert with error' do
|
62
|
+
ForemanSnapshotManagement::Snapshot.any_instance.stubs(:revert).returns(false)
|
63
|
+
put :revert, { :host_id => host.to_param, :id => snapshot_id }, set_session_user
|
64
|
+
assert_redirected_to host_url(host, :anchor => 'snapshots')
|
65
|
+
assert_includes flash[:error], 'Error occurred while rolling back VM'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'PUT #update' do
|
70
|
+
test 'update successful' do
|
71
|
+
data = { 'name' => 'test 2', 'description' => '' }
|
72
|
+
put :update, { :host_id => host.to_param, :id => snapshot_id, :snapshot => data }, set_session_user
|
73
|
+
assert_response :success
|
74
|
+
body = ActiveSupport::JSON.decode(@response.body)
|
75
|
+
assert_equal(data, body)
|
76
|
+
end
|
77
|
+
|
78
|
+
test 'update with error' do
|
79
|
+
ForemanSnapshotManagement::Snapshot.any_instance.stubs(:save).returns(false)
|
80
|
+
put :update, { :host_id => host.to_param, :id => snapshot_id, :snapshot => { :name => 'test 2' } }, set_session_user
|
81
|
+
assert_response :unprocessable_entity
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_snapshot_management
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ATIX AG
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -48,12 +48,25 @@ files:
|
|
48
48
|
- LICENSE
|
49
49
|
- README.md
|
50
50
|
- Rakefile
|
51
|
+
- app/controllers/api/v2/snapshots_controller.rb
|
52
|
+
- app/controllers/concerns/foreman/controller/parameters/snapshot.rb
|
51
53
|
- app/controllers/foreman_snapshot_management/snapshots_controller.rb
|
52
54
|
- app/helpers/foreman_snapshot_management/snapshot_helper.rb
|
53
|
-
- app/
|
55
|
+
- app/models/concerns/fog_extensions/vsphere/snapshots/mock.rb
|
56
|
+
- app/models/concerns/fog_extensions/vsphere/snapshots/real.rb
|
54
57
|
- app/models/foreman_snapshot_management/snapshot.rb
|
55
58
|
- app/models/foreman_snapshot_management/vmware_extensions.rb
|
56
59
|
- app/overrides/hosts/add_tab_to_host_overview.rb
|
60
|
+
- app/views/api/v2/snapshots/base.json.rabl
|
61
|
+
- app/views/api/v2/snapshots/create.json.rabl
|
62
|
+
- app/views/api/v2/snapshots/destroy.json.rabl
|
63
|
+
- app/views/api/v2/snapshots/index.json.rabl
|
64
|
+
- app/views/api/v2/snapshots/main.json.rabl
|
65
|
+
- app/views/api/v2/snapshots/revert.json.rabl
|
66
|
+
- app/views/api/v2/snapshots/show.json.rabl
|
67
|
+
- app/views/api/v2/snapshots/update.json.rabl
|
68
|
+
- app/views/foreman_snapshot_management/hosts/_snapshots_tab.html.erb
|
69
|
+
- app/views/foreman_snapshot_management/hosts/_snapshots_tab_content.html.erb
|
57
70
|
- app/views/foreman_snapshot_management/snapshots/_index.html.erb
|
58
71
|
- config/routes.rb
|
59
72
|
- lib/foreman_snapshot_management.rb
|
@@ -64,8 +77,9 @@ files:
|
|
64
77
|
- locale/en/foreman_snapshot_management.po
|
65
78
|
- locale/foreman_snapshot_management.pot
|
66
79
|
- locale/gemspec.rb
|
80
|
+
- test/controllers/api/v2/snapshots_test.rb
|
81
|
+
- test/controllers/foreman_snapshot_management/snapshots_controller_test.rb
|
67
82
|
- test/test_plugin_helper.rb
|
68
|
-
- test/unit/foreman_snapshot_management_test.rb
|
69
83
|
homepage: http://www.orcharhino.com
|
70
84
|
licenses: []
|
71
85
|
metadata: {}
|
@@ -85,10 +99,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
99
|
version: '0'
|
86
100
|
requirements: []
|
87
101
|
rubyforge_project:
|
88
|
-
rubygems_version: 2.5.2
|
102
|
+
rubygems_version: 2.5.2.1
|
89
103
|
signing_key:
|
90
104
|
specification_version: 4
|
91
105
|
summary: Snapshot Management for VMware vSphere
|
92
106
|
test_files:
|
107
|
+
- test/controllers/api/v2/snapshots_test.rb
|
108
|
+
- test/controllers/foreman_snapshot_management/snapshots_controller_test.rb
|
93
109
|
- test/test_plugin_helper.rb
|
94
|
-
- test/unit/foreman_snapshot_management_test.rb
|
@@ -1,72 +0,0 @@
|
|
1
|
-
module ForemanSnapshotManagement
|
2
|
-
module SnapshotadministrationHelper
|
3
|
-
end
|
4
|
-
end
|
5
|
-
|
6
|
-
module Fog
|
7
|
-
module Compute
|
8
|
-
class Vsphere
|
9
|
-
class Real
|
10
|
-
# Extends fog-vsphere gem for a remove Snapshot method.
|
11
|
-
def remove_snapshot(options = {})
|
12
|
-
raise ArgumentError, 'snapshot is a required parameter' unless options.key? 'snapshot'
|
13
|
-
raise ArgumentError, 'removeChildren is a required parameter' unless options.key? 'removeChildren'
|
14
|
-
|
15
|
-
unless Snapshot === options['snapshot']
|
16
|
-
raise ArgumentError, 'snapshot is a required parameter'
|
17
|
-
end
|
18
|
-
|
19
|
-
task = options['snapshot'].mo_ref.RemoveSnapshot_Task(
|
20
|
-
removeChildren: options['removeChildren']
|
21
|
-
)
|
22
|
-
task.wait_for_completion
|
23
|
-
|
24
|
-
{
|
25
|
-
'task_state' => task.info.state
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
29
|
-
# Extends fog-vsphere gem for a rename Snapshot method.
|
30
|
-
# TODO: Add info state
|
31
|
-
def rename_snapshot(options = {})
|
32
|
-
raise ArgumentError, 'snapshot is a required parameter' unless options.key? 'snapshot'
|
33
|
-
raise ArgumentError, 'name is a required parameter' unless options.key? 'name'
|
34
|
-
raise ArgumentError, 'description is a required parameter' unless options.key? 'description'
|
35
|
-
|
36
|
-
unless Snapshot === options['snapshot']
|
37
|
-
raise ArgumentError, 'snapshot is a required parameter'
|
38
|
-
end
|
39
|
-
|
40
|
-
task = options['snapshot'].mo_ref.RenameSnapshot(
|
41
|
-
name: options['name'],
|
42
|
-
description: options['description']
|
43
|
-
)
|
44
|
-
# task.wait_for_completion
|
45
|
-
|
46
|
-
# {
|
47
|
-
# 'task_state' => task.info.state
|
48
|
-
# }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
class Mock
|
52
|
-
def remove_snapshot(snapshot)
|
53
|
-
raise ArgumentError, 'snapshot is a required parameter' unless options.key? 'snapshot'
|
54
|
-
raise ArgumentError, 'removeChildren is a required parameter' unless options.key? 'removeChildren'
|
55
|
-
raise ArgumentError, 'snapshot is a required parameter' if snapshot.nil?
|
56
|
-
{
|
57
|
-
'task_state' => 'success'
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
def rename_snapshot(_snapshot)
|
62
|
-
raise ArgumentError, 'snapshot is a required parameter' unless options.key? 'snapshot'
|
63
|
-
raise ArgumentError, 'name is a required parameter' unless options.key? 'name'
|
64
|
-
raise ArgumentError, 'description is a required parameter' unless options.key? 'description'
|
65
|
-
{
|
66
|
-
'task_state' => 'success'
|
67
|
-
}
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|