foreman_wds 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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.eslintrc.json +20 -0
  3. data/.gitignore +17 -0
  4. data/.rubocop.yml +66 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +33 -0
  9. data/Rakefile +10 -0
  10. data/app/assets/javascripts/foreman_wds/wds_servers.js +19 -0
  11. data/app/assets/javascripts/host_edit_extensions.js +50 -0
  12. data/app/controllers/concerns/foreman/controller/parameters/wds_server.rb +19 -0
  13. data/app/controllers/concerns/foreman_wds/hosts_controller_extensions.rb +18 -0
  14. data/app/controllers/concerns/foreman_wds/unattended_controller_extensions.rb +21 -0
  15. data/app/controllers/wds_servers_controller.rb +85 -0
  16. data/app/lib/foreman_wds/wds_boot_image.rb +11 -0
  17. data/app/lib/foreman_wds/wds_image.rb +89 -0
  18. data/app/lib/foreman_wds/wds_install_image.rb +14 -0
  19. data/app/models/concerns/foreman_wds/compute_resource_extensions.rb +7 -0
  20. data/app/models/concerns/foreman_wds/host_extensions.rb +109 -0
  21. data/app/models/concerns/foreman_wds/nic_extensions.rb +40 -0
  22. data/app/models/foreman_wds/wds_facet.rb +48 -0
  23. data/app/models/wds_server.rb +239 -0
  24. data/app/services/wds_image_cache.rb +66 -0
  25. data/app/views/foreman_wds/unattend_2016.xml.erb +220 -0
  26. data/app/views/foreman_wds/windows_ptable.xml.erb +43 -0
  27. data/app/views/hosts/provision_method/wds/_form.html.erb +12 -0
  28. data/app/views/wds_servers/_form.html.erb +28 -0
  29. data/app/views/wds_servers/_image_select.html.erb +27 -0
  30. data/app/views/wds_servers/_server_select.html.erb +12 -0
  31. data/app/views/wds_servers/clients/_list.html.erb +43 -0
  32. data/app/views/wds_servers/edit.html.erb +3 -0
  33. data/app/views/wds_servers/images/_list.html.erb +36 -0
  34. data/app/views/wds_servers/index.html.erb +25 -0
  35. data/app/views/wds_servers/new.html.erb +3 -0
  36. data/app/views/wds_servers/show.html.erb +46 -0
  37. data/config/routes.rb +26 -0
  38. data/db/migrate/20180426133700_add_wds_servers.rb +23 -0
  39. data/db/seeds.d/50_ptable_templates.rb +16 -0
  40. data/db/seeds.d/50_unattend_templates.rb +19 -0
  41. data/foreman_wds.gemspec +23 -0
  42. data/lib/foreman_wds.rb +4 -0
  43. data/lib/foreman_wds/engine.rb +70 -0
  44. data/lib/foreman_wds/version.rb +3 -0
  45. data/test/foreman_wds_test.rb +11 -0
  46. data/test/test_helper.rb +4 -0
  47. metadata +147 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 13cda19881b8df7132970cb27a73bea4bc834abbbff33e2831ed98625cffa48a
4
+ data.tar.gz: 74f4f4f15f7ca7c17a5453b01ac60883279add2cf37b015b4fae54b38d7d3511
5
+ SHA512:
6
+ metadata.gz: 9deefe383a62458a87f288fc9b84825b236ccc10b05f25a983ed0a0e39467d66684ea2e093778d82e17c8756c6840f492069125be4b2208249e18ac9c4087792
7
+ data.tar.gz: 69e76584c0939c1a509d13774e936452bb5f38e79098491d81d6eb52e5136ca06813aeacbb70db357bb258cec2252baedb9d15247ce7f6715c0604104b26a576
data/.eslintrc.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "rules": {
3
+ "indent": [
4
+ "error",
5
+ 2
6
+ ],
7
+ "linebreak-style": [
8
+ "error",
9
+ "unix"
10
+ ],
11
+ "quotes": [
12
+ "error",
13
+ "single"
14
+ ],
15
+ "semi": [
16
+ "error",
17
+ "always"
18
+ ]
19
+ }
20
+ }
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /log/*.log
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
11
+ /test/dummy
12
+ /locale/*.mo
13
+ /locale/*/*.edit.po
14
+ /locale/*/*.po.time_stamp
15
+ /locale/*/*.pox
16
+
17
+ Gemfile.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,66 @@
1
+ ---
2
+ AllCops:
3
+ TargetRubyVersion: 2.3
4
+ TargetRailsVersion: 5.1
5
+ Exclude:
6
+ - '*.spec'
7
+ - 'Rakefile'
8
+
9
+ Rails:
10
+ Enabled: true
11
+
12
+ # Don't enforce documentation
13
+ Style/Documentation:
14
+ Enabled: false
15
+
16
+ Metrics/MethodLength:
17
+ Max: 40
18
+
19
+ Metrics/LineLength:
20
+ Max: 190
21
+
22
+ Rails/SkipsModelValidations:
23
+ Enabled: false
24
+
25
+ Lint/BooleanSymbol:
26
+ Enabled: false
27
+
28
+ Style/MultilineBlockChain:
29
+ Enabled: false
30
+
31
+ Metrics/AbcSize:
32
+ Enabled: false
33
+
34
+ Metrics/CyclomaticComplexity:
35
+ Enabled: false
36
+
37
+ Metrics/PerceivedComplexity:
38
+ Enabled: false
39
+
40
+ Style/FormatStringToken:
41
+ Enabled: false
42
+
43
+ Naming/AccessorMethodName:
44
+ Enabled: false
45
+
46
+ Performance/FixedSize:
47
+ Exclude:
48
+ - 'test/**/*'
49
+
50
+ Metrics/BlockLength:
51
+ Max: 35
52
+ Exclude:
53
+ - 'test/**/*'
54
+
55
+ Metrics/ClassLength:
56
+ Max: 200
57
+ Exclude:
58
+ - 'test/**/*'
59
+
60
+ Lint/AmbiguousBlockAssociation:
61
+ Enabled: false
62
+
63
+ Style/ClassAndModuleChildren:
64
+ Exclude:
65
+ - 'test/**/*'
66
+ - 'app/controllers/concerns/foreman/**/*'
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.6
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in foreman_wds.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Alexander Olofsson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Foreman Windows Deployment Service
2
+
3
+ This plugin adds extended support to Foreman for querying and orchestrating WDS servers.
4
+
5
+ *Nota Bene*; to support locally booting your installation, you need to have a regular DHCP/TFTP setup in place, that can serve PXE files for the PXE loader chosen for the WDS host.
6
+ If you don't want the lifecycle management features of Foreman, you can ignore this requirement and only PXE boot the WDS host when it is to be built.
7
+
8
+ ### Not yet implemented:
9
+
10
+ - Install image orchestration
11
+ - Unattend file deployment
12
+ - Recommended to build a http client (curl/wget/etc) into your boot image and download `https://foreman.example.com/unattend/wds_unattend` to drive the setup.
13
+
14
+ ## Compatibility
15
+
16
+ | Foreman Version | Plugin Version |
17
+ | --------------- | -------------- |
18
+ | >= 1.17 | any |
19
+
20
+ ## Installation
21
+
22
+ See the [Plugins install instructions, advanced installation from gems](https://theforeman.org/plugins/#2.3AdvancedInstallationfromGems) for information on how to install this plugins.
23
+
24
+ This plugin has JavaScript assets that require precompilation if installed into a packaged Foreman install.
25
+ You will need to install the `foreman-assets` package and run `foreman-rake plugin:assets:precompile[foreman_wds]` after installing it from gem.
26
+
27
+ ## Contributing
28
+
29
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ananace/foreman_wds
30
+
31
+ ## License
32
+
33
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,19 @@
1
+ function wds_load(element){
2
+ var url = $(element).attr('data-url');
3
+ tfm.tools.showSpinner();
4
+ $.ajax({
5
+ type:'get',
6
+ url: url,
7
+ complete: function(){
8
+ reloadOnAjaxComplete(element);
9
+ },
10
+ success: function(request) {
11
+ element.html(request);
12
+ }
13
+ });
14
+ }
15
+
16
+ $(function() {
17
+ wds_load($('#images'));
18
+ wds_load($('#clients'));
19
+ })();
@@ -0,0 +1,50 @@
1
+ function wds_server_selected(element){
2
+ var url = $(element).attr('data-url');
3
+ var type = $(element).attr('data-type');
4
+ var attrs = {};
5
+ attrs[type] = attribute_hash(['architecture_id', 'operatingsystem_id', 'wds_server_id']);
6
+ tfm.tools.showSpinner();
7
+ $.ajax({
8
+ data: attrs,
9
+ type:'post',
10
+ url: url,
11
+ complete: function(){
12
+ reloadOnAjaxComplete(element);
13
+ },
14
+ success: function(request) {
15
+ $('#wds_image_select').html(request);
16
+ }
17
+ });
18
+ }
19
+
20
+ var old_os_selected = os_selected;
21
+ os_selected = function(element){
22
+ old_os_selected(element);
23
+
24
+ if ($('#os_select select').val() === '') {
25
+ $('#wds_server_select select').val('');
26
+ $('#wds_image_select select').val('');
27
+ $('#wds_server_select select').prop('disabled', true);
28
+ $('#wds_image_select select').prop('disabled', true);
29
+ } else {
30
+ $('#wds_server_select select').prop('disabled', false);
31
+ }
32
+ };
33
+
34
+
35
+ function wds_provision_method_selected() {
36
+ build_provision_method_selected();
37
+ $('#wds_provisioning').show();
38
+
39
+ if ($('#wds_image_select select').val() === '')
40
+ $('#wds_image_select select').attr('disabled', true);
41
+ }
42
+ $(document).on('change', '#host_provision_method_wds', wds_provision_method_selected);
43
+
44
+ $(function() {
45
+ var caps = $('#capabilities').val() || $('#bare_metal_capabilities').val();
46
+ update_capabilities(caps);
47
+ $('#provisioning_method input[checked]').click();
48
+
49
+ $('#wds_provisioning').detach().insertBefore('#media_select');
50
+ });
@@ -0,0 +1,19 @@
1
+ module Foreman::Controller::Parameters::WdsServer
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def wds_server_params_filter
6
+ Foreman::ParameterFilter.new(::WdsServer).tap do |filter|
7
+ filter.permit :name,
8
+ :description,
9
+ :url,
10
+ :user,
11
+ :password
12
+ end
13
+ end
14
+ end
15
+
16
+ def wds_server_params
17
+ self.class.wds_server_params_filter.filter_params(params, parameter_filter_context, :wds_server)
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module ForemanWds
2
+ module HostsControllerExtensions
3
+ def wds_server_selected
4
+ host = @host || item_object
5
+ wds_facet = host.wds_facet || host.build_wds_facet
6
+ wds_facet.wds_server_id ||= (params[:wds_facet] || params[:host])[:wds_server_id]
7
+
8
+ render partial: 'wds_servers/image_select', locals: { item: wds_facet }
9
+ end
10
+
11
+ def host_params(top_level_hash = controller_name.singularize)
12
+ # Don't create a WDS facet unless provisioning with it
13
+ params[:host].delete :wds_facet_attributes if params[:host] && params[:host][:provision_method] != 'wds'
14
+
15
+ super(top_level_hash)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module ForemanWds
2
+ module UnattendedControllerExtensions
3
+ def render_template(kind)
4
+ return super unless kind == 'wds_localboot'
5
+
6
+ iface = @host.provision_interface
7
+
8
+ # Deploy regular DHCP and local boot TFTP
9
+ @host.provision_method = 'build'
10
+ @host.build = false
11
+ iface.send :rebuild_tftp
12
+ iface.send :rebuild_dhcp
13
+
14
+ render inline: "Success. Local boot template was deployed successfully.\n"
15
+ rescue StandardError => e
16
+ message = format('Failed to set local boot template: %{error}', error: e)
17
+ logger.error message
18
+ render text: message, status: :error, content_type: 'text/plain'
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,85 @@
1
+ class WdsServersController < ::ApplicationController
2
+ include Foreman::Controller::AutoCompleteSearch
3
+ include Foreman::Controller::Parameters::WdsServer
4
+
5
+ before_action :find_server, except: %i[index new create]
6
+
7
+ def index
8
+ @wds_servers = resource_base_search_and_page
9
+ end
10
+
11
+ def show; end
12
+
13
+ def new
14
+ @wds_server = WdsServer.new
15
+ end
16
+
17
+ def edit; end
18
+
19
+ def create
20
+ @wds_server = WdsServer.new(wds_server_params)
21
+ if @wds_server.save
22
+ process_success success_redirect: wds_server_path(@wds_server)
23
+ else
24
+ process_error
25
+ end
26
+ end
27
+
28
+ def update
29
+ if @wds_server.update(wds_server_params)
30
+ process_success
31
+ else
32
+ process_error
33
+ end
34
+ end
35
+
36
+ def destroy
37
+ if @wds_server.destroy
38
+ process_success
39
+ else
40
+ process_error
41
+ end
42
+ end
43
+
44
+ def test_connection
45
+ # wds_id is posted from AJAX function. wds_id is nil if new
46
+ if params[:wds_id].present?
47
+ @wds_server = WdsServer.authorized(:edit_wds_server).find(params[:wds_id])
48
+ @wds_server.attributes = wds_server_params.reject { |k, v| k == :password && v.blank? }
49
+ else
50
+ @wds_server = WdsServer.new(wds_server_params)
51
+ end
52
+
53
+ @wds_server.test_connection
54
+ render partial: 'form', locals: { wds_server: @wds_server }
55
+ end
56
+
57
+ def refresh_cache
58
+ @wds_server.refresh_cache
59
+
60
+ render partial: 'form', locals: { wds_server: @wds_server }
61
+ end
62
+
63
+ def wds_clients
64
+ @clients = @wds_server.clients
65
+
66
+ render partial: 'wds_servers/clients/list'
67
+ end
68
+
69
+ def wds_images
70
+ @images = @wds_server.boot_images + @wds_server.install_images
71
+
72
+ render partial: 'wds_servers/images/list'
73
+ end
74
+
75
+ def delete_wds_client
76
+ host = Host::Managed.find(params[:client])
77
+ client = @wds_server.client(host)
78
+ end
79
+
80
+ private
81
+
82
+ def find_server
83
+ @wds_server = WdsServer.find(params[:id])
84
+ end
85
+ end
@@ -0,0 +1,11 @@
1
+ class ForemanWds::WdsBootImage < ForemanWds::WdsImage
2
+ def initialize(json = {})
3
+ super json
4
+ end
5
+
6
+ def reload
7
+ return false if wds_server.nil?
8
+ @json = wds_server.boot_image(name)
9
+ load!
10
+ end
11
+ end
@@ -0,0 +1,89 @@
1
+ class ForemanWds::WdsImage
2
+ WDS_IMAGE_ARCHES = [nil, /^i.86|x86$/i, /^ia64$/i, /^x86_64|x64$/i, /^arm$/i].freeze
3
+ WDS_ARCH_NAMES = [nil, 'x86', 'ia64', 'x64', 'arm'].freeze
4
+ WDS_PROCESSOR_NAMES = [nil, 'x86', 'ia64', 'amd64', 'arm'].freeze
5
+
6
+ attr_accessor :id, :name, :description, :enabled, :file_name,
7
+ :architecture, :product_family, :product_name, :version,
8
+ :wds_server
9
+ attr_reader :creation_time, :last_modification_time
10
+
11
+ def inspect
12
+ Kernel.format('#<%<class>s:%<id>x %<variables>s>',
13
+ class: self.class,
14
+ id: (object_id << 1),
15
+ variables: instance_variables
16
+ .reject { |v| %i[@json @wds_server].include?(v) }
17
+ .map { |v| "#{v}=#{instance_variable_get(v).inspect}" }
18
+ .join(' '))
19
+ end
20
+
21
+ def type_name
22
+ self.class
23
+ .name
24
+ .demodulize
25
+ .underscore
26
+ .split('_')
27
+ .map.with_index { |v, i| i.zero? ? v.upcase : v.capitalize }
28
+ .join ' '
29
+ end
30
+
31
+ def architecture_name
32
+ return architecture unless architecture.is_a?(Integer) && WDS_ARCH_NAMES[architecture]
33
+ WDS_ARCH_NAMES[architecture]
34
+ end
35
+
36
+ def creation_time=(time)
37
+ @creation_time = parse_time time
38
+ end
39
+
40
+ def last_modification_time=(time)
41
+ @last_modification_time = parse_time time
42
+ end
43
+
44
+ def matches_architecture?(architecture)
45
+ return nil unless WDS_IMAGE_ARCHES[self.architecture]
46
+ architecture = architecture.name if architecture.is_a? Architecture
47
+ !(WDS_IMAGE_ARCHES[self.architecture] =~ architecture).nil?
48
+ end
49
+
50
+ def marshal_dump
51
+ @json
52
+ end
53
+
54
+ def marshal_load(json)
55
+ @json = json
56
+ load!
57
+ end
58
+
59
+ protected
60
+
61
+ def initialize(json = {})
62
+ @json = json if json.is_a? Hash
63
+ @wds_server = self.json.delete(:wds_server)
64
+ load!
65
+ end
66
+
67
+ def json
68
+ @json || {}
69
+ end
70
+
71
+ def parse_time(time)
72
+ return time unless time.is_a?(String) && time =~ /\/Date\(.*\)\//
73
+
74
+ time = Time.at(time.scan(/\((.*)\)/).flatten.first.to_i / 1000)
75
+ return time unless wds_server
76
+
77
+ time.utc
78
+ sec_diff = wds_server.timezone - Time.at(0).utc_offset
79
+ time += sec_diff
80
+ time.localtime
81
+ end
82
+
83
+ def load!
84
+ json.each do |k, v|
85
+ sym = "#{k.to_s.underscore}=".to_sym
86
+ send sym, v if respond_to? sym
87
+ end
88
+ end
89
+ end