foreman_azure_rm 1.3.1 → 2.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +36 -8
- data/app/controllers/foreman_azure_rm/concerns/hosts_controller_extensions.rb +6 -6
- data/app/models/concerns/foreman_azure_rm/vm_extensions/managed_vm.rb +216 -0
- data/app/models/foreman_azure_rm/azure_rm.rb +132 -188
- data/app/models/foreman_azure_rm/azure_rm_compute.rb +95 -0
- data/app/views/compute_resources/form/_azurerm.html.erb +6 -7
- data/app/views/compute_resources_vms/form/azurerm/_base.html.erb +62 -67
- data/app/views/compute_resources_vms/index/_azurerm.html.erb +3 -3
- data/app/views/compute_resources_vms/show/_azurerm.html.erb +17 -5
- data/lib/foreman_azure_rm.rake +13 -0
- data/lib/foreman_azure_rm/azure_sdk_adapter.rb +141 -0
- data/lib/foreman_azure_rm/engine.rb +16 -57
- data/lib/foreman_azure_rm/version.rb +2 -2
- metadata +109 -20
- data/app/models/concerns/fog_extensions/azurerm/compute.rb +0 -249
- data/app/models/concerns/fog_extensions/azurerm/data_disk.rb +0 -24
- data/app/models/concerns/fog_extensions/azurerm/managed_disk.rb +0 -10
- data/app/models/concerns/fog_extensions/azurerm/managed_disks.rb +0 -15
- data/app/models/concerns/fog_extensions/azurerm/network_interface.rb +0 -19
- data/app/models/concerns/fog_extensions/azurerm/network_interfaces.rb +0 -16
- data/app/models/concerns/fog_extensions/azurerm/server.rb +0 -131
- data/app/models/concerns/fog_extensions/azurerm/servers.rb +0 -23
- data/app/views/compute_resources_vms/form/azurerm/_volume.html.erb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8cdbc28091795bf21377b298699209cc2a0d06365858d117b9468a466fb0ffe2
|
4
|
+
data.tar.gz: 938f88f1ac6e6f9d9101ce57d2bf187e5c1196fd4abf68c667ae38db5c3270e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4def716d7fa2719d629c0bb9029a60e079e5578c16a33c9baa624462e3da7cdf634fa8f64070fef86bd4b615f9bccafedb427f073facad2a3e5f021bb74eedc
|
7
|
+
data.tar.gz: e2dbb0b84df8d5d7a62fa87486b858c5ee11cd28dcdf072ada26cbd77036942837f92f699e7738f40378cc839d0fde9cfc113d53c846b5af84af17dec14f78f4
|
data/README.md
CHANGED
@@ -1,23 +1,51 @@
|
|
1
|
-
# Foreman
|
1
|
+
# Foreman AzureRM Plugin
|
2
2
|
|
3
3
|
## Description
|
4
|
-
|
4
|
+
```foreman_azure_rm``` adds [Microsoft Azure Resource Manager](http://azure.com/) as a compute resource for The Foreman
|
5
|
+
|
6
|
+
* Website: [TheForeman.org](http://theforeman.org)
|
7
|
+
* Support: [Foreman support](http://theforeman.org/support.html)
|
5
8
|
|
6
9
|
## Features
|
7
|
-
* Managed disks support
|
8
10
|
* Support for most typical IaaS operations
|
9
11
|
* VM creation
|
10
|
-
*
|
11
|
-
*
|
12
|
+
* Provisions using Finish and User data templates from Foreman
|
13
|
+
* Supports cloud-config provisioning
|
14
|
+
* Currently supports single NIC
|
15
|
+
* Currently supports single default OS Disk
|
16
|
+
* Currently supports only provisioning of Linux platforms
|
17
|
+
* Provisioning using [Public Images](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/cli-ps-findimage)
|
12
18
|
* Static or dynamic addresses on a per NIC basis
|
13
19
|
* Limited extension support
|
14
20
|
* Microsoft's custom script extension
|
15
21
|
* Puppet Lab's Puppet agent extension for Windows
|
22
|
+
|
23
|
+
## Configuration
|
24
|
+
Go to **Infrastructure > Compute Resources** and click on "New Compute Resource".
|
25
|
+
|
26
|
+
Choose the **AzureRM provider**, and fill in all the fields. You need a Subscription ID, Tenant ID, Client ID and a Client Secret which you can generate from your [Microsoft Azure subscription](https://docs.bmc.com/docs/cloudlifecyclemanagement/46/setting-up-a-tenant-id-client-id-and-client-secret-for-azure-resource-manager-provisioning-669202145.html#SettingupaTenantID,ClientID,andClientSecretforAzureResourceManagerprovisioning-SetupTenantIDPrereqPrerequisites)
|
27
|
+
|
28
|
+
That's it. You're now ready to create and manage Azure resources in your new AzureRM compute resource. You should see something like this in the Compute Resource page:
|
29
|
+
|
30
|
+
|
31
|
+
![](https://i.imgur.com/9J7tPJa.png)
|
32
|
+
|
33
|
+
|
34
|
+
![](https://i.imgur.com/eFHucdb.png)
|
35
|
+
|
36
|
+
|
37
|
+
![](https://i.imgur.com/RTBlMeE.png)
|
38
|
+
|
16
39
|
|
17
40
|
## Planned Features
|
41
|
+
* Multiple NICs support
|
42
|
+
* Support to add multiple data disks (standard or premium)
|
43
|
+
* Provision using custom images
|
44
|
+
* Provision using shared image galleries
|
18
45
|
* Improved extension support
|
19
46
|
|
20
47
|
## Known Limitations
|
21
|
-
*
|
22
|
-
|
23
|
-
|
48
|
+
* Unable to provision using Windows Images
|
49
|
+
|
50
|
+
## Links
|
51
|
+
* [Issue tracker](https://projects.theforeman.org/projects/azurerm)
|
@@ -5,9 +5,9 @@ module ForemanAzureRM
|
|
5
5
|
def sizes
|
6
6
|
if (azure_rm_resource = Image.unscoped.find_by_uuid(params[:image_id])).present?
|
7
7
|
resource = azure_rm_resource.compute_resource
|
8
|
-
render :json => resource.vm_sizes(params[:
|
8
|
+
render :json => resource.vm_sizes(params[:region_string]).map { |size| size.name }
|
9
9
|
else
|
10
|
-
no_sizes = _('The
|
10
|
+
no_sizes = _('The region you selected has no sizes associated with it')
|
11
11
|
render :json => "[\"#{no_sizes}\"]"
|
12
12
|
end
|
13
13
|
end
|
@@ -16,11 +16,11 @@ module ForemanAzureRM
|
|
16
16
|
azure_rm_image = Image.unscoped.find_by_uuid(params[:image_id])
|
17
17
|
if azure_rm_image.present?
|
18
18
|
azure_rm_resource = azure_rm_image.compute_resource
|
19
|
-
subnets = azure_rm_resource.subnets(params[:
|
19
|
+
subnets = azure_rm_resource.subnets(params[:region])
|
20
20
|
if subnets.present?
|
21
|
-
render :json =>
|
21
|
+
render :json => subnets
|
22
22
|
else
|
23
|
-
no_subnets = _('The selected
|
23
|
+
no_subnets = _('The selected region has no subnets')
|
24
24
|
render :json => "[\"#{no_subnets}\"]"
|
25
25
|
end
|
26
26
|
else
|
@@ -30,4 +30,4 @@ module ForemanAzureRM
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
|
-
end
|
33
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# This concern has the methods to be called inside azure_rm.rb
|
2
|
+
module ForemanAzureRM
|
3
|
+
module VMExtensions
|
4
|
+
module ManagedVM
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def define_managed_storage_profile(vm_name, vhd_path, publisher, offer, sku, version,
|
8
|
+
os_disk_caching, platform, premium_os_disk)
|
9
|
+
storage_profile = ComputeModels::StorageProfile.new
|
10
|
+
os_disk = ComputeModels::OSDisk.new
|
11
|
+
managed_disk_params = ComputeModels::ManagedDiskParameters.new
|
12
|
+
|
13
|
+
# Create OS disk
|
14
|
+
os_disk.name = "#{vm_name}-osdisk"
|
15
|
+
os_disk.os_type = platform
|
16
|
+
os_disk.create_option = ComputeModels::DiskCreateOptionTypes::FromImage
|
17
|
+
os_disk.caching = if os_disk_caching.present?
|
18
|
+
case os_disk_caching
|
19
|
+
when 'None'
|
20
|
+
ComputeModels::CachingTypes::None
|
21
|
+
when 'ReadOnly'
|
22
|
+
ComputeModels::CachingTypes::ReadOnly
|
23
|
+
when 'ReadWrite'
|
24
|
+
ComputeModels::CachingTypes::ReadWrite
|
25
|
+
else
|
26
|
+
# ARM best practices stipulate RW caching on the OS disk
|
27
|
+
ComputeModels::CachingTypes::ReadWrite
|
28
|
+
end
|
29
|
+
end
|
30
|
+
managed_disk_params.storage_account_type = if premium_os_disk == 'true'
|
31
|
+
ComputeModels::StorageAccountTypes::PremiumLRS
|
32
|
+
else
|
33
|
+
ComputeModels::StorageAccountTypes::StandardLRS
|
34
|
+
end
|
35
|
+
os_disk.managed_disk = managed_disk_params
|
36
|
+
storage_profile.os_disk = os_disk
|
37
|
+
|
38
|
+
# Currently eliminating data disk creation since capability does not exist.
|
39
|
+
|
40
|
+
if vhd_path.nil?
|
41
|
+
# We are using a marketplace image
|
42
|
+
storage_profile.image_reference = image_reference(publisher, offer,
|
43
|
+
sku, version)
|
44
|
+
else
|
45
|
+
# We are using a custom managed image
|
46
|
+
image_ref = ComputeModels::ImageReference.new
|
47
|
+
image_ref.id = vhd_path
|
48
|
+
storage_profile.image_reference = image_ref
|
49
|
+
end
|
50
|
+
storage_profile
|
51
|
+
end
|
52
|
+
|
53
|
+
def image_reference(publisher, offer, sku, version)
|
54
|
+
image_reference = ComputeModels::ImageReference.new
|
55
|
+
image_reference.publisher = publisher
|
56
|
+
image_reference.offer = offer
|
57
|
+
image_reference.sku = sku
|
58
|
+
image_reference.version = version
|
59
|
+
image_reference
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_network_profile(network_interface_card_ids)
|
63
|
+
network_interface_cards = []
|
64
|
+
network_interface_card_ids.each_with_index do |id, index|
|
65
|
+
nic = ComputeModels::NetworkInterfaceReference.new
|
66
|
+
nic.id = id
|
67
|
+
nic.primary = true
|
68
|
+
network_interface_cards << nic
|
69
|
+
end
|
70
|
+
network_profile = ComputeModels::NetworkProfile.new
|
71
|
+
network_profile.network_interfaces = network_interface_cards
|
72
|
+
network_profile
|
73
|
+
end
|
74
|
+
|
75
|
+
def create_nics(args = {})
|
76
|
+
nics = []
|
77
|
+
formatted_region = args[:azure_vm][:location].gsub(/\s+/, '').downcase
|
78
|
+
args[:interfaces_attributes].each do |nic, attrs|
|
79
|
+
attrs[:pubip_alloc] = attrs[:bridge]
|
80
|
+
attrs[:privip_alloc] = (attrs[:name] == 'false') ? false : true
|
81
|
+
pip_alloc = case attrs[:pubip_alloc]
|
82
|
+
when 'Static'
|
83
|
+
NetworkModels::IPAllocationMethod::Static
|
84
|
+
when 'Dynamic'
|
85
|
+
NetworkModels::IPAllocationMethod::Dynamic
|
86
|
+
when 'None'
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
priv_ip_alloc = if attrs[:priv_ip_alloc]
|
90
|
+
NetworkModels::IPAllocationMethod::Static
|
91
|
+
else
|
92
|
+
NetworkModels::IPAllocationMethod::Dynamic
|
93
|
+
end
|
94
|
+
if pip_alloc.present?
|
95
|
+
public_ip_params = NetworkModels::PublicIPAddress.new.tap do |ip|
|
96
|
+
ip.location = formatted_region
|
97
|
+
ip.public_ipallocation_method = pip_alloc
|
98
|
+
end
|
99
|
+
pip = sdk.create_or_update_pip(args[:azure_vm][:resource_group],
|
100
|
+
"#{args[:azure_vm][:vm_name]}-pip#{nic}",
|
101
|
+
public_ip_params)
|
102
|
+
end
|
103
|
+
new_nic = sdk.create_or_update_nic(
|
104
|
+
args[:azure_vm][:resource_group],
|
105
|
+
"#{args[:azure_vm][:vm_name]}-nic#{nic}",
|
106
|
+
NetworkModels::NetworkInterface.new.tap do |interface|
|
107
|
+
interface.location = formatted_region
|
108
|
+
interface.ip_configurations = [
|
109
|
+
NetworkModels::NetworkInterfaceIPConfiguration.new.tap do |nic_conf|
|
110
|
+
nic_conf.name = "#{args[:azure_vm][:vm_name]}-nic#{nic}"
|
111
|
+
nic_conf.private_ipallocation_method = priv_ip_alloc
|
112
|
+
nic_conf.subnet = subnets(args[:azure_vm][:location]).select{ |subnet| subnet.id == attrs[:network] }.first
|
113
|
+
nic_conf.public_ipaddress = pip.present? ? pip : nil
|
114
|
+
end
|
115
|
+
]
|
116
|
+
end
|
117
|
+
)
|
118
|
+
nics << new_nic
|
119
|
+
end
|
120
|
+
nics
|
121
|
+
end
|
122
|
+
|
123
|
+
def create_managed_virtual_machine(vm_hash)
|
124
|
+
custom_data = vm_hash[:custom_data]
|
125
|
+
msg = "Creating Virtual Machine #{vm_hash[:name]} in Resource Group #{vm_hash[:resource_group]}."
|
126
|
+
logger.debug msg
|
127
|
+
vm_create_params = ComputeModels::VirtualMachine.new.tap do |vm|
|
128
|
+
vm.location = vm_hash[:location]
|
129
|
+
unless vm_hash[:availability_set_id].nil?
|
130
|
+
sub_resource = MsRestAzure::SubResource.new
|
131
|
+
sub_resource.id = vm_hash[:availability_set_id]
|
132
|
+
vm.availability_set = sub_resource
|
133
|
+
end
|
134
|
+
# If image UUID begins with / it is a custom managed image
|
135
|
+
# Otherwise it is a marketplace URN
|
136
|
+
unless vm_hash[:vhd_path].start_with?('/')
|
137
|
+
urn = vm_hash[:vhd_path].split(':')
|
138
|
+
vm_hash[:publisher] = urn[0]
|
139
|
+
vm_hash[:offer] = urn[1]
|
140
|
+
vm_hash[:sku] = urn[2]
|
141
|
+
vm_hash[:version] = urn[3]
|
142
|
+
vm_hash[:vhd_path] = nil
|
143
|
+
end
|
144
|
+
|
145
|
+
vm.os_profile = ComputeModels::OSProfile.new.tap do |os_profile|
|
146
|
+
os_profile.computer_name = vm_hash[:name]
|
147
|
+
os_profile.admin_username = vm_hash[:username]
|
148
|
+
os_profile.admin_password = vm_hash[:password]
|
149
|
+
|
150
|
+
# Adding the ssh-key support for authentication
|
151
|
+
os_profile.linux_configuration = ComputeModels::LinuxConfiguration.new.tap do |linux|
|
152
|
+
linux.disable_password_authentication = vm_hash[:disable_password_authentication]
|
153
|
+
linux.ssh = ComputeModels::SshConfiguration.new.tap do |ssh_config|
|
154
|
+
ssh_config.public_keys = [
|
155
|
+
ComputeModels::SshPublicKey.new.tap do |foreman_key|
|
156
|
+
foreman_key.key_data = key_pair.public
|
157
|
+
foreman_key.path = "/home/#{vm_hash[:username]}/.ssh/authorized_keys"
|
158
|
+
end
|
159
|
+
]
|
160
|
+
if vm_hash[:ssh_key_data].present?
|
161
|
+
key_data = vm_hash[:ssh_key_data]
|
162
|
+
pub_key = ComputeModels::SshPublicKey.new
|
163
|
+
pub_key.key_data = key_data
|
164
|
+
pub_key.path = "/home/#{vm_hash[:username]}/.ssh/authorized_keys"
|
165
|
+
ssh_config.public_keys << pub_key
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
# added custom_data here so that azure's vm gets this
|
170
|
+
os_profile.custom_data = Base64.strict_encode64(custom_data) unless vm_hash[:custom_data].nil?
|
171
|
+
end
|
172
|
+
vm.storage_profile = define_managed_storage_profile(
|
173
|
+
vm_hash[:name],
|
174
|
+
vm_hash[:vhd_path],
|
175
|
+
vm_hash[:publisher],
|
176
|
+
vm_hash[:offer],
|
177
|
+
vm_hash[:sku],
|
178
|
+
vm_hash[:version],
|
179
|
+
vm_hash[:os_disk_caching],
|
180
|
+
vm_hash[:platform],
|
181
|
+
vm_hash[:premium_os_disk],
|
182
|
+
)
|
183
|
+
vm.hardware_profile = ComputeModels::HardwareProfile.new.tap do |hw_profile|
|
184
|
+
hw_profile.vm_size = vm_hash[:vm_size]
|
185
|
+
end
|
186
|
+
vm.network_profile = define_network_profile(vm_hash[:network_interface_card_ids])
|
187
|
+
end
|
188
|
+
|
189
|
+
response = sdk.create_or_update_vm(vm_hash[:resource_group], vm_hash[:name], vm_create_params)
|
190
|
+
logger.debug "Virtual Machine #{vm_hash[:name]} Created Successfully."
|
191
|
+
response
|
192
|
+
end
|
193
|
+
|
194
|
+
def create_vm_extension(args = {})
|
195
|
+
if args[:azure_vm][:script_command].present? || args[:azure_vm][:script_uris].present?
|
196
|
+
extension = ComputeModels::VirtualMachineExtension.new
|
197
|
+
if args[:azure_vm][:platform] == 'Linux'
|
198
|
+
extension.publisher = 'Microsoft.Azure.Extensions'
|
199
|
+
extension.virtual_machine_extension_type = 'CustomScript'
|
200
|
+
extension.type_handler_version = '2.0'
|
201
|
+
end
|
202
|
+
extension.auto_upgrade_minor_version = true
|
203
|
+
extension.location = args[:azure_vm][:location].gsub(/\s+/, '').downcase
|
204
|
+
extension.settings = {
|
205
|
+
'commandToExecute' => args[:azure_vm][:script_command],
|
206
|
+
'fileUris' => args[:azure_vm][:script_uris].split(',')
|
207
|
+
}
|
208
|
+
sdk.create_or_update_vm_extensions(args[:azure_vm][:resource_group],
|
209
|
+
args[:azure_vm][:vm_name],
|
210
|
+
'ForemanCustomScript',
|
211
|
+
extension)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -1,11 +1,25 @@
|
|
1
|
+
# This Model contains code modified as per azure-sdk
|
2
|
+
# and removed dependencies from fog-azure-rm.
|
3
|
+
#
|
4
|
+
require 'base64'
|
5
|
+
|
1
6
|
module ForemanAzureRM
|
2
7
|
class AzureRM < ComputeResource
|
8
|
+
|
9
|
+
include VMExtensions::ManagedVM
|
3
10
|
alias_attribute :sub_id, :user
|
4
11
|
alias_attribute :secret_key, :password
|
5
12
|
alias_attribute :app_ident, :url
|
6
13
|
alias_attribute :tenant, :uuid
|
7
14
|
|
8
|
-
|
15
|
+
validates :user, :presence => true
|
16
|
+
validates :password, :presence => true
|
17
|
+
validates :url, :presence => true
|
18
|
+
validates :uuid, :presence => true
|
19
|
+
|
20
|
+
has_one :key_pair, :foreign_key => :compute_resource_id, :dependent => :destroy
|
21
|
+
|
22
|
+
before_create :test_connection, :setup_key_pair
|
9
23
|
|
10
24
|
class VMContainer
|
11
25
|
attr_accessor :virtualmachines
|
@@ -19,6 +33,10 @@ module ForemanAzureRM
|
|
19
33
|
end
|
20
34
|
end
|
21
35
|
|
36
|
+
def sdk
|
37
|
+
@sdk ||= ForemanAzureRM::AzureSDKAdapter.new(tenant, app_ident, secret_key, sub_id)
|
38
|
+
end
|
39
|
+
|
22
40
|
def to_label
|
23
41
|
"#{name} (#{provider_friendly_name})"
|
24
42
|
end
|
@@ -35,8 +53,9 @@ module ForemanAzureRM
|
|
35
53
|
[:image]
|
36
54
|
end
|
37
55
|
|
38
|
-
def
|
56
|
+
def regions
|
39
57
|
[
|
58
|
+
'West Europe',
|
40
59
|
'Central US',
|
41
60
|
'South Central US',
|
42
61
|
'North Central US',
|
@@ -48,34 +67,41 @@ module ForemanAzureRM
|
|
48
67
|
]
|
49
68
|
end
|
50
69
|
|
51
|
-
def
|
52
|
-
|
53
|
-
rg_names = []
|
54
|
-
rgs.each do |rg|
|
55
|
-
rg_names << rg.name
|
56
|
-
end
|
57
|
-
rg_names
|
58
|
-
end
|
59
|
-
|
60
|
-
def available_resource_groups
|
61
|
-
rg_client.list_resource_groups
|
62
|
-
end
|
63
|
-
|
64
|
-
def storage_accts(location = nil)
|
65
|
-
stripped_location = location.gsub(/\s+/, '').downcase
|
70
|
+
def storage_accts(region = nil)
|
71
|
+
stripped_region = region.gsub(/\s+/, '').downcase
|
66
72
|
acct_names = []
|
67
|
-
if
|
68
|
-
|
73
|
+
if region.nil?
|
74
|
+
sdk.get_storage_accts.each do |acct|
|
69
75
|
acct_names << acct.name
|
70
76
|
end
|
71
77
|
else
|
72
|
-
(
|
78
|
+
(sdk.get_storage_accts.select { |acct| acct.region == stripped_region }).each do |acct|
|
73
79
|
acct_names << acct.name
|
74
80
|
end
|
75
81
|
end
|
76
82
|
acct_names
|
77
83
|
end
|
78
84
|
|
85
|
+
def resource_groups
|
86
|
+
sdk.rgs
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_connection(options = {})
|
90
|
+
sdk.rgs.each do |rg|
|
91
|
+
puts "#{rg}"
|
92
|
+
end
|
93
|
+
super(options)
|
94
|
+
end
|
95
|
+
|
96
|
+
def new_vm(attr = {})
|
97
|
+
AzureRMCompute.new(sdk: sdk)
|
98
|
+
end
|
99
|
+
|
100
|
+
def stop
|
101
|
+
power_off
|
102
|
+
deallocate
|
103
|
+
end
|
104
|
+
|
79
105
|
def provided_attributes
|
80
106
|
super.merge({ :ip => :provisioning_ip_address })
|
81
107
|
end
|
@@ -98,226 +124,144 @@ module ForemanAzureRM
|
|
98
124
|
subnets
|
99
125
|
end
|
100
126
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
def virtual_networks(location = nil)
|
107
|
-
if location.nil?
|
108
|
-
azure_network_service.virtual_networks
|
127
|
+
def virtual_networks(region = nil)
|
128
|
+
if region.nil?
|
129
|
+
sdk.vnets
|
109
130
|
else
|
110
|
-
|
111
|
-
|
131
|
+
stripped_region = region.gsub(/\s+/, '').downcase
|
132
|
+
sdk.vnets.select { |vnet| vnet.location == stripped_region }
|
112
133
|
end
|
113
134
|
end
|
114
135
|
|
115
|
-
def subnets(
|
116
|
-
|
136
|
+
def subnets(region = nil)
|
137
|
+
stripped_region = region.gsub(/\s+/, '').downcase
|
138
|
+
vnets = virtual_networks(stripped_region)
|
117
139
|
subnets = []
|
118
140
|
vnets.each do |vnet|
|
119
|
-
subnets.concat(
|
120
|
-
virtual_network_name: vnet.name).all)
|
141
|
+
subnets.concat(sdk.subnets(vnet.resource_group, vnet.name))
|
121
142
|
end
|
122
143
|
subnets
|
123
144
|
end
|
124
145
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
146
|
+
def new_interface(attr = {})
|
147
|
+
# WIP
|
148
|
+
# calls nic_cards method in adapter
|
149
|
+
# causes compute profiles issue
|
150
|
+
# NetworkModels::NetworkInterface.new
|
130
151
|
end
|
131
152
|
|
132
|
-
def
|
133
|
-
|
153
|
+
def vm_sizes(region)
|
154
|
+
sdk.list_vm_sizes(region)
|
134
155
|
end
|
135
156
|
|
136
|
-
def
|
137
|
-
|
157
|
+
def vm_instance_defaults
|
158
|
+
ActiveSupport::HashWithIndifferentAccess.new
|
138
159
|
end
|
139
160
|
|
140
161
|
def vms
|
141
162
|
container = VMContainer.new
|
142
|
-
|
143
|
-
|
144
|
-
|
163
|
+
# Load all vms
|
164
|
+
resource_groups.each do |rg|
|
165
|
+
sdk.list_vms(rg).each do |vm|
|
166
|
+
container.virtualmachines << AzureRMCompute.new(azure_vm: vm, sdk:sdk)
|
145
167
|
end
|
146
168
|
end
|
147
169
|
container
|
148
170
|
end
|
149
171
|
|
150
|
-
def
|
151
|
-
|
172
|
+
def setup_key_pair
|
173
|
+
require 'sshkey'
|
174
|
+
name = "foreman-#{id}#{Foreman.uuid}"
|
175
|
+
key = ::SSHKey.generate
|
176
|
+
build_key_pair :name => name, :secret => key.private_key, :public => key.ssh_public_key
|
152
177
|
end
|
153
178
|
|
154
179
|
def find_vm_by_uuid(uuid)
|
155
|
-
# TODO: Find a better way to handle this than loading and sorting through
|
156
|
-
# all VMs, which also requires that names be globally unique, instead of
|
157
|
-
# unique within a resource group
|
158
180
|
vm = vms.all.find { |vm| vm.name == uuid }
|
159
181
|
raise ActiveRecord::RecordNotFound unless vm.present?
|
160
182
|
vm
|
161
183
|
end
|
162
184
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
args[:interfaces_attributes].each do |nic, attrs|
|
167
|
-
attrs[:pubip_alloc] = attrs[:bridge]
|
168
|
-
attrs[:privip_alloc] = (attrs[:name] == 'false') ? false : true
|
169
|
-
pip_alloc = case attrs[:pubip_alloc]
|
170
|
-
when 'Static'
|
171
|
-
Fog::ARM::Network::Models::IPAllocationMethod::Static
|
172
|
-
when 'Dynamic'
|
173
|
-
Fog::ARM::Network::Models::IPAllocationMethod::Dynamic
|
174
|
-
when 'None'
|
175
|
-
nil
|
176
|
-
end
|
177
|
-
priv_ip_alloc = if attrs[:priv_ip_alloc]
|
178
|
-
Fog::ARM::Network::Models::IPAllocationMethod::Static
|
179
|
-
else
|
180
|
-
Fog::ARM::Network::Models::IPAllocationMethod::Dynamic
|
181
|
-
end
|
182
|
-
if pip_alloc.present?
|
183
|
-
pip = azure_network_service.public_ips.create(
|
184
|
-
name: "#{args[:vm_name]}-pip#{nic}",
|
185
|
-
resource_group: args[:resource_group],
|
186
|
-
location: formatted_location,
|
187
|
-
public_ip_allocation_method: pip_alloc
|
188
|
-
)
|
189
|
-
end
|
190
|
-
new_nic = azure_network_service.network_interfaces.create(
|
191
|
-
name: "#{args[:vm_name]}-nic#{nic}",
|
192
|
-
resource_group: args[:resource_group],
|
193
|
-
location: formatted_location,
|
194
|
-
subnet_id: attrs[:network],
|
195
|
-
public_ip_address_id: pip.present? ? pip.id : nil,
|
196
|
-
ip_configuration_name: 'ForemanIPConfiguration',
|
197
|
-
private_ip_allocation_method: priv_ip_alloc
|
198
|
-
)
|
199
|
-
nics << new_nic
|
200
|
-
end
|
201
|
-
nics
|
185
|
+
# user data support
|
186
|
+
def user_data_supported?
|
187
|
+
true
|
202
188
|
end
|
203
189
|
|
204
|
-
# Preferred behavior is to utilize Fog but Fog Azure RM
|
205
|
-
# does not currently support creating managed VMs
|
206
190
|
def create_vm(args = {})
|
207
|
-
args[:vm_name] = args[:name].split('.')[0]
|
208
|
-
nics
|
209
|
-
if args[:ssh_key_data].present?
|
191
|
+
args[:azure_vm][:vm_name] = args[:name].split('.')[0]
|
192
|
+
nics = create_nics(args)
|
193
|
+
if args[:azure_vm][:password].present? && !args[:azure_vm][:ssh_key_data].present?
|
194
|
+
sudoers_cmd = "$echo #{args[:azure_vm][:password]} | sudo -S echo '\"#{args[:azure_vm][:username]}\" ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/waagent"
|
195
|
+
if args[:azure_vm][:script_command].present?
|
196
|
+
# to run the script_cmd given through form
|
197
|
+
# as username
|
198
|
+
args[:azure_vm][:script_command] = sudoers_cmd + " ; su - \"#{args[:azure_vm][:username]}\" -c \"#{args[:azure_vm][:script_command]}\""
|
199
|
+
else
|
200
|
+
args[:azure_vm][:script_command] = sudoers_cmd
|
201
|
+
end
|
202
|
+
disable_password_auth = false
|
203
|
+
elsif args[:azure_vm][:ssh_key_data].present? && !args[:azure_vm][:password].present?
|
210
204
|
disable_password_auth = true
|
211
|
-
ssh_key_path = "/home/#{args[:username]}/.ssh/authorized_keys"
|
212
205
|
else
|
213
206
|
disable_password_auth = false
|
214
|
-
ssh_key_path = nil
|
215
207
|
end
|
216
|
-
vm
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
208
|
+
vm = create_managed_virtual_machine(
|
209
|
+
name: args[:azure_vm][:vm_name],
|
210
|
+
location: args[:azure_vm][:location],
|
211
|
+
resource_group: args[:azure_vm][:resource_group],
|
212
|
+
vm_size: args[:azure_vm][:vm_size],
|
213
|
+
username: args[:azure_vm][:username],
|
214
|
+
password: args[:azure_vm][:password],
|
215
|
+
ssh_key_data: args[:azure_vm][:ssh_key_data],
|
216
|
+
disable_password_authentication: disable_password_auth,
|
217
|
+
network_interface_card_ids: nics.map(&:id),
|
218
|
+
platform: args[:azure_vm][:platform],
|
219
|
+
vhd_path: args[:image_id],
|
220
|
+
os_disk_caching: args[:azure_vm][:os_disk_caching],
|
221
|
+
premium_os_disk: args[:azure_vm][:premium_os_disk],
|
222
|
+
custom_data: args[:user_data],
|
223
|
+
script_command: args[:azure_vm][:script_command],
|
224
|
+
script_uris: args[:azure_vm][:script_uris],
|
233
225
|
)
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
vm_hash[:script_command] = args[:script_command]
|
239
|
-
vm_hash[:script_uris] = args[:script_uris]
|
240
|
-
client.create_vm_extension(vm_hash)
|
241
|
-
client.servers.new vm_hash
|
242
|
-
# fog-azure-rm raises all ARM errors as RuntimeError
|
243
|
-
rescue Fog::Errors::Error, RuntimeError => e
|
226
|
+
create_vm_extension(args)
|
227
|
+
# return the vm object using azure_vm
|
228
|
+
return_vm = AzureRMCompute.new(azure_vm: vm, sdk: sdk)
|
229
|
+
rescue RuntimeError => e
|
244
230
|
Foreman::Logging.exception('Unhandled Azure RM error', e)
|
245
231
|
destroy_vm vm.id if vm
|
246
232
|
raise e
|
247
233
|
end
|
248
234
|
|
249
235
|
def destroy_vm(uuid)
|
236
|
+
#vm.azure_vm because that's the azure object and vm is the wrapper
|
250
237
|
vm = find_vm_by_uuid(uuid)
|
251
|
-
|
252
|
-
|
253
|
-
|
238
|
+
vm_name = vm.name
|
239
|
+
rg_name = vm.azure_vm.resource_group
|
240
|
+
os_disk = vm.azure_vm.storage_profile.os_disk
|
241
|
+
data_disks = vm.azure_vm.storage_profile.data_disks
|
254
242
|
nic_ids = vm.network_interface_card_ids
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
243
|
+
|
244
|
+
sdk.delete_vm(rg_name, vm_name)
|
245
|
+
|
246
|
+
nic_ids.each do |nic_id|
|
247
|
+
nic = sdk.vm_nic(rg_name, nic_id.split('/')[-1])
|
248
|
+
if nic.present?
|
249
|
+
public_ip = nic.ip_configurations.first.public_ipaddress
|
250
|
+
sdk.delete_nic(rg_name, nic_id.split('/')[-1])
|
251
|
+
if public_ip.present?
|
252
|
+
ip_id = public_ip.id
|
253
|
+
sdk.delete_pip(rg_name, ip_id.split('/')[-1])
|
254
|
+
end
|
265
255
|
end
|
266
256
|
end
|
267
|
-
|
268
|
-
|
269
|
-
client.managed_disks.get(vm.resource_group, disk.name).destroy
|
257
|
+
if os_disk.present?
|
258
|
+
sdk.delete_disk(rg_name, os_disk.name)
|
270
259
|
end
|
271
260
|
|
261
|
+
true
|
272
262
|
rescue ActiveRecord::RecordNotFound
|
273
|
-
|
263
|
+
logger.info "Could not find the selected vm."
|
274
264
|
true
|
275
265
|
end
|
276
|
-
|
277
|
-
protected
|
278
|
-
|
279
|
-
def client
|
280
|
-
@client ||= Fog::Compute.new(
|
281
|
-
:provider => 'AzureRM',
|
282
|
-
:tenant_id => tenant,
|
283
|
-
:client_id => app_ident,
|
284
|
-
:client_secret => secret_key,
|
285
|
-
:subscription_id => sub_id,
|
286
|
-
:environment => 'AzureCloud'
|
287
|
-
)
|
288
|
-
end
|
289
|
-
|
290
|
-
def rg_client
|
291
|
-
# noinspection RubyArgCount
|
292
|
-
@rg_client ||= Fog::Resources::AzureRM.new(
|
293
|
-
tenant_id: tenant,
|
294
|
-
client_id: app_ident,
|
295
|
-
client_secret: secret_key,
|
296
|
-
subscription_id: sub_id,
|
297
|
-
:environment => 'AzureCloud'
|
298
|
-
)
|
299
|
-
end
|
300
|
-
|
301
|
-
def storage_client
|
302
|
-
@storage_client ||= Fog::Storage.new(
|
303
|
-
:provider => 'AzureRM',
|
304
|
-
:tenant_id => tenant,
|
305
|
-
:client_id => app_ident,
|
306
|
-
:client_secret => secret_key,
|
307
|
-
:subscription_id => sub_id,
|
308
|
-
:environment => 'AzureCloud'
|
309
|
-
)
|
310
|
-
end
|
311
|
-
|
312
|
-
def azure_network_service
|
313
|
-
# noinspection RubyArgCount
|
314
|
-
@azure_network_service ||= Fog::Network::AzureRM.new(
|
315
|
-
:tenant_id => tenant,
|
316
|
-
:client_id => app_ident,
|
317
|
-
:client_secret => secret_key,
|
318
|
-
:subscription_id => sub_id,
|
319
|
-
:environment => 'AzureCloud'
|
320
|
-
)
|
321
|
-
end
|
322
266
|
end
|
323
|
-
end
|
267
|
+
end
|