Skip to content

(FM-2303, FM-2790, FM-2445) WMI / Registry impl of Discovery Report #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

Iristyle
Copy link
Contributor

  • Provide a nearly drop-in replacement for the existing SQL discovery
    report based on sqlcmd.exe - but by using WMI queries and Registry
    spleunking. This code:
    • does not shell to PowerShell
    • does not call sqlcmd.exe
    • does not parse XML
    • runs all code in the Ruby process
  • As a result:
    • it's a bit quicker to execute
    • it doesn't require any heuristics to find files
    • it doesn't need to write / secure temporary files
  • Of note, the public API on PuppetX::SqlServer::Features is rather
    small and comprises only 3 public methods:
    • get_instances - returns a detailed hash for both SQL_2012 and
      SQL_2014 that contains instances and their features
    • get_features - returns a hash of SQL_2012 / SQL_2014 to the
      installed shared features
    • get_instance_info(version, instance_name) - returns a hash for a
      given instance with a given version. Called by get_installations
    • The shape of the data returned is slightly different than what
      run_discovery_report previously used, but it was close to a drop-in
      replacement.
    • Note that since the module doesn't specify a SQL version for install
      and the current code is not set to handle multiple versions. The
      Features.get_installations returns info for both SQL_2014 and
      SQL_2012 should they both be installed. For the sake of consumers
      of the discovery_script code path, only feature data for one
      version of SQL will be installed, and SQL_2014 is prioritized.

@Iristyle
Copy link
Contributor Author

Supercedes #83

@Iristyle Iristyle changed the title (FM-2303) WMI / Registry impl of Activity Report (WIP) (FM-2303) WMI / Registry impl of Activity Report May 27, 2015
@Iristyle
Copy link
Contributor Author

Need to verify 2nd commit - but ready for a look @cyberious. I believe this should effectively reproduce the behavior you were looking for.

@Iristyle Iristyle force-pushed the ticket/master/FM-2303-feature-hash branch 3 times, most recently from 17cc9b6 to 95a6d5d Compare May 27, 2015 23:18
@Iristyle Iristyle changed the title (WIP) (FM-2303) WMI / Registry impl of Activity Report (FM-2303) WMI / Registry impl of Activity Report May 27, 2015
@Iristyle Iristyle changed the title (FM-2303) WMI / Registry impl of Activity Report (FM-2303, FM-2790) WMI / Registry impl of Activity Report May 27, 2015
@Iristyle
Copy link
Contributor Author

@cyberious / @ferventcoder whenever you get a chance - I think this guy should be pretty good to go.

@Iristyle Iristyle force-pushed the ticket/master/FM-2303-feature-hash branch from 95a6d5d to 5a5946f Compare May 27, 2015 23:37
@Iristyle Iristyle changed the title (FM-2303, FM-2790) WMI / Registry impl of Activity Report (FM-2303, FM-2790) WMI / Registry impl of Discovery Report May 27, 2015
@cyberious
Copy link
Contributor

Debug: /Stage[main]/Win2012_sql2012/Sql_2012_instance[MSSQLSERVER]/Registry::Value[Hide Instance-MSSQLSERVER]/Registry_value[HKLM:\SOFTWARE\Microsoft\
Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQLServer\SuperSocketNetLib\HideInstance]: Autorequiring Registry_key[HKLM:\SOFTWARE\Microsoft\Microsoft SQ
L Server\MSSQL11.MSSQLSERVER\MSSQLServer\SuperSocketNetLib]
Info: Applying configuration version '1432771206'
Debug: Prefetching mssql resources for sqlserver_features
Could not retrieve fact='sqlserver_installs', resolution='<anonymous>': The system cannot find the file specified.
Debug: Puppet::Type::Sqlserver_features::ProviderMssql: Parsing result
Error: Could not prefetch sqlserver_features provider 'mssql': undefined method `[]' for nil:NilClass
Debug: /Stage[main]/Sql_2012_features/Sqlserver_features[Generic]: Nothing to manage: no ensure and the resource doesn't exist
Debug: Failed to load library 'msgpack' for feature 'msgpack'
Debug: Puppet::Network::Format[msgpack]: feature msgpack is missing
Debug: file_metadata supports formats: pson b64_zlib_yaml yaml raw
Debug: Closing connection for https://puppet:8140
Debug: Creating new connection for https://puppet:8140
Debug: Starting connection for https://puppet:8140
Debug: Caching connection for https://puppet:8140
Debug: /Stage[main]/Puppet_enterprise::Mcollective::Server::Plugins/File[C:\ProgramData/PuppetLabs/mcollective/etc/plugins/mcollective/agent/puppetd.r
b]: Nothing to manage: no ensure and the resource doesn't exist
Debug: /Stage[main]/Puppet_enterprise::Mcollective::Server::Plugins/File[C:\ProgramData/PuppetLabs/mcollective/etc/plugins/mcollective/agent/puppetd.d
dl]: Nothing to manage: no ensure and the resource doesn't exist
Debug: /Stage[main]/Puppet_enterprise::Mcollective::Server::Plugins/File[C:\ProgramData/PuppetLabs/mcollective/etc/plugins/mcollective/application/pup
petd.rb]: Nothing to manage: no ensure and the resource doesn't exist
Debug: /Stage[main]/Puppet_enterprise::Mcollective::Server::Facter/File[C:\ProgramData/PuppetLabs/facter/etc/facts.d/puppet_enterprise_installer.txt]:
 Nothing to manage: no ensure and the resource doesn't exist
Debug: /Stage[main]/Puppet_enterprise::Profile::Mcollective::Agent/File[C:\ProgramData/PuppetLabs/mcollective/etc/ssl/clients/mcollective-public.pem]:
 Nothing to manage: no ensure and the resource doesn't exist
Debug: Service[pe-mcollective](provider=windows): Service pe-mcollective is running
Debug: Service[pe-mcollective](provider=windows): Service pe-mcollective start type is auto start
Debug: Prefetching mssql resources for sqlserver_instance
Could not retrieve fact='sqlserver_installs', resolution='<anonymous>': The system cannot find the file specified.
Debug: Puppet::Type::Sqlserver_instance::ProviderMssql: Parsing result
Error: Could not prefetch sqlserver_instance provider 'mssql': undefined method `[]' for nil:NilClass
Debug: Executing 'C:\Windows\system32\WindowsPowershell\v1.0\powershell.exe Install-WindowsFeature  NET-Framework-Core

@cyberious
Copy link
Contributor

puppet resource sqlserver_instance on existing nodes

PS C:\Users\puppet> puppet resource sqlserver_instance
Could not retrieve fact='sqlserver_installs', resolution='<anonymous>': The system cannot find the file specified.
Error: Could not run: undefined method `[]' for nil:NilClass
PS C:\Users\puppet> puppet resource sqlserver_instance --trace
Could not retrieve fact='sqlserver_installs', resolution='<anonymous>': The system cannot find the file specified.
Error: Could not run: undefined method `[]' for nil:NilClass
C:/ProgramData/PuppetLabs/puppet/var/lib/puppet/provider/sqlserver_instance/mssql.rb:14:in `instances'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/type.rb:1148:in `block in instances'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/type.rb:1141:in `collect'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/type.rb:1141:in `instances'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/indirector/resource/ral.rb:25:in `search'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/indirector/indirection.rb:267:in `search'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/application/resource.rb:225:in `find_or_save_resources'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/application/resource.rb:142:in `main'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/application.rb:389:in `run_command'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/application.rb:381:in `block (2 levels) in run'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/application.rb:507:in `plugin_hook'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/application.rb:381:in `block in run'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/util.rb:488:in `exit_on_fail'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/application.rb:381:in `run'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/util/command_line.rb:146:in `run'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/lib/puppet/util/command_line.rb:92:in `execute'
C:/Program Files/Puppet Labs/Puppet Enterprise/puppet/bin/puppet:8:in `<main>'

@Iristyle
Copy link
Contributor Author

I'm not seeing that issue @cyberious

  • How is it installed?
  • Can you do some output with --debug?
  • Is that first run a puppet apply?

@Iristyle
Copy link
Contributor Author

I've been manually copying the module code over VMs that have been setup for acceptance testing.

To verify the Facter facts, I've been adding the sqlserver modules lib/facter directory to FACTERLIB like so:

set FACTERLIB=C:/ProgramData/PuppetLabs/puppet/etc/modules/sqlserver/lib/facter

And then I can run facter normally to see something like

sqlserver_installs => {"SQL_2012"=>{}, "SQL_2014"=>{"MSSQLSERVER"=>{"name"=>"MSSQLSERVER", "version_friendly"=>"SQL_2014", "version"=>"12.0.2000.8", "reg_root"=>"Software\\Microsoft\\Microsoft SQL Server\\MSSQL12.MSSQLSERVER", "features"=>["Replication", "SQLEngine", "FullText", "DQ", "AS", "RS"]}, "MSSQLSERVER2"=>{"name"=>"MSSQLSERVER2", "version_friendly"=>"SQL_2014", "version"=>"12.0.2000.8", "reg_root"=>"Software\\Microsoft\\Microsoft SQL Server\\MSSQL12.MSSQLSERVER2", "features"=>["Replication", "SQLEngine", "FullText", "DQ", "AS", "RS"]}, "features"=>["Conn", "SDK", "MDS", "BC", "ADV_SSMS", "SSMS", "IS"]}}

sqlserver_instance_names => {"SQL_2012"=>[], "SQL_2014"=>["MSSQLSERVER", "MSSQLSERVER2"]}

Running puppet resource I get:

C:\Program Files\Puppet Labs\Puppet Enterprise\bin>puppet resource sqlserver_instance
sqlserver_instance { 'MSSQLSERVER':
  ensure   => 'present',
  features => ['AS', 'DQ', 'FullText', 'RS', 'Replication', 'SQLEngine'],
}
sqlserver_instance { 'MSSQLSERVER2':
  ensure   => 'present',
  features => ['AS', 'DQ', 'FullText', 'RS', 'Replication', 'SQLEngine'],
}

And

C:\Program Files\Puppet Labs\Puppet Enterprise\bin>puppet resource sqlserver_features
sqlserver_features { 'Generic Features':
  ensure   => 'present',
  features => ['ADV_SSMS', 'BC', 'Conn', 'IS', 'MDS', 'SDK', 'SSMS'],
}

Since it looks like there's a failure in generating the Facter fact in your tests, we should start there and see if

  • the fact is not even resolving
  • there's a bug in the code when resolving the fact that throws an exception (seems more likely)

@cyberious
Copy link
Contributor

This is all with puppet agent -t and not using the puppet apply

@cyberious
Copy link
Contributor

The only facts being reported to the master are {"SQL_2012":["MSSQLSERVER","SECONDARY"],"SQL_2014":[]}

@cyberious
Copy link
Contributor

added stack trace in ticket as it is easier to track

require 'win32/registry'

installed = reg_key_feat_hash.select do |subkey, feat_name|
Win32::Registry::HKEY_LOCAL_MACHINE.open("#{key_name}\\#{subkey}") do |feat_key|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requires a begin rescue in case subkey does not exist. I tried and this resolved the issue with not returning anything, facter swallowed the error and says there is nothing to return which is the nil class exception

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 begin
   Win32::Registry::HKEY_LOCAL_MACHINE.open("#{key_name}\\#{subkey}") do |feat_key|
        get_reg_key_val(feat_key, instance_name, Win32::Registry::REG_SZ)
  end
rescue Exception => e
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, looks like we both came to the same conclusion on this one. I forgot that I was going to do this - and failed to test the scenario where both instances had OLAP off.

I was able to repro locally the crash, and the fix - and I repushed with code that rescues the Win32::Registry::Error. In the other spot where there's a rescue I changed the error to be more specific as well.

@Iristyle Iristyle force-pushed the ticket/master/FM-2303-feature-hash branch from 5a5946f to 896aa16 Compare May 28, 2015 06:29
@Iristyle
Copy link
Contributor Author

We should be in better shape now @cyberious - thanks for catching that bug.

@cyberious
Copy link
Contributor

@Iristyle updates worked, however now we are not idempotent if we have

sqlserver_features { 'Generic': 
  ensure => absent,
}

when doing ``puppet resource sqlserver_features` we are getting

sqlserver_features { 'Generic Features':
  ensure => 'present',
}

Comment no longer valid.

:features =>
PuppetX::Sqlserver::ServerHelper.translate_features(
jsonResult['Generic Features']).sort!
:features => result['features'].sort
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should set :ensure => result['features'].empty? ? :absent : :present,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cyberious Did the previous PowerShell based Discovery Report code omit 'Generic Features' completely from the hash when empty?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, which is why we had the `has_key?['features']``

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if we do what you're suggesting, we end up following a different code path. To mimic the existing behavior we'd want

if !result['features'].empty?

instead of

if result.has_key?('features')

Correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@Iristyle Iristyle force-pushed the ticket/master/FM-2303-feature-hash branch 4 times, most recently from 4fd3734 to 30e7597 Compare June 1, 2015 23:13
@Iristyle Iristyle changed the title (FM-2303, FM-2790) WMI / Registry impl of Discovery Report (FM-2303, FM-2790, FM-2445) WMI / Registry impl of Discovery Report Jun 1, 2015
features = ['Tools', 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS']

before(:all) do
names = eval(fact_on(host, 'sqlserver_instance_names')).values.flatten
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the test change in b2e989d to introduce fact_on, a new failure has cropped up. I'm guessing there's an issue with Beaker being able to retrieve facts from modules - not sure if this can be fixed with facter -p, but I'm reluctant to do that given there is no facter -p in cfacter.

The actual Beaker code that implements the facter call is at https://github.com/puppetlabs/beaker/blob/0ac1460dc3af4d4e10062333c8d931351157d71d/lib/beaker/dsl/wrappers.rb#L15-L20

Failures:

  1) sqlserver_features with no installed instances can install all possible features
     Failure/Error: names = eval(fact_on(host, 'sqlserver_instance_names')).values.flatten
     NoMethodError:
       undefined method `values' for nil:NilClass

     # ./spec/acceptance/sqlserver_features_spec.rb:244:in `block (4 levels) in <top (required)>'

Finished in 52 minutes 8 seconds (files took 17 minutes 47 seconds to load)
13 examples, 1 failure, 1 pending

@kylog / @peterhuene / @MikaelSmith / @anodelman any feedback here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've temporarily removed the usage of fact_on, so I'm OK with this being merged as-is - with a solution for fact_on in a subsequent commit @cyberious

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fact_on by default uses no arguments, and it's true that facter -p is being deprecated. One way to test would be to point --custom-dir at the module's lib/facter directory, but that will only work with Facter 3. So the only way that works with modules is to use puppet facts, and parse the json output.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if you look at the Beaker code above @MikaelSmith - you can actually pass in a hash with :environment - so I can use the FACTERLIB (which is something I'm testing now) -- but it seems a little hokey to jam in #{distmoduledir}/sqlserver/lib/facter. Undecided on that approach as of yet.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FACT-1015 is related to this, and may lead to a new solution for Beaker.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI - my test worked through Beaker - see commit at Iristyle@c184ed9

Not sure yet if this the way forward or not....

@Iristyle Iristyle force-pushed the ticket/master/FM-2303-feature-hash branch from 30e7597 to 21f7bca Compare June 2, 2015 06:02
instances = instance_names
.map { |name| [ name, get_instance_info(version, name) ] }

instances.push(['features', get_shared_features(version, SQL_REG_ROOT)])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be wrapped in a try as retrieving could throw an error.

@Iristyle Iristyle force-pushed the ticket/master/FM-2303-feature-hash branch from 21f7bca to 0923382 Compare June 2, 2015 21:08
 - Previously, tests relied on the assumption that SQL Server instances
   were installed already.  Add a new test that verifies all shared
   features can be installed through a manifest, even with no SQL
   instances installed.

 - This test is necessary to validate the reimplementation of the
   underlying feature detection code from using sqlcmd.exe to using
   a WMI / Registry approach.
@Iristyle Iristyle force-pushed the ticket/master/FM-2303-feature-hash branch 4 times, most recently from 69dd9a0 to 62410a0 Compare June 3, 2015 06:06
Iristyle added 5 commits June 3, 2015 00:46
 - Provide a nearly drop-in replacement for the existing SQL discovery
   report based on sqlcmd.exe - but by using WMI queries and Registry
   spleunking.  This code:

   - does not shell to PowerShell
   - does not call sqlcmd.exe
   - does not parse XML
   - runs all code in the Ruby process

 - As a result:

    - it's a bit quicker to execute
    - it doesn't require any heuristics to find files
    - it doesn't need to write / secure temporary files
    - it can use native Ruby symbols

 - Of note, the public API on PuppetX::SqlServer::Features is rather
   small and comprises only 3 public methods:

   - get_instances - returns a hash of all instance specific details
     for both SQL_2012 and SQL_2014 instances munged together given
     in a side by side SQL install, name collisions are not allowed

   - get_features - returns a hash of SQL_2012 / SQL_2014 shared
     features

   - get_instance_info(version, instance_name) - returns a hash for a
     given instance with a given version.  Called by get_instances

 - The shape of the data returned is slightly different than what
   run_discovery_report previously used, but it was close to a drop-in
   replacement.

 - Note that since the module doesn't specify a SQL version for install
   and the current code is not set to handle multiple versions.  The
   Features.get_instances returns info for both SQL_2014 and
   SQL_2012 should they both be installed.  For the sake of consumers
   of the discovery_script code path, only feature shared feature data
   for one version of SQL will be installed, with SQL_2014 prioritized.

 - Note that when run under Puppet 32-bit, the flag is passed for
   Registry access to ensure that the right hive is used.  Furthermore,
   the WMI scripting access passes the 64-bit flag, so that WMI data
   can be queried correctly for 64-bit SQL server from a 32-bit Ruby
   process.
 - Fact 'sqlserver_instances' contains all detailed instance information
   and is not segregated by SQL version given instance names are unique
   even in a side by side installation
 - Fact 'sqlserver_features' contains all shared features
   segregated by SQL version
 - run_discovery_script method has been completely removed in favor
   of leveraging Facts

 - note that to make the output compatible with Facter, the Ruby symbols
   used in the hashes returned from PuppetX::Sqlserver::Features have
   all been changed to bare strings
 - Now that Ruby code is used to build the description of a current
   installation, it's no longer necessary to perform string translation
   from SQL feature names such as 'SQL Server Replication' to their
   command line switch counterparts.  This should remove any concerns
   around localization since command line switches are universal.
 - Remove code that's no longer needed as a result.
 - Fact values for sqlserver_instances and sqlserver_features now
   contain these command line switches instead of human-friendly
   feature names.
 - According to the MSDN documentation for the 'SQL' feature
   https://msdn.microsoft.com/en-us/library/ms144259.aspx#Feature

   The following feature parameters should be included:

   BC
   Conn
   SSMS
   ADV_SSMS
   SDK
 - Previously the test that removes instance was hard-coded to use
   'MSSQLSERVER', but this makes the test a bit more fragile.

   Instead, use Beaker to load the agents facts, passing the
   module sqlserver/lib/facter directory in via FACTERLIB so that
   the names of all instances can be retrieved.
@Iristyle Iristyle force-pushed the ticket/master/FM-2303-feature-hash branch from 62410a0 to 24d7d8a Compare June 3, 2015 07:47
require 'win32ole'
ver = SQL_WMI_PATH[version]
context = WIN32OLE.new('WbemScripting.SWbemNamedValueSet')
context.Add("__ProviderArchitecture", 64)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cyberious this is the solution for the 32-bit Puppet agent not seeing the 64-bit SQL servers. WMI has redirection as well (since it maps over the registry), so the solution was to always force it look at 64-bit data, since we only support SQL 64-bit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find 👍 fixed issue on 32, testing further to see if anything else shakes out but I think we are good to go now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for testing with the 32-bit installer - otherwise this would have never been caught.

@Iristyle
Copy link
Contributor Author

Iristyle commented Jun 3, 2015

FYI @cyberious - this code has passed via a local beaker acceptance run, and via Jenkins CI (aside from the unrelated future parser issues). So as long as it passes your manual verification, it should be good to go. Finally!

cyberious added a commit that referenced this pull request Jun 3, 2015
(FM-2303, FM-2790, FM-2445) WMI / Registry impl of Discovery Report
@cyberious cyberious merged commit 83a1091 into puppetlabs:master Jun 3, 2015
@Iristyle Iristyle deleted the ticket/master/FM-2303-feature-hash branch June 3, 2015 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants