-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OrchestrationStack provisioning #7
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you able to distinguish stack_not_exist vs stack_status_error? This difference is critical for debugging provisioning issues. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, updated. |
||
$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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This little typo almost made me mad because orch stack was getting "Succeeded" status upon polling but |
||
def succeeded? | ||
status.downcase == "succeeded" | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
class ManageIQ::Providers::AzureStack::CloudManager::OrchestrationTemplate < ::OrchestrationTemplate | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This entire file is a copy-paste from Azure provider, not sure whether I should explicitly give credit in a comment or something? |
||
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 |
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 |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
raw_update_stack
method is missing in this class?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added it now, thanks. Didn't plan to do it all in one PR but actually it's not that complicated.