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
@@ -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>