foreman_orchestration 0.0.1

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.
Files changed (40) hide show
  1. data/LICENSE +619 -0
  2. data/README.md +38 -0
  3. data/Rakefile +47 -0
  4. data/app/assets/javascripts/foreman_orchestration/stacks.js +129 -0
  5. data/app/assets/stylesheets/foreman_orchestration/stacks.css.scss +3 -0
  6. data/app/assets/stylesheets/foreman_orchestration/templates.css.scss +4 -0
  7. data/app/controllers/concerns/foreman_orchestration/compute_resources_controller_extensions.rb +17 -0
  8. data/app/controllers/foreman_orchestration/stack_templates_controller.rb +57 -0
  9. data/app/controllers/foreman_orchestration/stacks_controller.rb +75 -0
  10. data/app/controllers/foreman_orchestration/tenants_controller.rb +26 -0
  11. data/app/helpers/concerns/foreman_orchestration/layout_helper_extensions.rb +22 -0
  12. data/app/models/concerns/foreman_orchestration/openstack_extensions.rb +51 -0
  13. data/app/models/foreman_orchestration/orchestration_client.rb +51 -0
  14. data/app/models/foreman_orchestration/stack.rb +91 -0
  15. data/app/models/foreman_orchestration/stack_template.rb +31 -0
  16. data/app/views/dashboard/_foreman_orchestration_widget.html.erb +2 -0
  17. data/app/views/foreman_orchestration/stack_templates/_form.html.erb +8 -0
  18. data/app/views/foreman_orchestration/stack_templates/_with_params.html.erb +31 -0
  19. data/app/views/foreman_orchestration/stack_templates/edit.html.erb +3 -0
  20. data/app/views/foreman_orchestration/stack_templates/index.html.erb +27 -0
  21. data/app/views/foreman_orchestration/stack_templates/new.html.erb +3 -0
  22. data/app/views/foreman_orchestration/stacks/_form.html.erb +40 -0
  23. data/app/views/foreman_orchestration/stacks/all.html.erb +40 -0
  24. data/app/views/foreman_orchestration/stacks/index.html.erb +49 -0
  25. data/app/views/foreman_orchestration/stacks/new.html.erb +6 -0
  26. data/app/views/foreman_orchestration/tenants/_for_select.html.erb +21 -0
  27. data/config/routes.rb +30 -0
  28. data/db/migrate/20151112145323_create_stack_templates.rb +10 -0
  29. data/lib/foreman_orchestration.rb +4 -0
  30. data/lib/foreman_orchestration/engine.rb +71 -0
  31. data/lib/foreman_orchestration/version.rb +3 -0
  32. data/lib/tasks/foreman_orchestration_tasks.rake +39 -0
  33. data/locale/Makefile +62 -0
  34. data/locale/en/foreman_orchestration.po +19 -0
  35. data/locale/foreman_orchestration.pot +19 -0
  36. data/locale/gemspec.rb +2 -0
  37. data/test/factories/foreman_orchestration_factories.rb +5 -0
  38. data/test/test_plugin_helper.rb +6 -0
  39. data/test/unit/foreman_orchestration_test.rb +11 -0
  40. metadata +137 -0
@@ -0,0 +1,51 @@
1
+ module ForemanOrchestration
2
+ class OrchestrationClient
3
+ extend Forwardable
4
+ def_delegators :@tenant, :id, :name, :description, :enabled
5
+
6
+ def initialize(tenant, credentials)
7
+ @tenant = tenant
8
+ @credentials = credentials
9
+ end
10
+
11
+ def stacks
12
+ client.stacks
13
+ end
14
+
15
+ def create_stack(stack)
16
+ params = {
17
+ files: {},
18
+ disable_rollback: true,
19
+ parameters: stack.parameters,
20
+ stack_name: stack.name,
21
+ environment: {},
22
+ template: stack.template_body
23
+ }
24
+ client.create_stack(params)
25
+ end
26
+
27
+ def delete_stack(stack)
28
+ fog_model = fog_model_for_stack(stack.id)
29
+ client.delete_stack(fog_model)
30
+ end
31
+
32
+ private
33
+
34
+ def credentials
35
+ @credentials.merge(openstack_tenant: name)
36
+ end
37
+
38
+ def client
39
+ @client ||= Fog::Orchestration.new(credentials)
40
+ end
41
+
42
+ def fog_model_for_stack(id)
43
+ model = stacks.find { |s| s.id == id }
44
+ if model
45
+ model
46
+ else
47
+ raise "Cannot find a stack with the specified id: #{id}"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,91 @@
1
+ module ForemanOrchestration
2
+ class Stack
3
+ include ActiveModel::Validations
4
+ include ActiveModel::Conversion
5
+
6
+ attr_accessor :id, :compute_resource_id, :tenant_id, :name, :template_id
7
+ attr_accessor :parameters
8
+
9
+ validates :compute_resource_id, presence: true
10
+ validates :tenant_id, presence: true
11
+ validates :name, presence: true, format: {with: /^[a-zA-Z][a-zA-Z0-9_.-]*$/}
12
+ validates :template_id, presence: true
13
+
14
+ def initialize(params = {})
15
+ @id = params[:id]
16
+ @name = params[:name]
17
+ @template_id = params[:template_id]
18
+ @parameters = params[:parameters] || {}
19
+ compute_resource_params(params)
20
+ tenant_params(params)
21
+ end
22
+
23
+ def persisted?
24
+ false
25
+ end
26
+
27
+ def save
28
+ if valid?
29
+ tenant.create_stack(self)
30
+ else
31
+ false
32
+ end
33
+ end
34
+
35
+ def destroy
36
+ tenant.delete_stack(self)
37
+ end
38
+
39
+ def template
40
+ if template_id
41
+ @template ||= StackTemplate.find(template_id)
42
+ end
43
+ end
44
+
45
+ def template_body
46
+ if template
47
+ YAML.load(template.template)
48
+ end
49
+ end
50
+
51
+ def tenant
52
+ if tenant_id && compute_resource
53
+ @tenant ||= compute_resource.orchestration_client_for(tenant_id)
54
+ end
55
+ end
56
+
57
+ def compute_resource
58
+ if compute_resource_id
59
+ @compute_resource ||= Foreman::Model::Openstack.find(compute_resource_id)
60
+ end
61
+ end
62
+
63
+ def tenants
64
+ # TODO: find a better place for this method
65
+ if compute_resource
66
+ @tenants ||= compute_resource.orchestration_clients
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def tenant_params(params)
73
+ if params[:tenant]
74
+ @tenant = params[:tenant]
75
+ @tenant_id = @tenant.id
76
+ else
77
+ @tenant_id = params[:tenant_id]
78
+ end
79
+ end
80
+
81
+ def compute_resource_params(params)
82
+ if params[:compute_resource]
83
+ @compute_resource = params[:compute_resource]
84
+ @compute_resource_id = @compute_resource.id
85
+ else
86
+ @compute_resource_id = params[:compute_resource_id]
87
+ end
88
+ end
89
+ end
90
+ end
91
+
@@ -0,0 +1,31 @@
1
+ module ForemanOrchestration
2
+ class StackTemplate < ActiveRecord::Base
3
+ attr_accessible :name, :template
4
+
5
+ validates :name, presence: true
6
+ validates :template, presence: true
7
+
8
+ validate do
9
+ begin
10
+ YAML.load(template)
11
+ rescue Psych::SyntaxError
12
+ errors.add(:template, 'Provided template is not a valid YAML file')
13
+ end
14
+ end
15
+
16
+ def self.for_select_tag
17
+ select([:id, :name]).order(:name).all
18
+ end
19
+
20
+ def parameters
21
+ @template_parameters ||= parse_parameters
22
+ end
23
+
24
+ private
25
+
26
+ def parse_parameters
27
+ yaml = YAML.load(template)
28
+ yaml['parameters']
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,2 @@
1
+ <h4 class="header ca"><%= _('ForemanOrchestration') %></h4>
2
+ <%= _('Widget content') %>
@@ -0,0 +1,8 @@
1
+ <%= form_for @template, url: (@template.new_record? ? stack_templates_path : stack_template_path(@template)) do |f| %>
2
+ <%= base_errors_for @template %>
3
+
4
+ <%= text_f f, :name %>
5
+ <%= textarea_f f, :template, size: 'col-md-8', rows: 20 %>
6
+
7
+ <%= submit_or_cancel f %>
8
+ <% end %>
@@ -0,0 +1,31 @@
1
+ <%= hidden_field_tag 'foreman_orchestration_stack[template_id]', template.id %>
2
+ <h4>Review template body:</h4>
3
+ <div class="clearfix">
4
+ <div class="form-group">
5
+ <div class="row">
6
+ <div class="col-md-8">
7
+ <pre class="stack-template-body"><%= h template.template %></pre>
8
+ </div>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ <% unless template.parameters.empty? %>
13
+ <h4>Specify parameters for the chosen template:</h4>
14
+ <% template.parameters.each do |key, value| %>
15
+ <div class="clearfix">
16
+ <div class="form-group">
17
+ <div class="row">
18
+ <div class="col-md-2 control-label">
19
+ <%= label_tag key, key %>
20
+ </div>
21
+ <div class="col-md-6">
22
+ <%= text_field_tag "foreman_orchestration_stack[parameters[#{key}]]",
23
+ parameters[key] || value['default'],
24
+ class: 'form-control' %>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ <% end %>
30
+ <% end %>
31
+ <%= submit_tag 'Submit', class: 'btn btn-primary' %>
@@ -0,0 +1,3 @@
1
+ <% title(_("Edit %s") % @template.name) %>
2
+
3
+ <%= render 'form' %>
@@ -0,0 +1,27 @@
1
+ <% title _("Templates") %>
2
+ <% title_actions display_link_if_authorized(_("New Template"), hash_for_new_stack_template_path), help_path %>
3
+
4
+ <table class="table table-bordered table-striped table-two-pane">
5
+ <thead>
6
+ <tr>
7
+ <th>Name</th>
8
+ <th>Created at</th>
9
+ <th>Updated at</th>
10
+ <th>Action</th>
11
+ </tr>
12
+ </thead>
13
+ <tbody>
14
+ <% for template in @templates %>
15
+ <tr>
16
+ <td><%= h template.name %></td>
17
+ <td><%= template.created_at %></td>
18
+ <td><%= template.updated_at %></td>
19
+ <td>
20
+ <%= action_buttons(
21
+ display_link_if_authorized(_("Edit"), hash_for_edit_stack_template_path(:id => template).merge(:auth_object => template, :authorizer => authorizer)),
22
+ display_delete_if_authorized(hash_for_stack_template_path(:id => template).merge(:auth_object => template, :authorizer => authorizer), :data => { :confirm => 'Are you sure?'}, :action => :destroy)) %>
23
+ </td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+ </table>
@@ -0,0 +1,3 @@
1
+ <% title _("New template") %>
2
+
3
+ <%= render 'form' %>
@@ -0,0 +1,40 @@
1
+ <%= form_for @stack, url: stacks_path do |f| %>
2
+ <%= base_errors_for @stack %>
3
+
4
+ <%= selectable_f f, :compute_resource_id,
5
+ options_from_collection_for_select(@compute_resources, 'id', 'name', @stack.compute_resource_id),
6
+ {:include_blank => _('Choose a compute resource')},
7
+ {:label => _('Compute resource'),
8
+ :onchange => 'stacksNewComputeResourceSelected(this);',
9
+ :'data-url' => new_stack_path,
10
+ :help_inline => :indicator, :required => true} %>
11
+
12
+ <div id="tenants-list-container">
13
+ <div id="tenants-list">
14
+ <% if @stack.tenants %>
15
+ <%= selectable_f f, :tenant_id,
16
+ options_from_collection_for_select(@stack.tenants, 'id', 'name', @stack.tenant_id),
17
+ {:include_blank => _('Choose a tenant')},
18
+ {:label => _('Tenant'), :required => true} %>
19
+
20
+ <% end %>
21
+ </div>
22
+ </div>
23
+
24
+ <div id="main-stack-parameters" class="<%= 'hidden' unless @stack.compute_resource_id %>">
25
+ <%= text_f f, :name, required: true, label: _('Stack name'),
26
+ help_inline: _('This value must match the following pattern: ^[a-zA-Z][a-zA-Z0-9_.-]*$') %>
27
+ <%= selectable_f f, :template_id,
28
+ options_from_collection_for_select(ForemanOrchestration::StackTemplate.for_select_tag, 'id', 'name', @stack.template_id),
29
+ {include_blank: _('Choose a template')},
30
+ {:label => _('Template'), :'data-url' => with_params_stack_template_path(':template_id'),
31
+ :onchange => 'stacksLoadTemplateWithParams(this)', required: true,
32
+ :help_inline => :indicator} %>
33
+ <div id="template-with-params">
34
+ <% if @stack.template %>
35
+ <%= render partial: 'foreman_orchestration/stack_templates/with_params',
36
+ locals: {template: @stack.template, parameters: @stack.parameters} %>
37
+ <% end %>
38
+ </div>
39
+ </div>
40
+ <% end %>
@@ -0,0 +1,40 @@
1
+ <% javascript 'foreman_orchestration/stacks' %>
2
+ <% stylesheet 'foreman_orchestration/stacks' %>
3
+
4
+ <%= form_for :stack do %>
5
+ <% title _("All Stacks") %>
6
+
7
+ <% if @compute_resources.empty? %>
8
+ <div class="alert alert-info">
9
+ There are no compute resources. But you can create a new one
10
+ <%= link_to 'here', new_compute_resource_path %>
11
+ </div>
12
+ <% else %>
13
+ <%= selectable_tag :compute_resource,
14
+ options_from_collection_for_select(@compute_resources, 'id', 'name', @compute_resource.try(:id)),
15
+ {:label => _('Compute resource'),
16
+ :onchange => 'stacksComputeResourceSelected(this);',
17
+ :help_inline => :indicator, :include_blank => true,
18
+ :'data-url' => for_select_compute_resource_tenants_path(':id'),
19
+ :'data-default-value' => @compute_resource.try(:id) } do %>
20
+ <%= content_tag :button, :id => 'set-default-compute-resource',
21
+ :type => 'button', :class => 'btn btn-primary',
22
+ :disabled => 'disabled', :onclick => 'setDefaultComputeResource(this);',
23
+ :'data-url' => default_compute_resource_path(':id'),
24
+ :'data-disable-with' => 'Processing...' do -%>
25
+ Set as default
26
+ <% end -%>
27
+ <% end %>
28
+ <% end %>
29
+
30
+ <div id="tenants-list">
31
+ <% if @tenants %>
32
+ <%= render partial: 'foreman_orchestration/tenants/for_select' %>
33
+ <% end %>
34
+ </div>
35
+ <div id="stacks-list">
36
+ <% if @stacks %>
37
+ <%= render template: 'foreman_orchestration/stacks/index', layout: nil %>
38
+ <% end %>
39
+ </div>
40
+ <% end %>
@@ -0,0 +1,49 @@
1
+ <div class="row">
2
+ <div id="title_action" class="col-sm-12">
3
+ <%= link_to(
4
+ 'New Stack',
5
+ new_stack_path(
6
+ :compute_resource_id => @compute_resource.id,
7
+ :tenant_id => @tenant.id
8
+ ),
9
+ :class => 'btn btn-success'
10
+ ) %>
11
+ </div>
12
+ </div>
13
+
14
+ <%# add: sorting, filters, checkboxes %>
15
+ <table class="table table-bordered table-striped table-two-pane">
16
+ <thead>
17
+ <tr>
18
+ <th>Name</th>
19
+ <th>Description</th>
20
+ <th>Status</th>
21
+ <th>Creation time</th>
22
+ <th>Updated time</th>
23
+ <th>Action</th>
24
+ </tr>
25
+ </thead>
26
+ <tbody>
27
+ <% for stack in @stacks %>
28
+ <tr>
29
+ <td><%= h stack.stack_name %></td>
30
+ <td><%= h stack.description %></td>
31
+ <td><%= h stack.stack_status %></td>
32
+ <td><%= stack.creation_time %></td>
33
+ <td><%= stack.updated_time %></td>
34
+ <td>
35
+ <%= link_to(
36
+ 'Delete',
37
+ compute_resource_tenant_stack_path(
38
+ :compute_resource_id => @compute_resource.id,
39
+ :tenant_id => @tenant.id,
40
+ :id => stack.id
41
+ ),
42
+ :method => :delete, :confirm => 'Are you sure?',
43
+ :class => 'delete'
44
+ )%>
45
+ </td>
46
+ </tr>
47
+ <% end %>
48
+ </tbody>
49
+ </table>
@@ -0,0 +1,6 @@
1
+ <% javascript 'foreman_orchestration/stacks' %>
2
+ <% stylesheet 'foreman_orchestration/templates' %>
3
+
4
+ <% title _("New stack") %>
5
+
6
+ <%= render 'form' %>
@@ -0,0 +1,21 @@
1
+ <% if @tenants.empty? %>
2
+ <div class="alert alert-info">
3
+ Selected compute resource doesn't have any tenant
4
+ </div>
5
+ <% else %>
6
+ <%= selectable_tag :tenant,
7
+ options_from_collection_for_select(@tenants, 'id', 'name', @tenant.try(:id)),
8
+ {:label => _('Tenant'),
9
+ :onchange => 'stacksTenantSelected(this);',
10
+ :'data-url' => compute_resource_tenant_stacks_path(@compute_resource, ':id'),
11
+ :'data-default-value' => @compute_resource.default_tenant_id,
12
+ :help_inline => :indicator, :include_blank => true} do %>
13
+ <%= content_tag :button, :id => 'set-default-tenant',
14
+ :type => 'button', :class => 'btn btn-primary',
15
+ :disabled => 'disabled', :onclick => 'setDefaultTenant(this);',
16
+ :'data-url' => default_compute_resource_tenant_path(@compute_resource, ':id'),
17
+ :'data-disable-with' => 'Processing...' do -%>
18
+ Set as default
19
+ <% end -%>
20
+ <% end %>
21
+ <% end %>
data/config/routes.rb ADDED
@@ -0,0 +1,30 @@
1
+ Rails.application.routes.draw do
2
+ resources :compute_resources, :only => [] do
3
+ member do
4
+ post :default
5
+ end
6
+ scope :module => :foreman_orchestration do
7
+ resources :tenants, :only => [:index] do
8
+ collection do
9
+ get :for_select
10
+ end
11
+ member do
12
+ post :default
13
+ end
14
+ resources :stacks, :only => [:index, :destroy]
15
+ end
16
+ end
17
+ end
18
+ scope :module => :foreman_orchestration do
19
+ resources :stacks, :only => [:new, :create] do
20
+ collection do
21
+ get :all
22
+ end
23
+ end
24
+ resources :stack_templates do
25
+ member do
26
+ get :with_params
27
+ end
28
+ end
29
+ end
30
+ end