foreman_snapshot_management 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|