foreman_azure_rm 1.3.1 → 2.0.0.pre1
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/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
|
+

|
|
32
|
+
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
|
|
37
|
+

|
|
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
|