foreman_orchestration 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +619 -0
- data/README.md +38 -0
- data/Rakefile +47 -0
- data/app/assets/javascripts/foreman_orchestration/stacks.js +129 -0
- data/app/assets/stylesheets/foreman_orchestration/stacks.css.scss +3 -0
- data/app/assets/stylesheets/foreman_orchestration/templates.css.scss +4 -0
- data/app/controllers/concerns/foreman_orchestration/compute_resources_controller_extensions.rb +17 -0
- data/app/controllers/foreman_orchestration/stack_templates_controller.rb +57 -0
- data/app/controllers/foreman_orchestration/stacks_controller.rb +75 -0
- data/app/controllers/foreman_orchestration/tenants_controller.rb +26 -0
- data/app/helpers/concerns/foreman_orchestration/layout_helper_extensions.rb +22 -0
- data/app/models/concerns/foreman_orchestration/openstack_extensions.rb +51 -0
- data/app/models/foreman_orchestration/orchestration_client.rb +51 -0
- data/app/models/foreman_orchestration/stack.rb +91 -0
- data/app/models/foreman_orchestration/stack_template.rb +31 -0
- data/app/views/dashboard/_foreman_orchestration_widget.html.erb +2 -0
- data/app/views/foreman_orchestration/stack_templates/_form.html.erb +8 -0
- data/app/views/foreman_orchestration/stack_templates/_with_params.html.erb +31 -0
- data/app/views/foreman_orchestration/stack_templates/edit.html.erb +3 -0
- data/app/views/foreman_orchestration/stack_templates/index.html.erb +27 -0
- data/app/views/foreman_orchestration/stack_templates/new.html.erb +3 -0
- data/app/views/foreman_orchestration/stacks/_form.html.erb +40 -0
- data/app/views/foreman_orchestration/stacks/all.html.erb +40 -0
- data/app/views/foreman_orchestration/stacks/index.html.erb +49 -0
- data/app/views/foreman_orchestration/stacks/new.html.erb +6 -0
- data/app/views/foreman_orchestration/tenants/_for_select.html.erb +21 -0
- data/config/routes.rb +30 -0
- data/db/migrate/20151112145323_create_stack_templates.rb +10 -0
- data/lib/foreman_orchestration.rb +4 -0
- data/lib/foreman_orchestration/engine.rb +71 -0
- data/lib/foreman_orchestration/version.rb +3 -0
- data/lib/tasks/foreman_orchestration_tasks.rake +39 -0
- data/locale/Makefile +62 -0
- data/locale/en/foreman_orchestration.po +19 -0
- data/locale/foreman_orchestration.pot +19 -0
- data/locale/gemspec.rb +2 -0
- data/test/factories/foreman_orchestration_factories.rb +5 -0
- data/test/test_plugin_helper.rb +6 -0
- data/test/unit/foreman_orchestration_test.rb +11 -0
- metadata +137 -0
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# ForemanOrchestration
|
2
|
+
|
3
|
+
*Introdction here*
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
See [How_to_Install_a_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Plugin)
|
8
|
+
for how to install Foreman plugins
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
*Usage here*
|
13
|
+
|
14
|
+
## TODO
|
15
|
+
|
16
|
+
*Todo list here*
|
17
|
+
|
18
|
+
## Contributing
|
19
|
+
|
20
|
+
Fork and send a Pull Request. Thanks!
|
21
|
+
|
22
|
+
## Copyright
|
23
|
+
|
24
|
+
Copyright (c) *year* *your name*
|
25
|
+
|
26
|
+
This program is free software: you can redistribute it and/or modify
|
27
|
+
it under the terms of the GNU General Public License as published by
|
28
|
+
the Free Software Foundation, either version 3 of the License, or
|
29
|
+
(at your option) any later version.
|
30
|
+
|
31
|
+
This program is distributed in the hope that it will be useful,
|
32
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
33
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
34
|
+
GNU General Public License for more details.
|
35
|
+
|
36
|
+
You should have received a copy of the GNU General Public License
|
37
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
38
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'ForemanOrchestration'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
task default: :test
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'rubocop/rake_task'
|
40
|
+
RuboCop::RakeTask.new
|
41
|
+
rescue => _
|
42
|
+
puts 'Rubocop not loaded.'
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default do
|
46
|
+
Rake::Task['rubocop'].execute
|
47
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
function updateDefaultEntityButton(selector, defaultValue, currentValue) {
|
2
|
+
var $button = $(selector);
|
3
|
+
if (currentValue == defaultValue || currentValue == '') {
|
4
|
+
$button.prop('disabled', true);
|
5
|
+
} else {
|
6
|
+
$button.prop('disabled', false);
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
function stacksComputeResourceSelected(item) {
|
11
|
+
var $item = $(item);
|
12
|
+
var computeResource = $item.val();
|
13
|
+
if (computeResource === '') {
|
14
|
+
return false;
|
15
|
+
} else {
|
16
|
+
$item.indicator_show();
|
17
|
+
var url = $item.data('url').replace(':id', computeResource);
|
18
|
+
var defaultValue = $item.data('default-value');
|
19
|
+
var $tenants = $('#tenants-list');
|
20
|
+
$('#stacks-list').empty();
|
21
|
+
$tenants.empty();
|
22
|
+
$tenants.load(url, function () {
|
23
|
+
$tenants.find('select').select2({allowClear: true});
|
24
|
+
$item.indicator_hide();
|
25
|
+
updateDefaultEntityButton('#set-default-compute-resource', defaultValue, computeResource);
|
26
|
+
});
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
function stacksNewComputeResourceSelected(item) {
|
31
|
+
var $item = $(item);
|
32
|
+
var computeResource = $item.val();
|
33
|
+
if (computeResource === '') {
|
34
|
+
return false;
|
35
|
+
} else {
|
36
|
+
$item.indicator_show();
|
37
|
+
var url = $item.data('url');
|
38
|
+
var params = $.param({compute_resource_id: computeResource});
|
39
|
+
var $main = $('#main-stack-parameters');
|
40
|
+
$main.hide();
|
41
|
+
var $container = $('#tenants-list-container');
|
42
|
+
$container.empty();
|
43
|
+
$container.load(url + ' #tenants-list', params, function () {
|
44
|
+
$container.find('select').select2({allowClear: true});
|
45
|
+
$main.show();
|
46
|
+
$item.indicator_hide();
|
47
|
+
});
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
function stacksTenantSelected(item) {
|
52
|
+
var $item = $(item);
|
53
|
+
var tenant = $item.val();
|
54
|
+
if (tenant === '') {
|
55
|
+
return false;
|
56
|
+
} else {
|
57
|
+
$item.indicator_show();
|
58
|
+
var url = $item.data('url').replace(':id', tenant);
|
59
|
+
var defaultValue = $item.data('default-value');
|
60
|
+
var $stacks = $('#stacks-list');
|
61
|
+
$stacks.empty();
|
62
|
+
$stacks.load(url, function () {
|
63
|
+
$item.indicator_hide();
|
64
|
+
updateDefaultEntityButton('#set-default-tenant', defaultValue, tenant);
|
65
|
+
});
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
function stacksLoadTemplateWithParams(item) {
|
70
|
+
var $item = $(item);
|
71
|
+
var templateId = $item.val();
|
72
|
+
if (templateId === '') {
|
73
|
+
return false;
|
74
|
+
} else {
|
75
|
+
$item.indicator_show();
|
76
|
+
var url = $item.data('url').replace(':template_id', templateId);
|
77
|
+
var params = $.param({template_id: templateId});
|
78
|
+
var $container = $('#template-with-params');
|
79
|
+
$container.hide();
|
80
|
+
$container.load(url, params, function () {
|
81
|
+
$container.show();
|
82
|
+
$item.indicator_hide();
|
83
|
+
});
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
function setDefaultComputeResource(button) {
|
88
|
+
var $button = $(button);
|
89
|
+
var $select = $('#compute_resource');
|
90
|
+
var computeResourceId = $select.val();
|
91
|
+
if (!$button.data('processing') && computeResourceId !== '') {
|
92
|
+
$button.data('processing', true);
|
93
|
+
var url = $button.data('url').replace(':id', computeResourceId);
|
94
|
+
var oldText = $button.text();
|
95
|
+
$button.text($button.data('disable-with'));
|
96
|
+
$.post(url).done(function () {
|
97
|
+
$button.prop('disabled', true);
|
98
|
+
$select.data('default-value', Number(computeResourceId));
|
99
|
+
}).fail(function () {
|
100
|
+
alert('Something went wrong');
|
101
|
+
}).always(function () {
|
102
|
+
$button.text(oldText);
|
103
|
+
$button.data('processing', false);
|
104
|
+
});
|
105
|
+
}
|
106
|
+
return false;
|
107
|
+
}
|
108
|
+
|
109
|
+
function setDefaultTenant(button) {
|
110
|
+
var $button = $(button);
|
111
|
+
var $select = $('#tenant');
|
112
|
+
var tenantId = $select.val();
|
113
|
+
if (!$button.data('processing') && tenantId !== '') {
|
114
|
+
$button.data('processing', true);
|
115
|
+
var url = $button.data('url').replace(':id', tenantId);
|
116
|
+
var oldText = $button.text();
|
117
|
+
$button.text($button.data('disable-with'));
|
118
|
+
$.post(url).done(function () {
|
119
|
+
$button.prop('disabled', true);
|
120
|
+
$select.data('default-value', tenantId);
|
121
|
+
}).fail(function () {
|
122
|
+
alert('Something went wrong');
|
123
|
+
}).always(function () {
|
124
|
+
$button.text(oldText);
|
125
|
+
$button.data('processing', false);
|
126
|
+
});
|
127
|
+
}
|
128
|
+
return false;
|
129
|
+
}
|
data/app/controllers/concerns/foreman_orchestration/compute_resources_controller_extensions.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module ForemanOrchestration
|
2
|
+
module ComputeResourcesControllerExtensions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
before_filter :ajax_request, only: [:default]
|
7
|
+
end
|
8
|
+
|
9
|
+
def default
|
10
|
+
Foreman::Model::Openstack.find(params[:id])
|
11
|
+
.mark_as_default
|
12
|
+
respond_to do |format|
|
13
|
+
format.json { render :json => {}, :status => :ok }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ForemanOrchestration
|
2
|
+
class StackTemplatesController < ::ApplicationController
|
3
|
+
before_filter :find_resource, only: [:edit, :update, :destroy, :with_params]
|
4
|
+
|
5
|
+
def index
|
6
|
+
@templates = StackTemplate.order(:name).all
|
7
|
+
end
|
8
|
+
|
9
|
+
def new
|
10
|
+
@template = StackTemplate.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO: there is an API for template validation:
|
14
|
+
# http://developer.openstack.org/api-ref-orchestration-v1.html#template_validate
|
15
|
+
# Maybe we can use it here?
|
16
|
+
def create
|
17
|
+
@template = StackTemplate.new(template_params)
|
18
|
+
if @template.save
|
19
|
+
process_success object: @template
|
20
|
+
else
|
21
|
+
process_error object: @template
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def update
|
26
|
+
if @template.update_attributes(template_params)
|
27
|
+
process_success object: @template
|
28
|
+
else
|
29
|
+
process_error object: @template
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def destroy
|
34
|
+
if @template.destroy
|
35
|
+
process_success object: @template
|
36
|
+
else
|
37
|
+
process_error object: @template
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# ajax methods
|
42
|
+
def with_params
|
43
|
+
render partial: 'with_params', locals: {template: @template, parameters: {}}
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def template_params
|
49
|
+
params.fetch(:foreman_orchestration_stack_template)
|
50
|
+
.slice(:name, :template)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_resource
|
54
|
+
@template = StackTemplate.find(params[:id])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module ForemanOrchestration
|
2
|
+
class StacksController < ::ApplicationController
|
3
|
+
before_filter :load_compute_resources, only: [:all, :new]
|
4
|
+
|
5
|
+
def all
|
6
|
+
@compute_resource = find_default_compute_resource
|
7
|
+
if @compute_resource
|
8
|
+
@tenants = @compute_resource.orchestration_clients
|
9
|
+
@tenant = @compute_resource.default_tenant
|
10
|
+
@stacks = @tenant.stacks if @tenant
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def index
|
15
|
+
@compute_resource = find_compute_resource
|
16
|
+
@tenant = @compute_resource.orchestration_client_for(params[:tenant_id])
|
17
|
+
@stacks = @tenant.stacks
|
18
|
+
render layout: !ajax?
|
19
|
+
end
|
20
|
+
|
21
|
+
def new
|
22
|
+
@stack = Stack.new(new_stack_params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def create
|
26
|
+
@stack = Stack.new(create_stack_params)
|
27
|
+
if @stack.save
|
28
|
+
flash[:notice] = "Stack '#{@stack.name}' is being created now"
|
29
|
+
redirect_to all_stacks_path
|
30
|
+
else
|
31
|
+
load_compute_resources
|
32
|
+
process_error object: @stack
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy
|
37
|
+
@stack = Stack.new(compute_resource_id: params[:compute_resource_id],
|
38
|
+
tenant_id: params[:tenant_id],
|
39
|
+
id: params[:id])
|
40
|
+
@stack.destroy
|
41
|
+
redirect_to all_stacks_path, notice: 'Stack is being deleted now'
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def new_stack_params
|
47
|
+
if params[:compute_resource_id] && params[:tenant_id]
|
48
|
+
params.slice(:compute_resource_id, :tenant_id)
|
49
|
+
else
|
50
|
+
compute_resource = find_default_compute_resource
|
51
|
+
{
|
52
|
+
compute_resource: compute_resource,
|
53
|
+
tenant: compute_resource.default_tenant
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_stack_params
|
59
|
+
params.fetch(:foreman_orchestration_stack)
|
60
|
+
.slice(:compute_resource_id, :tenant_id, :name, :template_id, :parameters)
|
61
|
+
end
|
62
|
+
|
63
|
+
def load_compute_resources
|
64
|
+
@compute_resources = ::Foreman::Model::Openstack.all
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_default_compute_resource
|
68
|
+
@compute_resources.find(&:is_default) || @compute_resources.first
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_compute_resource
|
72
|
+
Foreman::Model::Openstack.find(params[:compute_resource_id])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ForemanOrchestration
|
2
|
+
class TenantsController < ::ApplicationController
|
3
|
+
before_filter :ajax_request, only: [:default]
|
4
|
+
before_filter :find_compute_resource, only: [:for_select, :default]
|
5
|
+
|
6
|
+
def for_select
|
7
|
+
@tenants = @compute_resource.tenants
|
8
|
+
render partial: 'for_select'
|
9
|
+
end
|
10
|
+
|
11
|
+
def default
|
12
|
+
tenant = @compute_resource.orchestration_client_for(params[:id])
|
13
|
+
@compute_resource.default_tenant_id = tenant.id
|
14
|
+
@compute_resource.save!
|
15
|
+
respond_to do |format|
|
16
|
+
format.json { render :json => {}, :status => :ok }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def find_compute_resource
|
23
|
+
@compute_resource = ::Foreman::Model::Openstack.find(params[:compute_resource_id])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ForemanOrchestration
|
2
|
+
module LayoutHelperExtensions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def selectable_tag(name, options_tags = nil, options = {})
|
6
|
+
content_tag(:div, :class => 'clearfix') do
|
7
|
+
content_tag(:div, :class => 'form-group') do
|
8
|
+
label = label_tag(name, options.delete(:label), :class => 'col-md-3 control-label')
|
9
|
+
help = help_inline(:indicator, '')
|
10
|
+
add_help_to_label('col-md-4', label, help) do
|
11
|
+
addClass options, 'form-control'
|
12
|
+
select_tag name, options_tags, options
|
13
|
+
end + if block_given?
|
14
|
+
content_tag(:div, :class => 'col-md-2') do
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ForemanOrchestration
|
2
|
+
module OpenstackExtensions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def orchestration_clients
|
6
|
+
# TODO: select only enabled: true tenants?
|
7
|
+
@orchestration_clients ||= tenants.map do |t|
|
8
|
+
create_orchestration_client(t)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def orchestration_client_for(tenant_id)
|
13
|
+
orchestration_clients.find { |client| client.id == tenant_id }
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_orchestration_client(tenant)
|
17
|
+
ForemanOrchestration::OrchestrationClient.new(tenant, fog_credentials)
|
18
|
+
end
|
19
|
+
|
20
|
+
def is_default
|
21
|
+
attrs[:is_default]
|
22
|
+
end
|
23
|
+
|
24
|
+
def mark_as_default
|
25
|
+
ActiveRecord::Base.transaction do
|
26
|
+
self.class.all.each do |record|
|
27
|
+
record.attrs[:is_default] = false
|
28
|
+
record.save!
|
29
|
+
end
|
30
|
+
attrs[:is_default] = true
|
31
|
+
save!
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_tenant_id
|
36
|
+
attrs[:default_tenant_id]
|
37
|
+
end
|
38
|
+
|
39
|
+
def default_tenant_id=(tenant_id)
|
40
|
+
attrs[:default_tenant_id] = tenant_id
|
41
|
+
end
|
42
|
+
|
43
|
+
def default_tenant
|
44
|
+
if default_tenant_id
|
45
|
+
orchestration_client_for(default_tenant_id)
|
46
|
+
else
|
47
|
+
orchestration_clients.first
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|