-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
With this commit we implement Deployment Template provisioning aka OrchestrationStack provisioning. Provisioning is supposed to be started from the ServiceCatalogItem of type "Orchestration" which admin is expected to provide with a template deployment JSON. User is then prompted to input template parameter values upon item ordering. Signed-off-by: Miha Pleško <[email protected]>
- Loading branch information
1 parent
5cb7d06
commit ab96d73
Showing
10 changed files
with
676 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
...ls/manageiq/providers/azure_stack/cloud_manager/orchestration_service_option_converter.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
78 changes: 78 additions & 0 deletions
78
app/models/manageiq/providers/azure_stack/cloud_manager/orchestration_stack.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
2 changes: 1 addition & 1 deletion
2
app/models/manageiq/providers/azure_stack/cloud_manager/orchestration_stack/status.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
app/models/manageiq/providers/azure_stack/cloud_manager/orchestration_template.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
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.\n'\ | ||
'WARNING: Complete mode will delete all resources from '\ | ||
'the group that are not in the template.' | ||
choices = {'Incremental' => 'Incremental (Default)', 'Complete' => 'Complete (Delete other resources in the group)'} | ||
OrchestrationTemplate::OrchestrationParameter.new( | ||
:name => 'deploy_mode', | ||
:label => 'Mode', | ||
:data_type => 'string', | ||
:description => description, | ||
: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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.