module Chef::Util::DSC::LocalConfigurationManager::Parser

Public Class Methods

parse(lcm_output, test_dsc_configuration) click to toggle source

Parses the output from LCM and returns a list of Chef::Util::DSC::ResourceInfo objects that describe how the resources affected the system

Example for WhatIfParser:

parse <<-EOF
What if: [Machine]: LCM: [Start Set      ]
What if: [Machine]: LCM: [Start Resource ] [[File]FileToNotBeThere]
What if: [Machine]: LCM: [Start Set      ] [[File]FileToNotBeThere]
What if:                                   [C:\ShouldNotExist.txt] removed
What if: [Machine]: LCM: [End Set        ] [[File]FileToNotBeThere] in 0.1 seconds
What if: [Machine]: LCM: [End Resource   ] [[File]FileToNotBeThere]
What if: [Machine]: LCM: [End Set        ]
EOF

would return

[
  Chef::Util::DSC::ResourceInfo.new(
    '[[File]FileToNotBeThere]',
    true,
    [
      '[[File]FileToNotBeThere]',
      '[C:\Shouldnotexist.txt]',
      '[[File]FileToNotBeThere] in 0.1 seconds'
    ]
  )
]

Example for TestDSCParser:

parse <<-EOF
InDesiredState            : False
ResourcesInDesiredState   :
ResourcesNotInDesiredState: {[Environment]texteditor}
ReturnValue               : 0
PSComputerName            : .
EOF

would return

[
  Chef::Util::DSC::ResourceInfo.new(
    '{[Environment]texteditor}',
    true,
    [
    ]
  )
]
# File lib/chef/util/dsc/lcm_output_parser.rb, line 77
def self.parse(lcm_output, test_dsc_configuration)
  test_dsc_configuration ? test_dsc_parser(lcm_output) : what_if_parser(lcm_output)
end
test_dsc_parser(lcm_output) click to toggle source
# File lib/chef/util/dsc/lcm_output_parser.rb, line 81
def self.test_dsc_parser(lcm_output)
  lcm_output ||= ""
  current_resource = Hash.new

  resources = []
  lcm_output.lines.each do |line|
    op_action , op_value = line.strip.split(":")
    op_action&.strip!
    case op_action
    when "InDesiredState"
      current_resource[:skipped] = op_value.strip == "True" ? true : false
    when "ResourcesInDesiredState"
      current_resource[:name] = op_value.strip if op_value
    when "ResourcesNotInDesiredState"
      current_resource[:name] = op_value.strip if op_value
    when "ReturnValue"
      current_resource[:context] = nil
    end
  end
  if current_resource[:name]
    resources.push(current_resource)
  end

  if resources.length > 0
    build_resource_info(resources)
  else
    raise Chef::Exceptions::LCMParser, "Could not parse:\n#{lcm_output}"
  end
end
what_if_parser(lcm_output) click to toggle source
# File lib/chef/util/dsc/lcm_output_parser.rb, line 111
def self.what_if_parser(lcm_output)
  lcm_output ||= ""
  current_resource = Hash.new

  resources = []
  lcm_output.lines.each do |line|
    op_action, op_type, info = parse_line(line)

    case op_action
    when :start
      case op_type
      when :set
        if current_resource[:name]
          current_resource[:context] = :logging
          current_resource[:logs] = [info]
        end
      when :resource
        if current_resource[:name]
          resources.push(current_resource)
        end
        current_resource = { name: info }
      else
        Chef::Log.trace("Ignoring op_action #{op_action}: Read line #{line}")
      end
    when :end
      # Make sure we log the last line
      if current_resource[:context] == :logging && info.include?(current_resource[:name])
        current_resource[:logs].push(info)
      end
      current_resource[:context] = nil
    when :skip
      current_resource[:skipped] = true
    when :info
      if current_resource[:context] == :logging
        current_resource[:logs].push(info)
      end
    end
  end

  if current_resource[:name]
    resources.push(current_resource)
  end

  if resources.length > 0
    build_resource_info(resources)
  else
    raise Chef::Exceptions::LCMParser, "Could not parse:\n#{lcm_output}"
  end
end

Private Class Methods

build_resource_info(resources) click to toggle source
# File lib/chef/util/dsc/lcm_output_parser.rb, line 181
def self.build_resource_info(resources)
  resources.map do |r|
    Chef::Util::DSC::ResourceInfo.new(r[:name], !r[:skipped], r[:logs])
  end
end
parse_line(line) click to toggle source
# File lib/chef/util/dsc/lcm_output_parser.rb, line 161
def self.parse_line(line)
  if match = line.match(/^.*?:.*?:\s*LCM:\s*\[(.*?)\](.*)/)
      # If the line looks like
      # What If: [machinename]: LCM: [op_action op_type] message
      # extract op_action, op_type, and message
    operation, info = match.captures
    op_action, op_type = operation.strip.split(" ").map { |m| m.downcase.to_sym }
  else
    op_action = op_type = :info
    if match = line.match(/^.*?:.*?: \s+(.*)/)
      info = match.captures[0]
    else
      info = line
    end
  end
  info.strip! # Because this was formatted for humans
  [op_action, op_type, info]
end