Skip to content

Commit 4e2b79f

Browse files
authored
Merge pull request #215 from glennsarti/modules-4257-modify-instance-and-features
(MODULES-4257) Modify instance and features for SQL Server 2016
2 parents a8d7398 + dc14cb4 commit 4e2b79f

14 files changed

+215
-174
lines changed

README.md

+7-10
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,6 @@ This example creates the same MS SQL instance as shown above with additional opt
8080

8181
### Install SQL Server tools and features not specific to a SQL Server instance
8282

83-
```puppet
84-
sqlserver_features { 'Generic Features':
85-
source => 'E:/',
86-
features => ['Tools'],
87-
}
88-
```
89-
9083
```puppet
9184
sqlserver_features { 'Generic Features':
9285
source => 'E:/',
@@ -203,8 +196,10 @@ Default: 'present'.
203196
##### `features`
204197

205198
*Required.*
206-
207-
Specifies one or more features to manage. Valid options: 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS', and 'Tools' (the Tools feature includes SSMS, ADV_SSMS, and Conn).
199+
200+
Specifies one or more features to manage. Valid options: 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS', 'BOL', 'DREPLAY_CTLR', 'DREPLAY_CLT'.
201+
202+
The 'Tools' feature is deprecated. Instead specify 'BC', 'SSMS', 'ADV_SSMS', 'Conn', and 'SDK' explicitly.
208203

209204
##### `install_switches`
210205

@@ -294,7 +289,9 @@ Default: 'present'.
294289

295290
##### `features`
296291

297-
*Required.* Specifies one or more features to manage. The list of top-level features includes 'SQL', 'AS', and 'RS'. The 'SQL' feature includes the Database Engine, Replication, Full-Text, and Data Quality Services (DQS) server. Valid options: an array containing one or more of the strings 'SQL', 'SQLEngine', 'Replication', 'FullText', 'DQ', 'AS', and 'RS'.
292+
*Required.* Specifies one or more features to manage. The list of top-level features includes 'AS' and 'RS'. Valid options: an array containing one or more of the strings 'SQL', 'SQLEngine', 'Replication', 'FullText', 'DQ', 'AS', 'RS', 'POLYBASE', and 'ADVANCEDANALYTICS'.
293+
294+
The 'SQL' feature is deprecated. Instead specify 'DQ', 'FullText', 'Replication', and 'SQLEngine' explicitly.
298295

299296
##### `install_switches`
300297

lib/puppet/provider/sqlserver_features/mssql.rb

+22-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
require 'json'
22
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'sqlserver'))
3+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x/sqlserver/server_helper'))
4+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x/sqlserver/features'))
35

46
FEATURE_RESERVED_SWITCHES =
57
%w(AGTSVCACCOUNT AGTSVCPASSWORD ASSVCACCOUNT AGTSVCPASSWORD PID
@@ -10,18 +12,25 @@ def self.instances
1012
instances = []
1113
result = Facter.value(:sqlserver_features)
1214
debug "Parsing result #{result}"
13-
result = !result[SQL_2014].empty? ? result[SQL_2014] : result[SQL_2012]
14-
if !result.empty?
15-
existing_instance = {:name => "Generic Features",
16-
:ensure => :present,
17-
:features => result.sort
15+
16+
ALL_SQL_VERSIONS.each do |sql_version|
17+
next if result[sql_version].empty?
18+
instance_props = {:name => "Generic Features",
19+
:ensure => :present,
20+
:features => result[sql_version].sort
1821
}
19-
debug "Parsed features = #{existing_instance[:features]}"
22+
debug "Parsed features = #{instance_props[:features]}"
2023

21-
instance = new(existing_instance)
24+
instance = new(instance_props)
2225
debug "Created instance #{instance}"
2326
instances << instance
27+
28+
# Due to MODULES-5060 we can only output one feature set. If we output
29+
# multiple then it is not possible to install or uninstall due to multiple
30+
# resources with the same name.
31+
break
2432
end
33+
2534
instances
2635
end
2736

@@ -82,8 +91,7 @@ def create_temp_for_install_switch
8291
config_file = ["[OPTIONS]"]
8392
@resource[:install_switches].each_pair do |k, v|
8493
if FEATURE_RESERVED_SWITCHES.include? k
85-
warn("Reserved switch [#{k}] found for `install_switches`, please know the provided value
86-
may be overridden by some command line arguments")
94+
warn("Reserved switch [#{k}] found for `install_switches`, please know the provided value may be overridden by some command line arguments")
8795
end
8896
if v.is_a?(Numeric) || (v.is_a?(String) && v =~ /^(true|false|1|0)$/i)
8997
config_file << "#{k}=#{v}"
@@ -111,7 +119,11 @@ def create
111119
warn "Uninstalling all sql server features not tied into an instance because an empty array was passed, please use ensure absent instead."
112120
destroy
113121
else
114-
installNet35(@resource[:windows_feature_source])
122+
instance_version = PuppetX::Sqlserver::ServerHelper.sql_version_from_install_source(@resource[:source])
123+
Puppet.debug("Installation source detected as version #{instance_version}") unless instance_version.nil?
124+
125+
installNet35(@resource[:windows_feature_source]) unless instance_version == SQL_2016
126+
115127
debug "Installing features #{@resource[:features]}"
116128
add_features(@resource[:features])
117129
@property_hash[:features] = @resource[:features]

lib/puppet/provider/sqlserver_instance/mssql.rb

+8-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'json'
22
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'sqlserver'))
3-
3+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x/sqlserver/server_helper'))
4+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x/sqlserver/features'))
45

56
INSTANCE_RESERVED_SWITCHES =
67
%w(AGTSVCACCOUNT AGTSVCPASSWORD ASSVCACCOUNT AGTSVCPASSWORD PID
@@ -70,19 +71,12 @@ def create
7071
warn "Uninstalling all features for instance #{@resource[:name]} because an empty array was passed, please use ensure absent instead."
7172
destroy
7273
else
73-
installNet35(@resource[:windows_feature_source])
74+
instance_version = PuppetX::Sqlserver::ServerHelper.sql_version_from_install_source(@resource[:source])
75+
Puppet.debug("Installation source detected as version #{instance_version}") unless instance_version.nil?
76+
77+
installNet35(@resource[:windows_feature_source]) unless instance_version == SQL_2016
78+
7479
add_features(@resource[:features])
75-
# cmd_args = build_cmd_args(@resource[:features])
76-
# begin
77-
# config_file = create_temp_for_install_switch
78-
# cmd_args << "/ConfigurationFile=\"#{config_file.path}\"" unless config_file.nil?
79-
# try_execute(cmd_args)
80-
# ensure
81-
# if config_file
82-
# config_file.close
83-
# config_file.unlink
84-
# end
85-
# end
8680
end
8781
end
8882

@@ -91,8 +85,7 @@ def create_temp_for_install_switch
9185
config_file = ["[OPTIONS]"]
9286
@resource[:install_switches].each_pair do |k, v|
9387
if INSTANCE_RESERVED_SWITCHES.include? k
94-
warn("Reserved switch [#{k}] found for `install_switches`, please know the provided value
95-
may be overridden by some command line arguments")
88+
warn("Reserved switch [#{k}] found for `install_switches`, please know the provided value may be overridden by some command line arguments")
9689
end
9790
if v.is_a?(Numeric) || (v.is_a?(String) && v =~ /^(true|false|1|0)$/i)
9891
config_file << "#{k}=#{v}"

lib/puppet/type/sqlserver_features.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636

3737
newproperty(:features, :array_matching => :all) do
3838
desc 'Specifies features to install, uninstall, or upgrade. The list of top-level features include
39-
Tools, BC, Conn, SSMS, ADV_SSMS, SDK, IS and MDS. The Tools feature will install Management
40-
Tools, Books online components, SQL Server Data Tools, and other shared components.'
41-
newvalues(:Tools, :BC, :Conn, :SSMS, :ADV_SSMS, :SDK, :IS, :MDS)
39+
BC, Conn, SSMS, ADV_SSMS, SDK, IS and MDS.'
40+
newvalues(:Tools, :BC, :Conn, :SSMS, :ADV_SSMS, :SDK, :IS, :MDS, :BOL, :DREPLAY_CTLR, :DREPLAY_CLT)
4241
munge do |value|
4342
if PuppetX::Sqlserver::ServerHelper.is_super_feature(value)
43+
Puppet.deprecation_warning("Using #{value} is deprecated for features in sql_features resources")
4444
PuppetX::Sqlserver::ServerHelper.get_sub_features(value).collect { |v| v.to_s }
4545
else
4646
value

lib/puppet/type/sqlserver_instance.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@
2727

2828
newproperty(:features, :array_matching => :all) do
2929
desc 'Specifies features to install, uninstall, or upgrade. The list of top-level features include
30-
SQL, SQLEngine, Replication, FullText, DQ AS, and RS. The SQL feature will install the Database Engine,
31-
Replication, Full-Text, and Data Quality Services (DQS) server.'
32-
newvalues(:SQL, :SQLEngine, :Replication, :FullText, :DQ, :AS, :RS)
30+
SQLEngine, Replication, FullText, DQ AS, and RS.'
31+
newvalues(:SQL, :SQLEngine, :Replication, :FullText, :DQ, :AS, :RS, :POLYBASE, :ADVANCEDANALYTICS)
3332
munge do |value|
3433
if PuppetX::Sqlserver::ServerHelper.is_super_feature(value)
34+
Puppet.deprecation_warning("Using #{value} is deprecated for features in sql_instance resources")
3535
PuppetX::Sqlserver::ServerHelper.get_sub_features(value).collect { |v| v.to_s }
3636
else
3737
value

lib/puppet_x/sqlserver/features.rb

+68-22
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
11
SQL_2012 ||= 'SQL_2012'
22
SQL_2014 ||= 'SQL_2014'
3+
SQL_2016 ||= 'SQL_2016'
4+
5+
ALL_SQL_VERSIONS = [SQL_2012, SQL_2014, SQL_2016]
36

47
module PuppetX
58
module Sqlserver
69
# https://msdn.microsoft.com/en-us/library/ms143786.aspx basic feature docs
710
class Features
811
private
912

10-
SQL_WMI_PATH ||= {
11-
SQL_2012 => 'ComputerManagement11',
12-
SQL_2014 => 'ComputerManagement12',
13+
SQL_CONFIGURATION ||= {
14+
SQL_2012 => {
15+
:major_version => 11,
16+
:wmi_path => 'ComputerManagement11',
17+
:registry_path => '110',
18+
},
19+
SQL_2014 => {
20+
:major_version => 12,
21+
:wmi_path => 'ComputerManagement12',
22+
:registry_path => '120',
23+
},
24+
SQL_2016 => {
25+
:major_version => 13,
26+
:wmi_path => 'ComputerManagement13',
27+
:registry_path => '130',
28+
}
1329
}
1430

1531
SQL_REG_ROOT ||= 'Software\Microsoft\Microsoft SQL Server'
@@ -20,11 +36,12 @@ class Features
2036

2137
def self.connect(version)
2238
require 'win32ole'
23-
ver = SQL_WMI_PATH[version]
39+
ver = SQL_CONFIGURATION[version][:wmi_path]
2440
context = WIN32OLE.new('WbemScripting.SWbemNamedValueSet')
2541
context.Add("__ProviderArchitecture", 64)
2642
locator = WIN32OLE.new('WbemScripting.SWbemLocator')
27-
locator.ConnectServer('', "root/Microsoft/SqlServer/#{ver}", '', '', nil, nil, nil, context)
43+
# WMI Path must use backslashes. Ruby 2.3 can cause crashes with forward slashes
44+
locator.ConnectServer(nil, "root\\Microsoft\\SqlServer\\#{ver}", nil, nil, nil, nil, nil, context)
2845
end
2946

3047
def self.get_parent_path(key_path)
@@ -118,9 +135,11 @@ def self.get_instance_features(reg_root, instance_name)
118135
# also reg Replication/IsInstalled set to 1
119136
'SQL_Replication_Core_Inst' => 'Replication', # SQL Server Replication
120137
# also WMI: SqlService WHERE SQLServiceType = 1 # MSSQLSERVER
121-
'SQL_Engine_Core_Inst' => 'SQLEngine', # Database Engine Services
122-
'SQL_FullText_Adv' => 'FullText', # Full-Text and Semantic Extractions for Search
123-
'SQL_DQ_Full' => 'DQ', # Data Quality Services
138+
'SQL_Engine_Core_Inst' => 'SQLEngine', # Database Engine Services
139+
'SQL_FullText_Adv' => 'FullText', # Full-Text and Semantic Extractions for Search
140+
'SQL_DQ_Full' => 'DQ', # Data Quality Services
141+
'sql_inst_mr' => 'ADVANCEDANALYTICS', # R Services (In-Database)
142+
'SQL_Polybase_Core_Inst' => 'POLYBASE', # PolyBase Query Service for External Data
124143
}
125144

126145
feat_root = "#{reg_root}\\ConfigurationState"
@@ -144,18 +163,25 @@ def self.get_instance_features(reg_root, instance_name)
144163

145164
def self.get_shared_features(version)
146165
shared_features = {
147-
'Connectivity_Full' => 'Conn', # Client Tools Connectivity
148-
'SDK_Full' => 'SDK', # Client Tools SDK
149-
'MDSCoreFeature' => 'MDS', # Master Data Services
150-
'Tools_Legacy_Full' => 'BC', # Client Tools Backwards Compatibility
151-
'SQL_SSMS_Full' => 'ADV_SSMS', # Management Tools - Complete
152-
'SQL_SSMS_Adv' => 'SSMS', # Management Tools - Basic
166+
'Connectivity_Full' => 'Conn', # Client Tools Connectivity
167+
'SDK_Full' => 'SDK', # Client Tools SDK
168+
'MDSCoreFeature' => 'MDS', # Master Data Services
169+
'Tools_Legacy_Full' => 'BC', # Client Tools Backwards Compatibility
170+
'SQL_SSMS_Full' => 'ADV_SSMS', # Management Tools - Complete (Does not exist in SQL 2016)
171+
'SQL_SSMS_Adv' => 'SSMS', # Management Tools - Basic (Does not exist in SQL 2016)
172+
'SQL_DQ_CLIENT_Full' => 'DQC', # Data Quality Client
173+
'SQL_BOL_Components' => 'BOL', # Documentation Components
174+
'SQL_DReplay_Controller' => 'DREPLAY_CTLR', # Distributed Replay Controller
175+
'SQL_DReplay_Client' => 'DREPLAY_CLT', # Distributed Replay Client
176+
'sql_shared_mr' => 'SQL_SHARED_MR', # R Server (Standalone)
177+
153178
# also WMI: SqlService WHERE SQLServiceType = 4 # MsDtsServer
154-
'SQL_DTS_Full' => 'IS', # Integration Services
179+
'SQL_DTS_Full' => 'IS', # Integration Services
155180
# currently ignoring Reporting Services Shared
181+
# currently ignoring R Server Standalone
156182
}
157183

158-
reg_ver = (version == SQL_2014 ? '120' : '110')
184+
reg_ver = SQL_CONFIGURATION[version][:registry_path]
159185
reg_root = "#{SQL_REG_ROOT}\\#{reg_ver}\\ConfigurationState"
160186

161187
get_sql_reg_val_features(reg_root, shared_features)
@@ -185,11 +211,27 @@ def self.get_shared_features(version)
185211
# }
186212
# }
187213
def self.get_instances
188-
version_instance_map = [SQL_2012, SQL_2014]
214+
version_instance_map = ALL_SQL_VERSIONS
189215
.map do |version|
216+
major_version = SQL_CONFIGURATION[version][:major_version]
217+
190218
instances = get_instance_names_by_ver(version)
191219
.map { |name| [ name, get_instance_info(version, name) ] }
192220

221+
# Instance names are unique on a single host, but not for a particular SQL Server version therefore
222+
# it's possible to request information for a valid instance_name but not for version. In this case
223+
# we just reject any instances that have no information
224+
instances.reject! { |value| value[1].nil? }
225+
226+
# Unfortunately later SQL versions can return previous version SQL instances. We can weed these out
227+
# by inspecting the major version of the instance_version
228+
instances.reject! do |value|
229+
return true if value[1]['version'].nil?
230+
ver = Gem::Version.new(value[1]['version'])
231+
# Segment 0 is the major version number of the SQL Instance
232+
ver.segments[0] != major_version
233+
end
234+
193235
[ version, Hash[instances] ]
194236
end
195237

@@ -203,10 +245,9 @@ def self.get_instances
203245
# "SQL_2014" => []
204246
# }
205247
def self.get_features
206-
{
207-
SQL_2012 => get_shared_features(SQL_2012),
208-
SQL_2014 => get_shared_features(SQL_2014),
209-
}
248+
features = {}
249+
ALL_SQL_VERSIONS.each { |version| features[version] = get_shared_features(version) }
250+
features
210251
end
211252

212253
# returns a hash containing instance details
@@ -225,8 +266,13 @@ def self.get_features
225266
# "RS"
226267
# ]
227268
# }
228-
def self.get_instance_info(version = SQL_2014, instance_name)
269+
def self.get_instance_info(version, instance_name)
270+
return nil if version.nil?
229271
sql_instance = get_wmi_instance_info(version, instance_name)
272+
# Instance names are unique on a single host, but not for a particular SQL Server version therefore
273+
# it's possible to request information for a valid instance_name but not for version. In this case
274+
# we just return nil.
275+
return nil if sql_instance['reg_root'].nil?
230276
feats = get_instance_features(sql_instance['reg_root'], sql_instance['name'])
231277
sql_instance.merge({'features' => feats})
232278
end

lib/puppet_x/sqlserver/server_helper.rb

+27
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,33 @@ def self.is_domain_or_local_user?(user, hostname)
2121
true
2222
end
2323
end
24+
25+
# Returns either SQL_2016, SQL_2014 or SQL_2012 if it can determine the SQL Version from the install source
26+
# Returns nil if it can not be determined
27+
def self.sql_version_from_install_source(source_dir)
28+
# Attempt to read the Mediainfo.xml file in the root of the install media
29+
media_file = File.expand_path("#{source_dir}/MediaInfo.xml")
30+
return nil unless File.exist?(media_file)
31+
# As we don't have a XML parser easily, just use simple text matching to find the following XML element. This
32+
# also means we can just ignore BOM markers etc.
33+
# <Property Id="BaselineVersion" Value="xx.yyy.zz." />
34+
content = File.read(media_file)
35+
index1 = content.index('"BaselineVersion"')
36+
return nil if index1.nil?
37+
index2 = content.index('/>',index1)
38+
return nil if index2.nil?
39+
content = content.slice(index1 + 18, index2 - index1 - 18)
40+
# Extract the version number from the text snippet
41+
# Value="xx.yyy.zz."
42+
ver = content.match('"(.+)"')
43+
return nil if ver.nil?
44+
45+
return SQL_2016 if ver[1].start_with?('13.')
46+
return SQL_2014 if ver[1].start_with?('12.')
47+
return SQL_2012 if ver[1].start_with?('11.')
48+
49+
nil
50+
end
2451
end
2552
end
2653
end

0 commit comments

Comments
 (0)