foreman_hyperv 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/LICENSE.txt +1451 -12
- data/README.md +8 -2
- data/app/assets/javascripts/foreman_hyperv/compute_resource_base.js +78 -0
- data/app/helpers/foreman_hyperv/compute_resources_vms_helper.rb +19 -0
- data/app/models/concerns/fog_extensions/hyperv/compute.rb +6 -0
- data/app/models/concerns/fog_extensions/hyperv/hard_drive.rb +37 -0
- data/app/models/concerns/fog_extensions/hyperv/network_adapter.rb +115 -12
- data/app/models/concerns/fog_extensions/hyperv/server.rb +90 -31
- data/app/models/concerns/foreman_hyperv/host_managed_extensions.rb +66 -0
- data/app/models/foreman_hyperv/hyperv.rb +366 -131
- data/app/views/compute_resources/form/_hyperv.html.erb +6 -2
- data/app/views/compute_resources_vms/form/hyperv/_base.html.erb +53 -21
- data/app/views/compute_resources_vms/form/hyperv/_network.html.erb +34 -10
- data/app/views/compute_resources_vms/form/hyperv/_volume.html.erb +5 -2
- data/app/views/compute_resources_vms/index/_hyperv.html.erb +2 -2
- data/app/views/compute_resources_vms/show/_hyperv.html.erb +27 -9
- data/lib/foreman_hyperv/engine.rb +38 -17
- data/lib/foreman_hyperv/version.rb +3 -1
- data/lib/foreman_hyperv.rb +2 -0
- data/lib/tasks/foreman_hyperv_tasks.rake +33 -0
- metadata +25 -37
- data/.gitignore +0 -9
- data/.rubocop.yml +0 -22
- data/.rubocop_todo.yml +0 -20
- data/.travis.yml +0 -5
- data/CHANGELOG.md +0 -17
- data/Gemfile +0 -6
- data/app/assets/javascripts/compute_resources/hyperv/base.js +0 -32
- data/app/models/concerns/fog_extensions/hyperv/vhd.rb +0 -12
- data/foreman_hyperv.gemspec +0 -27
- data/test/foreman_hyperv_test.rb +0 -11
- data/test/test_helper.rb +0 -4
data/README.md
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
Microsoft Hyper-V compute resource for Foreman
|
|
6
6
|
|
|
7
|
-
Uses the in-development `fog-hyperv` gem found [here](https://github.com/
|
|
7
|
+
Uses the in-development `fog-hyperv` gem found [here](https://github.com/ananace/fog-hyperv).
|
|
8
|
+
|
|
9
|
+
## Nota Bene
|
|
10
|
+
|
|
11
|
+
Currently the plugin only supports Hyper-V hosts where the names are well defined in DNS and in connection strings, avoid using IP addresses for now.
|
|
12
|
+
|
|
13
|
+
If you're using SELinux, you may need to enable the connect_all boolean. For Foreman 2.0 and earlier, run `setsebool -P passenger_can_connect_all 1`. For Foreman 2.1 and later the command would be `setsebool -P foreman_rails_can_connect_all 1`.
|
|
8
14
|
|
|
9
15
|
## Testing/Installing
|
|
10
16
|
|
|
@@ -16,7 +22,7 @@ Do bear in mind that this is still very early in development, so plenty of issue
|
|
|
16
22
|
|
|
17
23
|
## Contributing
|
|
18
24
|
|
|
19
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
25
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ananace/foreman_hyperv.
|
|
20
26
|
|
|
21
27
|
## License
|
|
22
28
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
$(document).on('ContentLoad', function() { tfm.numFields.initAll(); });
|
|
2
|
+
|
|
3
|
+
function hypervGenerationChange(item) {
|
|
4
|
+
var toIter = ['#host_compute_attributes_secure_boot_enabled', '#compute_attribute_vm_attrs_secure_boot_enabled'];
|
|
5
|
+
gen = $(item).val();
|
|
6
|
+
|
|
7
|
+
if (gen == 'BIOS') {
|
|
8
|
+
for (var i = 0; i < toIter.length; ++i) {
|
|
9
|
+
$(toIter[i]).attr('disabled', true);
|
|
10
|
+
}
|
|
11
|
+
} else {
|
|
12
|
+
for (var i = 0; i < toIter.length; ++i) {
|
|
13
|
+
$(toIter[i]).removeAttr('disabled');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function hypervDynamicMemoryChange(item) {
|
|
19
|
+
var toIter = [
|
|
20
|
+
'#host_compute_attributes_memory_maximum',
|
|
21
|
+
'#host_compute_attributes_memory_minimum',
|
|
22
|
+
'#compute_attribute_vm_attrs_memory_maximum',
|
|
23
|
+
'#compute_attribute_vm_attrs_memory_minimum',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
if (item.checked) {
|
|
27
|
+
for (var i = 0; i < toIter.length; ++i) {
|
|
28
|
+
$(toIter[i]).removeAttr('disabled');
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
for (var i = 0; i < toIter.length; ++i) {
|
|
32
|
+
$(toIter[i]).attr('disabled', true);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function hypervHostChange(item) {
|
|
38
|
+
console.log('Hyper-V host changed to ' + $(item).val());
|
|
39
|
+
|
|
40
|
+
$('table.hyperv-host-info').hide();
|
|
41
|
+
$('table.hyperv-host-info[data-host="'+$(item).val()+'"]').show();
|
|
42
|
+
|
|
43
|
+
// TODO: Reload available switches
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function hypervVLANModeChange(item) {
|
|
47
|
+
var parent = [...document.querySelectorAll('fieldset.compute_attributes')].filter(el => el.contains(item))[0];
|
|
48
|
+
if (parent == undefined) {
|
|
49
|
+
parent = [...document.querySelectorAll('div.fields')].filter(el => el.contains(item))[0];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$(parent.querySelectorAll('[data-hyperv-vlan-mode]')).hide();
|
|
53
|
+
$(parent.querySelectorAll('[data-hyperv-vlan-mode] input')).attr('disabled', true);
|
|
54
|
+
$(parent.querySelectorAll('[data-hyperv-vlan-mode] select')).attr('disabled', true);
|
|
55
|
+
|
|
56
|
+
var id = '[data-hyperv-vlan-mode="' + $(item).val().toLowerCase() + '"]';
|
|
57
|
+
$(parent.querySelectorAll(id)).show();
|
|
58
|
+
$(parent.querySelectorAll(id + ' input')).removeAttr('disabled');
|
|
59
|
+
$(parent.querySelectorAll(id + ' select')).removeAttr('disabled');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function hypervVLANPrivateModeChange(item) {
|
|
63
|
+
var parent = [...document.querySelectorAll('fieldset.compute_attributes')].filter(el => el.contains(item))[0];
|
|
64
|
+
if (parent == undefined) {
|
|
65
|
+
parent = [...document.querySelectorAll('div.fields')].filter(el => el.contains(item))[0];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
$(parent.querySelectorAll('[data-hyperv-vlan-private-mode]')).hide();
|
|
69
|
+
$(parent.querySelectorAll('[data-hyperv-vlan-private-mode] input')).attr('disabled', true);
|
|
70
|
+
|
|
71
|
+
if ($(item).val() == 'Promiscuous') {
|
|
72
|
+
var id = '[data-hyperv-vlan-private-mode="plural"]';
|
|
73
|
+
} else {
|
|
74
|
+
var id = '[data-hyperv-vlan-private-mode="singular"]';
|
|
75
|
+
}
|
|
76
|
+
$(parent.querySelectorAll(id)).show();
|
|
77
|
+
$(parent.querySelectorAll(id + ' input')).removeAttr('disabled');
|
|
78
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module ForemanHyperv
|
|
2
|
+
module ComputeResourcesVmsHelper
|
|
3
|
+
def hyperv_networks(compute_resource)
|
|
4
|
+
compute_resource.switches(nil).map do |sw|
|
|
5
|
+
[ sw.id, "#{sw.name}#{sw.switch_type ? " (#{sw.switch_type})" : nil}" ]
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def hyperv_generations
|
|
10
|
+
Fog::Hyperv::Compute::Server::VM_GENERATION_VALUES.map { |gen, num| [gen, "Generation #{num} (#{gen})"] }
|
|
11
|
+
end
|
|
12
|
+
def hyperv_vlan_modes
|
|
13
|
+
Fog::Hyperv::Compute::NetworkAdapterVlan::VLAN_OPERATION_MODE.map { |mode| [mode, mode] }
|
|
14
|
+
end
|
|
15
|
+
def hyperv_private_vlan_modes
|
|
16
|
+
Fog::Hyperv::Compute::NetworkAdapterVlan::PRIVATE_VLAN_MODE.reject { |mode| mode == :Unknown }.map { |mode| [mode, mode] }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module FogExtensions
|
|
2
|
+
module Hyperv
|
|
3
|
+
module HardDrive
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
include ActionView::Helpers::NumberHelper
|
|
6
|
+
|
|
7
|
+
def size_bytes=(new_size)
|
|
8
|
+
raise ArgumentError, "Can't modify a physical disk" if disk
|
|
9
|
+
|
|
10
|
+
self.vhd ||= Fog::Hyperv::Compute::Vhd.new unless persisted?
|
|
11
|
+
vhd.size = new_size
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def basename
|
|
15
|
+
vhd&.basename
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def basename=(new_basename)
|
|
19
|
+
raise ArgumentError, "Can't modify a physical disk" if disk
|
|
20
|
+
|
|
21
|
+
self.vhd ||= Fog::Hyperv::Compute::Vhd.new unless persisted?
|
|
22
|
+
vhd.basename = new_basename
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def compute_attributes
|
|
26
|
+
attributes
|
|
27
|
+
.slice(:id)
|
|
28
|
+
.merge(
|
|
29
|
+
{
|
|
30
|
+
basename:,
|
|
31
|
+
size_bytes:
|
|
32
|
+
}.compact
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,28 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module FogExtensions
|
|
2
4
|
module Hyperv
|
|
3
5
|
module NetworkAdapter
|
|
4
6
|
extend ActiveSupport::Concern
|
|
5
|
-
include ActionView::Helpers::NumberHelper
|
|
6
|
-
|
|
7
|
-
def to_s
|
|
8
|
-
name
|
|
9
|
-
end
|
|
10
7
|
|
|
11
8
|
def mac
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
return unless mac_address
|
|
10
|
+
return if mac_address.to_i(16) == 0
|
|
11
|
+
|
|
12
|
+
# Downcase and split every 2 chars, join with :
|
|
13
|
+
mac_address.downcase.scan(%r{.{2}}).join(':')
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def mac=(m)
|
|
17
|
-
|
|
17
|
+
mac_address = m&.upcase&.delete(':')
|
|
18
|
+
mac_address = Fog::Hyperv::Compute::NetworkAdapter::NIC_FALLBACK_MAC if mac_address.nil? || mac_address.blank?
|
|
19
|
+
dynamic_mac_address_enabled = (mac_address.to_i(16) == 0)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# VLAN settings
|
|
23
|
+
|
|
24
|
+
def vlan_operation_mode
|
|
25
|
+
vlan_setting.operation_mode
|
|
26
|
+
end
|
|
27
|
+
def vlan_operation_mode=(mode)
|
|
28
|
+
vlan_setting.operation_mode = mode
|
|
18
29
|
end
|
|
19
30
|
|
|
20
|
-
def
|
|
21
|
-
|
|
31
|
+
def vlan_private_mode
|
|
32
|
+
return nil if vlan_setting.private_vlan_mode == :Unknown
|
|
33
|
+
|
|
34
|
+
vlan_setting.private_vlan_mode
|
|
35
|
+
end
|
|
36
|
+
def vlan_private_mode=(mode)
|
|
37
|
+
vlan_setting.private_vlan_mode = mode
|
|
22
38
|
end
|
|
23
39
|
|
|
24
|
-
def
|
|
25
|
-
|
|
40
|
+
def access_vlan_id
|
|
41
|
+
return nil if vlan_setting.access_vlan_id.zero?
|
|
42
|
+
|
|
43
|
+
vlan_setting.access_vlan_id
|
|
44
|
+
end
|
|
45
|
+
def access_vlan_id=(id)
|
|
46
|
+
vlan_setting.access_vlan_id = id
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def native_vlan_id
|
|
50
|
+
return nil if vlan_setting.native_vlan_id.zero?
|
|
51
|
+
|
|
52
|
+
vlan_setting.native_vlan_id
|
|
53
|
+
end
|
|
54
|
+
def native_vlan_id=(id)
|
|
55
|
+
vlan_setting.native_vlan_id = id
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def allowed_vlan_ids
|
|
59
|
+
return nil unless vlan_setting.allowed_vlan_id_list&.any?
|
|
60
|
+
|
|
61
|
+
Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list vlan_setting.allowed_vlan_id_list
|
|
62
|
+
end
|
|
63
|
+
def allowed_vlan_ids=(ids)
|
|
64
|
+
ids ||= ''
|
|
65
|
+
vlan_setting.allowed_vlan_id_list = parse_vlan_list(ids)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def primary_vlan_id
|
|
69
|
+
return nil if vlan_setting.primary_vlan_id.zero?
|
|
70
|
+
|
|
71
|
+
vlan_setting.primary_vlan_id
|
|
72
|
+
end
|
|
73
|
+
def primary_vlan_id=(id)
|
|
74
|
+
vlan_setting.primary_vlan_id = id
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def secondary_vlan_id
|
|
78
|
+
return nil if vlan_setting.secondary_vlan_id.zero?
|
|
79
|
+
|
|
80
|
+
vlan_setting.secondary_vlan_id
|
|
81
|
+
end
|
|
82
|
+
def secondary_vlan_id=(id)
|
|
83
|
+
vlan_setting.secondary_vlan_id = id
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def secondary_vlan_ids
|
|
87
|
+
return nil unless vlan_setting.secondary_vlan_id_list&.any?
|
|
88
|
+
|
|
89
|
+
Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list vlan_setting.secondary_vlan_id_list
|
|
90
|
+
end
|
|
91
|
+
def secondary_vlan_ids=(ids)
|
|
92
|
+
ids ||= ''
|
|
93
|
+
vlan_setting.secondary_vlan_id_list = parse_vlan_list(ids)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def compute_attributes
|
|
97
|
+
attributes
|
|
98
|
+
.slice(
|
|
99
|
+
:id,
|
|
100
|
+
:switch_id
|
|
101
|
+
)
|
|
102
|
+
.merge(
|
|
103
|
+
{
|
|
104
|
+
vlan_operation_mode:,
|
|
105
|
+
vlan_private_mode:,
|
|
106
|
+
access_vlan_id:,
|
|
107
|
+
native_vlan_id:,
|
|
108
|
+
allowed_vlan_ids:,
|
|
109
|
+
primary_vlan_id:,
|
|
110
|
+
secondary_vlan_id:,
|
|
111
|
+
secondary_vlan_ids:
|
|
112
|
+
}.compact
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def parse_vlan_list(list)
|
|
119
|
+
ret = []
|
|
120
|
+
list.split(',').map do |num|
|
|
121
|
+
if num.include? '-'
|
|
122
|
+
rstart, rend = num.split('-')
|
|
123
|
+
ret += (rstart.to_i..rend.to_i).to_a
|
|
124
|
+
else
|
|
125
|
+
ret << num.to_i
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
ret.sort.uniq
|
|
26
129
|
end
|
|
27
130
|
end
|
|
28
131
|
end
|
|
@@ -4,8 +4,6 @@ module FogExtensions
|
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
include ActionView::Helpers::NumberHelper
|
|
6
6
|
|
|
7
|
-
attr_accessor :start
|
|
8
|
-
|
|
9
7
|
def to_s
|
|
10
8
|
name
|
|
11
9
|
end
|
|
@@ -14,12 +12,8 @@ module FogExtensions
|
|
|
14
12
|
name.gsub(/[^0-9A-Za-z.\-]/, '_')
|
|
15
13
|
end
|
|
16
14
|
|
|
17
|
-
def mac
|
|
18
|
-
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def clean_mac_addresses
|
|
22
|
-
network_adapters.map { |n| mac(n.mac_address) }
|
|
15
|
+
def mac
|
|
16
|
+
network_adapters.first.mac
|
|
23
17
|
end
|
|
24
18
|
|
|
25
19
|
def interfaces
|
|
@@ -27,18 +21,54 @@ module FogExtensions
|
|
|
27
21
|
end
|
|
28
22
|
|
|
29
23
|
def volumes
|
|
30
|
-
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def persisted?
|
|
34
|
-
identity.present?
|
|
24
|
+
hard_drives
|
|
35
25
|
end
|
|
36
26
|
|
|
37
27
|
def interfaces_attributes=(_attributes); end
|
|
38
28
|
|
|
39
29
|
def volumes_attributes=(_attributes); end
|
|
40
30
|
|
|
31
|
+
# Override fog configuration with explicit cluster_name handling
|
|
32
|
+
def cluster
|
|
33
|
+
@cluster
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def cluster_name
|
|
37
|
+
cluster&.name
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def cluster_name=(name)
|
|
41
|
+
@cluster = service.clusters.get(name)
|
|
42
|
+
end
|
|
43
|
+
#
|
|
44
|
+
|
|
45
|
+
def vlan
|
|
46
|
+
nic = network_adapters.first
|
|
47
|
+
|
|
48
|
+
nic.access_vlan_id || nic.native_vlan_id || nic.primary_vlan_id
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def vlan=(vlan)
|
|
52
|
+
logger.warn "using vlan=#{vlan.inspect} on Hyper-V VM, this can lead to unexpected results"
|
|
53
|
+
nic = network_adapters.first
|
|
54
|
+
if vlan.present? && vlan.to_i > 0
|
|
55
|
+
nic.vlan_operation_mode = :Access if nic.vlan_operation_mode == :Untagged
|
|
56
|
+
case nic.vlan_operation_mode
|
|
57
|
+
when :Access
|
|
58
|
+
nic.access_vlan_id = vlan
|
|
59
|
+
when :Trunk
|
|
60
|
+
nic.native_vlan_id = vlan
|
|
61
|
+
when :Private
|
|
62
|
+
nic.primary_vlan_id = vlan
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
nic.vlan_operation_mode = :Untagged
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
41
69
|
def secure_boot_enabled=(enabled)
|
|
70
|
+
return if generation != :UEFI
|
|
71
|
+
|
|
42
72
|
@secure_boot = enabled
|
|
43
73
|
return unless persisted?
|
|
44
74
|
|
|
@@ -46,37 +76,66 @@ module FogExtensions
|
|
|
46
76
|
end
|
|
47
77
|
|
|
48
78
|
def secure_boot_enabled
|
|
49
|
-
return false if generation
|
|
79
|
+
return false if generation != :UEFI
|
|
50
80
|
return @secure_boot unless persisted?
|
|
51
81
|
|
|
52
82
|
firmware.secure_boot == :On
|
|
53
83
|
end
|
|
54
84
|
|
|
55
|
-
def reset
|
|
56
|
-
restart(force: true)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def stop
|
|
60
|
-
requires :name, :computer_name
|
|
61
|
-
service.stop_vm options.merge(
|
|
62
|
-
name: name,
|
|
63
|
-
computer_name: computer_name,
|
|
64
|
-
force: true
|
|
65
|
-
)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
85
|
def vm_description
|
|
69
86
|
format _('%{cpus} CPUs and %{ram} memory'),
|
|
70
87
|
cpus: processor_count,
|
|
71
88
|
ram: number_to_human_size(memory_startup)
|
|
72
89
|
end
|
|
73
90
|
|
|
91
|
+
def compute_attributes
|
|
92
|
+
attributes.merge(
|
|
93
|
+
interfaces_attributes: vm.network_adapters.each_with_index.to_h { |nic, idx| [idx, nic.compute_attributes] },
|
|
94
|
+
volumes_attributes: vm.hard_drives.each_with_index.to_h { |hdd, idx| [idx, hdd.compute_attributes] }
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
74
98
|
def select_nic(fog_nics, nic)
|
|
75
99
|
nic_attrs = nic.compute_attributes
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
match
|
|
79
|
-
match ||= fog_nics.detect { |fn| fn.
|
|
100
|
+
|
|
101
|
+
# Match for exact data
|
|
102
|
+
match = fog_nics.detect { |fn| fn.id == nic_attrs['id'].presence }
|
|
103
|
+
match ||= fog_nics.detect { |fn| fn.mac == nic.mac }
|
|
104
|
+
# match ||= fog_nics.detect { |fn| fn.name == nic_attrs['name'].presence }
|
|
105
|
+
|
|
106
|
+
if !match
|
|
107
|
+
# Match on networking, limit potentials down to identical configuration and then pick the first
|
|
108
|
+
potential = fog_nics.select do |fn|
|
|
109
|
+
fn.switch_id == nic_attrs['switch_id'].presence || fn.switch_name == nic_attrs['switch_name'].presence
|
|
110
|
+
end
|
|
111
|
+
potential.select! { |fn| fn.vlan_operation_mode.to_s == nic_attrs['vlan_operation_mode'] }
|
|
112
|
+
if nic_attrs['vlan_operation_mode'] == 'Access'
|
|
113
|
+
potential.select! { |fn| fn.access_vlan_id.to_s == nic_attrs['access_vlan_id'] }
|
|
114
|
+
elsif nic_attrs['vlan_operation_mode'] == 'Trunk'
|
|
115
|
+
potential.select! { |fn| fn.native_vlan_id.to_s == nic_attrs['native_vlan_id'] }
|
|
116
|
+
potential.select! { |fn| fn.allowed_vlan_ids.split(',').map(&:strip) == nic_attrs['allowed_vlan_ids'].split(',').map(&:strip) } \
|
|
117
|
+
if nic_attrs['allowed_vlan_ids'].present?
|
|
118
|
+
elsif nic_attrs['vlan_operation_mode'] == 'Private'
|
|
119
|
+
potential.select! { |fn| fn.vlan_private_mode.to_s == nic_attrs['vlan_private_mode'] }
|
|
120
|
+
potential.select! { |fn| fn.primary_vlan_id.to_s == nic_attrs['primary_vlan_id'].to_s } \
|
|
121
|
+
if nic_attrs['primary_vlan_id'].present?
|
|
122
|
+
|
|
123
|
+
if nic_attrs['vlan_private_mode'] == 'Promiscuous'
|
|
124
|
+
potential.select! { |fn| fn.secondary_vlan_ids.split(',').map(&:strip) == nic_attrs['secondary_vlan_ids'].split(',').map(&:strip) } \
|
|
125
|
+
if nic_attrs['secondary_vlan_ids'].present?
|
|
126
|
+
else
|
|
127
|
+
potential.select! { |fn| fn.secondary_vlan_id.to_s == nic_attrs['secondary_vlan_id'].to_s } \
|
|
128
|
+
if nic_attrs['secondary_vlan_id'].present?
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
match = potential.first
|
|
133
|
+
end
|
|
134
|
+
return unless match
|
|
135
|
+
|
|
136
|
+
# Store Hyper-V ID in compute attributes
|
|
137
|
+
nic.compute_attributes['identity'] = match.id
|
|
138
|
+
|
|
80
139
|
match
|
|
81
140
|
end
|
|
82
141
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ForemanHyperv
|
|
4
|
+
module HostManagedExtensions
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
def update(attributes = {})
|
|
8
|
+
hyperv_add_attributes(attributes) if provider == 'Hyper-V' && attributes.key?('compute_attributes')
|
|
9
|
+
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def setComputeUpdate
|
|
14
|
+
ret = super
|
|
15
|
+
return ret unless provider == 'Hyper-V'
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
hyperv_sync_interfaces
|
|
19
|
+
rescue => e
|
|
20
|
+
failure _("Failed to update a compute %{compute_resource} instance %{name}: %{e}") %
|
|
21
|
+
{ compute_resource:, name:, e: }, e
|
|
22
|
+
end
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
# Reconcile the mapping between Hyper-V and Foreman interfaces
|
|
29
|
+
def hyperv_sync_interfaces
|
|
30
|
+
unmapped_ifaces = interfaces.select { |iface| iface.physical? && !iface.compute_attributes['identity'].present? }
|
|
31
|
+
return true if unmapped_ifaces.empty?
|
|
32
|
+
|
|
33
|
+
logger.info "Mapping #{unmapped_ifaces.count} unknown interfaces for #{name}"
|
|
34
|
+
|
|
35
|
+
self.vm = compute_object
|
|
36
|
+
fog_nics = vm.interfaces.dup
|
|
37
|
+
interfaces.each { |iface| fog_nics.delete_if { |vmiface| vmiface.id == iface.compute_attributes['identity'] } }
|
|
38
|
+
|
|
39
|
+
unmapped_ifaces.each do |nic|
|
|
40
|
+
logger.debug "Matching #{nic.inspect} against #{fog_nics}"
|
|
41
|
+
selected_nic = vm.select_nic(fog_nics, nic)
|
|
42
|
+
if selected_nic.nil?
|
|
43
|
+
logger.warn "Orchestration::Compute: Could not match network interface #{nic.inspect}"
|
|
44
|
+
raise ArgumentError, \
|
|
45
|
+
_("Could not find virtual machine network interface matching %s") %
|
|
46
|
+
[nic.identifier, nic.ip, nic.name, nic.type].find(&:present?)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
logger.debug "Orchestration::Compute: nic #{nic.inspect} assigned to #{selected_nic.inspect}"
|
|
50
|
+
nic.mac ||= selected_nic.mac
|
|
51
|
+
nic.save
|
|
52
|
+
fog_nics.delete selected_nic
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Inject interface attributes into the compute values, to allow modifying Hyper-V configuration on hosts
|
|
57
|
+
def hyperv_add_attributes(attributes)
|
|
58
|
+
compute = attributes['compute_attributes'] ||= {}
|
|
59
|
+
compute['interfaces_attributes'] ||= {}
|
|
60
|
+
|
|
61
|
+
attributes['interfaces_attributes'].each do |idx, interface|
|
|
62
|
+
compute['interfaces_attributes'][idx] = interface
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|