foreman_wds 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,66 @@
1
+ class WdsImageCache
2
+ attr_accessor :wds_server, :cache_duration
3
+
4
+ delegate :logger, to: ::Rails
5
+
6
+ def initialize(wds_server, cache_duration: 180.minutes)
7
+ self.wds_server = wds_server
8
+ self.cache_duration = cache_duration
9
+ end
10
+
11
+ def cache(key, &block)
12
+ cached_value = read(key)
13
+ return cached_value if cached_value
14
+ return unless block_given?
15
+
16
+ uncached_value = get_uncached_value(&block)
17
+ write(key, uncached_value)
18
+ uncached_value
19
+ end
20
+
21
+ def delete(key)
22
+ Rails.cache.delete(cache_key + key.to_s)
23
+ end
24
+
25
+ def read(key)
26
+ Rails.cache.read(cache_key + key.to_s, cache_options)
27
+ end
28
+
29
+ def write(key, value)
30
+ Rails.cache.write(cache_key + key.to_s, value, cache_options)
31
+ end
32
+
33
+ def refresh
34
+ Rails.cache.delete(cache_scope_key)
35
+ true
36
+ rescue StandardError => e
37
+ logger.exception('Failed to refresh the WDS image cache', e)
38
+ false
39
+ end
40
+
41
+ def cache_scope
42
+ Rails.cache.fetch(cache_scope_key, cache_options) { Foreman.uuid }
43
+ end
44
+
45
+ private
46
+
47
+ def get_uncached_value(&block)
48
+ return unless block_given?
49
+ wds_server.instance_eval(&block)
50
+ end
51
+
52
+ def cache_key
53
+ "wds_server_#{wds_server.id}-#{cache_scope}/"
54
+ end
55
+
56
+ def cache_scope_key
57
+ "wds_server_#{wds_server.id}-cache_scope_key"
58
+ end
59
+
60
+ def cache_options
61
+ {
62
+ expires_in: cache_duration,
63
+ race_condition_ttl: 1.minute
64
+ }
65
+ end
66
+ end
@@ -0,0 +1,220 @@
1
+ <%#
2
+ kind: wds_unattend
3
+ name: Unattend Windows Server 2016
4
+ oses:
5
+ - Windows Server 2016
6
+ -%>
7
+ <%
8
+ registration = {
9
+ organization: 'Organization name',
10
+ owner: 'Owner name'
11
+ }
12
+ timezone = 'W. Europe Standard Time'
13
+
14
+ if @host.realm && @host.realm.realm_type == 'Active Directory'
15
+ realm = {
16
+ type: :machine,
17
+ domain: @host.realm.name.downcase,
18
+ password: @host.otp || '$HOST[OTP]'
19
+ # ou: 'OU=Servers,OU=Hardware,DC=example,DC=com'
20
+ }
21
+ elsif @host.domain.to_s == 'example.com' # If you want to let the WDS install do the join
22
+ realm = {
23
+ type: :credentials,
24
+ domain: @host.domain,
25
+ username: 'AD_ADMIN',
26
+ password: 'AD_ADMIN_PW',
27
+ ou: 'OU=Servers,OU=Hardware,DC=example,DC=com'
28
+ }
29
+ end
30
+ -%>
31
+ <?xml version="1.0" encoding="utf-8"?>
32
+ <unattend xmlns="urn:schemas-microsoft-com:unattend">
33
+ <settings pass="windowsPE">
34
+ <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
35
+ <SetupUILanguage>
36
+ <UILanguage>en-US</UILanguage>
37
+ </SetupUILanguage>
38
+ <InputLocale>en-US</InputLocale>
39
+ <SystemLocale>en-US</SystemLocale>
40
+ <UserLocale>en-US</UserLocale>
41
+ </component>
42
+ <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
43
+ <WindowsDeploymentServices>
44
+ <Login>
45
+ <Credentials>
46
+ <Domain><%= @host.wds_server.shortname %></Domain>
47
+ <Username>wdsinstall</Username>
48
+ <Password>1ccg81hfh097hfqca08h</Password>
49
+ </Credentials>
50
+ </Login>
51
+ <ImageSelection>
52
+ <InstallImage>
53
+ <ImageGroup><%= @host.wds_install_image_group %></ImageGroup>
54
+ <ImageName><%= @host.wds_install_image_name %></ImageName>
55
+ <Filename><%= @host.wds_install_image_file %></Filename>
56
+ </InstallImage>
57
+ <InstallTo>
58
+ <DiskID>0</DiskID>
59
+ <PartitionID><%= @host.pxe_loader.include? 'UEFI' ? '3' : '2' %></PartitionID>
60
+ </InstallTo>
61
+ </ImageSelection>
62
+ </WindowsDeploymentServices>
63
+ <EnableNetwork>true</EnableNetwork>
64
+ <%= indent(6) { @host.diskLayout } %>
65
+ <RunAsynchronous>
66
+ <RunAsynchronousCommand wcm:action="add">
67
+ <Path>\\exampleserver\tools\wget.exe <%= foreman_url('wds_localboot') %> --no-check-certificate
68
+ <Order>1</Order>
69
+ <Description>Switch PXE to local boot</Description>
70
+ </RunAsynchronousCommand>
71
+ </RunAsynchronous>
72
+ </component>
73
+ </settings>
74
+ <settings pass="specialize">
75
+ <%- if realm -%>
76
+ <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
77
+ <Identification>
78
+ <JoinDomain><%= realm[:domain] %></JoinDomain>
79
+ <%- if realm[:type] == :machine -%>
80
+ <MachinePassword><%= realm[:password] %></MachinePassword>
81
+ <UnsecureJoin>true</UnsecureJoin>
82
+ <%- else -%>
83
+ <Credentials>
84
+ <Domain><%= realm[:domain] %></Domain>
85
+ <Username><%= realm[:username] %></Username>
86
+ <Password><%= realm[:password] %></Password>
87
+ </Credentials>
88
+ <%- end -%>
89
+ <%- if realm[:ou] -%>
90
+ <MachineObjectOU><%= realm[:ou] %></MachineObjectOU>
91
+ <%- end -%>
92
+ </Identification>
93
+ </component>
94
+ <%- end -%>
95
+ <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
96
+ <ComputerName><%= @host.shortname %></ComputerName>
97
+ <RegisteredOrganization><%= registration[:organization] %></RegisteredOrganization>
98
+ <RegisteredOwner><%= registration[:owner] %></RegisteredOwner>
99
+ <TimeZone><%= timezone %></TimeZone>
100
+ </component>
101
+ <component name="Microsoft-Windows-TCPIP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
102
+ <Interfaces>
103
+ <%- iface = @host.primary_interface # @host.managed_interfaces.each.with_index do |iface, i| -%>
104
+ <Interface wcm:action="add">
105
+ <Identifier><%= iface.identifier.empty? ? 'Ethernet' : iface.identifier %></Identifier>
106
+ <UnicastIpAddresses>
107
+ <%- if !iface.ip.empty? && iface.subnet -%>
108
+ <IpAddress wcm:action="add" wcm:keyValue="1"><%= iface.ip %>/<%= iface.subnet.cidr %></IpAddress>
109
+ <%- end -%>
110
+ <%- if !iface.ip6.empty? && iface.subnet6 -%>
111
+ <IpAddress wcm:action="add" wcm:keyValue="2"><%= iface.ip6 %>/<%= iface.subnet6.cidr %></IpAddress>
112
+ <%- end -%>
113
+ </UnicastIpAddresses>
114
+ <Routes>
115
+ <%- if iface.subnet && !iface.subnet.gateway.empty? -%>
116
+ <Route wcm:action="add">
117
+ <Identifier>1</Identifier>
118
+ <Metric>10</Metric>
119
+ <NextHopAddress><%= iface.subnet.gateway %></NextHopAddress>
120
+ <Prefix>0.0.0.0/0</Prefix>
121
+ </Route>
122
+ <%- end -%>
123
+ <%- if iface.subnet6 && !iface.subnet6.gateway.empty? -%>
124
+ <Route wcm:action="add">
125
+ <Identifier>1</Identifier>
126
+ <Metric>10</Metric>
127
+ <NextHopAddress><%= iface.subnet6.gateway %></NextHopAddress>
128
+ <Prefix>::/0</Prefix>
129
+ </Route>
130
+ <%- end -%>
131
+ </Routes>
132
+ </Interface>
133
+ </Interfaces>
134
+ </component>
135
+ <component name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
136
+ <DNSSuffixSearchOrder>
137
+ <DomainName wcm:action="add" wcm:keyValue="1"><%= @host.domain %></DomainName>
138
+ </DNSSuffixSearchOrder>
139
+ <Interfaces>
140
+ <Interface wcm:action="add">
141
+ <DNSServerSearchOrder>
142
+ <%- [iface.subnet.dns_primary, iface.subnet.dns_secondary].reject(&:empty?).each.with_index do |dns_ip, i| -%>
143
+ <IpAddress wcm:action="add" wcm:keyValue="<%= i %>"><%= dns_ip %></IpAddress>
144
+ <%- end -%>
145
+ </DNSServerSearchOrder>
146
+ <Identifier><%= iface.identifier.empty? ? 'Ethernet' : iface.identifier %></Identifier>
147
+ </Interface>
148
+ </Interfaces>
149
+ <DNSDomain><%= @host.domain %></DNSDomain>
150
+ </component>
151
+ </settings>
152
+ <settings pass="oobeSystem">
153
+ <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
154
+ <OOBE>
155
+ <HideEULAPage>true</HideEULAPage>
156
+ <HideLocalAccountScreen>true</HideLocalAccountScreen>
157
+ <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
158
+ <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
159
+ <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
160
+ <ProtectYourPC>1</ProtectYourPC>
161
+ </OOBE>
162
+ <UserAccounts>
163
+ <AdministratorPassword>
164
+ <Value><%= @host.unattend_pass 'AdministratorPassword' %></Value>
165
+ <PlainText>false</PlainText>
166
+ </AdministratorPassword>
167
+ </UserAccounts>
168
+ <AutoLogon>
169
+ <Password>
170
+ <Value><%= @host.unattend_pass 'Password' %></Value>
171
+ <PlainText>false</PlainText>
172
+ </Password>
173
+ <Domain><%= @host.shortname -%></Domain>
174
+ <LogonCount>1</LogonCount>
175
+ <Enabled>true</Enabled>
176
+ <Username>administrator</Username>
177
+ </AutoLogon>
178
+ <FirstLogonCommands>
179
+ <SynchronousCommand wcm:action="add">
180
+ <CommandLine>powershell.exe -noprofile -executionpolicy bypass -command &quot;&amp;{Start-Service W32Time -ErrorAction SilentlyContinue; .\w32tm.exe /resync}&quot;</CommandLine>
181
+ <Description>Start Time Service</Description>
182
+ <Order>1</Order>
183
+ <RequiresUserInput>false</RequiresUserInput>
184
+ </SynchronousCommand>
185
+ <SynchronousCommand wcm:action="add">
186
+ <CommandLine>powershell.exe -noprofile -executionpolicy bypass -command &quot;&amp;{iex ((New-Object System.Net.WebClient).DownloadString(&apos;https://chocolatey.org/install.ps1&apos;))}&quot;</CommandLine>
187
+ <Description>Install Chocolatey</Description>
188
+ <Order>10</Order>
189
+ <RequiresUserInput>false</RequiresUserInput>
190
+ </SynchronousCommand>
191
+ <SynchronousCommand wcm:action="add">
192
+ <CommandLine>powershell.exe -noprofile -executionpolicy bypass -command &quot;&amp;{C:\ProgramData\chocolatey\choco.exe install puppet-agent -y}&quot;</CommandLine>
193
+ <Description>Install Puppet</Description>
194
+ <Order>11</Order>
195
+ <RequiresUserInput>false</RequiresUserInput>
196
+ </SynchronousCommand>
197
+ <%- if host_param_true?('run-puppet-in-installer') -%>
198
+ <SynchronousCommand wcm:action="add">
199
+ <CommandLine>powershell.exe -noprofile -executionpolicy bypass -command &quot;&amp;{C:\Program Files\Puppet Labs\Puppet\bin\puppet agent --onetime --tags no_such_tag --server <%= @host.puppetmaster %> --no-daemonize}&quot;</CommandLine>
200
+ <Description>First Puppet run</Description>
201
+ <Order>11</Order>
202
+ <RequiresUserInput>false</RequiresUserInput>
203
+ </SynchronousCommand>
204
+ <%- end -%>
205
+ <SynchronousCommand wcm:action="add">
206
+ <CommandLine>powershell.exe -noprofile -ExecutionPolicy Bypass -Command &quot;&amp;{invoke-webrequest -Uri <%= foreman_url("built") -%>}&quot;</CommandLine>
207
+ <Description>Report built</Description>
208
+ <Order>100</Order>
209
+ <RequiresUserInput>false</RequiresUserInput>
210
+ </SynchronousCommand>
211
+ <SynchronousCommand wcm:action="add">
212
+ <CommandLine>powershell.exe -noprofile -executionpolicy bypass -command &quot;&amp;{Restart-Computer}&quot;</CommandLine>
213
+ <Description>Restart computer</Description>
214
+ <Order>1000</Order>
215
+ <RequiresUserInput>false</RequiresUserInput>
216
+ </SynchronousCommand>
217
+ </FirstLogonCommands>
218
+ </component>
219
+ </settings>
220
+ </unattend>
@@ -0,0 +1,43 @@
1
+ <%#
2
+ name: Windows default
3
+ snippet: false
4
+ model: Ptable
5
+ oses:
6
+ - Windows
7
+ -%>
8
+ <DiskConfiguration>
9
+ <Disk wcm:action="add">
10
+ <CreatePartitions>
11
+ <CreatePartition wcm:action="add">
12
+ <Order>1</Order>
13
+ <Type>Primary</Type>
14
+ <Size>350</Size>
15
+ </CreatePartition>
16
+ <CreatePartition wcm:action="add">
17
+ <Order>2</Order>
18
+ <Type>Primary</Type>
19
+ <Size>122880</Size>
20
+ </CreatePartition>
21
+ </CreatePartitions>
22
+ <ModifyPartitions>
23
+ <ModifyPartition wcm:action="add">
24
+ <Order>1</Order>
25
+ <PartitionID>1</PartitionID>
26
+ <Label>System Reserved</Label>
27
+ <Format>NTFS</Format>
28
+ <Active>true</Active>
29
+ </ModifyPartition>
30
+ <ModifyPartition wcm:action="add">
31
+ <Active>false</Active>
32
+ <Extend>false</Extend>
33
+ <Format>NTFS</Format>
34
+ <Label>SYSTEM</Label>
35
+ <Letter>C</Letter>
36
+ <Order>2</Order>
37
+ <PartitionID>2</PartitionID>
38
+ </ModifyPartition>
39
+ </ModifyPartitions>
40
+ <DiskID>0</DiskID>
41
+ <WillWipeDisk>true</WillWipeDisk>
42
+ </Disk>
43
+ </DiskConfiguration>
@@ -0,0 +1,12 @@
1
+ <%= javascript 'host_edit_extensions' %>
2
+ <% wds_facet = @host.wds_facet || @host.build_wds_facet -%>
3
+
4
+ <div id="wds_provisioning" <%= display? !@host.wds_build? %>>
5
+ <span id="wds_server_select">
6
+ <%= render 'wds_servers/server_select', item: wds_facet %>
7
+ </span>
8
+
9
+ <span id="wds_image_select">
10
+ <%= render 'wds_servers/image_select', item: wds_facet %>
11
+ </span>
12
+ </div>
@@ -0,0 +1,28 @@
1
+ <%= form_for @wds_server, :url => (@wds_server.new_record? ? wds_servers_path : wds_server_path(name: @wds_server.name)), html: {data: {id: @wds_server.try(:id)}} do |f| %>
2
+ <%= base_errors_for @wds_server %>
3
+ <ul class="nav nav-tabs" data-tabs="tabs">
4
+ <li class="active"><a href="#primary" data-toggle="tab"><%= _("Compute Resource") %></a></li>
5
+ <% if show_location_tab? %>
6
+ <li><a href="#locations" data-toggle="tab"><%= _("Locations") %></a></li>
7
+ <% end %>
8
+ <% if show_organization_tab? %>
9
+ <li><a href="#organizations" data-toggle="tab"><%= _("Organizations") %></a></li>
10
+ <% end %>
11
+ </ul>
12
+
13
+ <div class="tab-content">
14
+ <div class="tab-pane active" id="primary">
15
+ <%= text_f f, :name %>
16
+ <%= text_f f, :url, placeholder: 'http://host.example.com:5985/wsman' %>
17
+ <%= textarea_f f, :description, :rows => 3 %>
18
+ <%= text_f f, :user %>
19
+ <%= password_f f, :password %>
20
+
21
+ <!--<div class="col-md-2">&nbsp;</div><button class="btn btn-success col-md-2" type="button">Test connection</button>-->
22
+ <%= link_to_function _("Test Connection"), "testConnection(this)", :class => "btn #{@wds_server.test_connection.is_a?(FalseClass) ? "btn-default" : "btn-success"}", :'data-url' => test_connection_wds_servers_path %>
23
+ </div>
24
+ <%#= render 'taxonomies/loc_org_tabs', :f => f, :obj => @wds_server %>
25
+ </div>
26
+
27
+ <%= submit_or_cancel f %>
28
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <%= fields_for 'host[wds_facet_attributes]', item do |f| %>
2
+ <%-
3
+ host = @host || f.object.host
4
+ server = host.public_send(:wds_server) rescue nil
5
+ images = server.all_images rescue []
6
+ images = images.select { |img| img.matches_architecture? host.architecture } if host.architecture
7
+ boot_images = images.select { |img| img.is_a?(ForemanWds::WdsBootImage) }
8
+ install_images = images.select { |img| img.is_a?(ForemanWds::WdsInstallImage) }
9
+ install_images = install_images.select { |img| img.version == "#{host.operatingsystem.major}.#{host.operatingsystem.minor}" } if host.operatingsystem
10
+ -%>
11
+ <%= select_f f, :install_image_name, install_images, :name, :name,
12
+ { selected: item.install_image_name },
13
+ { label: _("WDS Install Image"),
14
+ disabled: install_images.empty?,
15
+ help_inline: :indicator,
16
+ required: true,
17
+ }
18
+ %>
19
+ <%= select_f f, :boot_image_name, boot_images, :name, :name,
20
+ { selected: item.boot_image_name || (boot_images.count == 1 && boot_images.first.name) },
21
+ { label: _("WDS Boot Image"),
22
+ disabled: boot_images.count < 2 ? true : false,
23
+ help_inline: :indicator,
24
+ required: true,
25
+ }
26
+ %>
27
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%= fields_for 'host[wds_facet_attributes]', item do |f| %>
2
+ <%-
3
+ wds_servers = WdsServer.all
4
+ -%>
5
+ <%= select_f f, :wds_server_id, wds_servers, :id, :name,
6
+ { include_blank: true },
7
+ { label: _('WDS Server'),
8
+ disabled: f.object.host.operatingsystem.nil? || f.object.host.operatingsystem.family != 'Windows',
9
+ help_inline: :indicator,
10
+ required: true,
11
+ onchange: 'wds_server_selected(this);', :'data-url' => method_path('wds_server_selected'), :'data-type' => controller_name.singularize } %>
12
+ <% end %>
@@ -0,0 +1,43 @@
1
+ <% title _('WDS Clients') %>
2
+
3
+ <table id="wds_clients" class="<%= table_css_classes %>" data-table='inline'>
4
+ <thead>
5
+ <tr>
6
+ <th><%= s_("Client|Name") %></th>
7
+ <th><%= s_("Client|Group") %></th>
8
+ <th><%= s_("Client|ID") %></th>
9
+ <th><%= s_("Client|Architecture") %></th>
10
+ <th><%= s_("Client|Boot Image") %></th>
11
+ <th><%= s_("Client|Unattend") %></th>
12
+ <th><%= s_("Client|Actions") %></th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <%- @clients.each do |client| -%>
17
+ <%-
18
+ host = begin
19
+ if client[:device_id] =~ /([0-9A-F]{2}-){5}[0-9A-F]{2}/
20
+ nic = Nic::Managed.find_by(mac: client[:device_id].downcase.tr('-', ':'))
21
+ nic && nic.host
22
+ else
23
+ Host::Managed.find_by(name: client[:device_name])
24
+ end
25
+ end
26
+ -%>
27
+ <tr>
28
+ <td><%= host ? link_to_if_authorized(client[:device_name], hash_for_host_path(:id => host)) : client[:device_name] %></td>
29
+ <td><%= client[:group] %></td>
30
+ <td><%= client[:device_id] %></td>
31
+ <td><%= client[:architecture] %></td>
32
+ <td><%= client[:boot_image_path] %></td>
33
+ <td><%= client[:wds_client_unattend] %></td>
34
+ <td><%= host && action_buttons(
35
+ display_link_if_authorized(
36
+ _('Delete'),
37
+ hash_for_delete_wds_client_wds_server_path(id: @wds_server, client: host)
38
+ )
39
+ ) %></td>
40
+ </tr>
41
+ <%- end -%>
42
+ </tbody>
43
+ </table>