foreman_templates 5.0.0 → 5.0.1
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/app/controllers/api/v2/template_controller.rb +32 -5
- data/app/models/concerns/foreman_templates/provisioning_template_import.rb +88 -54
- data/app/models/concerns/foreman_templates/ptable_import.rb +83 -51
- data/app/models/concerns/foreman_templates/template_import.rb +55 -44
- data/app/models/setting/template_sync.rb +47 -45
- data/app/services/foreman_templates/action.rb +1 -1
- data/app/services/foreman_templates/template_exporter.rb +4 -3
- data/app/services/foreman_templates/template_importer.rb +35 -23
- data/config/routes.rb +4 -5
- data/lib/foreman_templates/engine.rb +3 -0
- data/lib/foreman_templates/version.rb +1 -1
- data/lib/tasks/foreman_templates_tasks.rake +10 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3889bfa2270632f8e0bfacc4a35a1a85c3eca1e6
|
4
|
+
data.tar.gz: 797795544b68cbb4b2d408e8293e6689d1e3ff02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da4517d69b72c25c3065db6f0afba3621b6165c9c7a8e317a52e0a4c24a81b369fac520cc50ea102ea7accc8b9934452ef38b4a6e270e15d28c2a606e4105688
|
7
|
+
data.tar.gz: ae821ae58dedaaa29a83e41924b1ac30383c0d40e827c6d039397ac7bfd95f7923c94d418fcc1f89bcc59a020ae3b58b385db90bcb71353420384e8b31d4ba01
|
@@ -1,15 +1,17 @@
|
|
1
1
|
module Api
|
2
2
|
module V2
|
3
3
|
class TemplateController < ::Api::V2::BaseController
|
4
|
-
|
5
4
|
api :POST, "/template/import/", N_("Initiate Import")
|
6
|
-
param :
|
5
|
+
param :verbose, :bool, :required => false, :desc => N_("Set verbosity of import")
|
6
|
+
param :repo, String, :required => false, :desc => N_("Override the default repo from settings.")
|
7
7
|
param :branch, String, :required => false, :desc => N_("Branch in Git repo.")
|
8
8
|
param :prefix, String, :required => false, :desc => N_("The string all imported templates should begin with.")
|
9
9
|
param :dirname, String, :required => false, :desc => N_("The directory within the git tree containing the templates.")
|
10
|
-
param :filter, String, :required => false, :desc => N_("Import names matching this regex (case-insensitive; snippets are not filtered).")
|
10
|
+
param :filter, String, :required => false, :desc => N_("Import templates with names matching this regex (case-insensitive; snippets are not filtered).")
|
11
11
|
param :negate, :bool, :required => false, :desc => N_("Negate the prefix (for purging).")
|
12
|
-
param :associate,
|
12
|
+
param :associate, Setting::TemplateSync.associate_types.keys, :required => false, :desc => N_("Associate to OS's, Locations & Organizations. Options are: always, new or never.")
|
13
|
+
param :force, :bool, :required => false, :desc => N_("Update templates that are locked")
|
14
|
+
|
13
15
|
def import
|
14
16
|
results = ForemanTemplates::TemplateImporter.new({
|
15
17
|
verbose: params['verbose'],
|
@@ -20,8 +22,33 @@ module Api
|
|
20
22
|
filter: params['filter'],
|
21
23
|
associate: params['associate'],
|
22
24
|
negate: params['negate'],
|
25
|
+
force: params['force']
|
23
26
|
}).import!
|
24
|
-
render :json => {:message => results}
|
27
|
+
render :json => { :message => results }
|
28
|
+
end
|
29
|
+
|
30
|
+
api :POST, "/template/export", N_("Initiate Export")
|
31
|
+
param :verbose, :bool, :required => false, :desc => N_("Set verbosity of export")
|
32
|
+
param :repo, String, :required => false, :desc => N_("Override the default repo from settings")
|
33
|
+
param :branch, String, :required => false, :desc => N_("Branch in Git repo.")
|
34
|
+
param :filter, String, :required => false, :desc => N_("Export templates with names matching this regex (case-insensitive; snippets are not filtered).")
|
35
|
+
param :negate, :bool, :required => false, :desc => N_("Negate the prefix (for purging).")
|
36
|
+
param :metadata_export_mode, Setting::TemplateSync.metadata_export_mode_types.keys, :required => false, :desc => N_("Specify how to handle metadata")
|
37
|
+
param :dir, String, :required => false, :desc => N_("The directory within Git repo")
|
38
|
+
def export
|
39
|
+
ForemanTemplates::TemplateExporter.new({
|
40
|
+
verbose: params['verbose'],
|
41
|
+
repo: params['repo'],
|
42
|
+
branch: params['branch'],
|
43
|
+
filter: params['filter'],
|
44
|
+
negate: params['negate'],
|
45
|
+
metadata_export_mode: params['metadata_export_mode'],
|
46
|
+
dir: params['dir']
|
47
|
+
}).export!
|
48
|
+
render :json => { :message => _('Success') }
|
49
|
+
rescue => e
|
50
|
+
logger.debug e
|
51
|
+
render :json => { :message => (_('Something went wrong during export: %s') % e.message) }, :status => 500
|
25
52
|
end
|
26
53
|
end
|
27
54
|
end
|
@@ -1,60 +1,94 @@
|
|
1
|
-
module ForemanTemplates
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
1
|
+
module ForemanTemplates
|
2
|
+
module ProvisioningTemplateImport
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def import!(name, text, metadata, force = false)
|
7
|
+
# Check for snippet type
|
8
|
+
return import_snippet!(name, text, force) if metadata['snippet'] || metadata['kind'] == 'snippet'
|
9
|
+
|
10
|
+
# Get template type
|
11
|
+
kind = TemplateKind.find_by(name: metadata['kind'])
|
12
|
+
raise NoKindError unless kind
|
13
|
+
|
14
|
+
# Data
|
15
|
+
template = ProvisioningTemplate.where(:name => name).first_or_initialize
|
16
|
+
data = {
|
17
|
+
:template => text,
|
18
|
+
:snippet => false,
|
19
|
+
:template_kind_id => kind.id
|
20
|
+
}
|
21
|
+
oses = map_metadata(metadata, 'oses')
|
22
|
+
locations = map_metadata(metadata, 'locations')
|
23
|
+
organizations = map_metadata(metadata, 'organizations')
|
24
|
+
|
25
|
+
# Printout helpers
|
26
|
+
c_or_u = template.new_record? ? 'Creating' : 'Updating'
|
27
|
+
id_string = template.new_record? ? '' : "id #{template.id}"
|
28
|
+
if template.locked? && !template.new_record? && !force
|
29
|
+
return { :diff => nil,
|
30
|
+
:status => false,
|
31
|
+
:result => "Skipping Template #{id_string}:#{name} - template is locked" }
|
32
|
+
end
|
33
|
+
|
34
|
+
associate_metadata data, template, metadata, oses, organizations, locations
|
35
|
+
|
36
|
+
diff = nil
|
37
|
+
status = nil
|
38
|
+
if template_changed?(data, template)
|
39
|
+
diff = create_diff(data, template)
|
40
|
+
template.ignore_locking do
|
41
|
+
status = template.update_attributes(data)
|
42
|
+
end
|
43
|
+
result = build_associations_result c_or_u, id_string, name, oses, organizations, locations
|
44
|
+
else
|
45
|
+
status = true
|
46
|
+
result = " No change to Template #{id_string}:#{name}"
|
47
|
+
end
|
48
|
+
|
49
|
+
{ :diff => diff, :status => status, :result => result, :errors => template.errors }
|
50
|
+
end
|
51
|
+
|
52
|
+
def associate_metadata(data, template, metadata, oses, organizations, locations)
|
53
|
+
if (metadata['associate'] == 'new' && template.new_record?) || (metadata['associate'] == 'always')
|
54
|
+
data[:operatingsystem_ids] = oses.map(&:id)
|
55
|
+
data[:location_ids] = locations.map(&:id)
|
56
|
+
data[:organization_ids] = organizations.map(&:id)
|
57
|
+
end
|
58
|
+
data
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_associations_result(c_or_u, id_string, name, oses, organizations, locations)
|
62
|
+
res = " #{c_or_u} Template #{id_string}:#{name}"
|
63
|
+
res += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
|
64
|
+
res += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
|
65
|
+
res += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
|
66
|
+
res
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_diff(data, template)
|
70
|
+
if template_content_changed?(template.template, data[:template])
|
71
|
+
Diffy::Diff.new(
|
72
|
+
template.template,
|
73
|
+
data[:template],
|
74
|
+
:include_diff_info => true
|
75
|
+
).to_s(:color)
|
76
|
+
else
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def template_content_changed?(template_template, data_template)
|
82
|
+
template_template != data_template
|
83
|
+
end
|
84
|
+
|
85
|
+
def associations_changed?(data)
|
86
|
+
!(data[:operatingsystem_ids] || data[:location_ids] || data[:organization_ids]).nil?
|
32
87
|
end
|
33
88
|
|
34
|
-
|
35
|
-
|
36
|
-
template.template,
|
37
|
-
data[:template],
|
38
|
-
:include_diff_info => true
|
39
|
-
).to_s(:color)
|
40
|
-
status = template.update_attributes(data)
|
41
|
-
result = " #{c_or_u} Template #{id_string}:#{name}"
|
42
|
-
result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
|
43
|
-
result += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
|
44
|
-
result += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
|
45
|
-
elsif data[:operatingsystem_ids] || data[:location_ids] || data[:organization_ids]
|
46
|
-
diff = nil
|
47
|
-
status = template.update_attributes(data)
|
48
|
-
result = " #{c_or_u} Template Associations #{id_string}:#{name}"
|
49
|
-
result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
|
50
|
-
result += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
|
51
|
-
result += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
|
52
|
-
else
|
53
|
-
diff = nil
|
54
|
-
status = true
|
55
|
-
result = " No change to Template #{id_string}:#{name}"
|
89
|
+
def template_changed?(data, template)
|
90
|
+
template_content_changed?(template.template, data[:template]) || associations_changed?(data)
|
56
91
|
end
|
57
|
-
{ :diff => diff, :status => status, :result => result }
|
58
92
|
end
|
59
93
|
end
|
60
94
|
end
|
@@ -1,57 +1,89 @@
|
|
1
|
-
module ForemanTemplates
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
1
|
+
module ForemanTemplates
|
2
|
+
module PtableImport
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def import!(name, text, metadata, force = false)
|
7
|
+
# Check for snippet type
|
8
|
+
return import_snippet!(name, text, force) if metadata['snippet']
|
9
|
+
|
10
|
+
# Data
|
11
|
+
ptable = Ptable.where(:name => name).first_or_initialize
|
12
|
+
data = {
|
13
|
+
:layout => text
|
14
|
+
}
|
15
|
+
oses = map_metadata(metadata, 'oses')
|
16
|
+
locations = map_metadata(metadata, 'locations')
|
17
|
+
organizations = map_metadata(metadata, 'organizations')
|
18
|
+
|
19
|
+
# Printout helpers
|
20
|
+
c_or_u = ptable.new_record? ? 'Creating' : 'Updating'
|
21
|
+
id_string = ptable.new_record? ? '' : "id #{ptable.id}"
|
22
|
+
if ptable.locked? && !ptable.new_record? && !force
|
23
|
+
return { :diff => nil,
|
24
|
+
:status => false,
|
25
|
+
:result => "Skipping Partition Table #{id_string}:#{name} - partition table is locked" }
|
26
|
+
end
|
27
|
+
|
28
|
+
associate_metadata data, ptable, metadata, oses, organizations, locations
|
29
|
+
|
30
|
+
diff = nil
|
31
|
+
status = nil
|
32
|
+
if ptable_changed?(data, ptable)
|
33
|
+
diff = create_diff(data, ptable)
|
34
|
+
ptable.ignore_locking do
|
35
|
+
status = ptable.update_attributes(data)
|
36
|
+
end
|
37
|
+
result = build_associations_result c_or_u, id_string, name, oses, organizations, locations
|
38
|
+
else
|
39
|
+
status = true
|
40
|
+
result = " No change to Ptable #{id_string}:#{name}"
|
41
|
+
end
|
42
|
+
{ :diff => diff, :status => status, :result => result, :errors => ptable.errors }
|
43
|
+
end
|
44
|
+
|
45
|
+
def associate_metadata(data, ptable, metadata, oses, organizations, locations)
|
46
|
+
if (metadata['associate'] == 'new' && ptable.new_record?) || (metadata['associate'] == 'always')
|
47
|
+
data[:operatingsystem_ids] = oses.map(&:id)
|
48
|
+
data[:os_family] = oses.map(&:family).uniq.first
|
49
|
+
data[:location_ids] = locations.map(&:id)
|
50
|
+
data[:organization_ids] = organizations.map(&:id)
|
51
|
+
end
|
52
|
+
data
|
53
|
+
end
|
54
|
+
|
55
|
+
def ptable_content_changed?(data_layout, ptable_layout)
|
56
|
+
data_layout != ptable_layout
|
57
|
+
end
|
58
|
+
|
59
|
+
def associations_changed?(data)
|
60
|
+
!(data[:os_family] || data[:location_ids] || data[:organization_ids]).nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_diff(data, ptable)
|
64
|
+
if ptable_content_changed?(data[:layout], ptable.layout)
|
65
|
+
Diffy::Diff.new(
|
66
|
+
ptable.layout,
|
67
|
+
data[:layout],
|
68
|
+
:include_diff_info => true
|
69
|
+
).to_s(:color)
|
70
|
+
else
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_associations_result(c_or_u, id_string, name, oses, organizations, locations)
|
76
|
+
res = " #{c_or_u} Ptable #{id_string}:#{name}"
|
77
|
+
res += "\n Operatingsystem Family:\n - #{oses.map(&:family).uniq.first}" unless oses.empty?
|
78
|
+
res += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
|
79
|
+
res += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
|
80
|
+
res += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
|
81
|
+
res
|
27
82
|
end
|
28
83
|
|
29
|
-
|
30
|
-
|
31
|
-
ptable.layout,
|
32
|
-
data[:layout],
|
33
|
-
:include_diff_info => true
|
34
|
-
).to_s(:color)
|
35
|
-
status = ptable.update_attributes(data)
|
36
|
-
result = " #{c_or_u} Ptable #{id_string}:#{name}"
|
37
|
-
result += "\n Operatingsystem Family:\n - #{oses.map(&:family).uniq.first}" unless oses.empty?
|
38
|
-
result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
|
39
|
-
result += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
|
40
|
-
result += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
|
41
|
-
elsif data[:os_family] || data[:location_ids] || data[:organization_ids]
|
42
|
-
diff = nil
|
43
|
-
status = ptable.update_attributes(data)
|
44
|
-
result = " #{c_or_u} Ptable Associations #{id_string}:#{name}"
|
45
|
-
result += "\n Operatingsystem Family:\n - #{oses.map(&:family).uniq.first}" unless oses.empty?
|
46
|
-
result += "\n Operatingsystem Associations:\n - #{oses.map(&:fullname).join("\n - ")}" unless oses.empty?
|
47
|
-
result += "\n Organizations Associations:\n - #{organizations.map(&:name).join("\n - ")}" unless organizations.empty?
|
48
|
-
result += "\n Location Associations:\n - #{locations.map(&:name).join("\n - ")}" unless locations.empty?
|
49
|
-
else
|
50
|
-
diff = nil
|
51
|
-
status = true
|
52
|
-
result = " No change to Ptable #{id_string}:#{name}"
|
84
|
+
def ptable_changed?(data, ptable)
|
85
|
+
ptable_content_changed?(data[:layout], ptable.layout) || associations_changed?(data)
|
53
86
|
end
|
54
|
-
{ :diff => diff, :status => status, :result => result }
|
55
87
|
end
|
56
88
|
end
|
57
89
|
end
|
@@ -1,53 +1,64 @@
|
|
1
|
-
module ForemanTemplates
|
2
|
-
|
1
|
+
module ForemanTemplates
|
2
|
+
module TemplateImport
|
3
|
+
extend ActiveSupport::Concern
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
module ClassMethods
|
6
|
+
def import_snippet!(name, text, force = false)
|
7
|
+
# Data
|
8
|
+
snippet = self.where(:name => name).first_or_initialize
|
9
|
+
data = {
|
10
|
+
:template => text,
|
11
|
+
:snippet => true
|
12
|
+
}
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
# Printout helpers
|
15
|
+
c_or_u = snippet.new_record? ? 'Creating' : 'Updating'
|
16
|
+
id_string = snippet.new_record? ? '' : "id #{snippet.id}"
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
status
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
18
|
+
if snippet.locked? && !snippet.new_record? && !force
|
19
|
+
return { :diff => nil,
|
20
|
+
:status => false,
|
21
|
+
:result => "Skipping snippet #{id_string}:#{name} - template is locked" }
|
22
|
+
end
|
23
|
+
|
24
|
+
status = nil
|
25
|
+
if data[:template] != snippet.template
|
26
|
+
diff = Diffy::Diff.new(
|
27
|
+
snippet.template,
|
28
|
+
data[:template],
|
29
|
+
:include_diff_info => true
|
30
|
+
).to_s(:color)
|
31
|
+
snippet.ignore_locking do
|
32
|
+
status = snippet.update_attributes(data)
|
33
|
+
end
|
34
|
+
result = " #{c_or_u} Snippet #{id_string}:#{name}"
|
35
|
+
else
|
36
|
+
diff = nil
|
37
|
+
status = true
|
38
|
+
result = " No change to Snippet #{id_string}:#{name}"
|
39
|
+
end
|
40
|
+
{ :diff => diff, :status => status, :result => result, :errors => snippet.errors }
|
29
41
|
end
|
30
|
-
{ :diff => diff, :status => status, :result => result }
|
31
|
-
end
|
32
42
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
def map_metadata(metadata, param)
|
44
|
+
if metadata[param]
|
45
|
+
case param
|
46
|
+
when 'oses'
|
47
|
+
metadata[param].map do |os|
|
48
|
+
Operatingsystem.all.map { |db| db.to_label =~ /^#{os}/ ? db : nil }
|
49
|
+
end.flatten.compact
|
50
|
+
when 'locations'
|
51
|
+
metadata[param].map do |loc|
|
52
|
+
User.current.my_locations.map { |db| db.name =~ /^#{loc}/ ? db : nil }
|
53
|
+
end.flatten.compact
|
54
|
+
when 'organizations'
|
55
|
+
metadata[param].map do |org|
|
56
|
+
User.current.my_organizations.map { |db| db.name =~ /^#{org}/ ? db : nil }
|
57
|
+
end.flatten.compact
|
58
|
+
end
|
59
|
+
else
|
60
|
+
[]
|
48
61
|
end
|
49
|
-
else
|
50
|
-
[]
|
51
62
|
end
|
52
63
|
end
|
53
64
|
end
|
@@ -1,54 +1,56 @@
|
|
1
|
-
class Setting
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def self.metadata_export_mode_types
|
11
|
-
{
|
12
|
-
'refresh' => _('Refresh'),
|
13
|
-
'keep' => _('Keep'),
|
14
|
-
'remove' => _('Remove')
|
15
|
-
}
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
def self.load_defaults
|
20
|
-
return unless super
|
21
|
-
|
22
|
-
%w(template_sync_filter template_sync_branch).each { |s| Setting::BLANK_ATTRS << s }
|
1
|
+
class Setting
|
2
|
+
class TemplateSync < ::Setting
|
3
|
+
def self.associate_types
|
4
|
+
{
|
5
|
+
'always' => _('Always'),
|
6
|
+
'new' => _('New'),
|
7
|
+
'never' => _('Never')
|
8
|
+
}
|
9
|
+
end
|
23
10
|
|
24
|
-
self.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
self.set('template_sync_filter', N_('Import or export names matching this regex (case-insensitive; snippets are not filtered)'), nil, N_('Filter')),
|
31
|
-
self.set('template_sync_repo', N_('Default Git repo to sync from'), 'https://github.com/theforeman/community-templates.git', N_('Repo')),
|
32
|
-
self.set('template_sync_negate', N_('Negate the prefix (for purging) / filter (for importing/exporting)'), false, N_('Negate')),
|
33
|
-
self.set('template_sync_branch', N_('Default branch in Git repo'), nil, N_('Branch')),
|
34
|
-
self.set('template_sync_metadata_export_mode', N_('Default metadata export mode, refresh re-renders metadata, keep will keep existing metadata, remove exports template withou metadata'), 'refresh', N_('Metadata export mode'), nil, { :collection => Proc.new { self.metadata_export_mode_types } })
|
35
|
-
].compact.each { |s| self.create! s.update(:category => "Setting::TemplateSync") }
|
11
|
+
def self.metadata_export_mode_types
|
12
|
+
{
|
13
|
+
'refresh' => _('Refresh'),
|
14
|
+
'keep' => _('Keep'),
|
15
|
+
'remove' => _('Remove')
|
16
|
+
}
|
36
17
|
end
|
37
18
|
|
38
|
-
|
39
|
-
|
19
|
+
def self.load_defaults
|
20
|
+
return unless super
|
21
|
+
|
22
|
+
%w(template_sync_filter template_sync_branch).each { |s| Setting::BLANK_ATTRS << s }
|
23
|
+
|
24
|
+
self.transaction do
|
25
|
+
[
|
26
|
+
self.set('template_sync_verbose', N_('Choose verbosity for Rake task importing templates'), false, N_('Verbosity')),
|
27
|
+
self.set('template_sync_associate', N_('Associate templates to OS'), 'new', N_('Associate'), nil, { :collection => Proc.new { self.associate_types } }),
|
28
|
+
self.set('template_sync_prefix', N_('The string all imported templates should begin with'), "Community ", N_('Prefix')),
|
29
|
+
self.set('template_sync_dirname', N_('The directory within the Git repo containing the templates'), '/', N_('Dirname')),
|
30
|
+
self.set('template_sync_filter', N_('Import or export names matching this regex (case-insensitive; snippets are not filtered)'), nil, N_('Filter')),
|
31
|
+
self.set('template_sync_repo', N_('Default Git repo to sync from'), 'https://github.com/theforeman/community-templates.git', N_('Repo')),
|
32
|
+
self.set('template_sync_negate', N_('Negate the prefix (for purging) / filter (for importing/exporting)'), false, N_('Negate')),
|
33
|
+
self.set('template_sync_branch', N_('Default branch in Git repo'), nil, N_('Branch')),
|
34
|
+
self.set('template_sync_metadata_export_mode', N_('Default metadata export mode, refresh re-renders metadata, keep will keep existing metadata, remove exports template withou metadata'), 'refresh', N_('Metadata export mode'), nil, { :collection => Proc.new { self.metadata_export_mode_types } }),
|
35
|
+
self.set('template_sync_force', N_('Should importing overwrite locked templates?'), false, N_('Force import')),
|
36
|
+
].compact.each { |s| self.create! s.update(:category => "Setting::TemplateSync") }
|
37
|
+
end
|
38
|
+
|
39
|
+
true
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
def validate_template_sync_associate(record)
|
43
|
+
values = record.class.associate_types.keys
|
44
|
+
if record.value && !values.include?(record.value)
|
45
|
+
record.errors[:base] << (_("template_sync_associate must be one of %s") % values.join(', '))
|
46
|
+
end
|
45
47
|
end
|
46
|
-
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
def validate_template_sync_metadata_export_mode(record)
|
50
|
+
values = record.class.metadata_export_mode_types.keys
|
51
|
+
if record.value && !values.include?(record.value)
|
52
|
+
record.errors[:base] << (_("template_sync_metadata_export_mode must be one of %s") % values.join(', '))
|
53
|
+
end
|
52
54
|
end
|
53
55
|
end
|
54
56
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
1
3
|
module ForemanTemplates
|
2
4
|
class TemplateExporter < Action
|
3
5
|
def self.setting_overrides
|
@@ -43,7 +45,7 @@ module ForemanTemplates
|
|
43
45
|
|
44
46
|
status = git_repo.status
|
45
47
|
if status.added.any? || status.changed.any? || status.deleted.any? || status.untracked.any?
|
46
|
-
logger.debug
|
48
|
+
logger.debug 'committing changes in cloned repo'
|
47
49
|
git_repo.commit "Templates export made by Foreman user #{User.current.try(:login) || User::ANONYMOUS_ADMIN}"
|
48
50
|
|
49
51
|
logger.debug "pushing to branch #{branch} at origin #{@repo}"
|
@@ -70,7 +72,7 @@ module ForemanTemplates
|
|
70
72
|
end
|
71
73
|
|
72
74
|
def get_template_filename(template)
|
73
|
-
template.name.downcase.tr(' ', '_') + '.erb'
|
75
|
+
Shellwords.escape(template.name.downcase.tr(' /', '_') + '.erb')
|
74
76
|
end
|
75
77
|
|
76
78
|
def get_dump_dir(template)
|
@@ -105,6 +107,5 @@ module ForemanTemplates
|
|
105
107
|
raise "Unknown metadata export mode #{@metadata_export_mode}"
|
106
108
|
end
|
107
109
|
end
|
108
|
-
|
109
110
|
end
|
110
111
|
end
|
@@ -1,24 +1,18 @@
|
|
1
|
-
class NoKindError <
|
2
|
-
class MissingKindError <
|
1
|
+
class NoKindError < RuntimeError; end
|
2
|
+
class MissingKindError < RuntimeError; end
|
3
3
|
|
4
4
|
module ForemanTemplates
|
5
5
|
class TemplateImporter < Action
|
6
6
|
attr_accessor :metadata, :name, :text
|
7
7
|
|
8
8
|
def self.setting_overrides
|
9
|
-
super + %i(associate)
|
9
|
+
super + %i(associate force)
|
10
10
|
end
|
11
11
|
|
12
12
|
def initialize(args = {})
|
13
13
|
super
|
14
|
-
|
15
|
-
|
16
|
-
@verbose = if @verbose == 'false'
|
17
|
-
false
|
18
|
-
else
|
19
|
-
true
|
20
|
-
end
|
21
|
-
end
|
14
|
+
@verbose = parse_bool(@verbose)
|
15
|
+
@force = parse_bool(@force)
|
22
16
|
end
|
23
17
|
|
24
18
|
def import!
|
@@ -69,23 +63,23 @@ module ForemanTemplates
|
|
69
63
|
if @filter
|
70
64
|
matching = name.match(/#{@filter}/i)
|
71
65
|
matching = !matching if @negate
|
72
|
-
next
|
66
|
+
next unless matching
|
73
67
|
end
|
74
68
|
|
75
69
|
begin
|
76
|
-
# Expects a return of { :diff, :status, :result }
|
70
|
+
# Expects a return of { :diff, :status, :result, :errors }
|
77
71
|
data = if metadata['model'].present?
|
78
|
-
metadata['model'].constantize.import!(name, text, metadata)
|
72
|
+
metadata['model'].constantize.import!(name, text, metadata, @force)
|
79
73
|
else
|
80
74
|
# For backwards-compat before "model" metadata was added
|
81
75
|
case metadata['kind']
|
82
76
|
when 'ptable'
|
83
|
-
Ptable.import!(name, text, metadata)
|
77
|
+
Ptable.import!(name, text, metadata, @force)
|
84
78
|
when 'job_template'
|
85
79
|
# TODO: update REX templates to have `model` and delete this
|
86
80
|
update_job_template(name, text)
|
87
81
|
else
|
88
|
-
ProvisioningTemplate.import!(name, text, metadata)
|
82
|
+
ProvisioningTemplate.import!(name, text, metadata, @force)
|
89
83
|
end
|
90
84
|
end
|
91
85
|
|
@@ -93,12 +87,12 @@ module ForemanTemplates
|
|
93
87
|
data[:diff] = calculate_diff(data[:old], data[:new])
|
94
88
|
end
|
95
89
|
|
96
|
-
if
|
90
|
+
if @verbose
|
97
91
|
result_lines << data[:result]
|
98
92
|
result_lines << data[:diff] unless data[:diff].nil?
|
99
|
-
elsif data[:status] == false
|
100
|
-
result_lines << "Template \"#{name}\": #{data[:result]}"
|
101
93
|
end
|
94
|
+
result_lines << status_to_text(data[:status], name)
|
95
|
+
result_lines << data[:errors] unless data[:errors].empty?
|
102
96
|
rescue MissingKindError
|
103
97
|
result_lines << " Skipping: '#{name}' - No template kind or model detected"
|
104
98
|
next
|
@@ -136,10 +130,12 @@ module ForemanTemplates
|
|
136
130
|
puts 'Deprecation warning: JobTemplate support is moving to the Remote Execution plugin'
|
137
131
|
puts "- please add 'model: JobTemplate' to the metadata in '#{file}' to call the right method"
|
138
132
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
133
|
+
unless defined?(JobTemplate)
|
134
|
+
return {
|
135
|
+
:status => false,
|
136
|
+
:result => 'Skipping job template import, remote execution plugin is not installed.'
|
137
|
+
}
|
138
|
+
end
|
143
139
|
template = JobTemplate.import(
|
144
140
|
text.sub(/^name: .*$/, "name: #{name}"),
|
145
141
|
:update => true
|
@@ -167,5 +163,21 @@ module ForemanTemplates
|
|
167
163
|
template.destroy
|
168
164
|
end
|
169
165
|
end # :purge
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def parse_bool(bool_name)
|
170
|
+
bool_name.is_a?(String) ? bool_name != 'false' : bool_name
|
171
|
+
end
|
172
|
+
|
173
|
+
def status_to_text(status, name)
|
174
|
+
msg = "#{name} - import "
|
175
|
+
msg << if status
|
176
|
+
"success"
|
177
|
+
else
|
178
|
+
'failure'
|
179
|
+
end
|
180
|
+
msg
|
181
|
+
end
|
170
182
|
end
|
171
183
|
end
|
data/config/routes.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
resources :templates, :controller => :template, :only => :import do
|
2
|
+
namespace :api, :defaults => { :format => 'json' } do
|
3
|
+
scope "(:apiv)", :module => :v2, :defaults => { :apiv => 'v2' }, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2) do
|
4
|
+
resources :templates, :controller => :template, :only => [] do
|
6
5
|
collection do
|
7
6
|
post 'import'
|
7
|
+
post 'export'
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
13
12
|
end
|
@@ -23,6 +23,9 @@ module ForemanTemplates
|
|
23
23
|
permission :import_templates, {
|
24
24
|
:"api/v2/template" => [:import]
|
25
25
|
}, :resource_type => 'Template'
|
26
|
+
permission :export_templates, {
|
27
|
+
:"api/v2/template" => [:export]
|
28
|
+
}, :resource_type => 'Template'
|
26
29
|
end
|
27
30
|
add_all_permissions_to_default_roles
|
28
31
|
end
|
@@ -6,13 +6,13 @@ namespace :templates do
|
|
6
6
|
ActiveSupport::Deprecation.warn('templates:sync task has been renamed to templates:import and will be removed in a future version')
|
7
7
|
end
|
8
8
|
# Available options:
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
# * verbose => Print extra information during the run [false]
|
10
|
+
# * repo => Sync templates from a different Git repo [https://github.com/theforeman/community-templates]
|
11
|
+
# * branch => Branch in Git repo [default branch]
|
12
|
+
# * prefix => The string all imported templates should begin with [Community ]
|
13
|
+
# * dirname => The directory within the git tree containing the templates [/]
|
14
|
+
# * filter => Import names matching this regex (case-insensitive; snippets are not filtered)
|
15
|
+
# * associate => Associate to OS's, Locations & Organizations. Options are: always, new or never [new]
|
16
16
|
|
17
17
|
User.current = User.anonymous_admin
|
18
18
|
|
@@ -54,9 +54,9 @@ namespace :templates do
|
|
54
54
|
User.current = User.anonymous_admin
|
55
55
|
|
56
56
|
ForemanTemplates::TemplateImporter.new({
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
# * negate => negate query [false]
|
58
|
+
# * prefix => The string all templates to purge should ( or not ) begin with [Community ]
|
59
|
+
# * verbose => Print extra information during the run [false]
|
60
60
|
negate: ENV['negate'],
|
61
61
|
prefix: ENV['prefix'],
|
62
62
|
verbose: ENV['verbose'],
|
@@ -69,7 +69,6 @@ namespace :templates do
|
|
69
69
|
ForemanTemplates::Cleaner.new.clean_up!
|
70
70
|
puts 'Clean up finished, you can now remove the plugin from your system'
|
71
71
|
end
|
72
|
-
|
73
72
|
end
|
74
73
|
|
75
74
|
# Tests
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_templates
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Sutcliffe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diffy
|