Skip to content

Commit

Permalink
Merge pull request ManageIQ#7 from xlab-si/provision
Browse files Browse the repository at this point in the history
OrchestrationStack provisioning
  • Loading branch information
agrare authored Jul 19, 2019
2 parents 88e2155 + dbe0b34 commit 69165eb
Show file tree
Hide file tree
Showing 10 changed files with 678 additions and 1 deletion.
2 changes: 2 additions & 0 deletions app/models/manageiq/providers/azure_stack/cloud_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class ManageIQ::Providers::AzureStack::CloudManager < ManageIQ::Providers::Cloud
require_nested :RefreshWorker
require_nested :Vm
require_nested :OrchestrationStack
require_nested :OrchestrationTemplate
require_nested :OrchestrationServiceOptionConverter

include ManageIQ::Providers::AzureStack::ManagerMixin

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module ManageIQ::Providers
class AzureStack::CloudManager::OrchestrationServiceOptionConverter < ::ServiceOrchestration::OptionConverter
def stack_create_options
{
:parameters => stack_parameters,
:resource_group => @dialog_options['dialog_resource_group'].presence || @dialog_options['dialog_new_resource_group'],
:mode => @dialog_options['dialog_deploy_mode']
}
end
end
end
Original file line number Diff line number Diff line change
@@ -1,7 +1,85 @@
class ManageIQ::Providers::AzureStack::CloudManager::OrchestrationStack < ManageIQ::Providers::CloudManager::OrchestrationStack
require_nested :Status

def self.raw_create_stack(ems, stack_name, template, options = {})
create_or_update_stack(ems, stack_name, template, options)
rescue => err
$azure_stack_log.error("stack=[#{stack_name}], error: #{err}")
raise MiqException::MiqOrchestrationProvisionError, err.to_s, err.backtrace
end

def raw_update_stack(template, options)
self.class.create_or_update_stack(ext_management_system, name, template, options)
rescue => err
$azure_stack_log.error("stack=[#{name}], error: #{err}")
raise MiqException::MiqOrchestrationUpdateError, err.to_s, err.backtrace
end

def raw_delete_stack
$azure_stack_log.debug("Deleting orchestration stack (ems=#{ext_management_system.name}, stack_name=#{name})")
ext_management_system.with_provider_connection(:service => :Resources) do |client|
# TODO(miha-plesko): this only deletes the deployment leaving all resources still there. Need to remove those too.
client.deployments.delete(resource_group, name)
end
rescue => err
$azure_stack_log.error("stack=[#{name}], error: #{err}")
raise MiqException::MiqOrchestrationDeleteError, err.to_s, err.backtrace
end

def raw_status
ext_management_system.with_provider_connection(:service => :Resources) do |client|
state = client.deployments.get(resource_group, name).properties.provisioning_state.downcase
Status.new(state, state == 'succeeded' ? 'OK' : failure_reason(client))
end
rescue MsRestAzure::AzureOperationError => err
$azure_stack_log.error("stack=[#{name}], error: #{err}")
raise MiqException::MiqOrchestrationStackNotExistError, err.to_s, err.backtrace if err&.error_code == 'DeploymentNotFound'

raise MiqException::MiqOrchestrationStatusError, err.to_s, err.backtrace
rescue => err
$azure_stack_log.error("stack=[#{name}], error: #{err}")
raise MiqException::MiqOrchestrationStatusError, err.to_s, err.backtrace
end

def self.build_ems_ref(ems, resource_group, stack_name)
"/subscriptions/#{ems.subscription}"\
"/resourcegroups/#{resource_group}"\
"/providers/microsoft.resources/deployments/#{stack_name}".downcase
end

def self.display_name(number = 1)
n_('Orchestration Stack (Microsoft AzureStack)', 'Orchestration Stacks (Microsoft AzureStack)', number)
end

def self.create_or_update_stack(ems, stack_name, template, options)
$azure_stack_log.debug("Creating/Updating orchestration stack [ems=#{ems.name}, " \
"stack_name=#{stack_name}, template=#{template.name}, options=#{options}]")
ems.with_provider_connection(:service => :Resources) do |client|
# Ensure resource group exists because deployment assumes existing one.
client.resource_groups.create_or_update(
options[:resource_group],
client.model_classes.resource_group.new.tap { |g| g.location = ems.provider_region }
)
# Deploy into the resource group.
deployment = client.model_classes.deployment.new
deployment.properties = client.model_classes.deployment_properties.new.tap do |props|
props.template = JSON.parse(template.content)
props.mode = options[:mode]
props.parameters = options[:parameters].transform_values! { |v| { 'value' => v } }
end

client.deployments.create_or_update_async(options[:resource_group], stack_name, deployment)
build_ems_ref(ems, options[:resource_group], stack_name)
end
end

private

def failure_reason(client)
operations = client.deployment_operations.list(resource_group, name)
msg = operations.detect { |op| op.properties.provisioning_state.downcase != 'succeeded' }&.properties&.status_message
return nil unless msg && (reason = msg['error'])

"[#{reason['code']}][#{reason['target']}] #{reason['message']}"
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class ManageIQ::Providers::Azure::CloudManager::OrchestrationStack::Status < ::OrchestrationStack::Status
class ManageIQ::Providers::AzureStack::CloudManager::OrchestrationStack::Status < ::OrchestrationStack::Status
def succeeded?
status.downcase == "succeeded"
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
class ManageIQ::Providers::AzureStack::CloudManager::OrchestrationTemplate < ::OrchestrationTemplate
def format
'json'.freeze
end

def parameter_groups
[OrchestrationTemplate::OrchestrationParameterGroup.new(
:label => 'Parameters',
:parameters => parameters
)]
end

def parameters
raw_parameters = JSON.parse(content)['parameters']
(raw_parameters || {}).collect do |key, val|
parameter = OrchestrationTemplate::OrchestrationParameter.new(
:name => key,
:label => key.titleize,
:data_type => val['type'],
:default_value => val['defaultValue'],
:hidden => val['type'].casecmp('securestring').zero?,
:required => true
)

add_metadata(parameter, val['metadata'])
add_allowed_values(parameter, val['allowedValues'])

parameter
end
end

def deployment_options(_manager_class = nil)
super << resource_group_opt << new_resource_group_opt << mode_opt
end

def self.eligible_manager_types
[ManageIQ::Providers::AzureStack::CloudManager]
end

# return the parsing error message if not valid JSON; otherwise nil
def validate_format
JSON.parse(content) && nil if content
rescue JSON::ParserError => err
err.message
end

def self.display_name(number = 1)
n_('AzureStack Template', 'AzureStack Templates', number)
end

private

def mode_opt
description = 'Select deployment mode.'\
'WARNING: Complete mode will delete all resources from '\
'the group that are not in the template.'
choices = {'Incremental' => 'Incremental', 'Complete' => 'Complete'}
OrchestrationTemplate::OrchestrationParameter.new(
:name => 'deploy_mode',
:label => 'Mode',
:data_type => 'string',
:description => description,
:default_value => 'Incremental',
:required => true,
:constraints => [OrchestrationTemplate::OrchestrationParameterAllowed.new(:allowed_values => choices)]
)
end

def resource_group_opt
OrchestrationTemplate::OrchestrationParameter.new(
:name => 'resource_group',
:label => 'Existing Resource Group',
:data_type => 'string',
:description => 'Select an existing resource group for deployment',
:constraints => [
OrchestrationTemplate::OrchestrationParameterAllowedDynamic.new(
:fqname => '/Cloud/Orchestration/Operations/Methods/Available_Resource_Groups'
)
]
)
end

def new_resource_group_opt
OrchestrationTemplate::OrchestrationParameter.new(
:name => 'new_resource_group',
:label => '(or) New Resource Group',
:data_type => 'string',
:description => 'Create a new resource group upon deployment',
:constraints => [
OrchestrationTemplate::OrchestrationParameterPattern.new(:pattern => '^[A-Za-z][A-Za-z0-9\-_]*$')
]
)
end

def add_metadata(parameter, metadata)
return unless metadata

parameter.description = metadata['description']
end

def add_allowed_values(parameter, vals)
return unless vals

constraint = OrchestrationTemplate::OrchestrationParameterAllowed.new(:allowed_values => vals)
parameter.constraints << constraint
end
end
5 changes: 5 additions & 0 deletions spec/factories/orchestration_stack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FactoryBot.define do
factory :orchestration_stack_azure_stack,
:parent => :orchestration_stack,
:class => "ManageIQ::Providers::AzureStack::CloudManager::OrchestrationStack"
end
7 changes: 7 additions & 0 deletions spec/factories/orchestration_template.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryBot.define do
factory :orchestration_template_azure_stack,
:parent => :orchestration_template,
:class => 'ManageIQ::Providers::AzureStack::CloudManager::OrchestrationTemplate' do
content { File.read(ManageIQ::Providers::AzureStack::Engine.root.join('spec', 'fixtures', 'orchestration_templates', 'deployment.json')) }
end
end
Loading

0 comments on commit 69165eb

Please sign in to comment.