foreman_opentofu 0.0.2 → 0.0.4
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 +109 -12
- data/app/lib/foreman_opentofu/concerns/base_template_scope_extensions.rb +87 -44
- data/app/lib/foreman_opentofu/hcl_format.rb +95 -0
- data/app/lib/foreman_opentofu/nic_helpers.rb +47 -0
- data/app/models/concerns/foreman_opentofu/vm_command_collection_normalization.rb +22 -0
- data/app/models/concerns/orchestration/tofu/compute.rb +25 -6
- data/app/models/foreman_opentofu/compute_vm.rb +10 -0
- data/app/models/foreman_opentofu/opentofu_vm_commands.rb +22 -11
- data/app/models/foreman_opentofu/tofu.rb +43 -10
- data/app/overrides/compute_resources_vms/tofu_indexed_networks_fields.rb +7 -0
- data/app/overrides/compute_resources_vms/tofu_indexed_volumes_fields.rb +8 -0
- data/app/services/foreman_opentofu/app_wrapper.rb +51 -14
- data/app/services/foreman_opentofu/opentofu_executer.rb +67 -29
- data/app/services/foreman_opentofu/provider_type.rb +61 -7
- data/app/views/compute_resources/form/_tofu.html.erb +3 -0
- data/app/views/compute_resources/show/_tofu.html.erb +4 -0
- data/app/views/compute_resources/tofu.json.rabl +1 -0
- data/app/views/compute_resources_vms/form/tofu/_base.html.erb +12 -6
- data/app/views/compute_resources_vms/form/tofu/_dynamic_attrs.html.erb +17 -7
- data/app/views/compute_resources_vms/form/tofu/_network.html.erb +1 -5
- data/app/views/compute_resources_vms/form/tofu/_volume.html.erb +2 -0
- data/app/views/foreman_opentofu/compute_resources_vms/_indexed_networks_fields.html.erb +47 -0
- data/app/views/foreman_opentofu/compute_resources_vms/_indexed_volumes_fields.html.erb +30 -0
- data/app/views/foreman_opentofu/compute_resources_vms/form/tofu/_interfaces_fields.html.erb +12 -0
- data/app/views/foreman_opentofu/compute_resources_vms/form/tofu/_volumes_fields.html.erb +11 -0
- data/app/views/images/form/_tofu.html.erb +4 -0
- data/app/views/templates/provisioning/hetzner_provision_host.erb +64 -0
- data/app/views/templates/provisioning/nutanix_provision_default.erb +31 -22
- data/app/views/templates/provisioning/ovirt_provision_default.erb +7 -29
- data/lib/foreman_opentofu/provider_types/hetzner.rb +98 -0
- data/lib/foreman_opentofu/provider_types/nutanix.rb +74 -0
- data/lib/foreman_opentofu/provider_types/ovirt.rb +19 -0
- data/lib/foreman_opentofu/version.rb +1 -1
- data/lib/foreman_opentofu.rb +4 -0
- data/selinux/Makefile +22 -0
- data/selinux/foreman_opentofu.fc +3 -0
- data/selinux/foreman_opentofu.if +0 -0
- data/selinux/foreman_opentofu.te +93 -0
- data/test/fixtures/snapshots/foreman_opentofu/base_template_scope_extensions_test/backend_block.txt +6 -0
- data/test/fixtures/snapshots/foreman_opentofu/base_template_scope_extensions_test/terraform_block.txt +8 -0
- data/test/fixtures/snapshots/foreman_opentofu/base_template_scope_extensions_test/terraform_block_with_token.txt +14 -0
- data/test/fixtures/snapshots/foreman_opentofu/base_template_scope_extensions_test/vm_attributes.txt +3 -0
- data/test/fixtures/snapshots/foreman_opentofu/hcl_format_test/to_hcl.txt +19 -0
- data/test/fixtures/snapshots/foreman_opentofu/nic_helpers_test/nic_attributes.txt +8 -0
- data/test/fixtures/snapshots/foreman_opentofu/nic_helpers_test/nic_attributes_block.txt +6 -0
- data/test/lib/foreman_opentofu/concerns/base_template_scope_extensions_test.rb +137 -0
- data/test/lib/foreman_opentofu/concerns/nic_helpers_test.rb +36 -0
- data/test/lib/foreman_opentofu/hcl_format_test.rb +72 -0
- data/test/models/foreman_opentofu/opentofu_vm_commands_test.rb +41 -14
- data/test/models/foreman_opentofu/tofu_test.rb +22 -0
- data/test/services/app_wrapper_test.rb +51 -1
- data/test/services/foreman_opentofu/provider_type_test.rb +115 -10
- data/test/services/opentofu_executer_test.rb +60 -19
- data/test/test_plugin_helper.rb +6 -0
- metadata +40 -6
- data/app/services/foreman_opentofu/compute_fetcher.rb +0 -51
- data/config/initializers/compute_attrs.rb +0 -19
- data/config/nutanix.json +0 -27
- data/config/ovirt.json +0 -18
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
module ForemanOpentofu
|
|
19
19
|
class Tofu < ComputeResource
|
|
20
20
|
include OpentofuVMCommands
|
|
21
|
+
include ComputeResourceCaching
|
|
21
22
|
validates :provider, presence: true, inclusion: { in: %w[Tofu] }
|
|
22
23
|
validates :url, presence: true
|
|
23
24
|
validates :user, presence: true
|
|
@@ -26,12 +27,19 @@ module ForemanOpentofu
|
|
|
26
27
|
# alias_attribute :username, :user
|
|
27
28
|
# alias_attribute :endpoint, :url
|
|
28
29
|
|
|
29
|
-
delegate :available_attributes, to: :tofu_provider
|
|
30
|
+
delegate :available_attributes, :capabilities, :render_disk, :render_nic, to: :tofu_provider
|
|
31
|
+
|
|
32
|
+
def available_images
|
|
33
|
+
# make sure available_images can use this CR, e.g. for requesting data_source
|
|
34
|
+
tofu_provider.available_images(self)
|
|
35
|
+
end
|
|
30
36
|
|
|
31
37
|
def provided_attributes
|
|
32
|
-
super.merge(
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
super.merge(tofu_provider.provided_attributes || {})
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def user_data_supported?
|
|
42
|
+
true
|
|
35
43
|
end
|
|
36
44
|
|
|
37
45
|
def opentofu_provider
|
|
@@ -60,10 +68,6 @@ module ForemanOpentofu
|
|
|
60
68
|
'OpenTofu'
|
|
61
69
|
end
|
|
62
70
|
|
|
63
|
-
def capabilities
|
|
64
|
-
[:build]
|
|
65
|
-
end
|
|
66
|
-
|
|
67
71
|
def self.model_name
|
|
68
72
|
ComputeResource.model_name
|
|
69
73
|
end
|
|
@@ -76,16 +80,45 @@ module ForemanOpentofu
|
|
|
76
80
|
true
|
|
77
81
|
end
|
|
78
82
|
|
|
83
|
+
def vm_ready(vm)
|
|
84
|
+
return tofu_provider.vm_ready(vm) if tofu_provider.respond_to? :vm_ready
|
|
85
|
+
|
|
86
|
+
vm.wait_for { ready? }
|
|
87
|
+
end
|
|
88
|
+
|
|
79
89
|
def tofu_provider
|
|
80
90
|
ProviderTypeManager.find(opentofu_provider)
|
|
81
91
|
end
|
|
82
92
|
|
|
83
|
-
def new_interface
|
|
84
|
-
|
|
93
|
+
def new_interface(attr = {})
|
|
94
|
+
OpenStruct.new(attr)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def new_volume(attr = {})
|
|
98
|
+
OpenStruct.new(attr.merge({}))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def default_volumes
|
|
102
|
+
tofu_provider&.default_volumes || {}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def default_interfaces
|
|
106
|
+
tofu_provider&.default_interfaces || {}
|
|
85
107
|
end
|
|
86
108
|
|
|
87
109
|
def editable_network_interfaces?
|
|
88
110
|
true
|
|
89
111
|
end
|
|
112
|
+
|
|
113
|
+
def available_resource(resource_name, options = {})
|
|
114
|
+
cache.cache("#{name}_#{resource_name}") do
|
|
115
|
+
resource = fetch_resource(resource_name, options)
|
|
116
|
+
resource.map { |h| OpenStruct.new(h) }
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def available_resource_ui_select(resource_name, options = {})
|
|
121
|
+
available_resource(resource_name, options)&.map { |obj| [obj['name'], obj['id']] }
|
|
122
|
+
end
|
|
90
123
|
end
|
|
91
124
|
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Deface::Override.new(
|
|
2
|
+
virtual_path: 'compute_resources_vms/form/_volumes',
|
|
3
|
+
name: 'tofu_indexed_volumes_fields',
|
|
4
|
+
replace_contents: 'div.children_fields',
|
|
5
|
+
partial: 'foreman_opentofu/compute_resources_vms/indexed_volumes_fields',
|
|
6
|
+
original: '7d0607bbae4c5123e89fff9968e2740a3d0eb323',
|
|
7
|
+
namespaced: true
|
|
8
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module ForemanOpentofu
|
|
2
2
|
class AppWrapper
|
|
3
|
-
|
|
3
|
+
include HclFormat
|
|
4
|
+
attr_reader :workdir
|
|
4
5
|
|
|
5
6
|
# TODO: for future versions
|
|
6
7
|
# - manage temp-work-dir; problem: no auto-remove after finished :-(
|
|
@@ -9,14 +10,33 @@ module ForemanOpentofu
|
|
|
9
10
|
# - use JSON-output for easier parsing
|
|
10
11
|
# - do we need locking or has the object be atomic
|
|
11
12
|
|
|
12
|
-
def initialize(workdir)
|
|
13
|
+
def initialize(workdir, opts = {})
|
|
13
14
|
@workdir = workdir
|
|
14
|
-
@
|
|
15
|
-
@conffile = File.join(workdir, 'main.tf')
|
|
15
|
+
@variables = opts[:variables]
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
# rubocop:disable Style/SingleLineMethods
|
|
19
|
+
def planfile() File.join(workdir, 'plan.bin') end
|
|
20
|
+
def conffile() File.join(workdir, 'main.tf') end
|
|
21
|
+
def vardeffile() File.join(workdir, 'variables.tf') end
|
|
22
|
+
|
|
23
|
+
def base_command() 'tofu' end
|
|
24
|
+
# rubocop:enable Style/SingleLineMethods
|
|
25
|
+
|
|
26
|
+
# write variables definition file based on @variables into 'variables.tf'
|
|
27
|
+
def create_variables_file
|
|
28
|
+
File.open(vardeffile, 'w') do |f|
|
|
29
|
+
@variables.each_key do |var|
|
|
30
|
+
data = { 'type' => :string }
|
|
31
|
+
data['sensitive'] = var.to_s == 'password'
|
|
32
|
+
|
|
33
|
+
f << block_to_hcl(['variable', var], data)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def variable_params
|
|
39
|
+
@variables.map { |var, val| ['-var', "#{var}=#{val}"] }.flatten
|
|
20
40
|
end
|
|
21
41
|
|
|
22
42
|
def default_params
|
|
@@ -32,6 +52,8 @@ module ForemanOpentofu
|
|
|
32
52
|
# end
|
|
33
53
|
# end
|
|
34
54
|
def init(params = [], &block)
|
|
55
|
+
create_variables_file if @variables
|
|
56
|
+
|
|
35
57
|
tofu_execute('init', ['-input=false'].concat(parse_params(params)), &block)
|
|
36
58
|
end
|
|
37
59
|
|
|
@@ -78,18 +100,33 @@ module ForemanOpentofu
|
|
|
78
100
|
cmd.map { |item| "'#{item}'" }.append('2>&1').join(' ')
|
|
79
101
|
end
|
|
80
102
|
|
|
103
|
+
def common_envvars
|
|
104
|
+
{
|
|
105
|
+
'TF_PLUGIN_CACHE_DIR' => ForemanOpentofu::OPENTOFU_PLUGIN_CACHE_PATH,
|
|
106
|
+
'TEMPDIR' => ForemanOpentofu::OPENTOFU_TMP_PATH,
|
|
107
|
+
'TMPDIR' => ForemanOpentofu::OPENTOFU_TMP_PATH,
|
|
108
|
+
'TMP' => ForemanOpentofu::OPENTOFU_TMP_PATH,
|
|
109
|
+
'TEMP' => ForemanOpentofu::OPENTOFU_TMP_PATH,
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def terraform_envvars
|
|
114
|
+
@variables.transform_keys { |variable| "TF_VAR_#{variable}" }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def envvars
|
|
118
|
+
common_envvars.merge(terraform_envvars)
|
|
119
|
+
end
|
|
120
|
+
|
|
81
121
|
def execute(cmd)
|
|
82
122
|
output = nil
|
|
83
123
|
# quote cmdline parameters and add stderr to stdout
|
|
84
124
|
commandline = command(cmd)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
else
|
|
91
|
-
output = pipe.read
|
|
92
|
-
end
|
|
125
|
+
IO.popen(envvars, commandline, 'r+', chdir: workdir) do |pipe|
|
|
126
|
+
if block_given?
|
|
127
|
+
yield pipe
|
|
128
|
+
else
|
|
129
|
+
output = pipe.read
|
|
93
130
|
end
|
|
94
131
|
end
|
|
95
132
|
ret = $CHILD_STATUS
|
|
@@ -1,21 +1,35 @@
|
|
|
1
|
-
require 'json'
|
|
2
|
-
|
|
3
1
|
module ForemanOpentofu
|
|
4
2
|
class OpentofuExecuter
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
# 'destroy' works with TfState rather than the actual tf-file
|
|
4
|
+
# also it might not receive all the necessary data to create a valid tf-file
|
|
5
|
+
DRY_RUN_MODES = %w[destroy test].freeze
|
|
6
|
+
|
|
7
|
+
def initialize(compute_resource, args = {})
|
|
8
|
+
@compute_resource = compute_resource
|
|
9
|
+
@cr_attrs = args.to_h.with_indifferent_access
|
|
10
|
+
@resource = @cr_attrs['resource']
|
|
8
11
|
@host_name = @cr_attrs['name'] || 'test'
|
|
9
12
|
end
|
|
10
13
|
|
|
11
|
-
def run(mode = '
|
|
12
|
-
Dir.mktmpdir('opentofu_') do |dir|
|
|
13
|
-
|
|
14
|
+
def run(mode = '')
|
|
15
|
+
Dir.mktmpdir('opentofu_', ForemanOpentofu::OPENTOFU_TMP_PATH) do |dir|
|
|
16
|
+
# FIXME: integrate the user_data-file into AppWrapper!
|
|
17
|
+
if @cr_attrs['user_data']
|
|
18
|
+
@user_data_filename = File.join(dir, 'userdata')
|
|
19
|
+
File.open(@user_data_filename, 'w') do |f|
|
|
20
|
+
f.write(@cr_attrs['user_data'])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
tofu = AppWrapper.new(dir, variables: {
|
|
24
|
+
username: @compute_resource.user,
|
|
25
|
+
password: @compute_resource.password,
|
|
26
|
+
endpoint: @compute_resource.url,
|
|
27
|
+
})
|
|
14
28
|
@use_backend = %w[create destroy output].include?(mode)
|
|
15
29
|
@token = create_token(@host_name) if @use_backend
|
|
16
|
-
tofu.main_configuration = render_template
|
|
30
|
+
tofu.main_configuration = render_template(mode)
|
|
17
31
|
tofu.init
|
|
18
|
-
|
|
32
|
+
yield(tofu)
|
|
19
33
|
end
|
|
20
34
|
end
|
|
21
35
|
|
|
@@ -40,45 +54,69 @@ module ForemanOpentofu
|
|
|
40
54
|
new_token
|
|
41
55
|
end
|
|
42
56
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
when 'new'
|
|
57
|
+
def run_new
|
|
58
|
+
run('new') do |tofu|
|
|
46
59
|
tofu.plan
|
|
47
60
|
tofu.show_plan
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def run_test_connection
|
|
65
|
+
run('test', &:plan)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def run_create
|
|
69
|
+
run('create') do |tofu|
|
|
52
70
|
tofu.apply
|
|
53
71
|
attrs = tofu.output('vm_attrs')
|
|
54
72
|
ForemanOpentofu::TfState.find_by(name: @cr_attrs['name'])&.update(uuid: attrs['identity'])
|
|
55
73
|
attrs
|
|
56
|
-
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def run_output
|
|
78
|
+
run('output') do |tofu|
|
|
57
79
|
tofu.output('vm_attrs')
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def run_destroy
|
|
84
|
+
run('destroy', &:destroy)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def run_fetch
|
|
88
|
+
run do |tofu|
|
|
89
|
+
tofu.apply
|
|
90
|
+
tofu.output('resources')
|
|
62
91
|
end
|
|
63
92
|
end
|
|
64
93
|
|
|
65
94
|
private
|
|
66
95
|
|
|
67
|
-
def render_template
|
|
96
|
+
def render_template(mode)
|
|
68
97
|
template = provision_template
|
|
69
|
-
|
|
98
|
+
variables = {
|
|
99
|
+
compute_resource: @compute_resource,
|
|
100
|
+
cr_attrs: @cr_attrs,
|
|
101
|
+
use_backend: @use_backend,
|
|
102
|
+
token: @token,
|
|
103
|
+
host_name: @host_name,
|
|
104
|
+
resource: @resource,
|
|
105
|
+
dry_run: dry_run(mode),
|
|
106
|
+
user_data_filename: @user_data_filename,
|
|
107
|
+
}
|
|
108
|
+
scope = Foreman::Renderer.get_scope(source: template, variables: variables)
|
|
70
109
|
source = Foreman::Renderer.get_source(template: template)
|
|
71
|
-
scope.instance_variable_set(:@compute_resource, @compute_resource)
|
|
72
|
-
scope.instance_variable_set(:@cr_attrs, @cr_attrs) if @cr_attrs
|
|
73
|
-
scope.instance_variable_set(:@use_backend, @use_backend)
|
|
74
|
-
scope.instance_variable_set(:@token, @token) if @use_backend
|
|
75
|
-
scope.instance_variable_set(:@host_name, @host_name)
|
|
76
110
|
rendered_template = Foreman::Renderer::UnsafeModeRenderer.render(source, scope)
|
|
77
111
|
raise ::Foreman::Exception, N_('Unable to render provisioning template') unless rendered_template
|
|
78
112
|
|
|
79
113
|
rendered_template
|
|
80
114
|
end
|
|
81
115
|
|
|
116
|
+
def dry_run(mode)
|
|
117
|
+
mode.empty? || DRY_RUN_MODES.include?(mode)
|
|
118
|
+
end
|
|
119
|
+
|
|
82
120
|
def provision_template
|
|
83
121
|
name = @compute_resource.opentofu_template.name
|
|
84
122
|
template = ProvisioningTemplate.unscoped.find_by(name: name)
|
|
@@ -1,30 +1,84 @@
|
|
|
1
1
|
module ForemanOpentofu
|
|
2
2
|
class ProviderType
|
|
3
3
|
attr_reader :id, :name, :default_attributes
|
|
4
|
+
attr_accessor :capabilities, :disk_renderer, :nic_renderer
|
|
4
5
|
|
|
5
6
|
def initialize(id)
|
|
6
7
|
@id = id.to_sym
|
|
7
8
|
@name = id.capitalize
|
|
9
|
+
@capabilities = [:build]
|
|
10
|
+
@provider_attrs = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def provider_attrs=(input)
|
|
14
|
+
@provider_attrs = Array(input).map do |attr|
|
|
15
|
+
ActiveSupport::HashWithIndifferentAccess.new(attr)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# if necessary, select-parameter named 'available_images' must be specified!
|
|
20
|
+
def available_images(compute_resource)
|
|
21
|
+
attribute = find_attr_by('name', 'available_images')
|
|
22
|
+
raise NotImplementedError if attribute.nil?
|
|
23
|
+
|
|
24
|
+
query_opts = attribute['options']
|
|
25
|
+
case query_opts
|
|
26
|
+
when nil then raise NotImplementedError
|
|
27
|
+
# TODO: Check if Array really works!
|
|
28
|
+
when Array then query_opts
|
|
29
|
+
when Hash then compute_resource.available_resource(query_opts.dig('data_source', 'name'), query_opts)
|
|
30
|
+
else raise 'available_images in ProviderType config is of unknown type.'
|
|
31
|
+
end
|
|
8
32
|
end
|
|
9
33
|
|
|
10
34
|
# returns hash of available-attributes with attr-name as key
|
|
11
|
-
def available_attributes
|
|
35
|
+
def available_attributes(group = nil)
|
|
12
36
|
raise "No available-attributes found for #{name}" unless attributes?
|
|
13
37
|
|
|
14
|
-
attributes
|
|
38
|
+
attributes(group).index_by { |e| e['name'] }.with_indifferent_access
|
|
15
39
|
end
|
|
16
40
|
|
|
17
41
|
def attributes?
|
|
18
|
-
|
|
42
|
+
@provider_attrs.present?
|
|
19
43
|
end
|
|
20
44
|
|
|
21
45
|
def attributes(group = nil)
|
|
22
|
-
return
|
|
46
|
+
return [] unless attributes?
|
|
47
|
+
|
|
48
|
+
return @provider_attrs if group.nil?
|
|
49
|
+
|
|
50
|
+
@provider_attrs.select { |e| e['group'] == group }
|
|
51
|
+
end
|
|
23
52
|
|
|
24
|
-
|
|
25
|
-
|
|
53
|
+
# return Array of Hashes of all attributes that have `key` set to `value`.
|
|
54
|
+
# Optional: limited to `group`
|
|
55
|
+
def search_attr_by(key, value, group = nil)
|
|
56
|
+
attributes(group).select { |attr| attr[key] == value }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# return Hash of attribute that has `key` set to `value`.
|
|
60
|
+
# If multiple exists, first in list is returned
|
|
61
|
+
# Returns `nil` if none is found
|
|
62
|
+
# Optional: limited to `group`
|
|
63
|
+
def find_attr_by(key, value, group = nil)
|
|
64
|
+
search_attr_by(key, value, group).first
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def provided_attributes
|
|
68
|
+
# TODO: maybe we need to do something more sophisticated, here.
|
|
69
|
+
# network-based deployment needs MAC to set-up DHCP, but
|
|
70
|
+
# on image-based deployment we usually only get IPv4/6-address
|
|
71
|
+
{ mac: :mac }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def render_disk(disk, context, *args)
|
|
75
|
+
return '' unless disk_renderer
|
|
76
|
+
context.instance_exec(disk, *args, &disk_renderer)
|
|
77
|
+
end
|
|
26
78
|
|
|
27
|
-
|
|
79
|
+
def render_nic(nic, context, *args)
|
|
80
|
+
return '' unless nic_renderer
|
|
81
|
+
context.instance_exec(nic, *args, &nic_renderer)
|
|
28
82
|
end
|
|
29
83
|
end
|
|
30
84
|
end
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
<%= text_f f, :url, :help_block => _("127.0.0.1") %>
|
|
5
5
|
<%= text_f f, :user , :help_block => _("e.g. admin") %>
|
|
6
6
|
<%= password_f f, :password, :unset => unset_password? %>
|
|
7
|
+
<%= checkbox_f f, :caching_enabled, :label => _("Enable caching"),
|
|
8
|
+
:help_inline => _("Cache slow calls to Opentofu Compute resources to speed up page rendering") %>
|
|
9
|
+
|
|
7
10
|
<% end %>
|
|
8
11
|
<div class="col-md-offset-2">
|
|
9
12
|
<%= test_connection_button_f(f, (true)) %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
attributes :caching_enabled
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
<% provider = compute_resource.tofu_provider %>
|
|
2
2
|
<% if provider.attributes.present? %>
|
|
3
3
|
<% vm_attrs = provider.attributes('vm') %>
|
|
4
|
-
<% disk_attrs = provider.attributes('disk') %>
|
|
5
4
|
<%= field_set_tag _("VM Config") do %>
|
|
6
|
-
<%= render :partial => provider_partial(compute_resource, "dynamic_attrs"), :locals => { f: f, attrs: vm_attrs } %>
|
|
7
|
-
<% end %>
|
|
8
|
-
|
|
9
|
-
<%= field_set_tag _("Storage") do %>
|
|
10
|
-
<%= render :partial => provider_partial(compute_resource, "dynamic_attrs"), :locals => { f: f, attrs: disk_attrs } %>
|
|
5
|
+
<%= render :partial => provider_partial(compute_resource, "dynamic_attrs"), :locals => { f: f, attrs: vm_attrs, compute_resource: compute_resource } %>
|
|
11
6
|
<% end %>
|
|
12
7
|
<% end %>
|
|
13
8
|
|
|
9
|
+
<%
|
|
10
|
+
arch ||= nil ; os ||= nil
|
|
11
|
+
images = possible_images(compute_resource, arch, os)
|
|
12
|
+
%>
|
|
13
|
+
|
|
14
|
+
<div id='image_selection'>
|
|
15
|
+
<%= select_f f, :image_id, images, :uuid, :name,
|
|
16
|
+
{ :include_blank => (images.empty? || images.size == 1) ? false : _('Please select an image') },
|
|
17
|
+
{ :disabled => images.empty?, :label => _('Image'), :label_size => "col-md-2" } %>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
<% attrs.each do |attr|
|
|
2
|
-
label = attr.fetch('label', attr['name'].humanize)
|
|
2
|
+
label = attr.fetch('label', attr['name'].humanize)
|
|
3
|
+
common_options = {
|
|
4
|
+
label: _(label),
|
|
5
|
+
label_help: attr['help'],
|
|
6
|
+
required: attr['mandatory'],
|
|
7
|
+
} %>
|
|
3
8
|
|
|
4
|
-
<% case attr[
|
|
9
|
+
<% case attr['type'] %>
|
|
5
10
|
<% when 'string' %>
|
|
6
|
-
<%= text_f f, attr[
|
|
11
|
+
<%= text_f f, attr['name'].to_sym, common_options %>
|
|
7
12
|
<% when 'bool' %>
|
|
8
|
-
<%= checkbox_f f, attr[
|
|
13
|
+
<%= checkbox_f f, attr['name'].to_sym, common_options %>
|
|
9
14
|
<% when 'number' %>
|
|
10
|
-
<%= counter_f f, attr[
|
|
15
|
+
<%= counter_f f, attr['name'].to_sym, common_options %>
|
|
11
16
|
<% when 'select' %>
|
|
12
17
|
<% if attr.key? 'options' %>
|
|
13
|
-
|
|
18
|
+
<% if attr['options'].is_a?(Hash) && attr['options'].key?('data_source') -%>
|
|
19
|
+
<%= selectable_f f, attr['name'].to_sym, compute_resource.available_resource_ui_select(attr.dig('options', 'data_source', 'name'), attr.dig('options')), { :include_blank => !attr['mandatory'] }, common_options %>
|
|
20
|
+
<% else -%>
|
|
21
|
+
<%= select_f f, attr['name'].to_sym, attr.fetch('options', []), :to_s, :to_s, { :include_blank => !attr['mandatory'] }, common_options %>
|
|
22
|
+
<%end -%>
|
|
14
23
|
<% else %>
|
|
15
|
-
|
|
24
|
+
<% #FIXME: what to do with this? %>
|
|
25
|
+
<%= select_f f, attr['name'].to_sym, (options || []), :id, :name, { :include_blank => true }, common_options %>
|
|
16
26
|
<% end %>
|
|
17
27
|
<% end %>
|
|
18
28
|
<% end %>
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
<% if CR_ATTRS.key?(provider) %>
|
|
3
|
-
<% nic_attrs = CR_ATTRS[provider].select { |a| a["group"] == "nic" } %>
|
|
4
|
-
<%= render :partial => provider_partial(compute_resource, "dynamic_attrs"), :locals => { f: f, attrs: nic_attrs, options: ForemanOpentofu::ComputeFetcher.fetch_subnets(compute_resource) } %>
|
|
5
|
-
<% end %>
|
|
1
|
+
<%= render :partial => provider_partial(compute_resource, "dynamic_attrs"), :locals => { f: f, attrs: compute_resource.tofu_provider.attributes('nic'), compute_resource: compute_resource } %>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<% if compute_resource.is_a?(ForemanOpentofu::Tofu) %>
|
|
2
|
+
<div class="<%= compute_resource.interfaces_attrs_name %>_fields_template form_template" style="display: none;">
|
|
3
|
+
<%= fields_for "#{f.object_name}[#{compute_resource.interfaces_attrs_name}][new_#{compute_resource.interfaces_attrs_name}]", compute_resource.new_interface do |i| %>
|
|
4
|
+
<%= render :partial => provider_partial(compute_resource, 'network'),
|
|
5
|
+
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :remove_title => _('remove network interface'), :selected_cluster => selected_cluster },
|
|
6
|
+
:layout => 'compute_resources_vms/form/deletable_layout' %>
|
|
7
|
+
<% end %>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<%= field_set_tag _("Network interfaces"), :id => "network_interfaces" do %>
|
|
11
|
+
<% if compute_resource.editable_network_interfaces? %>
|
|
12
|
+
<%= render :partial => 'foreman_opentofu/compute_resources_vms/form/tofu/interfaces_fields',
|
|
13
|
+
:locals => { :f => f, :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :item_layout => item_layout, :selected_cluster => selected_cluster } %>
|
|
14
|
+
|
|
15
|
+
<% if new_vm %>
|
|
16
|
+
<%= add_child_link '+ ' + _("Add Interface"), compute_resource.interfaces_attrs_name, { :class => "info", :title => _('add new network interface') } %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% else %>
|
|
19
|
+
<div class="col-md-12">
|
|
20
|
+
<%= _('No networks found.') %>
|
|
21
|
+
</div>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% else %>
|
|
25
|
+
<%= new_child_fields_template(f, compute_resource.interfaces_attrs_name, {
|
|
26
|
+
:object => compute_resource.new_interface,
|
|
27
|
+
:partial => provider_partial(compute_resource, 'network'),
|
|
28
|
+
:form_builder_attrs => { :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :remove_title => _('remove network interface') },
|
|
29
|
+
:layout => 'compute_resources_vms/form/deletable_layout' }) %>
|
|
30
|
+
|
|
31
|
+
<%= field_set_tag _("Network interfaces"), :id => "network_interfaces" do %>
|
|
32
|
+
<% if compute_resource.editable_network_interfaces? %>
|
|
33
|
+
<%= f.fields_for compute_resource.interfaces_attrs_name do |i| %>
|
|
34
|
+
<%= render :partial => provider_partial(compute_resource, 'network'),
|
|
35
|
+
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :remove_title => _('remove network interface'), :selected_cluster => selected_cluster },
|
|
36
|
+
:layout => 'compute_resources_vms/form/deletable_layout' %>
|
|
37
|
+
<% end %>
|
|
38
|
+
<% if new_vm %>
|
|
39
|
+
<%= add_child_link '+ ' + _("Add Interface"), compute_resource.interfaces_attrs_name, { :class => "info", :title => _('add new network interface') } %>
|
|
40
|
+
<% end %>
|
|
41
|
+
<% else %>
|
|
42
|
+
<div class="col-md-12">
|
|
43
|
+
<%= _('No networks found.') %>
|
|
44
|
+
</div>
|
|
45
|
+
<% end %>
|
|
46
|
+
<% end %>
|
|
47
|
+
<% end %>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<% if compute_resource.is_a?(ForemanOpentofu::Tofu) %>
|
|
2
|
+
<div class="volumes_fields_template form_template" style="display: none;">
|
|
3
|
+
<%= fields_for "#{f.object_name}[volumes][new_volumes]", volume do |i| %>
|
|
4
|
+
<%= render :partial => provider_partial(compute_resource, 'volume'),
|
|
5
|
+
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_vm, :new_vm => new_vm, :remove_title => _('remove storage volume') },
|
|
6
|
+
:layout => "compute_resources_vms/form/#{item_layout}_layout" %>
|
|
7
|
+
<% end %>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<%= render :partial => 'foreman_opentofu/compute_resources_vms/form/tofu/volumes_fields',
|
|
11
|
+
:locals => { :f => f, :compute_resource => compute_resource, :new_host => new_vm, :new_vm => new_vm, :item_layout => item_layout } %>
|
|
12
|
+
|
|
13
|
+
<% if new_vm %>
|
|
14
|
+
<%= add_child_link '+ ' + _("Add Volume"), :volumes, { :class => "info", :title => _('add new storage volume') } %>
|
|
15
|
+
<% end %>
|
|
16
|
+
<% else %>
|
|
17
|
+
<%= new_child_fields_template(f, :volumes, {
|
|
18
|
+
:object => volume,
|
|
19
|
+
:partial => provider_partial(compute_resource, 'volume'),
|
|
20
|
+
:form_builder_attrs => { :compute_resource => compute_resource, :new_host => new_vm, :new_vm => new_vm, :remove_title => _('remove storage volume') },
|
|
21
|
+
:layout => "compute_resources_vms/form/#{item_layout}_layout" }) %>
|
|
22
|
+
|
|
23
|
+
<%= f.fields_for :volumes do |i| %>
|
|
24
|
+
<%= render :partial => provider_partial(compute_resource, 'volume'), :locals => { :f => i, :compute_resource => compute_resource, :new_host => new_vm, :new_vm => new_vm, :remove_title => _('remove storage volume') }, :layout => "compute_resources_vms/form/#{item_layout}_layout" %>
|
|
25
|
+
<% end %>
|
|
26
|
+
|
|
27
|
+
<% if new_vm %>
|
|
28
|
+
<%= add_child_link '+ ' + _("Add Volume"), :volumes, { :class => "info", :title => _('add new storage volume') } %>
|
|
29
|
+
<% end %>
|
|
30
|
+
<% end %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%
|
|
2
|
+
association = compute_resource.interfaces_attrs_name
|
|
3
|
+
interfaces = Array.wrap(f.object.public_send(association))
|
|
4
|
+
interfaces = [compute_resource.new_interface].compact if interfaces.empty?
|
|
5
|
+
%>
|
|
6
|
+
<% interfaces.each_with_index do |interface_obj, idx| %>
|
|
7
|
+
<%= fields_for "#{f.object_name}[#{association}][#{idx}]", interface_obj do |i| %>
|
|
8
|
+
<%= render :partial => provider_partial(compute_resource, 'network'),
|
|
9
|
+
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :remove_title => _('remove network interface'), :selected_cluster => selected_cluster },
|
|
10
|
+
:layout => "compute_resources_vms/form/#{item_layout}_layout" %>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% end %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%
|
|
2
|
+
volumes = Array.wrap(f.object.volumes)
|
|
3
|
+
volumes = [compute_resource.new_volume].compact if volumes.empty?
|
|
4
|
+
%>
|
|
5
|
+
<% volumes.each_with_index do |volume_obj, idx| %>
|
|
6
|
+
<%= fields_for "#{f.object_name}[volumes][#{idx}]", volume_obj do |i| %>
|
|
7
|
+
<%= render :partial => provider_partial(compute_resource, 'volume'),
|
|
8
|
+
:locals => { :f => i, :compute_resource => compute_resource, :new_host => new_host, :new_vm => new_vm, :remove_title => _('remove storage volume') },
|
|
9
|
+
:layout => "compute_resources_vms/form/#{item_layout}_layout" %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% end %>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<%= text_f f, :username, :value => @image.username || "root", :help_inline => _("The user that is used to ssh into the instance, normally cloud-user, ec2-user, ubuntu, root etc") %>
|
|
2
|
+
<%= checkbox_f f, :user_data, :help_inline => _("Does this image support user data input (e.g. via cloud-init)?") %>
|
|
3
|
+
<%= password_f f, :password, :help_inline => _("Password to authenticate with - used for SSH finish step.") %>
|
|
4
|
+
<%= image_field(f, :label => _("Image ID"), :help_inline => _("ID of the Image on the Hypervisor")) %>
|